Blender  V3.3
bmesh_py_types_select.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2012 Blender Foundation. All rights reserved. */
3 
14 #include <Python.h>
15 
16 #include "BLI_listbase.h"
17 #include "BLI_utildefines.h"
18 
19 #include "bmesh.h"
20 
21 #include "bmesh_py_types.h"
22 #include "bmesh_py_types_select.h"
23 
24 #include "../generic/py_capi_utils.h"
25 #include "../generic/python_utildefines.h"
26 
27 PyDoc_STRVAR(bpy_bmeditselseq_active_doc,
28  "The last selected element or None (read-only).\n\n:type: :class:`BMVert`, "
29  ":class:`BMEdge` or :class:`BMFace`");
30 static PyObject *bpy_bmeditselseq_active_get(BPy_BMEditSelSeq *self, void *UNUSED(closure))
31 {
32  BMEditSelection *ese;
33  BPY_BM_CHECK_OBJ(self);
34 
35  if ((ese = self->bm->selected.last)) {
36  return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
37  }
38 
39  Py_RETURN_NONE;
40 }
41 
42 static PyGetSetDef bpy_bmeditselseq_getseters[] = {
43  {"active",
45  (setter)NULL,
46  bpy_bmeditselseq_active_doc,
47  NULL},
48  {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
49 };
50 
51 PyDoc_STRVAR(bpy_bmeditselseq_validate_doc,
52  ".. method:: validate()\n"
53  "\n"
54  " Ensures all elements in the selection history are selected.\n");
56 {
57  BPY_BM_CHECK_OBJ(self);
59  Py_RETURN_NONE;
60 }
61 
62 PyDoc_STRVAR(bpy_bmeditselseq_clear_doc,
63  ".. method:: clear()\n"
64  "\n"
65  " Empties the selection history.\n");
67 {
68  BPY_BM_CHECK_OBJ(self);
70  Py_RETURN_NONE;
71 }
72 
74  bpy_bmeditselseq_add_doc,
75  ".. method:: add(element)\n"
76  "\n"
77  " Add an element to the selection history (no action taken if its already added).\n");
78 static PyObject *bpy_bmeditselseq_add(BPy_BMEditSelSeq *self, BPy_BMElem *value)
79 {
80  BPY_BM_CHECK_OBJ(self);
81 
82  if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
83  PyErr_Format(
84  PyExc_TypeError, "Expected a BMVert/BMedge/BMFace not a %.200s", Py_TYPE(value)->tp_name);
85  return NULL;
86  }
87 
88  BPY_BM_CHECK_SOURCE_OBJ(self->bm, "select_history.add()", value);
89 
90  BM_select_history_store(self->bm, value->ele);
91 
92  Py_RETURN_NONE;
93 }
94 
95 PyDoc_STRVAR(bpy_bmeditselseq_remove_doc,
96  ".. method:: remove(element)\n"
97  "\n"
98  " Remove an element from the selection history.\n");
99 static PyObject *bpy_bmeditselseq_remove(BPy_BMEditSelSeq *self, BPy_BMElem *value)
100 {
101  BPY_BM_CHECK_OBJ(self);
102 
103  if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
104  PyErr_Format(
105  PyExc_TypeError, "Expected a BMVert/BMedge/BMFace not a %.200s", Py_TYPE(value)->tp_name);
106  return NULL;
107  }
108 
109  BPY_BM_CHECK_SOURCE_OBJ(self->bm, "select_history.remove()", value);
110 
111  if (BM_select_history_remove(self->bm, value->ele) == false) {
112  PyErr_SetString(PyExc_ValueError, "Element not found in selection history");
113  return NULL;
114  }
115 
116  Py_RETURN_NONE;
117 }
118 
120  bpy_bmeditselseq_discard_doc,
121  ".. method:: discard(element)\n"
122  "\n"
123  " Discard an element from the selection history.\n"
124  "\n"
125  " Like remove but doesn't raise an error when the elements not in the selection list.\n");
126 static PyObject *bpy_bmeditselseq_discard(BPy_BMEditSelSeq *self, BPy_BMElem *value)
127 {
128  BPY_BM_CHECK_OBJ(self);
129 
130  if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
131  PyErr_Format(
132  PyExc_TypeError, "Expected a BMVert/BMedge/BMFace not a %.200s", Py_TYPE(value)->tp_name);
133  return NULL;
134  }
135 
136  BPY_BM_CHECK_SOURCE_OBJ(self->bm, "select_history.discard()", value);
137 
138  BM_select_history_remove(self->bm, value->ele);
139 
140  Py_RETURN_NONE;
141 }
142 
143 static struct PyMethodDef bpy_bmeditselseq_methods[] = {
144  {"validate",
145  (PyCFunction)bpy_bmeditselseq_validate,
146  METH_NOARGS,
147  bpy_bmeditselseq_validate_doc},
148  {"clear", (PyCFunction)bpy_bmeditselseq_clear, METH_NOARGS, bpy_bmeditselseq_clear_doc},
149 
150  {"add", (PyCFunction)bpy_bmeditselseq_add, METH_O, bpy_bmeditselseq_add_doc},
151  {"remove", (PyCFunction)bpy_bmeditselseq_remove, METH_O, bpy_bmeditselseq_remove_doc},
152  {"discard", (PyCFunction)bpy_bmeditselseq_discard, METH_O, bpy_bmeditselseq_discard_doc},
153  {NULL, NULL, 0, NULL},
154 };
155 
156 /* Sequences
157  * ========= */
158 
160 {
161  BPY_BM_CHECK_INT(self);
162 
163  return BLI_listbase_count(&self->bm->selected);
164 }
165 
166 static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, int keynum)
167 {
168  BMEditSelection *ese;
169 
170  BPY_BM_CHECK_OBJ(self);
171 
172  if (keynum < 0) {
173  ese = BLI_rfindlink(&self->bm->selected, -1 - keynum);
174  }
175  else {
176  ese = BLI_findlink(&self->bm->selected, keynum);
177  }
178 
179  if (ese) {
180  return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
181  }
182 
183  PyErr_Format(PyExc_IndexError, "BMElemSeq[index]: index %d out of range", keynum);
184  return NULL;
185 }
186 
188  Py_ssize_t start,
189  Py_ssize_t stop)
190 {
191  int count = 0;
192 
193  PyObject *list;
194  BMEditSelection *ese;
195 
196  BPY_BM_CHECK_OBJ(self);
197 
198  list = PyList_New(0);
199 
200  /* First loop up-until the start. */
201  for (ese = self->bm->selected.first; ese; ese = ese->next) {
202  if (count == start) {
203  break;
204  }
205  count++;
206  }
207 
208  /* Add items until stop. */
209  for (; ese; ese = ese->next) {
210  PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head));
211  count++;
212  if (count == stop) {
213  break;
214  }
215  }
216 
217  return list;
218 }
219 
220 static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
221 {
222  /* don't need error check here */
223  if (PyIndex_Check(key)) {
224  const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
225  if (i == -1 && PyErr_Occurred()) {
226  return NULL;
227  }
228  return bpy_bmeditselseq_subscript_int(self, i);
229  }
230  if (PySlice_Check(key)) {
231  PySliceObject *key_slice = (PySliceObject *)key;
232  Py_ssize_t step = 1;
233 
234  if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
235  return NULL;
236  }
237  if (step != 1) {
238  PyErr_SetString(PyExc_TypeError, "BMElemSeq[slice]: slice steps not supported");
239  return NULL;
240  }
241  if (key_slice->start == Py_None && key_slice->stop == Py_None) {
242  return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
243  }
244 
245  Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
246 
247  /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
248  if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) {
249  return NULL;
250  }
251  if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop)) {
252  return NULL;
253  }
254 
255  if (start < 0 || stop < 0) {
256  /* only get the length for negative values */
257  const Py_ssize_t len = bpy_bmeditselseq_length(self);
258  if (start < 0) {
259  start += len;
260  CLAMP_MIN(start, 0);
261  }
262  if (stop < 0) {
263  stop += len;
264  CLAMP_MIN(stop, 0);
265  }
266  }
267 
268  if (stop - start <= 0) {
269  return PyList_New(0);
270  }
271 
272  return bpy_bmeditselseq_subscript_slice(self, start, stop);
273  }
274 
275  PyErr_SetString(PyExc_AttributeError, "BMElemSeq[key]: invalid key, key must be an int");
276  return NULL;
277 }
278 
279 static int bpy_bmeditselseq_contains(BPy_BMEditSelSeq *self, PyObject *value)
280 {
281  BPy_BMElem *value_bm_ele;
282 
283  BPY_BM_CHECK_INT(self);
284 
285  value_bm_ele = (BPy_BMElem *)value;
286  if (value_bm_ele->bm == self->bm) {
287  return BM_select_history_check(self->bm, value_bm_ele->ele);
288  }
289 
290  return 0;
291 }
292 
293 static PySequenceMethods bpy_bmeditselseq_as_sequence = {
294  (lenfunc)bpy_bmeditselseq_length, /* sq_length */
295  NULL, /* sq_concat */
296  NULL, /* sq_repeat */
297  (ssizeargfunc)bpy_bmeditselseq_subscript_int,
298  /* sq_item */ /* Only set this so PySequence_Check() returns True */
299  NULL, /* sq_slice */
300  (ssizeobjargproc)NULL, /* sq_ass_item */
301  NULL, /* *was* sq_ass_slice */
302  (objobjproc)bpy_bmeditselseq_contains, /* sq_contains */
303  (binaryfunc)NULL, /* sq_inplace_concat */
304  (ssizeargfunc)NULL, /* sq_inplace_repeat */
305 };
306 
307 static PyMappingMethods bpy_bmeditselseq_as_mapping = {
308  (lenfunc)bpy_bmeditselseq_length, /* mp_length */
309  (binaryfunc)bpy_bmeditselseq_subscript, /* mp_subscript */
310  (objobjargproc)NULL, /* mp_ass_subscript */
311 };
312 
313 /* Iterator
314  * -------- */
315 
317 {
318  BPy_BMEditSelIter *py_iter;
319 
320  BPY_BM_CHECK_OBJ(self);
322  py_iter->ese = self->bm->selected.first;
323  return (PyObject *)py_iter;
324 }
325 
327 {
328  BMEditSelection *ese = self->ese;
329  if (ese == NULL) {
330  PyErr_SetNone(PyExc_StopIteration);
331  return NULL;
332  }
333 
334  self->ese = ese->next;
335  return (PyObject *)BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
336 }
337 
340 
342 {
344  self->bm = bm;
345  /* caller must initialize 'iter' member */
346  return (PyObject *)self;
347 }
348 
350 {
352  self->bm = bm;
353  /* caller must initialize 'iter' member */
354  return (PyObject *)self;
355 }
356 
358 {
359  BPy_BMEditSelSeq_Type.tp_basicsize = sizeof(BPy_BMEditSelSeq);
360  BPy_BMEditSelIter_Type.tp_basicsize = sizeof(BPy_BMEditSelIter);
361 
362  BPy_BMEditSelSeq_Type.tp_name = "BMEditSelSeq";
363  BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
364 
365  BPy_BMEditSelSeq_Type.tp_doc = NULL; /* todo */
366  BPy_BMEditSelIter_Type.tp_doc = NULL;
367 
368  BPy_BMEditSelSeq_Type.tp_repr = (reprfunc)NULL;
369  BPy_BMEditSelIter_Type.tp_repr = (reprfunc)NULL;
370 
372  BPy_BMEditSelIter_Type.tp_getset = NULL;
373 
375  BPy_BMEditSelIter_Type.tp_methods = NULL;
376 
378 
380 
381  BPy_BMEditSelSeq_Type.tp_iter = (getiterfunc)bpy_bmeditselseq_iter;
382 
383  /* only 1 iteratir so far */
384  BPy_BMEditSelIter_Type.tp_iternext = (iternextfunc)bpy_bmeditseliter_next;
385 
386  BPy_BMEditSelSeq_Type.tp_dealloc = NULL; //(destructor)bpy_bmeditselseq_dealloc;
387  BPy_BMEditSelIter_Type.tp_dealloc = NULL; //(destructor)bpy_bmvert_dealloc;
388 
389  BPy_BMEditSelSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT;
390  BPy_BMEditSelIter_Type.tp_flags = Py_TPFLAGS_DEFAULT;
391 
392  PyType_Ready(&BPy_BMEditSelSeq_Type);
393  PyType_Ready(&BPy_BMEditSelIter_Type);
394 }
395 
396 /* utility function */
397 
398 int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
399 {
400  BMesh *bm;
401  Py_ssize_t value_len;
402  Py_ssize_t i;
403  BMElem **value_array = NULL;
404 
405  BPY_BM_CHECK_INT(self);
406 
407  bm = self->bm;
408 
409  value_array = BPy_BMElem_PySeq_As_Array(&bm,
410  value,
411  0,
412  PY_SSIZE_T_MAX,
413  &value_len,
414  BM_VERT | BM_EDGE | BM_FACE,
415  true,
416  true,
417  "BMesh.select_history = value");
418 
419  if (value_array == NULL) {
420  return -1;
421  }
422 
424 
425  for (i = 0; i < value_len; i++) {
426  BM_select_history_store_notest(bm, value_array[i]);
427  }
428 
429  PyMem_FREE(value_array);
430  return 0;
431 }
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 UNUSED(x)
#define CLAMP_MIN(a, b)
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_VERT
Definition: bmesh_class.h:383
@ BM_EDGE
Definition: bmesh_class.h:384
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)
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 PyObject * bpy_bmeditselseq_remove(BPy_BMEditSelSeq *self, BPy_BMElem *value)
static PyObject * bpy_bmeditselseq_clear(BPy_BMEditSelSeq *self)
static int bpy_bmeditselseq_contains(BPy_BMEditSelSeq *self, PyObject *value)
static PyObject * bpy_bmeditselseq_add(BPy_BMEditSelSeq *self, BPy_BMElem *value)
static PyObject * bpy_bmeditseliter_next(BPy_BMEditSelIter *self)
PyDoc_STRVAR(bpy_bmeditselseq_active_doc, "The last selected element or None (read-only).\n\n:type: :class:`BMVert`, " ":class:`BMEdge` or :class:`BMFace`")
int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
void BPy_BM_init_types_select(void)
static PyObject * bpy_bmeditselseq_iter(BPy_BMEditSelSeq *self)
static PyObject * bpy_bmeditselseq_validate(BPy_BMEditSelSeq *self)
static PyMappingMethods bpy_bmeditselseq_as_mapping
static PyObject * bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
PyObject * BPy_BMEditSel_CreatePyObject(BMesh *bm)
PyTypeObject BPy_BMEditSelSeq_Type
static PyGetSetDef bpy_bmeditselseq_getseters[]
static Py_ssize_t bpy_bmeditselseq_length(BPy_BMEditSelSeq *self)
static PyObject * bpy_bmeditselseq_active_get(BPy_BMEditSelSeq *self, void *UNUSED(closure))
PyTypeObject BPy_BMEditSelIter_Type
static struct PyMethodDef bpy_bmeditselseq_methods[]
static PyObject * bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, int keynum)
static PyObject * bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, Py_ssize_t start, Py_ssize_t stop)
PyObject * BPy_BMEditSelIter_CreatePyObject(BMesh *bm)
static PySequenceMethods bpy_bmeditselseq_as_sequence
static PyObject * bpy_bmeditselseq_discard(BPy_BMEditSelSeq *self, BPy_BMElem *value)
struct BPy_BMEditSelIter BPy_BMEditSelIter
struct BPy_BMEditSelSeq BPy_BMEditSelSeq
PyObject * self
Definition: bpy_driver.c:165
int len
Definition: draw_manager.c:108
int count
struct BMEditSelection * next
Definition: bmesh_marking.h:10
BMHeader head
Definition: bmesh_class.h:243
struct BMEditSelection * ese
struct BMElem * ele
PyObject_VAR_HEAD struct BMesh * bm