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