Blender V4.5
bmesh_py_types_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2012 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
14
15#include <Python.h>
16
17#include "BLI_listbase.h"
18#include "BLI_utildefines.h"
19
20#include "bmesh.hh"
21
22#include "bmesh_py_types.hh"
24
26
28 /* Wrap. */
29 bpy_bmeditselseq_active_doc,
30 "The last selected element or None (read-only).\n"
31 "\n"
32 ":type: :class:`BMVert`, "
33 ":class:`BMEdge` or :class:`BMFace`");
34static PyObject *bpy_bmeditselseq_active_get(BPy_BMEditSelSeq *self, void * /*closure*/)
35{
36 BMEditSelection *ese;
38
39 if ((ese = static_cast<BMEditSelection *>(self->bm->selected.last))) {
40 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
41 }
42
43 Py_RETURN_NONE;
44}
45
46static PyGetSetDef bpy_bmeditselseq_getseters[] = {
47 {"active",
49 (setter) nullptr,
50 bpy_bmeditselseq_active_doc,
51 nullptr},
52 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
53};
54
56 /* Wrap. */
57 bpy_bmeditselseq_validate_doc,
58 ".. method:: validate()\n"
59 "\n"
60 " Ensures all elements in the selection history are selected.\n");
62{
65 Py_RETURN_NONE;
66}
67
69 /* Wrap. */
70 bpy_bmeditselseq_clear_doc,
71 ".. method:: clear()\n"
72 "\n"
73 " Empties the selection history.\n");
75{
78 Py_RETURN_NONE;
79}
80
82 /* Wrap. */
83 bpy_bmeditselseq_add_doc,
84 ".. method:: add(element)\n"
85 "\n"
86 " Add an element to the selection history (no action taken if its already added).\n");
88{
90
91 if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
92 PyErr_Format(
93 PyExc_TypeError, "Expected a BMVert/BMedge/BMFace not a %.200s", Py_TYPE(value)->tp_name);
94 return nullptr;
95 }
96
97 BPY_BM_CHECK_SOURCE_OBJ(self->bm, "select_history.add()", value);
98
99 BM_select_history_store(self->bm, value->ele);
100
101 Py_RETURN_NONE;
102}
103
105 /* Wrap. */
106 bpy_bmeditselseq_remove_doc,
107 ".. method:: remove(element)\n"
108 "\n"
109 " Remove an element from the selection history.\n");
111{
113
114 if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
115 PyErr_Format(
116 PyExc_TypeError, "Expected a BMVert/BMedge/BMFace not a %.200s", Py_TYPE(value)->tp_name);
117 return nullptr;
118 }
119
120 BPY_BM_CHECK_SOURCE_OBJ(self->bm, "select_history.remove()", value);
121
122 if (BM_select_history_remove(self->bm, value->ele) == false) {
123 PyErr_SetString(PyExc_ValueError, "Element not found in selection history");
124 return nullptr;
125 }
126
127 Py_RETURN_NONE;
128}
129
131 /* Wrap. */
132 bpy_bmeditselseq_discard_doc,
133 ".. method:: discard(element)\n"
134 "\n"
135 " Discard an element from the selection history.\n"
136 "\n"
137 " Like remove but doesn't raise an error when the elements not in the selection list.\n");
139{
141
142 if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
143 PyErr_Format(
144 PyExc_TypeError, "Expected a BMVert/BMedge/BMFace not a %.200s", Py_TYPE(value)->tp_name);
145 return nullptr;
146 }
147
148 BPY_BM_CHECK_SOURCE_OBJ(self->bm, "select_history.discard()", value);
149
150 BM_select_history_remove(self->bm, value->ele);
151
152 Py_RETURN_NONE;
153}
154
155#ifdef __GNUC__
156# ifdef __clang__
157# pragma clang diagnostic push
158# pragma clang diagnostic ignored "-Wcast-function-type"
159# else
160# pragma GCC diagnostic push
161# pragma GCC diagnostic ignored "-Wcast-function-type"
162# endif
163#endif
164
165static PyMethodDef bpy_bmeditselseq_methods[] = {
166 {"validate",
167 (PyCFunction)bpy_bmeditselseq_validate,
168 METH_NOARGS,
169 bpy_bmeditselseq_validate_doc},
170 {"clear", (PyCFunction)bpy_bmeditselseq_clear, METH_NOARGS, bpy_bmeditselseq_clear_doc},
171
172 {"add", (PyCFunction)bpy_bmeditselseq_add, METH_O, bpy_bmeditselseq_add_doc},
173 {"remove", (PyCFunction)bpy_bmeditselseq_remove, METH_O, bpy_bmeditselseq_remove_doc},
174 {"discard", (PyCFunction)bpy_bmeditselseq_discard, METH_O, bpy_bmeditselseq_discard_doc},
175 {nullptr, nullptr, 0, nullptr},
176};
177
178#ifdef __GNUC__
179# ifdef __clang__
180# pragma clang diagnostic pop
181# else
182# pragma GCC diagnostic pop
183# endif
184#endif
185
186/* Sequences
187 * ========= */
188
190{
192
193 return BLI_listbase_count(&self->bm->selected);
194}
195
196static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, Py_ssize_t keynum)
197{
198 BMEditSelection *ese;
199
201
202 if (keynum < 0) {
203 ese = static_cast<BMEditSelection *>(BLI_rfindlink(&self->bm->selected, -1 - keynum));
204 }
205 else {
206 ese = static_cast<BMEditSelection *>(BLI_findlink(&self->bm->selected, keynum));
207 }
208
209 if (ese) {
210 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
211 }
212
213 PyErr_Format(PyExc_IndexError, "BMElemSeq[index]: index %d out of range", keynum);
214 return nullptr;
215}
216
218 Py_ssize_t start,
219 Py_ssize_t stop)
220{
221 int count = 0;
222
223 PyObject *list;
224 BMEditSelection *ese;
225
227
228 list = PyList_New(0);
229
230 /* First loop up-until the start. */
231 for (ese = static_cast<BMEditSelection *>(self->bm->selected.first); ese; ese = ese->next) {
232 if (count == start) {
233 break;
234 }
235 count++;
236 }
237
238 /* Add items until stop. */
239 for (; ese; ese = ese->next) {
240 PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head));
241 count++;
242 if (count == stop) {
243 break;
244 }
245 }
246
247 return list;
248}
249
250static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
251{
252 /* don't need error check here */
253 if (PyIndex_Check(key)) {
254 const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
255 if (i == -1 && PyErr_Occurred()) {
256 return nullptr;
257 }
259 }
260 if (PySlice_Check(key)) {
261 PySliceObject *key_slice = (PySliceObject *)key;
262 Py_ssize_t step = 1;
263
264 if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
265 return nullptr;
266 }
267 if (step != 1) {
268 PyErr_SetString(PyExc_TypeError, "BMElemSeq[slice]: slice steps not supported");
269 return nullptr;
270 }
271 if (key_slice->start == Py_None && key_slice->stop == Py_None) {
272 return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
273 }
274
275 Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
276
277 /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
278 if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) {
279 return nullptr;
280 }
281 if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop)) {
282 return nullptr;
283 }
284
285 if (start < 0 || stop < 0) {
286 /* only get the length for negative values */
287 const Py_ssize_t len = bpy_bmeditselseq_length(self);
288 if (start < 0) {
289 start += len;
290 CLAMP_MIN(start, 0);
291 }
292 if (stop < 0) {
293 stop += len;
294 CLAMP_MIN(stop, 0);
295 }
296 }
297
298 if (stop - start <= 0) {
299 return PyList_New(0);
300 }
301
303 }
304
305 PyErr_SetString(PyExc_AttributeError, "BMElemSeq[key]: invalid key, key must be an int");
306 return nullptr;
307}
308
310{
311 BPy_BMElem *value_bm_ele;
312
314
315 value_bm_ele = (BPy_BMElem *)value;
316 if (value_bm_ele->bm == self->bm) {
317 return BM_select_history_check(self->bm, value_bm_ele->ele);
318 }
319
320 return 0;
321}
322
323static PySequenceMethods bpy_bmeditselseq_as_sequence = {
324 /*sq_length*/ (lenfunc)bpy_bmeditselseq_length,
325 /*sq_concat*/ nullptr,
326 /*sq_repeat*/ nullptr,
327 /* Only set this so `PySequence_Check()` returns True. */
328 /*sq_item*/ (ssizeargfunc)bpy_bmeditselseq_subscript_int,
329 /*was_sq_slice*/ nullptr,
330 /*sq_ass_item*/ nullptr,
331 /*was_sq_ass_slice*/ nullptr,
332 /*sq_contains*/ (objobjproc)bpy_bmeditselseq_contains,
333 /*sq_inplace_concat*/ nullptr,
334 /*sq_inplace_repeat*/ nullptr,
335};
336
337static PyMappingMethods bpy_bmeditselseq_as_mapping = {
338 /*mp_length*/ (lenfunc)bpy_bmeditselseq_length,
339 /*mp_subscript*/ (binaryfunc)bpy_bmeditselseq_subscript,
340 /*mp_ass_subscript*/ (objobjargproc) nullptr,
341};
342
343/* Iterator
344 * -------- */
345
347{
348 BPy_BMEditSelIter *py_iter;
349
352 py_iter->ese = static_cast<BMEditSelection *>(self->bm->selected.first);
353 return (PyObject *)py_iter;
354}
355
357{
358 BMEditSelection *ese = self->ese;
359 if (ese == nullptr) {
360 PyErr_SetNone(PyExc_StopIteration);
361 return nullptr;
362 }
363
364 self->ese = ese->next;
365 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
366}
367
370
372{
374 self->bm = bm;
375 /* caller must initialize 'iter' member */
376 return (PyObject *)self;
377}
378
380{
382 self->bm = bm;
383 /* caller must initialize 'iter' member */
384 return (PyObject *)self;
385}
386
388{
389 BPy_BMEditSelSeq_Type.tp_basicsize = sizeof(BPy_BMEditSelSeq);
390 BPy_BMEditSelIter_Type.tp_basicsize = sizeof(BPy_BMEditSelIter);
391
392 BPy_BMEditSelSeq_Type.tp_name = "BMEditSelSeq";
393 BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
394
395 BPy_BMEditSelSeq_Type.tp_doc = nullptr; /* todo */
396 BPy_BMEditSelIter_Type.tp_doc = nullptr;
397
398 BPy_BMEditSelSeq_Type.tp_repr = (reprfunc) nullptr;
399 BPy_BMEditSelIter_Type.tp_repr = (reprfunc) nullptr;
400
402 BPy_BMEditSelIter_Type.tp_getset = nullptr;
403
405 BPy_BMEditSelIter_Type.tp_methods = nullptr;
406
408
410
411 BPy_BMEditSelSeq_Type.tp_iter = (getiterfunc)bpy_bmeditselseq_iter;
412
413 /* Only 1 iterator so far. */
414 BPy_BMEditSelIter_Type.tp_iternext = (iternextfunc)bpy_bmeditseliter_next;
415
416 BPy_BMEditSelSeq_Type.tp_dealloc = nullptr; //(destructor)bpy_bmeditselseq_dealloc;
417 BPy_BMEditSelIter_Type.tp_dealloc = nullptr; //(destructor)bpy_bmvert_dealloc;
418
419 BPy_BMEditSelSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT;
420 BPy_BMEditSelIter_Type.tp_flags = Py_TPFLAGS_DEFAULT;
421
422 PyType_Ready(&BPy_BMEditSelSeq_Type);
423 PyType_Ready(&BPy_BMEditSelIter_Type);
424}
425
426/* utility function */
427
428int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
429{
430 BMesh *bm;
431 Py_ssize_t value_len;
432 Py_ssize_t i;
433 BMElem **value_array = nullptr;
434
436
437 bm = self->bm;
438
439 value_array = static_cast<BMElem **>(BPy_BMElem_PySeq_As_Array(&bm,
440 value,
441 0,
442 PY_SSIZE_T_MAX,
443 &value_len,
445 true,
446 true,
447 "BMesh.select_history = value"));
448
449 if (value_array == nullptr) {
450 return -1;
451 }
452
454
455 for (i = 0; i < value_len; i++) {
456 BM_select_history_store_notest(bm, value_array[i]);
457 }
458
459 PyMem_FREE(value_array);
460 return 0;
461}
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
void * BLI_rfindlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:549
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
#define CLAMP_MIN(a, b)
bool stop
Definition WM_types.hh:1016
BMesh * bm
void BM_select_history_clear(BMesh *bm)
void BM_select_history_validate(BMesh *bm)
#define BM_select_history_store_notest(bm, ele)
#define BM_select_history_store(bm, ele)
#define BM_select_history_check(bm, ele)
#define BM_select_history_remove(bm, ele)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
PyObject * BPy_BMElem_CreatePyObject(BMesh *bm, BMHeader *ele)
void * BPy_BMElem_PySeq_As_Array(BMesh **r_bm, PyObject *seq, Py_ssize_t min, Py_ssize_t max, Py_ssize_t *r_size, const char htype, const bool do_unique_check, const bool do_bm_check, const char *error_prefix)
#define BPy_BMFace_Check(v)
#define BPY_BM_CHECK_OBJ(obj)
#define BPy_BMVert_Check(v)
#define BPy_BMEdge_Check(v)
#define BPY_BM_CHECK_SOURCE_OBJ(bm, errmsg,...)
#define BPY_BM_CHECK_INT(obj)
static int bpy_bmeditselseq_contains(BPy_BMEditSelSeq *self, PyObject *value)
static PyObject * bpy_bmeditselseq_remove(BPy_BMEditSelSeq *self, BPy_BMElem *value)
static PyObject * bpy_bmeditselseq_discard(BPy_BMEditSelSeq *self, BPy_BMElem *value)
static PyMethodDef bpy_bmeditselseq_methods[]
int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
static PyObject * bpy_bmeditselseq_active_get(BPy_BMEditSelSeq *self, void *)
void BPy_BM_init_types_select()
static PyObject * bpy_bmeditselseq_add(BPy_BMEditSelSeq *self, BPy_BMElem *value)
static PyObject * bpy_bmeditselseq_clear(BPy_BMEditSelSeq *self)
static PyMappingMethods bpy_bmeditselseq_as_mapping
static PyObject * bpy_bmeditselseq_iter(BPy_BMEditSelSeq *self)
static PyObject * bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
static PyObject * bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, Py_ssize_t keynum)
PyTypeObject BPy_BMEditSelSeq_Type
static PyGetSetDef bpy_bmeditselseq_getseters[]
static PyObject * bpy_bmeditseliter_next(BPy_BMEditSelIter *self)
PyObject * BPy_BMEditSelIter_CreatePyObject(BMesh *bm)
static Py_ssize_t bpy_bmeditselseq_length(BPy_BMEditSelSeq *self)
static PyObject * bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, Py_ssize_t start, Py_ssize_t stop)
static PyObject * bpy_bmeditselseq_validate(BPy_BMEditSelSeq *self)
PyTypeObject BPy_BMEditSelIter_Type
PyObject * BPy_BMEditSel_CreatePyObject(BMesh *bm)
PyDoc_STRVAR(bpy_bmeditselseq_active_doc, "The last selected element or None (read-only).\n" "\n" ":type: :class:`BMVert`, " ":class:`BMEdge` or :class:`BMFace`")
static PySequenceMethods bpy_bmeditselseq_as_sequence
PyObject * self
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
int count
header-only utilities
struct BMEditSelection * next
BMHeader head
PyObject_VAR_HEAD BMesh * bm
i
Definition text_draw.cc:230
uint len