Blender V4.3
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
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#if (defined(__GNUC__) && !defined(__clang__))
156# pragma GCC diagnostic push
157# pragma GCC diagnostic ignored "-Wcast-function-type"
158#endif
159
160static PyMethodDef bpy_bmeditselseq_methods[] = {
161 {"validate",
162 (PyCFunction)bpy_bmeditselseq_validate,
163 METH_NOARGS,
164 bpy_bmeditselseq_validate_doc},
165 {"clear", (PyCFunction)bpy_bmeditselseq_clear, METH_NOARGS, bpy_bmeditselseq_clear_doc},
166
167 {"add", (PyCFunction)bpy_bmeditselseq_add, METH_O, bpy_bmeditselseq_add_doc},
168 {"remove", (PyCFunction)bpy_bmeditselseq_remove, METH_O, bpy_bmeditselseq_remove_doc},
169 {"discard", (PyCFunction)bpy_bmeditselseq_discard, METH_O, bpy_bmeditselseq_discard_doc},
170 {nullptr, nullptr, 0, nullptr},
171};
172
173#if (defined(__GNUC__) && !defined(__clang__))
174# pragma GCC diagnostic pop
175#endif
176
177/* Sequences
178 * ========= */
179
181{
183
184 return BLI_listbase_count(&self->bm->selected);
185}
186
187static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, Py_ssize_t keynum)
188{
189 BMEditSelection *ese;
190
192
193 if (keynum < 0) {
194 ese = static_cast<BMEditSelection *>(BLI_rfindlink(&self->bm->selected, -1 - keynum));
195 }
196 else {
197 ese = static_cast<BMEditSelection *>(BLI_findlink(&self->bm->selected, keynum));
198 }
199
200 if (ese) {
201 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
202 }
203
204 PyErr_Format(PyExc_IndexError, "BMElemSeq[index]: index %d out of range", keynum);
205 return nullptr;
206}
207
209 Py_ssize_t start,
210 Py_ssize_t stop)
211{
212 int count = 0;
213
214 PyObject *list;
215 BMEditSelection *ese;
216
218
219 list = PyList_New(0);
220
221 /* First loop up-until the start. */
222 for (ese = static_cast<BMEditSelection *>(self->bm->selected.first); ese; ese = ese->next) {
223 if (count == start) {
224 break;
225 }
226 count++;
227 }
228
229 /* Add items until stop. */
230 for (; ese; ese = ese->next) {
231 PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head));
232 count++;
233 if (count == stop) {
234 break;
235 }
236 }
237
238 return list;
239}
240
241static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
242{
243 /* don't need error check here */
244 if (PyIndex_Check(key)) {
245 const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
246 if (i == -1 && PyErr_Occurred()) {
247 return nullptr;
248 }
250 }
251 if (PySlice_Check(key)) {
252 PySliceObject *key_slice = (PySliceObject *)key;
253 Py_ssize_t step = 1;
254
255 if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
256 return nullptr;
257 }
258 if (step != 1) {
259 PyErr_SetString(PyExc_TypeError, "BMElemSeq[slice]: slice steps not supported");
260 return nullptr;
261 }
262 if (key_slice->start == Py_None && key_slice->stop == Py_None) {
263 return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
264 }
265
266 Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
267
268 /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
269 if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) {
270 return nullptr;
271 }
272 if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop)) {
273 return nullptr;
274 }
275
276 if (start < 0 || stop < 0) {
277 /* only get the length for negative values */
278 const Py_ssize_t len = bpy_bmeditselseq_length(self);
279 if (start < 0) {
280 start += len;
281 CLAMP_MIN(start, 0);
282 }
283 if (stop < 0) {
284 stop += len;
285 CLAMP_MIN(stop, 0);
286 }
287 }
288
289 if (stop - start <= 0) {
290 return PyList_New(0);
291 }
292
293 return bpy_bmeditselseq_subscript_slice(self, start, stop);
294 }
295
296 PyErr_SetString(PyExc_AttributeError, "BMElemSeq[key]: invalid key, key must be an int");
297 return nullptr;
298}
299
301{
302 BPy_BMElem *value_bm_ele;
303
305
306 value_bm_ele = (BPy_BMElem *)value;
307 if (value_bm_ele->bm == self->bm) {
308 return BM_select_history_check(self->bm, value_bm_ele->ele);
309 }
310
311 return 0;
312}
313
314static PySequenceMethods bpy_bmeditselseq_as_sequence = {
315 /*sq_length*/ (lenfunc)bpy_bmeditselseq_length,
316 /*sq_concat*/ nullptr,
317 /*sq_repeat*/ nullptr,
318 /* Only set this so `PySequence_Check()` returns True. */
319 /*sq_item*/ (ssizeargfunc)bpy_bmeditselseq_subscript_int,
320 /*was_sq_slice*/ nullptr,
321 /*sq_ass_item*/ nullptr,
322 /*was_sq_ass_slice*/ nullptr,
323 /*sq_contains*/ (objobjproc)bpy_bmeditselseq_contains,
324 /*sq_inplace_concat*/ nullptr,
325 /*sq_inplace_repeat*/ nullptr,
326};
327
328static PyMappingMethods bpy_bmeditselseq_as_mapping = {
329 /*mp_length*/ (lenfunc)bpy_bmeditselseq_length,
330 /*mp_subscript*/ (binaryfunc)bpy_bmeditselseq_subscript,
331 /*mp_ass_subscript*/ (objobjargproc) nullptr,
332};
333
334/* Iterator
335 * -------- */
336
338{
339 BPy_BMEditSelIter *py_iter;
340
343 py_iter->ese = static_cast<BMEditSelection *>(self->bm->selected.first);
344 return (PyObject *)py_iter;
345}
346
348{
349 BMEditSelection *ese = self->ese;
350 if (ese == nullptr) {
351 PyErr_SetNone(PyExc_StopIteration);
352 return nullptr;
353 }
354
355 self->ese = ese->next;
356 return (PyObject *)BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
357}
358
361
363{
365 self->bm = bm;
366 /* caller must initialize 'iter' member */
367 return (PyObject *)self;
368}
369
371{
373 self->bm = bm;
374 /* caller must initialize 'iter' member */
375 return (PyObject *)self;
376}
377
379{
380 BPy_BMEditSelSeq_Type.tp_basicsize = sizeof(BPy_BMEditSelSeq);
381 BPy_BMEditSelIter_Type.tp_basicsize = sizeof(BPy_BMEditSelIter);
382
383 BPy_BMEditSelSeq_Type.tp_name = "BMEditSelSeq";
384 BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
385
386 BPy_BMEditSelSeq_Type.tp_doc = nullptr; /* todo */
387 BPy_BMEditSelIter_Type.tp_doc = nullptr;
388
389 BPy_BMEditSelSeq_Type.tp_repr = (reprfunc) nullptr;
390 BPy_BMEditSelIter_Type.tp_repr = (reprfunc) nullptr;
391
393 BPy_BMEditSelIter_Type.tp_getset = nullptr;
394
396 BPy_BMEditSelIter_Type.tp_methods = nullptr;
397
399
401
402 BPy_BMEditSelSeq_Type.tp_iter = (getiterfunc)bpy_bmeditselseq_iter;
403
404 /* Only 1 iterator so far. */
405 BPy_BMEditSelIter_Type.tp_iternext = (iternextfunc)bpy_bmeditseliter_next;
406
407 BPy_BMEditSelSeq_Type.tp_dealloc = nullptr; //(destructor)bpy_bmeditselseq_dealloc;
408 BPy_BMEditSelIter_Type.tp_dealloc = nullptr; //(destructor)bpy_bmvert_dealloc;
409
410 BPy_BMEditSelSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT;
411 BPy_BMEditSelIter_Type.tp_flags = Py_TPFLAGS_DEFAULT;
412
413 PyType_Ready(&BPy_BMEditSelSeq_Type);
414 PyType_Ready(&BPy_BMEditSelIter_Type);
415}
416
417/* utility function */
418
419int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
420{
421 BMesh *bm;
422 Py_ssize_t value_len;
423 Py_ssize_t i;
424 BMElem **value_array = nullptr;
425
427
428 bm = self->bm;
429
430 value_array = static_cast<BMElem **>(BPy_BMElem_PySeq_As_Array(&bm,
431 value,
432 0,
433 PY_SSIZE_T_MAX,
434 &value_len,
436 true,
437 true,
438 "BMesh.select_history = value"));
439
440 if (value_array == nullptr) {
441 return -1;
442 }
443
445
446 for (i = 0; i < value_len; i++) {
447 BM_select_history_store_notest(bm, value_array[i]);
448 }
449
450 PyMem_FREE(value_array);
451 return 0;
452}
void * BLI_rfindlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define CLAMP_MIN(a, b)
ATTR_WARN_UNUSED_RESULT 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
int len
int count
header-only utilities
struct BMEditSelection * next
BMHeader head
PyObject_VAR_HEAD BMesh * bm