Blender V5.0
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:`bmesh.types.BMVert`, "
33 ":class:`bmesh.types.BMEdge` or :class:`bmesh.types.BMFace`\n");
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{
89 const char *error_prefix = "select_history.add(...)";
91
92 if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
93 PyErr_Format(PyExc_TypeError,
94 "%s: expected a BMVert/BMedge/BMFace not a %.200s",
95 error_prefix,
96 Py_TYPE(value)->tp_name);
97 return nullptr;
98 }
99
100 BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
101
102 BM_select_history_store(self->bm, value->ele);
103
104 Py_RETURN_NONE;
105}
106
108 /* Wrap. */
109 bpy_bmeditselseq_remove_doc,
110 ".. method:: remove(element)\n"
111 "\n"
112 " Remove an element from the selection history.\n");
114{
115 const char *error_prefix = "select_history.remove(...)";
117
118 if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
119 PyErr_Format(PyExc_TypeError,
120 "%s: expected a BMVert/BMedge/BMFace not a %.200s",
121 error_prefix,
122 Py_TYPE(value)->tp_name);
123 return nullptr;
124 }
125
126 BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
127
128 if (BM_select_history_remove(self->bm, value->ele) == false) {
129 PyErr_Format(PyExc_ValueError, "%s: element not found in selection history", error_prefix);
130 return nullptr;
131 }
132
133 Py_RETURN_NONE;
134}
135
137 /* Wrap. */
138 bpy_bmeditselseq_discard_doc,
139 ".. method:: discard(element)\n"
140 "\n"
141 " Discard an element from the selection history.\n"
142 "\n"
143 " Like remove but doesn't raise an error when the elements not in the selection list.\n");
145{
146 const char *error_prefix = "select_history.discard()";
148
149 if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
150 PyErr_Format(PyExc_TypeError,
151 "%s: expected a BMVert/BMedge/BMFace not a %.200s",
152 error_prefix,
153 Py_TYPE(value)->tp_name);
154 return nullptr;
155 }
156
157 BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
158
159 BM_select_history_remove(self->bm, value->ele);
160
161 Py_RETURN_NONE;
162}
163
164#ifdef __GNUC__
165# ifdef __clang__
166# pragma clang diagnostic push
167# pragma clang diagnostic ignored "-Wcast-function-type"
168# else
169# pragma GCC diagnostic push
170# pragma GCC diagnostic ignored "-Wcast-function-type"
171# endif
172#endif
173
174static PyMethodDef bpy_bmeditselseq_methods[] = {
175 {"validate",
176 (PyCFunction)bpy_bmeditselseq_validate,
177 METH_NOARGS,
178 bpy_bmeditselseq_validate_doc},
179 {"clear", (PyCFunction)bpy_bmeditselseq_clear, METH_NOARGS, bpy_bmeditselseq_clear_doc},
180
181 {"add", (PyCFunction)bpy_bmeditselseq_add, METH_O, bpy_bmeditselseq_add_doc},
182 {"remove", (PyCFunction)bpy_bmeditselseq_remove, METH_O, bpy_bmeditselseq_remove_doc},
183 {"discard", (PyCFunction)bpy_bmeditselseq_discard, METH_O, bpy_bmeditselseq_discard_doc},
184 {nullptr, nullptr, 0, nullptr},
185};
186
187#ifdef __GNUC__
188# ifdef __clang__
189# pragma clang diagnostic pop
190# else
191# pragma GCC diagnostic pop
192# endif
193#endif
194
195/* Sequences
196 * ========= */
197
199{
201
202 return BLI_listbase_count(&self->bm->selected);
203}
204
205static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, Py_ssize_t keynum)
206{
207 BMEditSelection *ese;
208
210
211 if (keynum < 0) {
212 ese = static_cast<BMEditSelection *>(BLI_rfindlink(&self->bm->selected, -1 - keynum));
213 }
214 else {
215 ese = static_cast<BMEditSelection *>(BLI_findlink(&self->bm->selected, keynum));
216 }
217
218 if (ese) {
219 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
220 }
221
222 PyErr_Format(PyExc_IndexError, "BMElemSeq[index]: index %d out of range", keynum);
223 return nullptr;
224}
225
227 Py_ssize_t start,
228 Py_ssize_t stop)
229{
230 int count = 0;
231
232 PyObject *list;
233 BMEditSelection *ese;
234
236
237 list = PyList_New(0);
238
239 /* First loop up-until the start. */
240 for (ese = static_cast<BMEditSelection *>(self->bm->selected.first); ese; ese = ese->next) {
241 if (count == start) {
242 break;
243 }
244 count++;
245 }
246
247 /* Add items until stop. */
248 for (; ese; ese = ese->next) {
249 PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head));
250 count++;
251 if (count == stop) {
252 break;
253 }
254 }
255
256 return list;
257}
258
259static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
260{
261 /* don't need error check here */
262 if (PyIndex_Check(key)) {
263 const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
264 if (i == -1 && PyErr_Occurred()) {
265 return nullptr;
266 }
268 }
269 if (PySlice_Check(key)) {
270 PySliceObject *key_slice = (PySliceObject *)key;
271 Py_ssize_t step = 1;
272
273 if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
274 return nullptr;
275 }
276 if (step != 1) {
277 PyErr_SetString(PyExc_TypeError, "BMElemSeq[slice]: slice steps not supported");
278 return nullptr;
279 }
280 if (key_slice->start == Py_None && key_slice->stop == Py_None) {
281 return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
282 }
283
284 Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
285
286 /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
287 if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) {
288 return nullptr;
289 }
290 if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop)) {
291 return nullptr;
292 }
293
294 if (start < 0 || stop < 0) {
295 /* only get the length for negative values */
296 const Py_ssize_t len = bpy_bmeditselseq_length(self);
297 if (start < 0) {
298 start += len;
299 CLAMP_MIN(start, 0);
300 }
301 if (stop < 0) {
302 stop += len;
303 CLAMP_MIN(stop, 0);
304 }
305 }
306
307 if (stop - start <= 0) {
308 return PyList_New(0);
309 }
310
311 return bpy_bmeditselseq_subscript_slice(self, start, stop);
312 }
313
314 PyErr_SetString(PyExc_AttributeError, "BMElemSeq[key]: invalid key, key must be an int");
315 return nullptr;
316}
317
319{
320 BPy_BMElem *value_bm_ele;
321
323
324 value_bm_ele = (BPy_BMElem *)value;
325 if (value_bm_ele->bm == self->bm) {
326 return BM_select_history_check(self->bm, value_bm_ele->ele);
327 }
328
329 return 0;
330}
331
332static PySequenceMethods bpy_bmeditselseq_as_sequence = {
333 /*sq_length*/ (lenfunc)bpy_bmeditselseq_length,
334 /*sq_concat*/ nullptr,
335 /*sq_repeat*/ nullptr,
336 /* Only set this so `PySequence_Check()` returns True. */
337 /*sq_item*/ (ssizeargfunc)bpy_bmeditselseq_subscript_int,
338 /*was_sq_slice*/ nullptr,
339 /*sq_ass_item*/ nullptr,
340 /*was_sq_ass_slice*/ nullptr,
341 /*sq_contains*/ (objobjproc)bpy_bmeditselseq_contains,
342 /*sq_inplace_concat*/ nullptr,
343 /*sq_inplace_repeat*/ nullptr,
344};
345
346static PyMappingMethods bpy_bmeditselseq_as_mapping = {
347 /*mp_length*/ (lenfunc)bpy_bmeditselseq_length,
348 /*mp_subscript*/ (binaryfunc)bpy_bmeditselseq_subscript,
349 /*mp_ass_subscript*/ (objobjargproc) nullptr,
350};
351
352/* Iterator
353 * -------- */
354
356{
357 BPy_BMEditSelIter *py_iter;
358
361 py_iter->ese = static_cast<BMEditSelection *>(self->bm->selected.first);
362 return (PyObject *)py_iter;
363}
364
366{
367 BMEditSelection *ese = self->ese;
368 if (ese == nullptr) {
369 PyErr_SetNone(PyExc_StopIteration);
370 return nullptr;
371 }
372
373 self->ese = ese->next;
374 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
375}
376
379
381{
383 self->bm = bm;
384 /* caller must initialize 'iter' member */
385 return (PyObject *)self;
386}
387
389{
391 self->bm = bm;
392 /* caller must initialize 'iter' member */
393 return (PyObject *)self;
394}
395
397{
398 BPy_BMEditSelSeq_Type.tp_basicsize = sizeof(BPy_BMEditSelSeq);
399 BPy_BMEditSelIter_Type.tp_basicsize = sizeof(BPy_BMEditSelIter);
400
401 BPy_BMEditSelSeq_Type.tp_name = "BMEditSelSeq";
402 BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
403
404 BPy_BMEditSelSeq_Type.tp_doc = nullptr; /* todo */
405 BPy_BMEditSelIter_Type.tp_doc = nullptr;
406
407 BPy_BMEditSelSeq_Type.tp_repr = (reprfunc) nullptr;
408 BPy_BMEditSelIter_Type.tp_repr = (reprfunc) nullptr;
409
411 BPy_BMEditSelIter_Type.tp_getset = nullptr;
412
414 BPy_BMEditSelIter_Type.tp_methods = nullptr;
415
417
419
420 BPy_BMEditSelSeq_Type.tp_iter = (getiterfunc)bpy_bmeditselseq_iter;
421
422 /* Only 1 iterator so far. */
423 BPy_BMEditSelIter_Type.tp_iternext = (iternextfunc)bpy_bmeditseliter_next;
424
425 BPy_BMEditSelSeq_Type.tp_dealloc = nullptr; //(destructor)bpy_bmeditselseq_dealloc;
426 BPy_BMEditSelIter_Type.tp_dealloc = nullptr; //(destructor)bpy_bmvert_dealloc;
427
428 BPy_BMEditSelSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT;
429 BPy_BMEditSelIter_Type.tp_flags = Py_TPFLAGS_DEFAULT;
430
431 PyType_Ready(&BPy_BMEditSelSeq_Type);
432 PyType_Ready(&BPy_BMEditSelIter_Type);
433}
434
435/* utility function */
436
437int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
438{
439 const char *error_prefix = "BMesh.select_history = value";
441
442 BMesh *bm = self->bm;
443
444 Py_ssize_t value_num;
445 BMElem **value_array = static_cast<BMElem **>(
447 value,
448 0,
449 PY_SSIZE_T_MAX,
450 &value_num,
452 true,
453 true,
454 error_prefix));
455
456 if (value_array == nullptr) {
457 return -1;
458 }
459
461
462 for (Py_ssize_t i = 0; i < value_num; i++) {
463 BM_select_history_store_notest(bm, value_array[i]);
464 }
465
466 PyMem_FREE(value_array);
467 return 0;
468}
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)
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_seq_num, 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)
PyDoc_STRVAR(bpy_bmeditselseq_active_doc, "The last selected element or None (read-only).\n" "\n" ":type: :class:`bmesh.types.BMVert`, " ":class:`bmesh.types.BMEdge` or :class:`bmesh.types.BMFace`\n")
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)
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