Add Chromium-only Blender WebEngine parity work
This commit is contained in:
54
blender-5.2.0/source/blender/python/bmesh/CMakeLists.txt
Normal file
54
blender-5.2.0/source/blender/python/bmesh/CMakeLists.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
bmesh_py_api.cc
|
||||
bmesh_py_geometry.cc
|
||||
bmesh_py_ops.cc
|
||||
bmesh_py_ops_call.cc
|
||||
bmesh_py_types.cc
|
||||
bmesh_py_types_customdata.cc
|
||||
bmesh_py_types_meshdata.cc
|
||||
bmesh_py_types_select.cc
|
||||
bmesh_py_utils.cc
|
||||
|
||||
bmesh_py_api.hh
|
||||
bmesh_py_geometry.hh
|
||||
bmesh_py_ops.hh
|
||||
bmesh_py_ops_call.hh
|
||||
bmesh_py_types.hh
|
||||
bmesh_py_types_customdata.hh
|
||||
bmesh_py_types_meshdata.hh
|
||||
bmesh_py_types_select.hh
|
||||
bmesh_py_utils.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
bf_python_mathutils
|
||||
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
)
|
||||
|
||||
if(WITH_FREESTYLE)
|
||||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
if(WITH_GMP)
|
||||
add_definitions(-DWITH_GMP)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_python_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
234
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.cc
Normal file
234
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.cc
Normal file
@@ -0,0 +1,234 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the 'bmesh' module.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
#include "bmesh_py_types_customdata.hh"
|
||||
#include "bmesh_py_types_meshdata.hh"
|
||||
#include "bmesh_py_types_select.hh"
|
||||
|
||||
#include "bmesh_py_geometry.hh"
|
||||
#include "bmesh_py_ops.hh"
|
||||
#include "bmesh_py_utils.hh"
|
||||
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_mesh_types.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "bmesh_py_api.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_new_doc,
|
||||
".. function:: new(*, use_operators=True)\n"
|
||||
"\n"
|
||||
" :param use_operators: Support calling operators in :mod:`bmesh.ops` (uses some "
|
||||
"extra memory per vert/edge/face).\n"
|
||||
" :type use_operators: bool\n"
|
||||
" :return: Return a new, empty BMesh.\n"
|
||||
" :rtype: :class:`bmesh.types.BMesh`\n");
|
||||
static PyObject *bpy_bm_new(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static const char *kwlist[] = {"use_operators", nullptr};
|
||||
BMesh *bm;
|
||||
|
||||
bool use_operators = true;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args, kw, "|$O&:new", const_cast<char **>(kwlist), PyC_ParseBool, &use_operators))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BMeshCreateParams params{};
|
||||
params.use_toolflags = use_operators;
|
||||
bm = BM_mesh_create(&bm_mesh_allocsize_default, ¶ms);
|
||||
bm->selectmode = SCE_SELECT_VERTEX;
|
||||
|
||||
return BPy_BMesh_CreatePyObject(bm, BPY_BMFLAG_NOP);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_from_edit_mesh_doc,
|
||||
".. function:: from_edit_mesh(mesh)\n"
|
||||
"\n"
|
||||
" Return a BMesh from this mesh, currently the mesh must already be in editmode.\n"
|
||||
"\n"
|
||||
" :param mesh: The editmode mesh.\n"
|
||||
" :type mesh: :class:`bpy.types.Mesh`\n"
|
||||
" :return: the BMesh associated with this mesh.\n"
|
||||
" :rtype: :class:`bmesh.types.BMesh`\n");
|
||||
static PyObject *bpy_bm_from_edit_mesh(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BMesh *bm;
|
||||
Mesh *mesh = static_cast<Mesh *>(PyC_RNA_AsPointer(value, "Mesh"));
|
||||
|
||||
if (mesh == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mesh->runtime->edit_mesh == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "The mesh must be in editmode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bm = mesh->runtime->edit_mesh->bm;
|
||||
|
||||
return BPy_BMesh_CreatePyObject(bm, BPY_BMFLAG_IS_WRAPPED);
|
||||
}
|
||||
|
||||
void EDBM_update_extern(Mesh *mesh, const bool do_tessface, const bool is_destructive);
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_update_edit_mesh_doc,
|
||||
".. function:: update_edit_mesh(mesh, *, loop_triangles=True, destructive=True)\n"
|
||||
"\n"
|
||||
" Update the mesh after changes to the BMesh in editmode,\n"
|
||||
" optionally recalculating n-gon tessellation.\n"
|
||||
"\n"
|
||||
" :param mesh: The editmode mesh.\n"
|
||||
" :type mesh: :class:`bpy.types.Mesh`\n"
|
||||
" :param loop_triangles: Option to recalculate n-gon tessellation.\n"
|
||||
" :type loop_triangles: bool\n"
|
||||
" :param destructive: Use when geometry has been added or removed.\n"
|
||||
" :type destructive: bool\n");
|
||||
static PyObject *bpy_bm_update_edit_mesh(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static const char *kwlist[] = {"mesh", "loop_triangles", "destructive", nullptr};
|
||||
PyObject *py_me;
|
||||
Mesh *mesh;
|
||||
bool do_loop_triangles = true;
|
||||
bool is_destructive = true;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args,
|
||||
kw,
|
||||
"O|$O&O&:update_edit_mesh",
|
||||
const_cast<char **>(kwlist),
|
||||
&py_me,
|
||||
PyC_ParseBool,
|
||||
&do_loop_triangles,
|
||||
PyC_ParseBool,
|
||||
&is_destructive))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mesh = static_cast<Mesh *>(PyC_RNA_AsPointer(py_me, "Mesh"));
|
||||
|
||||
if (mesh == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mesh->runtime->edit_mesh == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "The mesh must be in editmode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
EDBM_update_extern(mesh, do_loop_triangles, is_destructive);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef BPy_BM_methods[] = {
|
||||
{"new",
|
||||
reinterpret_cast<PyCFunction>(bpy_bm_new),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_bm_new_doc},
|
||||
{"from_edit_mesh",
|
||||
static_cast<PyCFunction>(bpy_bm_from_edit_mesh),
|
||||
METH_O,
|
||||
bpy_bm_from_edit_mesh_doc},
|
||||
{"update_edit_mesh",
|
||||
reinterpret_cast<PyCFunction>(bpy_bm_update_edit_mesh),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_bm_update_edit_mesh_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BPy_BM_doc,
|
||||
"This module provides access to Blender's bmesh data structures.\n"
|
||||
"\n"
|
||||
".. include:: include__bmesh.rst\n");
|
||||
static PyModuleDef BPy_BM_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bmesh",
|
||||
/*m_doc*/ BPy_BM_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ BPy_BM_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_bmesh()
|
||||
{
|
||||
PyObject *mod;
|
||||
PyObject *submodule;
|
||||
PyObject *sys_modules = PyImport_GetModuleDict();
|
||||
|
||||
BPy_BM_init_types();
|
||||
BPy_BM_init_types_select();
|
||||
BPy_BM_init_types_customdata();
|
||||
BPy_BM_init_types_meshdata();
|
||||
|
||||
mod = PyModule_Create(&BPy_BM_module_def);
|
||||
|
||||
/* bmesh.types */
|
||||
PyModule_AddObject(mod, "types", (submodule = BPyInit_bmesh_types()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
/* bmesh.ops (not a real module, exposes module like access). */
|
||||
PyModule_AddObject(mod, "ops", (submodule = BPyInit_bmesh_ops()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "utils", (submodule = BPyInit_bmesh_utils()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "geometry", (submodule = BPyInit_bmesh_geometry()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.hh
Normal file
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the 'bmesh.geometry' module.
|
||||
* Utility functions for operating on 'bmesh.types'
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "bmesh_py_geometry.hh" /* own include */
|
||||
#include "bmesh_py_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_geometry_intersect_face_point_doc,
|
||||
".. function:: intersect_face_point(face, point)\n"
|
||||
"\n"
|
||||
" Tests if the projection of a point is inside a face (using the face's normal).\n"
|
||||
"\n"
|
||||
" :param face: The face to test.\n"
|
||||
" :type face: :class:`bmesh.types.BMFace`\n"
|
||||
" :param point: The 3D point to test.\n"
|
||||
" :type point: tuple[float, float, float] | Sequence[float]\n"
|
||||
" :return: True when the projection of the point is in the face.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *bpy_bm_geometry_intersect_face_point(BPy_BMFace * /*self*/, PyObject *args)
|
||||
{
|
||||
BPy_BMFace *py_face;
|
||||
PyObject *py_point;
|
||||
float point[3];
|
||||
bool ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!O:intersect_face_point", &BPy_BMFace_Type, &py_face, &py_point)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_OBJ(py_face);
|
||||
if (mathutils_array_parse(point, 3, 3, py_point, "intersect_face_point") == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret = BM_face_point_inside_test(py_face->f, point);
|
||||
|
||||
return PyBool_FromLong(ret);
|
||||
}
|
||||
|
||||
static PyMethodDef BPy_BM_geometry_methods[] = {
|
||||
{"intersect_face_point",
|
||||
reinterpret_cast<PyCFunction>(bpy_bm_geometry_intersect_face_point),
|
||||
METH_VARARGS,
|
||||
bpy_bm_geometry_intersect_face_point_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BPy_BM_utils_doc,
|
||||
"This module provides access to bmesh geometry evaluation functions.\n");
|
||||
static PyModuleDef BPy_BM_geometry_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bmesh.geometry",
|
||||
/*m_doc*/ BPy_BM_utils_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ BPy_BM_geometry_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_bmesh_geometry()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&BPy_BM_geometry_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_geometry();
|
||||
|
||||
} // namespace blender
|
||||
286
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.cc
Normal file
286
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.cc
Normal file
@@ -0,0 +1,286 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the 'bmesh.ops' module.
|
||||
* Operators from 'opdefines' are wrapped.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_ops.hh" /* own include */
|
||||
#include "bmesh_py_ops_call.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* bmesh operator 'bmesh.ops.*' callable types
|
||||
* ******************************************* */
|
||||
|
||||
static PyObject *bpy_bmesh_op_repr(BPy_BMeshOpFunc *self)
|
||||
{
|
||||
return PyUnicode_FromFormat("<%.200s bmesh.ops.%.200s()>", Py_TYPE(self)->tp_name, self->opname);
|
||||
}
|
||||
|
||||
/* methods
|
||||
* ======= */
|
||||
|
||||
/* __doc__
|
||||
* ------- */
|
||||
|
||||
static char *bmp_slots_as_args(const BMOSlotType slot_types[BMO_OP_MAX_SLOTS], const bool is_out)
|
||||
{
|
||||
DynStr *dyn_str = BLI_dynstr_new();
|
||||
char *ret;
|
||||
bool quoted;
|
||||
bool set;
|
||||
|
||||
int i = 0;
|
||||
|
||||
while (*slot_types[i].name) {
|
||||
quoted = false;
|
||||
set = false;
|
||||
/* Cut off `.out` by using a string size argument. */
|
||||
const int name_len = is_out ? (strchr(slot_types[i].name, '.') - slot_types[i].name) :
|
||||
sizeof(slot_types[i].name);
|
||||
const char *value = "<Unknown>";
|
||||
switch (slot_types[i].type) {
|
||||
case BMO_OP_SLOT_BOOL:
|
||||
value = "False";
|
||||
break;
|
||||
case BMO_OP_SLOT_INT:
|
||||
if (slot_types[i].subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_ENUM) {
|
||||
value = slot_types[i].enum_flags[0].identifier;
|
||||
quoted = true;
|
||||
}
|
||||
else if (slot_types[i].subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_FLAG) {
|
||||
value = "";
|
||||
set = true;
|
||||
}
|
||||
else {
|
||||
value = "0";
|
||||
}
|
||||
break;
|
||||
case BMO_OP_SLOT_FLT:
|
||||
value = "0.0";
|
||||
break;
|
||||
case BMO_OP_SLOT_PTR:
|
||||
value = "None";
|
||||
break;
|
||||
case BMO_OP_SLOT_MAT:
|
||||
value = "Matrix()";
|
||||
break;
|
||||
case BMO_OP_SLOT_VEC:
|
||||
value = "Vector()";
|
||||
break;
|
||||
case BMO_OP_SLOT_ELEMENT_BUF:
|
||||
value = (slot_types[i].subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) ? "None" : "[]";
|
||||
break;
|
||||
case BMO_OP_SLOT_MAPPING:
|
||||
value = "{}";
|
||||
break;
|
||||
}
|
||||
BLI_dynstr_appendf(dyn_str,
|
||||
i ? ", %.*s=%s%s%s%s%s" : "%.*s=%s%s%s%s%s",
|
||||
name_len,
|
||||
slot_types[i].name,
|
||||
set ? "{" : "",
|
||||
quoted ? "'" : "",
|
||||
value,
|
||||
quoted ? "'" : "",
|
||||
set ? "}" : "");
|
||||
i++;
|
||||
}
|
||||
|
||||
ret = BLI_dynstr_get_cstring(dyn_str);
|
||||
BLI_dynstr_free(dyn_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmesh_op_doc_get(BPy_BMeshOpFunc *self, void * /*closure*/)
|
||||
{
|
||||
PyObject *ret;
|
||||
char *slot_in;
|
||||
char *slot_out;
|
||||
int i;
|
||||
|
||||
i = BMO_opcode_from_opname(self->opname);
|
||||
|
||||
slot_in = bmp_slots_as_args(bmo_opdefines[i]->slot_types_in, false);
|
||||
slot_out = bmp_slots_as_args(bmo_opdefines[i]->slot_types_out, true);
|
||||
|
||||
ret = PyUnicode_FromFormat("%.200s bmesh.ops.%.200s(bmesh, %s)\n -> dict(%s)",
|
||||
Py_TYPE(self)->tp_name,
|
||||
self->opname,
|
||||
slot_in,
|
||||
slot_out);
|
||||
|
||||
MEM_delete(slot_in);
|
||||
MEM_delete(slot_out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmesh_op_getseters[] = {
|
||||
{"__doc__",
|
||||
reinterpret_cast<getter>(bpy_bmesh_op_doc_get),
|
||||
static_cast<setter>(nullptr),
|
||||
nullptr,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
/* Types
|
||||
* ===== */
|
||||
|
||||
static PyTypeObject bmesh_op_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "BMeshOpFunc",
|
||||
/*tp_basicsize*/ sizeof(BPy_BMeshOpFunc),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ nullptr,
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ reinterpret_cast<reprfunc>(bpy_bmesh_op_repr),
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ reinterpret_cast<ternaryfunc>(BPy_BMO_call),
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ nullptr,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ nullptr,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ bpy_bmesh_op_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/* bmesh module 'bmesh.ops'
|
||||
* ************************ */
|
||||
|
||||
static PyObject *bpy_bmesh_op_CreatePyObject(const char *opname)
|
||||
{
|
||||
BPy_BMeshOpFunc *self = PyObject_New(BPy_BMeshOpFunc, &bmesh_op_Type);
|
||||
|
||||
self->opname = opname;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmesh_ops_module_getattro(PyObject * /*self*/, PyObject *pyname)
|
||||
{
|
||||
const char *opname = PyUnicode_AsUTF8(pyname);
|
||||
|
||||
if (BMO_opcode_from_opname(opname) != -1) {
|
||||
return bpy_bmesh_op_CreatePyObject(opname);
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_AttributeError, "BMeshOpsModule: operator \"%.200s\" doesn't exist", opname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmesh_ops_module_dir(PyObject * /*self*/)
|
||||
{
|
||||
const uint tot = bmo_opdefines_total;
|
||||
uint i;
|
||||
PyObject *ret;
|
||||
|
||||
ret = PyList_New(bmo_opdefines_total);
|
||||
|
||||
for (i = 0; i < tot; i++) {
|
||||
PyList_SET_ITEM(ret, i, PyUnicode_FromString(bmo_opdefines[i]->opname));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef BPy_BM_ops_methods[] = {
|
||||
{"__getattr__", static_cast<PyCFunction>(bpy_bmesh_ops_module_getattro), METH_O, nullptr},
|
||||
{"__dir__", reinterpret_cast<PyCFunction>(bpy_bmesh_ops_module_dir), METH_NOARGS, nullptr},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BPy_BM_ops_doc,
|
||||
"Access to BMesh operators.");
|
||||
static PyModuleDef BPy_BM_ops_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bmesh.ops",
|
||||
/*m_doc*/ BPy_BM_ops_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ BPy_BM_ops_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_bmesh_ops()
|
||||
{
|
||||
PyObject *submodule = PyModule_Create(&BPy_BM_ops_module_def);
|
||||
|
||||
if (PyType_Ready(&bmesh_op_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.hh
Normal file
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_ops();
|
||||
|
||||
} // namespace blender
|
||||
870
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops_call.cc
Normal file
870
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops_call.cc
Normal file
@@ -0,0 +1,870 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file provides __call__ aka BPy_BMO_call for
|
||||
* the bmesh operator and has been given its own file
|
||||
* because argument conversion is involved.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_ops_call.hh" /* own include */
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
BLI_STATIC_ASSERT(sizeof(PyC_FlagSet) == sizeof(BMO_FlagSet), "size mismatch");
|
||||
|
||||
static int bpy_bm_op_as_py_error(BMesh *bm)
|
||||
{
|
||||
if (BMO_error_occurred_at_level(bm, BMO_ERROR_FATAL)) {
|
||||
/* NOTE: we could have multiple errors. */
|
||||
const char *errmsg;
|
||||
if (BMO_error_get(bm, &errmsg, nullptr, nullptr)) {
|
||||
PyErr_Format(PyExc_RuntimeError, "bmesh operator: %.200s", errmsg);
|
||||
BMO_error_clear(bm);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Utility function to check BMVert/BMEdge/BMFace's
|
||||
*
|
||||
* \param value:
|
||||
* \param bm: Check the \a value against this.
|
||||
* \param htype: Test \a value matches this type.
|
||||
* \param descr: Description text.
|
||||
*/
|
||||
static int bpy_slot_from_py_elem_check(BPy_BMElem *value,
|
||||
BMesh *bm,
|
||||
const char htype,
|
||||
/* for error messages */
|
||||
const char *opname,
|
||||
const char *slot_name,
|
||||
const char *descr)
|
||||
{
|
||||
if (!BPy_BMElem_Check(value) || !(value->ele->head.htype & htype)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, expected a %.200s not *.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
descr,
|
||||
BPy_BMElem_StringFromHType(htype),
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
if (value->bm == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s invalidated element",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
if (value->bm != bm) { /* we may want to make this check optional by setting 'bm' to nullptr */
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s invalidated element",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Utility function to check BMVertSeq/BMEdgeSeq/BMFaceSeq's
|
||||
*
|
||||
* \param value: Caller must check its a BMeshSeq
|
||||
* \param bm: Check the \a value against this.
|
||||
* \param htype_py: The type(s) of \a value.
|
||||
* \param htype_bmo: The type(s) supported by the target slot.
|
||||
* \param descr: Description text.
|
||||
*/
|
||||
static int bpy_slot_from_py_elemseq_check(BPy_BMGeneric *value,
|
||||
BMesh *bm,
|
||||
const char htype_py,
|
||||
const char htype_bmo,
|
||||
/* for error messages */
|
||||
const char *opname,
|
||||
const char *slot_name,
|
||||
const char *descr)
|
||||
{
|
||||
if (value->bm == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, invalidated sequence",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
if (value->bm != bm) { /* we may want to make this check optional by setting 'bm' to nullptr */
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, invalidated sequence",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
if ((htype_py & htype_bmo) == 0) {
|
||||
char str_bmo[32];
|
||||
char str_py[32];
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, expected "
|
||||
"a sequence of %.200s not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
descr,
|
||||
BPy_BMElem_StringFromHType_ex(htype_bmo, str_bmo),
|
||||
BPy_BMElem_StringFromHType_ex(htype_py, str_py));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use for giving py args to an operator.
|
||||
*/
|
||||
static int bpy_slot_from_py(BMesh *bm,
|
||||
BMOperator *bmop,
|
||||
BMOpSlot *slot,
|
||||
PyObject *value,
|
||||
/* the are just for exception messages */
|
||||
const char *opname,
|
||||
const char *slot_name)
|
||||
{
|
||||
switch (slot->slot_type) {
|
||||
case BMO_OP_SLOT_BOOL: {
|
||||
const int param = PyC_Long_AsBool(value);
|
||||
|
||||
if (param == -1) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected True/False or 0/1, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_BOOL(slot) = param;
|
||||
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_INT: {
|
||||
if (slot->slot_subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_ENUM) {
|
||||
int enum_val = -1;
|
||||
PyC_FlagSet *items = reinterpret_cast<PyC_FlagSet *>(slot->data.enum_data.flags);
|
||||
const char *enum_str = PyUnicode_AsUTF8(value);
|
||||
|
||||
if (enum_str == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected a string, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (PyC_FlagSet_ValueFromID(items, enum_str, &enum_val, slot_name) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_INT(slot) = enum_val;
|
||||
}
|
||||
else if (slot->slot_subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_FLAG) {
|
||||
int flag = 0;
|
||||
PyC_FlagSet *items = reinterpret_cast<PyC_FlagSet *>(slot->data.enum_data.flags);
|
||||
|
||||
if (PyC_FlagSet_ToBitfield(items, value, &flag, slot_name) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_INT(slot) = flag;
|
||||
}
|
||||
else {
|
||||
const int param = PyC_Long_AsI32(value);
|
||||
|
||||
if (param == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected an int, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_INT(slot) = param;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_FLT: {
|
||||
const float param = PyFloat_AsDouble(value);
|
||||
if (param == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected a float, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_FLOAT(slot) = param;
|
||||
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_MAT: {
|
||||
/* XXX: BMesh operator design is crappy here, operator slot should define matrix size,
|
||||
* not the caller! */
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_ParseAny(value, &pymat)) {
|
||||
return -1;
|
||||
}
|
||||
const ushort size = pymat->col_num;
|
||||
if ((size != pymat->row_num) || !ELEM(size, 3, 4)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected a 3x3 or 4x4 matrix",
|
||||
opname,
|
||||
slot_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_mat_set(bmop, bmop->slots_in, slot_name, pymat->matrix, size);
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_VEC: {
|
||||
/* passing slot name here is a bit non-descriptive */
|
||||
if (mathutils_array_parse(BMO_SLOT_AS_VECTOR(slot), 3, 3, value, slot_name) == -1) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_ELEMENT_BUF: {
|
||||
if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(value),
|
||||
bm,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"single element") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_single(
|
||||
bmop, slot, &(reinterpret_cast<BPy_BMElem *>(value))->ele->head);
|
||||
}
|
||||
else {
|
||||
/* there are many ways we could interpret arguments, for now...
|
||||
* - verts/edges/faces from the mesh direct,
|
||||
* this way the operator takes every item.
|
||||
* - `TODO` a plain python sequence (list) of elements.
|
||||
* - `TODO` an iterator. eg.
|
||||
* face.verts
|
||||
* - `TODO` (type, flag) pair, eg.
|
||||
* ('VERT', {'TAG'})
|
||||
*/
|
||||
|
||||
if (BPy_BMVertSeq_Check(value)) {
|
||||
if (bpy_slot_from_py_elemseq_check(reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
BM_VERT,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_VERT);
|
||||
}
|
||||
else if (BPy_BMEdgeSeq_Check(value)) {
|
||||
if (bpy_slot_from_py_elemseq_check(reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
BM_EDGE,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_EDGE);
|
||||
}
|
||||
else if (BPy_BMFaceSeq_Check(value)) {
|
||||
if (bpy_slot_from_py_elemseq_check(reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
BM_FACE,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_FACE);
|
||||
}
|
||||
|
||||
else if (BPy_BMElemSeq_Check(value)) {
|
||||
BMIter iter;
|
||||
BMHeader *ele;
|
||||
int tot;
|
||||
uint i;
|
||||
|
||||
if (bpy_slot_from_py_elemseq_check(
|
||||
reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
bm_iter_itype_htype_map[(reinterpret_cast<BPy_BMElemSeq *>(value))->itype],
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
/* this will loop over all elements which is a shame but
|
||||
* we need to know this before alloc */
|
||||
/* calls bpy_bmelemseq_length() */
|
||||
tot = Py_TYPE(value)->tp_as_sequence->sq_length(value);
|
||||
|
||||
BMO_slot_buffer_alloc(bmop, bmop->slots_in, slot_name, tot);
|
||||
|
||||
i = 0;
|
||||
BM_ITER_BPY_BM_SEQ (ele, &iter, ((BPy_BMElemSeq *)value)) {
|
||||
slot->data.buf[i] = ele;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/* keep this last */
|
||||
else if (PySequence_Check(value)) {
|
||||
BMElem **elem_array = nullptr;
|
||||
Py_ssize_t elem_array_len;
|
||||
|
||||
elem_array = static_cast<BMElem **>(
|
||||
BPy_BMElem_PySeq_As_Array(&bm,
|
||||
value,
|
||||
0,
|
||||
PY_SSIZE_T_MAX,
|
||||
&elem_array_len,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
true,
|
||||
true,
|
||||
slot_name));
|
||||
|
||||
/* error is set above */
|
||||
if (elem_array == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_buffer_alloc(bmop, bmop->slots_in, slot_name, elem_array_len);
|
||||
memcpy(slot->data.buf, elem_array, sizeof(void *) * elem_array_len);
|
||||
PyMem_FREE(elem_array);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a bmesh sequence, list, (htype, flag) pair, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_MAPPING: {
|
||||
/* first check types */
|
||||
if (slot->slot_subtype.map != BMO_OP_SLOT_SUBTYPE_MAP_EMPTY) {
|
||||
if (!PyDict_Check(value)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!PySet_Check(value)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a set, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (slot->slot_subtype.map) {
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_ELEM: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_value),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid value in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_map_elem_insert(bmop,
|
||||
slot,
|
||||
(reinterpret_cast<BPy_BMElem *>(arg_key))->ele,
|
||||
(reinterpret_cast<BPy_BMElem *>(arg_value))->ele);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_FLT: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
float value_f;
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
value_f = PyFloat_AsDouble(arg_value);
|
||||
|
||||
if (value_f == -1.0f && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict with float values, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(arg_value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_map_float_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele, value_f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INT: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
int value_i;
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
value_i = PyC_Long_AsI32(arg_value);
|
||||
|
||||
if (value_i == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict with int values, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(arg_value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_map_int_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele, value_i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_BOOL: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
int value_i;
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
value_i = PyC_Long_AsI32(arg_value);
|
||||
|
||||
if (value_i == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict with bool values, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(arg_value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_map_bool_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele, value_i != 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY: {
|
||||
if (PySet_GET_SIZE(value) > 0) {
|
||||
PyObject *it = PyObject_GetIter(value);
|
||||
PyObject *arg_key;
|
||||
while ((arg_key = PyIter_Next(it))) {
|
||||
/* Borrow from the set. */
|
||||
Py_DECREF(arg_key);
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in set") == -1)
|
||||
{
|
||||
/* Error is set in #bpy_slot_from_py_elem_check(). */
|
||||
break;
|
||||
}
|
||||
|
||||
BMO_slot_map_empty_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele);
|
||||
}
|
||||
Py_DECREF(it);
|
||||
if (arg_key) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL: {
|
||||
/* can't convert from these */
|
||||
PyErr_Format(PyExc_NotImplementedError,
|
||||
"This arguments mapping subtype %d is not supported",
|
||||
slot->slot_subtype.map);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* TODO: many others. */
|
||||
PyErr_Format(PyExc_NotImplementedError,
|
||||
"%.200s: keyword \"%.200s\" type %d not working yet!",
|
||||
opname,
|
||||
slot_name,
|
||||
slot->slot_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* all is well */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use for getting return values from an operator that's already executed.
|
||||
*
|
||||
* \note Don't throw any exceptions and should always return a valid (PyObject *).
|
||||
*/
|
||||
static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
|
||||
{
|
||||
PyObject *item = nullptr;
|
||||
|
||||
/* keep switch in same order as above */
|
||||
switch (slot->slot_type) {
|
||||
case BMO_OP_SLOT_BOOL:
|
||||
item = PyBool_FromLong(BMO_SLOT_AS_BOOL(slot));
|
||||
break;
|
||||
case BMO_OP_SLOT_INT:
|
||||
item = PyLong_FromLong(BMO_SLOT_AS_INT(slot));
|
||||
break;
|
||||
case BMO_OP_SLOT_FLT:
|
||||
item = PyFloat_FromDouble(double(BMO_SLOT_AS_FLOAT(slot)));
|
||||
break;
|
||||
case BMO_OP_SLOT_MAT:
|
||||
item = Matrix_CreatePyObject(
|
||||
reinterpret_cast<float *> BMO_SLOT_AS_MATRIX(slot), 4, 4, nullptr);
|
||||
break;
|
||||
case BMO_OP_SLOT_VEC:
|
||||
item = Vector_CreatePyObject(BMO_SLOT_AS_VECTOR(slot), slot->len, nullptr);
|
||||
break;
|
||||
case BMO_OP_SLOT_PTR:
|
||||
BLI_assert(0); /* currently we don't have any pointer return values in use */
|
||||
item = Py_NewRef(Py_None);
|
||||
break;
|
||||
case BMO_OP_SLOT_ELEMENT_BUF: {
|
||||
if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
|
||||
BMHeader *ele = static_cast<BMHeader *>(BMO_slot_buffer_get_single(slot));
|
||||
item = ele ? BPy_BMElem_CreatePyObject(bm, ele) : Py_NewRef(Py_None);
|
||||
}
|
||||
else {
|
||||
const int size = slot->len;
|
||||
void **buffer = BMO_SLOT_AS_BUFFER(slot);
|
||||
int j;
|
||||
|
||||
item = PyList_New(size);
|
||||
for (j = 0; j < size; j++) {
|
||||
BMHeader *ele = static_cast<BMHeader *>(buffer[j]);
|
||||
PyList_SET_ITEM(item, j, BPy_BMElem_CreatePyObject(bm, ele));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_MAPPING: {
|
||||
GHash *slot_hash = BMO_SLOT_AS_GHASH(slot);
|
||||
GHashIterator hash_iter;
|
||||
|
||||
switch (slot->slot_subtype.map) {
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_ELEM: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = BPy_BMElem_CreatePyObject(bm, static_cast<BMHeader *>(ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_FLT: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = PyFloat_FromDouble(*reinterpret_cast<float *>(&ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INT: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = PyLong_FromLong(*reinterpret_cast<int *>(&ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_BOOL: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = PyBool_FromLong(*reinterpret_cast<bool *>(&ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY: {
|
||||
item = PySet_New(nullptr);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
|
||||
PySet_Add(item, py_key);
|
||||
|
||||
Py_DECREF(py_key);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
|
||||
/* can't convert from these */
|
||||
item = Py_NewRef(Py_None);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
BLI_assert(item != nullptr);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
PyObject *ret;
|
||||
BPy_BMesh *py_bm;
|
||||
BMesh *bm;
|
||||
|
||||
BMOperator bmop;
|
||||
|
||||
if ((PyTuple_GET_SIZE(args) == 1) &&
|
||||
(py_bm = reinterpret_cast<BPy_BMesh *> PyTuple_GET_ITEM(args, 0)) && BPy_BMesh_Check(py_bm))
|
||||
{
|
||||
BPY_BM_CHECK_OBJ(py_bm);
|
||||
bm = py_bm->bm;
|
||||
|
||||
if (bm->use_toolflags == false) {
|
||||
PyErr_SetString(PyExc_ValueError, "bmesh created with 'use_operators=False'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* could complain about entering with exceptions... */
|
||||
BMO_error_clear(bm);
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"bmesh operators expect a single BMesh positional argument, all other args "
|
||||
"must be keywords");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* TODO: error check this!, though we do the error check on attribute access. */
|
||||
/* TODO: make flags optional. */
|
||||
BMO_op_init(bm, &bmop, BMO_FLAG_DEFAULTS, self->opname);
|
||||
|
||||
if (kw && PyDict_Size(kw) > 0) {
|
||||
/* Setup properties, see `bpy_rna.cc`: #pyrna_py_to_prop()
|
||||
* which shares this logic for parsing properties. */
|
||||
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
while (PyDict_Next(kw, &pos, &key, &value)) {
|
||||
const char *slot_name = PyUnicode_AsUTF8(key);
|
||||
BMOpSlot *slot;
|
||||
|
||||
if (!BMO_slot_exists(bmop.slots_in, slot_name)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" is invalid for this operator",
|
||||
self->opname,
|
||||
slot_name);
|
||||
BMO_op_finish(bm, &bmop);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
slot = BMO_slot_get(bmop.slots_in, slot_name);
|
||||
|
||||
/* now assign the value */
|
||||
if (bpy_slot_from_py(bm, &bmop, slot, value, self->opname, slot_name) == -1) {
|
||||
BMO_op_finish(bm, &bmop);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BMO_op_exec(bm, &bmop);
|
||||
|
||||
/* from here until the end of the function, no returns, just set 'ret' */
|
||||
if (UNLIKELY(bpy_bm_op_as_py_error(bm) == -1)) {
|
||||
ret = nullptr; /* exception raised above */
|
||||
}
|
||||
else if (bmop.slots_out[0].slot_name == nullptr) {
|
||||
ret = Py_NewRef(Py_None);
|
||||
}
|
||||
else {
|
||||
/* build return value */
|
||||
int i;
|
||||
ret = PyDict_New();
|
||||
|
||||
for (i = 0; bmop.slots_out[i].slot_name; i++) {
|
||||
// BMOpDefine *op_def = opdefines[bmop.type];
|
||||
// BMOSlotType *slot_type = op_def->slot_types_out[i];
|
||||
BMOpSlot *slot = &bmop.slots_out[i];
|
||||
PyObject *item;
|
||||
|
||||
/* this function doesn't throw exceptions */
|
||||
item = bpy_slot_to_py(bm, slot);
|
||||
if (item == nullptr) {
|
||||
item = Py_NewRef(Py_None);
|
||||
}
|
||||
|
||||
#if 1
|
||||
/* Temporary code, strip off `.out` while we keep this convention. */
|
||||
{
|
||||
char slot_name_strip[MAX_SLOTNAME];
|
||||
const char *ch = strchr(slot->slot_name, '.'); /* can't fail! */
|
||||
const int tot = ch - slot->slot_name;
|
||||
BLI_assert(ch != nullptr);
|
||||
memcpy(slot_name_strip, slot->slot_name, tot);
|
||||
slot_name_strip[tot] = '\0';
|
||||
PyDict_SetItemString(ret, slot_name_strip, item);
|
||||
}
|
||||
#else
|
||||
PyDict_SetItemString(ret, slot->slot_name, item);
|
||||
#endif
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
||||
BMO_op_finish(bm, &bmop);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BPy_BMeshOpFunc {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
const char *opname;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the `__call__` for `bmesh.ops.xxx()`.
|
||||
*/
|
||||
[[nodiscard]] PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw);
|
||||
|
||||
} // namespace blender
|
||||
5715
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.cc
Normal file
5715
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.cc
Normal file
File diff suppressed because it is too large
Load Diff
283
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.hh
Normal file
283
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.hh
Normal file
@@ -0,0 +1,283 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMesh;
|
||||
struct BMEdge;
|
||||
struct BMElem;
|
||||
struct BMFace;
|
||||
struct BMLoop;
|
||||
struct BMVert;
|
||||
|
||||
extern PyTypeObject BPy_BMesh_Type;
|
||||
extern PyTypeObject BPy_BMVert_Type;
|
||||
extern PyTypeObject BPy_BMEdge_Type;
|
||||
extern PyTypeObject BPy_BMFace_Type;
|
||||
extern PyTypeObject BPy_BMLoop_Type;
|
||||
|
||||
extern PyTypeObject BPy_BMElemSeq_Type;
|
||||
extern PyTypeObject BPy_BMVertSeq_Type;
|
||||
extern PyTypeObject BPy_BMEdgeSeq_Type;
|
||||
extern PyTypeObject BPy_BMFaceSeq_Type;
|
||||
extern PyTypeObject BPy_BMLoopSeq_Type;
|
||||
|
||||
extern PyTypeObject BPy_BMIter_Type;
|
||||
|
||||
#define BPy_BMesh_Check(v) (Py_TYPE(v) == &BPy_BMesh_Type)
|
||||
#define BPy_BMVert_Check(v) (Py_TYPE(v) == &BPy_BMVert_Type)
|
||||
#define BPy_BMEdge_Check(v) (Py_TYPE(v) == &BPy_BMEdge_Type)
|
||||
#define BPy_BMFace_Check(v) (Py_TYPE(v) == &BPy_BMFace_Type)
|
||||
#define BPy_BMLoop_Check(v) (Py_TYPE(v) == &BPy_BMLoop_Type)
|
||||
#define BPy_BMElemSeq_Check(v) (Py_TYPE(v) == &BPy_BMElemSeq_Type)
|
||||
#define BPy_BMVertSeq_Check(v) (Py_TYPE(v) == &BPy_BMVertSeq_Type)
|
||||
#define BPy_BMEdgeSeq_Check(v) (Py_TYPE(v) == &BPy_BMEdgeSeq_Type)
|
||||
#define BPy_BMFaceSeq_Check(v) (Py_TYPE(v) == &BPy_BMFaceSeq_Type)
|
||||
#define BPy_BMLoopSeq_Check(v) (Py_TYPE(v) == &BPy_BMLoopSeq_Type)
|
||||
#define BPy_BMIter_Check(v) (Py_TYPE(v) == &BPy_BMIter_Type)
|
||||
/* trick since we know they share a hash function */
|
||||
#define BPy_BMElem_Check(v) (Py_TYPE(v)->tp_hash == BPy_BMVert_Type.tp_hash)
|
||||
|
||||
/* cast from _any_ bmesh type - they all have BMesh first */
|
||||
struct BPy_BMGeneric {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
};
|
||||
|
||||
/* BPy_BMVert/BPy_BMEdge/BPy_BMFace/BPy_BMLoop can cast to this */
|
||||
struct BPy_BMElem {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMElem *ele;
|
||||
};
|
||||
|
||||
struct BPy_BMesh {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
int flag;
|
||||
};
|
||||
|
||||
/* element types */
|
||||
struct BPy_BMVert {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMVert *v;
|
||||
};
|
||||
|
||||
struct BPy_BMEdge {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMEdge *e;
|
||||
};
|
||||
|
||||
struct BPy_BMFace {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMFace *f;
|
||||
};
|
||||
|
||||
struct BPy_BMLoop {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMLoop *l;
|
||||
};
|
||||
|
||||
/* iterators */
|
||||
|
||||
/* used for ...
|
||||
* - BPy_BMElemSeq_Type
|
||||
* - BPy_BMVertSeq_Type
|
||||
* - BPy_BMEdgeSeq_Type
|
||||
* - BPy_BMFaceSeq_Type
|
||||
* - BPy_BMLoopSeq_Type
|
||||
*/
|
||||
struct BPy_BMElemSeq {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
|
||||
/* if this is a sequence on an existing element,
|
||||
* loops of faces for eg.
|
||||
* If this variable is set, it will be used */
|
||||
|
||||
/* we hold a reference to this.
|
||||
* check in case the owner becomes invalid on access */
|
||||
/* TODO: make this a GC'd object!, will function OK without this though. */
|
||||
BPy_BMElem *py_ele;
|
||||
|
||||
/* iterator type */
|
||||
short itype;
|
||||
};
|
||||
|
||||
struct BPy_BMIter {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMIter iter;
|
||||
};
|
||||
|
||||
void BPy_BM_init_types();
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_types();
|
||||
|
||||
enum {
|
||||
BPY_BMFLAG_NOP = 0, /* do nothing */
|
||||
BPY_BMFLAG_IS_WRAPPED = 1, /* the mesh is owned by editmode */
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMesh_CreatePyObject(BMesh *bm, int flag);
|
||||
[[nodiscard]] PyObject *BPy_BMVert_CreatePyObject(BMesh *bm, BMVert *v);
|
||||
[[nodiscard]] PyObject *BPy_BMEdge_CreatePyObject(BMesh *bm, BMEdge *e);
|
||||
[[nodiscard]] PyObject *BPy_BMFace_CreatePyObject(BMesh *bm, BMFace *f);
|
||||
[[nodiscard]] PyObject *BPy_BMLoop_CreatePyObject(BMesh *bm, BMLoop *l);
|
||||
[[nodiscard]] PyObject *BPy_BMElemSeq_CreatePyObject(BMesh *bm, BPy_BMElem *py_ele, char itype);
|
||||
[[nodiscard]] PyObject *BPy_BMVertSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMEdgeSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMFaceSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMLoopSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMIter_CreatePyObject(BMesh *bm);
|
||||
|
||||
/** Just checks type and creates vert/edge/face/loop. */
|
||||
[[nodiscard]] PyObject *BPy_BMElem_CreatePyObject(BMesh *bm, BMHeader *ele);
|
||||
|
||||
/**
|
||||
* Generic python seq as BMVert/Edge/Face array,
|
||||
* return value must be freed with PyMem_FREE(...);
|
||||
*
|
||||
* The 'bm_r' value is assigned when empty, and used when set.
|
||||
*/
|
||||
[[nodiscard]] void *BPy_BMElem_PySeq_As_Array_FAST(BMesh **r_bm,
|
||||
PyObject *seq_fast,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
char htype,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] void *BPy_BMElem_PySeq_As_Array(BMesh **r_bm,
|
||||
PyObject *seq,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
char htype,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
|
||||
[[nodiscard]] BMVert **BPy_BMVert_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);
|
||||
[[nodiscard]] 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);
|
||||
[[nodiscard]] 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);
|
||||
[[nodiscard]] BMLoop **BPy_BMLoop_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);
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMElem_Array_As_Tuple(BMesh *bm, BMHeader **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMVert_Array_As_Tuple(BMesh *bm, BMVert **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMEdge_Array_As_Tuple(BMesh *bm, BMEdge **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMFace_Array_As_Tuple(BMesh *bm, BMFace **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMLoop_Array_As_Tuple(BMesh *bm,
|
||||
BMLoop *const *elem,
|
||||
Py_ssize_t elem_num);
|
||||
|
||||
[[nodiscard]] int BPy_BMElem_CheckHType(PyTypeObject *type, char htype);
|
||||
/**
|
||||
* Use for error strings only, not thread safe,
|
||||
*
|
||||
* \return a string like '(BMVert/BMEdge/BMFace/BMLoop)'
|
||||
*/
|
||||
[[nodiscard]] char *BPy_BMElem_StringFromHType_ex(char htype, char ret[32]);
|
||||
[[nodiscard]] char *BPy_BMElem_StringFromHType(char htype);
|
||||
|
||||
// void bpy_bm_generic_invalidate(BPy_BMGeneric *self);
|
||||
[[nodiscard]] int bpy_bm_generic_valid_check(BPy_BMGeneric *self);
|
||||
[[nodiscard]] int bpy_bm_generic_valid_check_source(BMesh *bm_source,
|
||||
const char *error_prefix,
|
||||
void **args,
|
||||
uint args_tot) ATTR_NONNULL(1, 2);
|
||||
[[nodiscard]] int bpy_bm_check_uv_select_sync_valid(BMesh *bm, const char *error_prefix);
|
||||
[[nodiscard]] int bpy_bm_uv_layer_offset_or_error(BMesh *bm, const char *error_prefix);
|
||||
[[nodiscard]] int bpy_bm_check_bm_match_or_error(BMesh *bm_a,
|
||||
BMesh *bm_b,
|
||||
const char *error_prefix);
|
||||
|
||||
#define BPY_BM_CHECK_OBJ(obj) \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check((BPy_BMGeneric *)obj) == -1)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
(void)0
|
||||
#define BPY_BM_CHECK_INT(obj) \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check((BPy_BMGeneric *)obj) == -1)) { \
|
||||
return -1; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* Macros like `BPY_BM_CHECK_OBJ/BPY_BM_CHECK_INT` that ensure we're from the right #BMesh.
|
||||
*/
|
||||
#define BPY_BM_CHECK_SOURCE_OBJ(bm, errmsg, ...) \
|
||||
{ \
|
||||
void *_args[] = {__VA_ARGS__}; \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check_source(bm, errmsg, _args, ARRAY_SIZE(_args)) == -1)) \
|
||||
{ \
|
||||
return NULL; \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
#define BPY_BM_CHECK_SOURCE_INT(bm, errmsg, ...) \
|
||||
{ \
|
||||
void *_args[] = {__VA_ARGS__}; \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check_source(bm, errmsg, _args, ARRAY_SIZE(_args)) == -1)) \
|
||||
{ \
|
||||
return -1; \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
#define BPY_BM_IS_VALID(obj) (LIKELY((obj)->bm != NULL))
|
||||
|
||||
#define BM_ITER_BPY_BM_SEQ(ele, iter, bpy_bmelemseq) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new( \
|
||||
iter, \
|
||||
(bpy_bmelemseq)->bm, \
|
||||
(bpy_bmelemseq)->itype, \
|
||||
(bpy_bmelemseq)->py_ele ? ((BPy_BMElem *)(bpy_bmelemseq)->py_ele)->ele : NULL); \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMesh;
|
||||
|
||||
/* All use #BPy_BMLayerAccess struct. */
|
||||
|
||||
extern PyTypeObject BPy_BMLayerAccessVert_Type;
|
||||
extern PyTypeObject BPy_BMLayerAccessEdge_Type;
|
||||
extern PyTypeObject BPy_BMLayerAccessFace_Type;
|
||||
extern PyTypeObject BPy_BMLayerAccessLoop_Type;
|
||||
|
||||
extern PyTypeObject BPy_BMLayerCollection_Type;
|
||||
extern PyTypeObject BPy_BMLayerItem_Type;
|
||||
|
||||
#define BPy_BMLayerAccess_Check(v) (Py_TYPE(v) == &BPy_BMLayerAccess_Type)
|
||||
#define BPy_BMLayerCollection_Check(v) (Py_TYPE(v) == &BPy_BMLayerCollection_Type)
|
||||
#define BPy_BMLayerItem_Check(v) (Py_TYPE(v) == &BPy_BMLayerItem_Type)
|
||||
|
||||
/** All layers for vert/edge/face/loop. */
|
||||
struct BPy_BMLayerAccess {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
char htype;
|
||||
};
|
||||
|
||||
/** Access different layer types deform/uv/vertex-color. */
|
||||
struct BPy_BMLayerCollection {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
char htype;
|
||||
int type; /* customdata type - CD_XXX */
|
||||
};
|
||||
|
||||
/** Access a specific layer directly. */
|
||||
struct BPy_BMLayerItem {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
char htype;
|
||||
int type; /* customdata type - CD_XXX */
|
||||
int index; /* index of this layer type */
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMLayerAccess_CreatePyObject(BMesh *bm, char htype);
|
||||
[[nodiscard]] PyObject *BPy_BMLayerCollection_CreatePyObject(BMesh *bm, char htype, int type);
|
||||
[[nodiscard]] PyObject *BPy_BMLayerItem_CreatePyObject(BMesh *bm, char htype, int type, int index);
|
||||
|
||||
void BPy_BM_init_types_customdata();
|
||||
|
||||
/**
|
||||
* \brief `BMElem.__getitem__() / __setitem__()`
|
||||
*
|
||||
* Assume all error checks are done, eg: `uv = vert[uv_layer]`
|
||||
*/
|
||||
[[nodiscard]] PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer);
|
||||
[[nodiscard]] int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele,
|
||||
BPy_BMLayerItem *py_layer,
|
||||
PyObject *value);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,787 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines custom-data types which can't be accessed as primitive
|
||||
* Python types such as #MDeformVert. It also exposed UV map data in a way
|
||||
* compatible with the (deprecated) #MLoopUV type.
|
||||
* MLoopUV used to be a struct containing both the UV information and various
|
||||
* selection flags. This has since been split up into a float2 attribute
|
||||
* and three boolean attributes for the selection/pin states.
|
||||
* For backwards compatibility, the original #MLoopUV is emulated in the
|
||||
* python API. This comes at a performance penalty however, and the plan is
|
||||
* to provide direct access to the boolean layers for faster access. Eventually
|
||||
* (probably in 4.0) #BPy_BMLoopUV should be removed on the Python side as well.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_deform.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "bmesh_py_types_meshdata.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Mesh Loop UV
|
||||
* ************ */
|
||||
|
||||
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
|
||||
|
||||
struct BPy_BMLoopUV {
|
||||
PyObject_HEAD
|
||||
float *uv;
|
||||
/**
|
||||
* Pin may be null, signifying the layer doesn't exist.
|
||||
*
|
||||
* Currently its always created on a #BMesh because adding UV layers to an existing #BMesh is
|
||||
* slow and invalidates existing Python objects having pointers into the original data-blocks
|
||||
* (since adding a layer re-generates all blocks).
|
||||
* But eventually the plan is to lazily allocate the boolean layers "on demand".
|
||||
* Therefore the code handles cases where the pin layer doesn't exist.
|
||||
*/
|
||||
bool *pin;
|
||||
BMLoop *loop;
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmloopuv_uv_doc,
|
||||
"Loop UV (as a 2D Vector).\n"
|
||||
"\n"
|
||||
":type: :class:`mathutils.Vector`\n");
|
||||
static PyObject *bpy_bmloopuv_uv_get(BPy_BMLoopUV *self, void * /*closure*/)
|
||||
{
|
||||
return Vector_CreatePyObject_wrap(self->uv, 2, nullptr);
|
||||
}
|
||||
|
||||
static int bpy_bmloopuv_uv_set(BPy_BMLoopUV *self, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
float tvec[2];
|
||||
if (mathutils_array_parse(tvec, 2, 2, value, "BMLoopUV.uv") != -1) {
|
||||
copy_v2_v2(self->uv, tvec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool bpy_bmloopuv_pin_uv_ok_or_error(const BPy_BMLoopUV *self)
|
||||
{
|
||||
if (self->pin == nullptr) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"active uv layer has no associated pin layer. This is a bug!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmloopuv_pin_uv_doc,
|
||||
"UV pin state.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
|
||||
static PyObject *bpy_bmloopuv_pin_uv_get(BPy_BMLoopUV *self, void * /*closure*/)
|
||||
{
|
||||
/* A non existing pin layer means nothing is currently pinned. */
|
||||
if (UNLIKELY(!bpy_bmloopuv_pin_uv_ok_or_error(self))) {
|
||||
return nullptr;
|
||||
}
|
||||
return PyBool_FromLong(*self->pin);
|
||||
}
|
||||
|
||||
static int bpy_bmloopuv_pin_uv_set(BPy_BMLoopUV *self, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
/* TODO: if we add lazy allocation of the associated uv map bool layers to BMesh we need
|
||||
* to add a pin layer and update self->pin in the case of self->pin being nullptr.
|
||||
* This isn't easy to do currently as adding CustomData layers to a BMesh invalidates
|
||||
* existing python objects. So for now lazy allocation isn't done and self->pin should
|
||||
* never be nullptr. */
|
||||
BLI_assert(self->pin);
|
||||
if (UNLIKELY(!bpy_bmloopuv_pin_uv_ok_or_error(self))) {
|
||||
return -1;
|
||||
}
|
||||
*self->pin = PyC_Long_AsBool(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmloopuv_getseters[] = {
|
||||
/* attributes match rna_def_mloopuv. */
|
||||
{"uv",
|
||||
reinterpret_cast<getter>(bpy_bmloopuv_uv_get),
|
||||
reinterpret_cast<setter>(bpy_bmloopuv_uv_set),
|
||||
bpy_bmloopuv_uv_doc,
|
||||
nullptr},
|
||||
{"pin_uv",
|
||||
reinterpret_cast<getter>(bpy_bmloopuv_pin_uv_get),
|
||||
reinterpret_cast<setter>(bpy_bmloopuv_pin_uv_set),
|
||||
bpy_bmloopuv_pin_uv_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject BPy_BMLoopUV_Type; /* bm.loops.layers.uv.active */
|
||||
|
||||
static void bm_init_types_bmloopuv()
|
||||
{
|
||||
BPy_BMLoopUV_Type.tp_basicsize = sizeof(BPy_BMLoopUV);
|
||||
|
||||
BPy_BMLoopUV_Type.tp_name = "BMLoopUV";
|
||||
|
||||
BPy_BMLoopUV_Type.tp_doc = nullptr; /* todo */
|
||||
|
||||
BPy_BMLoopUV_Type.tp_getset = bpy_bmloopuv_getseters;
|
||||
|
||||
BPy_BMLoopUV_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMLoopUV_Type);
|
||||
}
|
||||
|
||||
int BPy_BMLoopUV_AssignPyObject(BMesh *bm, BMLoop *loop, PyObject *value)
|
||||
{
|
||||
if (UNLIKELY(!BPy_BMLoopUV_Check(value))) {
|
||||
PyErr_Format(PyExc_TypeError, "expected BMLoopUV, not a %.200s", Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BPy_BMLoopUV *src = reinterpret_cast<BPy_BMLoopUV *>(value);
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
|
||||
|
||||
float *luv = BM_ELEM_CD_GET_FLOAT_P(loop, offsets.uv);
|
||||
copy_v2_v2(luv, src->uv);
|
||||
if (src->pin) {
|
||||
BM_ELEM_CD_SET_BOOL(loop, offsets.pin, *src->pin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMLoopUV_CreatePyObject(BMesh *bm, BMLoop *loop, int layer)
|
||||
{
|
||||
BPy_BMLoopUV *self = PyObject_New(BPy_BMLoopUV, &BPy_BMLoopUV_Type);
|
||||
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_from_layer(bm, layer);
|
||||
|
||||
self->uv = BM_ELEM_CD_GET_FLOAT_P(loop, offsets.uv);
|
||||
self->pin = offsets.pin >= 0 ? BM_ELEM_CD_GET_BOOL_P(loop, offsets.pin) : nullptr;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/* --- End Mesh Loop UV --- */
|
||||
|
||||
/* Mesh Vert Skin
|
||||
* ************ */
|
||||
|
||||
#define BPy_BMVertSkin_Check(v) (Py_TYPE(v) == &BPy_BMVertSkin_Type)
|
||||
|
||||
struct BPy_BMVertSkin {
|
||||
PyObject_HEAD
|
||||
MVertSkin *data;
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmvertskin_radius_doc,
|
||||
"Vert skin radii (as a 2D Vector).\n"
|
||||
"\n"
|
||||
":type: :class:`mathutils.Vector`\n");
|
||||
static PyObject *bpy_bmvertskin_radius_get(BPy_BMVertSkin *self, void * /*closure*/)
|
||||
{
|
||||
return Vector_CreatePyObject_wrap(self->data->radius, 2, nullptr);
|
||||
}
|
||||
|
||||
static int bpy_bmvertskin_radius_set(BPy_BMVertSkin *self, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
float tvec[2];
|
||||
if (mathutils_array_parse(tvec, 2, 2, value, "BMVertSkin.radius") != -1) {
|
||||
copy_v2_v2(self->data->radius, tvec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmvertskin_flag__use_root_doc,
|
||||
"Use as root vertex. Setting this flag does not clear other roots in the same mesh island.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmvertskin_flag__use_loose_doc,
|
||||
"Use loose vertex.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
|
||||
static PyObject *bpy_bmvertskin_flag_get(BPy_BMVertSkin *self, void *flag_p)
|
||||
{
|
||||
const int flag = POINTER_AS_INT(flag_p);
|
||||
return PyBool_FromLong(self->data->flag & flag);
|
||||
}
|
||||
|
||||
static int bpy_bmvertskin_flag_set(BPy_BMVertSkin *self, PyObject *value, void *flag_p)
|
||||
{
|
||||
const eMVertSkinFlag flag = eMVertSkinFlag(POINTER_AS_INT(flag_p));
|
||||
|
||||
switch (PyC_Long_AsBool(value)) {
|
||||
case true:
|
||||
self->data->flag |= flag;
|
||||
return 0;
|
||||
case false:
|
||||
self->data->flag &= ~flag;
|
||||
return 0;
|
||||
default:
|
||||
/* error is set */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmvertskin_getseters[] = {
|
||||
/* attributes match rna_mesh_gen. */
|
||||
{"radius",
|
||||
reinterpret_cast<getter>(bpy_bmvertskin_radius_get),
|
||||
reinterpret_cast<setter>(bpy_bmvertskin_radius_set),
|
||||
bpy_bmvertskin_radius_doc,
|
||||
nullptr},
|
||||
{"use_root",
|
||||
reinterpret_cast<getter>(bpy_bmvertskin_flag_get),
|
||||
reinterpret_cast<setter>(bpy_bmvertskin_flag_set),
|
||||
bpy_bmvertskin_flag__use_root_doc,
|
||||
reinterpret_cast<void *>(MVERT_SKIN_ROOT)},
|
||||
{"use_loose",
|
||||
reinterpret_cast<getter>(bpy_bmvertskin_flag_get),
|
||||
reinterpret_cast<setter>(bpy_bmvertskin_flag_set),
|
||||
bpy_bmvertskin_flag__use_loose_doc,
|
||||
reinterpret_cast<void *>(MVERT_SKIN_LOOSE)},
|
||||
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject BPy_BMVertSkin_Type; /* bm.loops.layers.skin.active */
|
||||
|
||||
static void bm_init_types_bmvertskin()
|
||||
{
|
||||
BPy_BMVertSkin_Type.tp_basicsize = sizeof(BPy_BMVertSkin);
|
||||
|
||||
BPy_BMVertSkin_Type.tp_name = "BMVertSkin";
|
||||
|
||||
BPy_BMVertSkin_Type.tp_doc = "Skin vertex data for the skin modifier.";
|
||||
|
||||
BPy_BMVertSkin_Type.tp_getset = bpy_bmvertskin_getseters;
|
||||
|
||||
BPy_BMVertSkin_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMVertSkin_Type);
|
||||
}
|
||||
|
||||
int BPy_BMVertSkin_AssignPyObject(MVertSkin *mvertskin, PyObject *value)
|
||||
{
|
||||
if (UNLIKELY(!BPy_BMVertSkin_Check(value))) {
|
||||
PyErr_Format(PyExc_TypeError, "expected BMVertSkin, not a %.200s", Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(mvertskin) = *((reinterpret_cast<BPy_BMVertSkin *>(value))->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMVertSkin_CreatePyObject(MVertSkin *mvertskin)
|
||||
{
|
||||
BPy_BMVertSkin *self = PyObject_New(BPy_BMVertSkin, &BPy_BMVertSkin_Type);
|
||||
self->data = mvertskin;
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/* --- End Mesh Vert Skin --- */
|
||||
|
||||
/* Mesh Loop Color
|
||||
* *************** */
|
||||
|
||||
/* This simply provides a color wrapper for
|
||||
* color which uses mathutils callbacks for mathutils.Color
|
||||
*/
|
||||
|
||||
#define MLOOPCOL_FROM_CAPSULE(color_capsule) \
|
||||
((MLoopCol *)PyCapsule_GetPointer(color_capsule, nullptr))
|
||||
|
||||
static void mloopcol_to_float(const MLoopCol *mloopcol, float r_col[4])
|
||||
{
|
||||
rgba_uchar_to_float(r_col, static_cast<const uchar *>(&mloopcol->r));
|
||||
}
|
||||
|
||||
static void mloopcol_from_float(MLoopCol *mloopcol, const float col[4])
|
||||
{
|
||||
rgba_float_to_uchar(static_cast<uchar *>(&mloopcol->r), col);
|
||||
}
|
||||
|
||||
static uchar mathutils_bmloopcol_cb_index = -1;
|
||||
|
||||
static int mathutils_bmloopcol_check(BaseMathObject * /*bmo*/)
|
||||
{
|
||||
/* always ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_get(BaseMathObject *bmo, int /*subtype*/)
|
||||
{
|
||||
MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
|
||||
mloopcol_to_float(mloopcol, bmo->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_set(BaseMathObject *bmo, int /*subtype*/)
|
||||
{
|
||||
MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
|
||||
mloopcol_from_float(mloopcol, bmo->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_get_index(BaseMathObject *bmo, int subtype, int /*index*/)
|
||||
{
|
||||
/* Lazy, avoid repeating the case statement. */
|
||||
if (mathutils_bmloopcol_get(bmo, subtype) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_set_index(BaseMathObject *bmo, int subtype, int index)
|
||||
{
|
||||
const float f = bmo->data[index];
|
||||
|
||||
/* Lazy, avoid repeating the case statement. */
|
||||
if (mathutils_bmloopcol_get(bmo, subtype) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bmo->data[index] = f;
|
||||
return mathutils_bmloopcol_set(bmo, subtype);
|
||||
}
|
||||
|
||||
static Mathutils_Callback mathutils_bmloopcol_cb = {
|
||||
mathutils_bmloopcol_check,
|
||||
mathutils_bmloopcol_get,
|
||||
mathutils_bmloopcol_set,
|
||||
mathutils_bmloopcol_get_index,
|
||||
mathutils_bmloopcol_set_index,
|
||||
};
|
||||
|
||||
static void bm_init_types_bmloopcol()
|
||||
{
|
||||
/* pass */
|
||||
mathutils_bmloopcol_cb_index = Mathutils_RegisterCallback(&mathutils_bmloopcol_cb);
|
||||
}
|
||||
|
||||
int BPy_BMLoopColor_AssignPyObject(MLoopCol *mloopcol, PyObject *value)
|
||||
{
|
||||
float tvec[4];
|
||||
if (mathutils_array_parse(tvec, 4, 4, value, "BMLoopCol") != -1) {
|
||||
mloopcol_from_float(mloopcol, tvec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMLoopColor_CreatePyObject(MLoopCol *mloopcol)
|
||||
{
|
||||
PyObject *color_capsule;
|
||||
color_capsule = PyCapsule_New(mloopcol, nullptr, nullptr);
|
||||
return Vector_CreatePyObject_cb(color_capsule, 4, mathutils_bmloopcol_cb_index, 0);
|
||||
}
|
||||
|
||||
#undef MLOOPCOL_FROM_CAPSULE
|
||||
|
||||
/* --- End Mesh Loop Color --- */
|
||||
|
||||
/* Mesh Deform Vert
|
||||
* **************** */
|
||||
|
||||
/**
|
||||
* This is python type wraps a deform vert as a python dictionary,
|
||||
* hiding the #MDeformWeight on access, since the mapping is very close, eg:
|
||||
*
|
||||
* \code{.c}
|
||||
* weight = BKE_defvert_find_weight(dv, group_nr);
|
||||
* BKE_defvert_remove_group(dv, dw)
|
||||
* \endcode
|
||||
*
|
||||
* \code{.py}
|
||||
* weight = dv[group_nr]
|
||||
* del dv[group_nr]
|
||||
* \endcode
|
||||
*
|
||||
* \note There is nothing BMesh specific here,
|
||||
* its only that BMesh is the only part of blender that uses a hand written API like this.
|
||||
* This type could eventually be used to access lattice weights.
|
||||
*
|
||||
* \note Many of Blender-API's dictionary-like-wrappers act like ordered dictionaries,
|
||||
* This is intentionally _not_ ordered, the weights can be in any order and it won't matter,
|
||||
* the order should not be used in the API in any meaningful way (as with a python dict)
|
||||
* only expose as mapping, not a sequence.
|
||||
*/
|
||||
|
||||
#define BPy_BMDeformVert_Check(v) (Py_TYPE(v) == &BPy_BMDeformVert_Type)
|
||||
|
||||
struct BPy_BMDeformVert {
|
||||
PyObject_HEAD
|
||||
MDeformVert *data;
|
||||
};
|
||||
|
||||
/* Mapping Protocols
|
||||
* ================= */
|
||||
|
||||
static Py_ssize_t bpy_bmdeformvert_len(BPy_BMDeformVert *self)
|
||||
{
|
||||
return self->data->totweight;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmdeformvert_subscript(BPy_BMDeformVert *self, PyObject *key)
|
||||
{
|
||||
if (PyIndex_Check(key)) {
|
||||
int i;
|
||||
i = PyNumber_AsSsize_t(key, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MDeformWeight *dw = BKE_defvert_find_index(self->data, i);
|
||||
|
||||
if (dw == nullptr) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"BMDeformVert[key] = x: "
|
||||
"key not found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return PyFloat_FromDouble(dw->weight);
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "BMDeformVert keys must be integers, not %.200s", Py_TYPE(key)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, PyObject *value)
|
||||
{
|
||||
if (PyIndex_Check(key)) {
|
||||
int i;
|
||||
|
||||
i = PyNumber_AsSsize_t(key, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
/* Handle `dvert[group_index] = 0.5`. */
|
||||
if (i < 0) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"BMDeformVert[key] = x: "
|
||||
"weight keys cannot be negative");
|
||||
return -1;
|
||||
}
|
||||
|
||||
MDeformWeight *dw = BKE_defvert_ensure_index(self->data, i);
|
||||
const float f = PyFloat_AsDouble(value);
|
||||
if (f == -1 && PyErr_Occurred()) { /* Parsed key not a number. */
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"BMDeformVert[key] = x: "
|
||||
"assigned value not a number");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dw->weight = clamp_f(f, 0.0f, 1.0f);
|
||||
}
|
||||
else {
|
||||
/* Handle `del dvert[group_index]`. */
|
||||
MDeformWeight *dw = BKE_defvert_find_index(self->data, i);
|
||||
|
||||
if (dw == nullptr) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"del BMDeformVert[key]: "
|
||||
"key not found");
|
||||
}
|
||||
BKE_defvert_remove_group(self->data, dw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "BMDeformVert keys must be integers, not %.200s", Py_TYPE(key)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int bpy_bmdeformvert_contains(BPy_BMDeformVert *self, PyObject *value)
|
||||
{
|
||||
const int key = PyLong_AsSsize_t(value);
|
||||
|
||||
if (key == -1 && PyErr_Occurred()) {
|
||||
PyErr_SetString(PyExc_TypeError, "BMDeformVert.__contains__: expected an int");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (BKE_defvert_find_index(self->data, key) != nullptr) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* only defined for __contains__ */
|
||||
static PySequenceMethods bpy_bmdeformvert_as_sequence = {
|
||||
/*sq_length*/ reinterpret_cast<lenfunc>(bpy_bmdeformvert_len),
|
||||
/*sq_concat*/ nullptr,
|
||||
/*sq_repeat*/ nullptr,
|
||||
/* NOTE: if this is set #PySequence_Check() returns True,
|
||||
* but in this case we don't want to be treated as a seq. */
|
||||
/*sq_item*/ nullptr,
|
||||
/*was_sq_slice*/ nullptr, /* DEPRECATED. */
|
||||
/*sq_ass_item*/ nullptr,
|
||||
/*was_sq_ass_slice*/ nullptr, /* DEPRECATED. */
|
||||
/*sq_contains*/ reinterpret_cast<objobjproc>(bpy_bmdeformvert_contains),
|
||||
/*sq_inplace_concat*/ nullptr,
|
||||
/*sq_inplace_repeat*/ nullptr,
|
||||
};
|
||||
|
||||
static PyMappingMethods bpy_bmdeformvert_as_mapping = {
|
||||
/*mp_length*/ reinterpret_cast<lenfunc>(bpy_bmdeformvert_len),
|
||||
/*mp_subscript*/ reinterpret_cast<binaryfunc>(bpy_bmdeformvert_subscript),
|
||||
/*mp_ass_subscript*/ reinterpret_cast<objobjargproc>(bpy_bmdeformvert_ass_subscript),
|
||||
};
|
||||
|
||||
/* Methods
|
||||
* ======= */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_keys_doc,
|
||||
".. method:: keys()\n"
|
||||
"\n"
|
||||
" Return the group indices used by this vertex\n"
|
||||
" (matching Python's dict.keys() functionality).\n"
|
||||
"\n"
|
||||
" :return: The deform group indices this vertex uses.\n"
|
||||
" :rtype: list[int]\n");
|
||||
static PyObject *bpy_bmdeformvert_keys(BPy_BMDeformVert *self)
|
||||
{
|
||||
PyObject *ret;
|
||||
int i;
|
||||
MDeformWeight *dw = self->data->dw;
|
||||
|
||||
ret = PyList_New(self->data->totweight);
|
||||
for (i = 0; i < self->data->totweight; i++, dw++) {
|
||||
PyList_SET_ITEM(ret, i, PyLong_FromLong(dw->def_nr));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_values_doc,
|
||||
".. method:: values()\n"
|
||||
"\n"
|
||||
" Return the weights of the deform vertex\n"
|
||||
" (matching Python's dict.values() functionality).\n"
|
||||
"\n"
|
||||
" :return: The weights that influence this vertex\n"
|
||||
" :rtype: list[float]\n");
|
||||
static PyObject *bpy_bmdeformvert_values(BPy_BMDeformVert *self)
|
||||
{
|
||||
PyObject *ret;
|
||||
int i;
|
||||
MDeformWeight *dw = self->data->dw;
|
||||
|
||||
ret = PyList_New(self->data->totweight);
|
||||
for (i = 0; i < self->data->totweight; i++, dw++) {
|
||||
PyList_SET_ITEM(ret, i, PyFloat_FromDouble(dw->weight));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_items_doc,
|
||||
".. method:: items()\n"
|
||||
"\n"
|
||||
" Return (group, weight) pairs for this vertex\n"
|
||||
" (matching Python's dict.items() functionality).\n"
|
||||
"\n"
|
||||
" :return: (key, value) pairs for each deform weight of this vertex.\n"
|
||||
" :rtype: list[tuple[int, float]]\n");
|
||||
static PyObject *bpy_bmdeformvert_items(BPy_BMDeformVert *self)
|
||||
{
|
||||
PyObject *ret;
|
||||
PyObject *item;
|
||||
int i;
|
||||
MDeformWeight *dw = self->data->dw;
|
||||
|
||||
ret = PyList_New(self->data->totweight);
|
||||
for (i = 0; i < self->data->totweight; i++, dw++) {
|
||||
item = PyTuple_New(2);
|
||||
PyTuple_SET_ITEMS(item, PyLong_FromLong(dw->def_nr), PyFloat_FromDouble(dw->weight));
|
||||
PyList_SET_ITEM(ret, i, item);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_get_doc,
|
||||
".. method:: get(key, default=None)\n"
|
||||
"\n"
|
||||
" Returns the deform weight matching the key or default\n"
|
||||
" when not found (matches Python's dictionary function of the same name).\n"
|
||||
"\n"
|
||||
" :param key: The vertex group index.\n"
|
||||
" :type key: int\n"
|
||||
" :param default: Optional argument for the value to return if\n"
|
||||
" *key* is not found.\n"
|
||||
" :type default: Any\n"
|
||||
" :return: The deform weight or the default when not found.\n"
|
||||
" :rtype: float | Any\n");
|
||||
static PyObject *bpy_bmdeformvert_get(BPy_BMDeformVert *self, PyObject *args)
|
||||
{
|
||||
int key;
|
||||
PyObject *def = Py_None;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i|O:get", &key, &def)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MDeformWeight *dw = BKE_defvert_find_index(self->data, key);
|
||||
|
||||
if (dw) {
|
||||
return PyFloat_FromDouble(dw->weight);
|
||||
}
|
||||
|
||||
return Py_NewRef(def);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_clear_doc,
|
||||
".. method:: clear()\n"
|
||||
"\n"
|
||||
" Clears all weights.\n");
|
||||
static PyObject *bpy_bmdeformvert_clear(BPy_BMDeformVert *self)
|
||||
{
|
||||
BKE_defvert_clear(self->data);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef bpy_bmdeformvert_methods[] = {
|
||||
{"keys",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_keys),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_keys_doc},
|
||||
{"values",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_values),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_values_doc},
|
||||
{"items",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_items),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_items_doc},
|
||||
{"get",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_get),
|
||||
METH_VARARGS,
|
||||
bpy_bmdeformvert_get_doc},
|
||||
/* BMESH_TODO `pop`, `popitem`, `update`. */
|
||||
{"clear",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_clear),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_clear_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyTypeObject BPy_BMDeformVert_Type; /* bm.loops.layers.uv.active */
|
||||
|
||||
static void bm_init_types_bmdvert()
|
||||
{
|
||||
BPy_BMDeformVert_Type.tp_basicsize = sizeof(BPy_BMDeformVert);
|
||||
|
||||
BPy_BMDeformVert_Type.tp_name = "BMDeformVert";
|
||||
|
||||
BPy_BMDeformVert_Type.tp_doc = nullptr; /* todo */
|
||||
|
||||
BPy_BMDeformVert_Type.tp_as_sequence = &bpy_bmdeformvert_as_sequence;
|
||||
BPy_BMDeformVert_Type.tp_as_mapping = &bpy_bmdeformvert_as_mapping;
|
||||
|
||||
BPy_BMDeformVert_Type.tp_methods = bpy_bmdeformvert_methods;
|
||||
|
||||
BPy_BMDeformVert_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMDeformVert_Type);
|
||||
}
|
||||
|
||||
int BPy_BMDeformVert_AssignPyObject(MDeformVert *dvert, PyObject *value)
|
||||
{
|
||||
if (UNLIKELY(!BPy_BMDeformVert_Check(value))) {
|
||||
PyErr_Format(PyExc_TypeError, "expected BMDeformVert, not a %.200s", Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MDeformVert *dvert_src = (reinterpret_cast<BPy_BMDeformVert *>(value))->data;
|
||||
if (LIKELY(dvert != dvert_src)) {
|
||||
BKE_defvert_copy(dvert, dvert_src);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMDeformVert_CreatePyObject(MDeformVert *dvert)
|
||||
{
|
||||
BPy_BMDeformVert *self = PyObject_New(BPy_BMDeformVert, &BPy_BMDeformVert_Type);
|
||||
self->data = dvert;
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/* --- End Mesh Deform Vert --- */
|
||||
|
||||
void BPy_BM_init_types_meshdata()
|
||||
{
|
||||
bm_init_types_bmloopuv();
|
||||
bm_init_types_bmloopcol();
|
||||
bm_init_types_bmdvert();
|
||||
bm_init_types_bmvertskin();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject BPy_BMLoopUV_Type;
|
||||
extern PyTypeObject BPy_BMDeformVert_Type;
|
||||
|
||||
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
|
||||
|
||||
struct BPy_BMGenericMeshData {
|
||||
PyObject_HEAD
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct MDeformVert;
|
||||
struct MLoopCol;
|
||||
struct MVertSkin;
|
||||
struct BMesh;
|
||||
|
||||
[[nodiscard]] int BPy_BMLoopUV_AssignPyObject(struct BMesh *bm, BMLoop *loop, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMLoopUV_CreatePyObject(struct BMesh *bm, BMLoop *loop, int layer);
|
||||
|
||||
[[nodiscard]] int BPy_BMVertSkin_AssignPyObject(struct MVertSkin *mvertskin, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMVertSkin_CreatePyObject(struct MVertSkin *mvertskin);
|
||||
|
||||
[[nodiscard]] int BPy_BMLoopColor_AssignPyObject(struct MLoopCol *mloopcol, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMLoopColor_CreatePyObject(struct MLoopCol *mloopcol);
|
||||
|
||||
[[nodiscard]] int BPy_BMDeformVert_AssignPyObject(struct MDeformVert *dvert, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMDeformVert_CreatePyObject(struct MDeformVert *dvert);
|
||||
|
||||
/* call to init all types */
|
||||
void BPy_BM_init_types_meshdata();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,491 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the types for 'BMesh.select_history'
|
||||
* sequence and iterator.
|
||||
*
|
||||
* select_history is very loosely based on pythons set() type,
|
||||
* since items can only exist once. however they do have an order.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
#include "bmesh_py_types_select.hh"
|
||||
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_active_doc,
|
||||
"The last selected element or None (read-only).\n"
|
||||
"\n"
|
||||
":type: :class:`bmesh.types.BMVert` | "
|
||||
":class:`bmesh.types.BMEdge` | :class:`bmesh.types.BMFace` | None\n");
|
||||
static PyObject *bpy_bmeditselseq_active_get(BPy_BMEditSelSeq *self, void * /*closure*/)
|
||||
{
|
||||
BMEditSelection *ese;
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((ese = static_cast<BMEditSelection *>(self->bm->selected.last))) {
|
||||
return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmeditselseq_getseters[] = {
|
||||
{"active",
|
||||
reinterpret_cast<getter>(bpy_bmeditselseq_active_get),
|
||||
static_cast<setter>(nullptr),
|
||||
bpy_bmeditselseq_active_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_validate_doc,
|
||||
".. method:: validate()\n"
|
||||
"\n"
|
||||
" Ensures all elements in the selection history are selected.\n");
|
||||
static PyObject *bpy_bmeditselseq_validate(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
BM_select_history_validate(self->bm);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_clear_doc,
|
||||
".. method:: clear()\n"
|
||||
"\n"
|
||||
" Empties the selection history.\n");
|
||||
static PyObject *bpy_bmeditselseq_clear(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
BM_select_history_clear(self->bm);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_add_doc,
|
||||
".. method:: add(element)\n"
|
||||
"\n"
|
||||
" Add an element to the selection history (no action taken if its already added).\n"
|
||||
"\n"
|
||||
" :param element: The element to add.\n"
|
||||
" :type element: :class:`BMVert` | :class:`BMEdge` | :class:`BMFace`\n");
|
||||
static PyObject *bpy_bmeditselseq_add(BPy_BMEditSelSeq *self, BPy_BMElem *value)
|
||||
{
|
||||
const char *error_prefix = "select_history.add(...)";
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a BMVert/BMedge/BMFace not a %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
|
||||
|
||||
BM_select_history_store(self->bm, value->ele);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_remove_doc,
|
||||
".. method:: remove(element)\n"
|
||||
"\n"
|
||||
" Remove an element from the selection history.\n"
|
||||
"\n"
|
||||
" :param element: The element to remove.\n"
|
||||
" :type element: :class:`BMVert` | :class:`BMEdge` | :class:`BMFace`\n");
|
||||
static PyObject *bpy_bmeditselseq_remove(BPy_BMEditSelSeq *self, BPy_BMElem *value)
|
||||
{
|
||||
const char *error_prefix = "select_history.remove(...)";
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a BMVert/BMedge/BMFace not a %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
|
||||
|
||||
if (BM_select_history_remove(self->bm, value->ele) == false) {
|
||||
PyErr_Format(PyExc_ValueError, "%s: element not found in selection history", error_prefix);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_discard_doc,
|
||||
".. method:: discard(element)\n"
|
||||
"\n"
|
||||
" Discard an element from the selection history.\n"
|
||||
"\n"
|
||||
" Like remove but doesn't raise an error when the element is not in the selection list.\n"
|
||||
"\n"
|
||||
" :param element: The element to discard.\n"
|
||||
" :type element: :class:`BMVert` | :class:`BMEdge` | :class:`BMFace`\n");
|
||||
static PyObject *bpy_bmeditselseq_discard(BPy_BMEditSelSeq *self, BPy_BMElem *value)
|
||||
{
|
||||
const char *error_prefix = "select_history.discard()";
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a BMVert/BMedge/BMFace not a %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
|
||||
|
||||
BM_select_history_remove(self->bm, value->ele);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef bpy_bmeditselseq_methods[] = {
|
||||
{"validate",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_validate),
|
||||
METH_NOARGS,
|
||||
bpy_bmeditselseq_validate_doc},
|
||||
{"clear",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_clear),
|
||||
METH_NOARGS,
|
||||
bpy_bmeditselseq_clear_doc},
|
||||
|
||||
{"add", reinterpret_cast<PyCFunction>(bpy_bmeditselseq_add), METH_O, bpy_bmeditselseq_add_doc},
|
||||
{"remove",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_remove),
|
||||
METH_O,
|
||||
bpy_bmeditselseq_remove_doc},
|
||||
{"discard",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_discard),
|
||||
METH_O,
|
||||
bpy_bmeditselseq_discard_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Sequences
|
||||
* ========= */
|
||||
|
||||
static Py_ssize_t bpy_bmeditselseq_length(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPY_BM_CHECK_INT(self);
|
||||
|
||||
return self->bm->selected.count();
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, Py_ssize_t keynum)
|
||||
{
|
||||
BMEditSelection *ese;
|
||||
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if (keynum < 0) {
|
||||
ese = static_cast<BMEditSelection *>(BLI_rfindlink(&self->bm->selected, -1 - keynum));
|
||||
}
|
||||
else {
|
||||
ese = static_cast<BMEditSelection *>(BLI_findlink(&self->bm->selected, keynum));
|
||||
}
|
||||
|
||||
if (ese) {
|
||||
return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_IndexError, "BMElemSeq[index]: index %d out of range", keynum);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self,
|
||||
Py_ssize_t start,
|
||||
Py_ssize_t stop)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
PyObject *list;
|
||||
BMEditSelection *ese;
|
||||
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
list = PyList_New(0);
|
||||
|
||||
/* First loop up-until the start. */
|
||||
for (ese = static_cast<BMEditSelection *>(self->bm->selected.first); ese; ese = ese->next) {
|
||||
if (count == start) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Add items until stop. */
|
||||
for (; ese; ese = ese->next) {
|
||||
PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head));
|
||||
count++;
|
||||
if (count == stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
|
||||
{
|
||||
/* don't need error check here */
|
||||
if (PyIndex_Check(key)) {
|
||||
const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
return bpy_bmeditselseq_subscript_int(self, i);
|
||||
}
|
||||
if (PySlice_Check(key)) {
|
||||
PySliceObject *key_slice = reinterpret_cast<PySliceObject *>(key);
|
||||
Py_ssize_t step = 1;
|
||||
|
||||
if (key_slice->step != Py_None && !_PyEval_SliceIndex(key_slice->step, &step)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (step != 1) {
|
||||
PyErr_SetString(PyExc_TypeError, "BMElemSeq[slice]: slice steps not supported");
|
||||
return nullptr;
|
||||
}
|
||||
if (key_slice->start == Py_None && key_slice->stop == Py_None) {
|
||||
return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
|
||||
}
|
||||
|
||||
Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
|
||||
|
||||
/* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
|
||||
if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (start < 0 || stop < 0) {
|
||||
/* only get the length for negative values */
|
||||
const Py_ssize_t len = bpy_bmeditselseq_length(self);
|
||||
if (start < 0) {
|
||||
start += len;
|
||||
CLAMP_MIN(start, 0);
|
||||
}
|
||||
if (stop < 0) {
|
||||
stop += len;
|
||||
CLAMP_MIN(stop, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (stop - start <= 0) {
|
||||
return PyList_New(0);
|
||||
}
|
||||
|
||||
return bpy_bmeditselseq_subscript_slice(self, start, stop);
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_AttributeError, "BMElemSeq[key]: invalid key, key must be an int");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int bpy_bmeditselseq_contains(BPy_BMEditSelSeq *self, PyObject *value)
|
||||
{
|
||||
BPy_BMElem *value_bm_ele;
|
||||
|
||||
BPY_BM_CHECK_INT(self);
|
||||
|
||||
value_bm_ele = reinterpret_cast<BPy_BMElem *>(value);
|
||||
if (value_bm_ele->bm == self->bm) {
|
||||
return BM_select_history_check(self->bm, value_bm_ele->ele);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PySequenceMethods bpy_bmeditselseq_as_sequence = {
|
||||
/*sq_length*/ reinterpret_cast<lenfunc>(bpy_bmeditselseq_length),
|
||||
/*sq_concat*/ nullptr,
|
||||
/*sq_repeat*/ nullptr,
|
||||
/* Only set this so `PySequence_Check()` returns True. */
|
||||
/*sq_item*/ reinterpret_cast<ssizeargfunc>(bpy_bmeditselseq_subscript_int),
|
||||
/*was_sq_slice*/ nullptr,
|
||||
/*sq_ass_item*/ nullptr,
|
||||
/*was_sq_ass_slice*/ nullptr,
|
||||
/*sq_contains*/ reinterpret_cast<objobjproc>(bpy_bmeditselseq_contains),
|
||||
/*sq_inplace_concat*/ nullptr,
|
||||
/*sq_inplace_repeat*/ nullptr,
|
||||
};
|
||||
|
||||
static PyMappingMethods bpy_bmeditselseq_as_mapping = {
|
||||
/*mp_length*/ reinterpret_cast<lenfunc>(bpy_bmeditselseq_length),
|
||||
/*mp_subscript*/ reinterpret_cast<binaryfunc>(bpy_bmeditselseq_subscript),
|
||||
/*mp_ass_subscript*/ static_cast<objobjargproc>(nullptr),
|
||||
};
|
||||
|
||||
/* Iterator
|
||||
* -------- */
|
||||
|
||||
static PyObject *bpy_bmeditselseq_iter(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPy_BMEditSelIter *py_iter;
|
||||
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
py_iter = reinterpret_cast<BPy_BMEditSelIter *>(BPy_BMEditSelIter_CreatePyObject(self->bm));
|
||||
py_iter->ese = static_cast<BMEditSelection *>(self->bm->selected.first);
|
||||
return reinterpret_cast<PyObject *>(py_iter);
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditseliter_next(BPy_BMEditSelIter *self)
|
||||
{
|
||||
BMEditSelection *ese = self->ese;
|
||||
if (ese == nullptr) {
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
self->ese = ese->next;
|
||||
return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
|
||||
}
|
||||
|
||||
PyTypeObject BPy_BMEditSelSeq_Type;
|
||||
PyTypeObject BPy_BMEditSelIter_Type;
|
||||
|
||||
PyObject *BPy_BMEditSel_CreatePyObject(BMesh *bm)
|
||||
{
|
||||
BPy_BMEditSelSeq *self = PyObject_New(BPy_BMEditSelSeq, &BPy_BMEditSelSeq_Type);
|
||||
self->bm = bm;
|
||||
/* caller must initialize 'iter' member */
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
PyObject *BPy_BMEditSelIter_CreatePyObject(BMesh *bm)
|
||||
{
|
||||
BPy_BMEditSelIter *self = PyObject_New(BPy_BMEditSelIter, &BPy_BMEditSelIter_Type);
|
||||
self->bm = bm;
|
||||
/* caller must initialize 'iter' member */
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void BPy_BM_init_types_select()
|
||||
{
|
||||
BPy_BMEditSelSeq_Type.tp_basicsize = sizeof(BPy_BMEditSelSeq);
|
||||
BPy_BMEditSelIter_Type.tp_basicsize = sizeof(BPy_BMEditSelIter);
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_name = "BMEditSelSeq";
|
||||
BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_doc = nullptr; /* todo */
|
||||
BPy_BMEditSelIter_Type.tp_doc = nullptr;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_repr = static_cast<reprfunc>(nullptr);
|
||||
BPy_BMEditSelIter_Type.tp_repr = static_cast<reprfunc>(nullptr);
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_getset = bpy_bmeditselseq_getseters;
|
||||
BPy_BMEditSelIter_Type.tp_getset = nullptr;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_methods = bpy_bmeditselseq_methods;
|
||||
BPy_BMEditSelIter_Type.tp_methods = nullptr;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_as_sequence = &bpy_bmeditselseq_as_sequence;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_as_mapping = &bpy_bmeditselseq_as_mapping;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_iter = reinterpret_cast<getiterfunc>(bpy_bmeditselseq_iter);
|
||||
|
||||
/* Only 1 iterator so far. */
|
||||
BPy_BMEditSelIter_Type.tp_iter = PyObject_SelfIter;
|
||||
BPy_BMEditSelIter_Type.tp_iternext = reinterpret_cast<iternextfunc>(bpy_bmeditseliter_next);
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_dealloc = nullptr; //(destructor)bpy_bmeditselseq_dealloc;
|
||||
BPy_BMEditSelIter_Type.tp_dealloc = nullptr; //(destructor)bpy_bmvert_dealloc;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
BPy_BMEditSelIter_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMEditSelSeq_Type);
|
||||
PyType_Ready(&BPy_BMEditSelIter_Type);
|
||||
}
|
||||
|
||||
/* utility function */
|
||||
|
||||
int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
|
||||
{
|
||||
const char *error_prefix = "BMesh.select_history = value";
|
||||
BPY_BM_CHECK_INT(self);
|
||||
|
||||
BMesh *bm = self->bm;
|
||||
|
||||
Py_ssize_t value_num;
|
||||
BMElem **value_array = static_cast<BMElem **>(
|
||||
BPy_BMElem_PySeq_As_Array(&bm,
|
||||
value,
|
||||
0,
|
||||
PY_SSIZE_T_MAX,
|
||||
&value_num,
|
||||
BM_VERT | BM_EDGE | BM_FACE,
|
||||
true,
|
||||
true,
|
||||
error_prefix));
|
||||
|
||||
if (value_array == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BM_select_history_clear(bm);
|
||||
|
||||
for (Py_ssize_t i = 0; i < value_num; i++) {
|
||||
BM_select_history_store_notest(bm, value_array[i]);
|
||||
}
|
||||
|
||||
PyMem_FREE(value_array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
struct BBMesh;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMEditSelection;
|
||||
struct BPy_BMesh;
|
||||
|
||||
extern PyTypeObject BPy_BMEditSelSeq_Type;
|
||||
extern PyTypeObject BPy_BMEditSelIter_Type;
|
||||
|
||||
#define BPy_BMSelectHistory_Check(v) (Py_TYPE(v) == &BPy_BMEditSelSeq_Type)
|
||||
#define BPy_BMSelectHistoryIter_Check(v) (Py_TYPE(v) == &BPy_BMEditSelIter_Type)
|
||||
|
||||
struct BPy_BMEditSelSeq {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
};
|
||||
|
||||
struct BPy_BMEditSelIter {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMEditSelection *ese;
|
||||
};
|
||||
|
||||
void BPy_BM_init_types_select();
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMEditSel_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMEditSelIter_CreatePyObject(BMesh *bm);
|
||||
/**
|
||||
* \note doesn't actually check selection.
|
||||
*/
|
||||
[[nodiscard]] int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value);
|
||||
|
||||
} // namespace blender
|
||||
1019
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.cc
Normal file
1019
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.cc
Normal file
File diff suppressed because it is too large
Load Diff
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.hh
Normal file
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_utils();
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user