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