bmesh python api:
[blender.git] / source / blender / python / bmesh / bmesh_py_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_select.c
27  *  \ingroup pybmesh
28  *
29  * This file defines the types for 'BMesh.select_history'
30  * sequence and iterator.
31  */
32
33 #include <Python.h>
34
35 #include "bmesh.h"
36
37 #include "bmesh_py_types.h"
38 #include "bmesh_py_select.h"
39
40 #include "BLI_utildefines.h"
41 #include "BLI_listbase.h"
42
43 #include "BKE_tessmesh.h"
44
45 #include "DNA_mesh_types.h"
46
47 #include "../generic/py_capi_utils.h"
48
49 #include "bmesh_py_api.h" /* own include */
50
51 PyDoc_STRVAR(bpy_bmeditselseq_active_doc,
52 "The last selected element or None (read-only).\n\n:type: :class:`BMVert`, :class:`BMEdge` or :class:`BMFace`"
53 );
54 static PyObject *bpy_bmeditselseq_active_get(BPy_BMEditSelSeq *self, void *UNUSED(closure))
55 {
56         BMEditSelection *ese;
57         BPY_BM_CHECK_OBJ(self);
58
59         if ((ese = self->bm->selected.last)) {
60                 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
61         }
62         else {
63                 Py_RETURN_NONE;
64         }
65 }
66
67 static PyGetSetDef bpy_bmeditselseq_getseters[] = {
68     {(char *)"active", (getter)bpy_bmeditselseq_active_get, (setter)NULL, (char *)bpy_bmeditselseq_active_doc, NULL},
69     {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
70 };
71
72 PyDoc_STRVAR(bpy_bmeditselseq_validate_doc,
73 ".. method:: validate()\n"
74 "\n"
75 "   Ensures all elements in the selection history are selected.\n"
76 );
77 static PyObject *bpy_bmeditselseq_validate(BPy_BMEditSelSeq *self)
78 {
79         BPY_BM_CHECK_OBJ(self);
80         BM_select_history_validate(self->bm);
81         Py_RETURN_NONE;
82 }
83
84 PyDoc_STRVAR(bpy_bmeditselseq_clear_doc,
85 ".. method:: clear()\n"
86 "\n"
87 "   Empties the selection history.\n"
88 );
89 static PyObject *bpy_bmeditselseq_clear(BPy_BMEditSelSeq *self)
90 {
91         BPY_BM_CHECK_OBJ(self);
92         BM_select_history_clear(self->bm);
93         Py_RETURN_NONE;
94 }
95
96 PyDoc_STRVAR(bpy_bmeditselseq_remove_doc,
97 ".. method:: remove(element)\n"
98 "\n"
99 "   Remove an element from the selection history.\n"
100 );
101 static PyObject *bpy_bmeditselseq_remove(BPy_BMEditSelSeq *self, BPy_BMElem *value)
102 {
103         BPY_BM_CHECK_OBJ(self);
104
105         if ((BPy_BMVert_Check(value) ||
106              BPy_BMEdge_Check(value) ||
107              BPy_BMFace_Check(value)) == FALSE)
108         {
109                 PyErr_Format(PyExc_TypeError,
110                              "Expected a BMVert/BMedge/BMFace not a %.200s", Py_TYPE(value)->tp_name);
111                 return NULL;
112         }
113
114         BPY_BM_CHECK_OBJ(value);
115
116         if ((self->bm != value->bm) ||
117             (BM_select_history_remove(self->bm, value->ele) == FALSE))
118         {
119                 PyErr_SetString(PyExc_ValueError,
120                                 "Element not found in selection history");
121                 return NULL;
122         }
123
124         Py_RETURN_NONE;
125 }
126
127 static struct PyMethodDef bpy_bmeditselseq_methods[] = {
128     {"validate", (PyCFunction)bpy_bmeditselseq_validate, METH_NOARGS, bpy_bmeditselseq_validate_doc},
129     {"clear",    (PyCFunction)bpy_bmeditselseq_clear,    METH_NOARGS, bpy_bmeditselseq_clear_doc},
130     {"remove",   (PyCFunction)bpy_bmeditselseq_remove,   METH_O,      bpy_bmeditselseq_remove_doc},
131     {NULL, NULL, 0, NULL}
132 };
133
134
135 /* Sequences
136  * ========= */
137
138 static Py_ssize_t bpy_bmeditselseq_length(BPy_BMEditSelSeq *self)
139 {
140         BPY_BM_CHECK_INT(self);
141
142         return BLI_countlist(&self->bm->selected);
143 }
144
145 static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, int keynum)
146 {
147         BMEditSelection *ese;
148
149         BPY_BM_CHECK_OBJ(self);
150
151         if (keynum < 0) {
152                 ese = BLI_rfindlink(&self->bm->selected, -1 - keynum);
153         }
154         else {
155                 ese = BLI_findlink(&self->bm->selected, keynum);
156         }
157
158         if (ese) {
159                 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
160         }
161         else {
162                 PyErr_Format(PyExc_IndexError,
163                              "BMElemSeq[index]: index %d out of range", keynum);
164                 return NULL;
165         }
166 }
167
168 static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, Py_ssize_t start, Py_ssize_t stop)
169 {
170         int count = 0;
171         int ok;
172
173         PyObject *list;
174         PyObject *item;
175         BMEditSelection *ese;
176
177         BPY_BM_CHECK_OBJ(self);
178
179         list = PyList_New(0);
180
181         ese = self->bm->selected.first;
182
183         ok = (ese != NULL);
184
185         if (UNLIKELY(ok == FALSE)) {
186                 return list;
187         }
188
189         /* first loop up-until the start */
190         for (ok = TRUE; ok; ok = ((ese = ese->next) != NULL)) {
191                 if (count == start) {
192                         break;
193                 }
194                 count++;
195         }
196
197         /* add items until stop */
198         while ((ese = ese->next)) {
199                 item = BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
200                 PyList_Append(list, item);
201                 Py_DECREF(item);
202
203                 count++;
204                 if (count == stop) {
205                         break;
206                 }
207         }
208
209         return list;
210 }
211
212 static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
213 {
214         /* dont need error check here */
215         if (PyIndex_Check(key)) {
216                 Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
217                 if (i == -1 && PyErr_Occurred())
218                         return NULL;
219                 return bpy_bmeditselseq_subscript_int(self, i);
220         }
221         else if (PySlice_Check(key)) {
222                 PySliceObject *key_slice = (PySliceObject *)key;
223                 Py_ssize_t step = 1;
224
225                 if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
226                         return NULL;
227                 }
228                 else if (step != 1) {
229                         PyErr_SetString(PyExc_TypeError,
230                                         "BMElemSeq[slice]: slice steps not supported");
231                         return NULL;
232                 }
233                 else if (key_slice->start == Py_None && key_slice->stop == Py_None) {
234                         return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
235                 }
236                 else {
237                         Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
238
239                         /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
240                         if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) return NULL;
241                         if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop))    return NULL;
242
243                         if (start < 0 || stop < 0) {
244                                 /* only get the length for negative values */
245                                 Py_ssize_t len = bpy_bmeditselseq_length(self);
246                                 if (start < 0) start += len;
247                                 if (stop < 0) start += len;
248                         }
249
250                         if (stop - start <= 0) {
251                                 return PyList_New(0);
252                         }
253                         else {
254                                 return bpy_bmeditselseq_subscript_slice(self, start, stop);
255                         }
256                 }
257         }
258         else {
259                 PyErr_SetString(PyExc_AttributeError,
260                                 "BMElemSeq[key]: invalid key, key must be an int");
261                 return NULL;
262         }
263 }
264
265 static int bpy_bmeditselseq_contains(BPy_BMEditSelSeq *self, PyObject *value)
266 {
267         BPy_BMElem *value_bm_ele;
268
269         BPY_BM_CHECK_INT(self);
270
271         value_bm_ele = (BPy_BMElem *)value;
272         if (value_bm_ele->bm == self->bm) {
273                 return BM_select_history_check(self->bm, value_bm_ele->ele);
274         }
275
276         return 0;
277 }
278
279 static PySequenceMethods bpy_bmeditselseq_as_sequence = {
280     (lenfunc)bpy_bmeditselseq_length,            /* sq_length */
281     NULL,                                        /* sq_concat */
282     NULL,                                        /* sq_repeat */
283     (ssizeargfunc)bpy_bmeditselseq_subscript_int,/* sq_item */ /* Only set this so PySequence_Check() returns True */
284     NULL,                                        /* sq_slice */
285     (ssizeobjargproc)NULL,                       /* sq_ass_item */
286     NULL,                                        /* *was* sq_ass_slice */
287     (objobjproc)bpy_bmeditselseq_contains,       /* sq_contains */
288     (binaryfunc) NULL,                           /* sq_inplace_concat */
289     (ssizeargfunc) NULL,                         /* sq_inplace_repeat */
290 };
291
292 static PyMappingMethods bpy_bmeditselseq_as_mapping = {
293     (lenfunc)bpy_bmeditselseq_length,            /* mp_length */
294     (binaryfunc)bpy_bmeditselseq_subscript,      /* mp_subscript */
295     (objobjargproc)NULL,                         /* mp_ass_subscript */
296 };
297
298
299 /* Iterator
300  * -------- */
301
302 static PyObject *bpy_bmeditselseq_iter(BPy_BMEditSelSeq *self)
303 {
304         BPy_BMEditSelIter *py_iter;
305
306         BPY_BM_CHECK_OBJ(self);
307         py_iter = (BPy_BMEditSelIter *)BPy_BMEditSelIter_CreatePyObject(self->bm);
308         py_iter->ese = self->bm->selected.first;
309         return (PyObject *)py_iter;
310 }
311
312 static PyObject *bpy_bmeditseliter_next(BPy_BMEditSelIter *self)
313 {
314         BMEditSelection *ese = self->ese;
315         if (ese == NULL) {
316                 PyErr_SetString(PyExc_StopIteration,
317                                 "bpy_bmiter_next stop");
318                 return NULL;
319         }
320         else {
321                 self->ese = ese->next;
322                 return (PyObject *)BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
323         }
324 }
325
326 PyTypeObject BPy_BMEditSelSeq_Type  = {{{0}}};
327 PyTypeObject BPy_BMEditSelIter_Type = {{{0}}};
328
329
330 PyObject *BPy_BMEditSel_CreatePyObject(BMesh *bm)
331 {
332         BPy_BMEditSelSeq *self = PyObject_New(BPy_BMEditSelSeq, &BPy_BMEditSelSeq_Type);
333         self->bm = bm;
334         /* caller must initialize 'iter' member */
335         return (PyObject *)self;
336 }
337
338 PyObject *BPy_BMEditSelIter_CreatePyObject(BMesh *bm)
339 {
340         BPy_BMEditSelIter *self = PyObject_New(BPy_BMEditSelIter, &BPy_BMEditSelIter_Type);
341         self->bm = bm;
342         /* caller must initialize 'iter' member */
343         return (PyObject *)self;
344 }
345
346 void BPy_BM_init_select_types(void)
347 {
348         BPy_BMEditSelSeq_Type.tp_basicsize     = sizeof(BPy_BMEditSelSeq);
349         BPy_BMEditSelIter_Type.tp_basicsize    = sizeof(BPy_BMEditSelIter);
350
351         BPy_BMEditSelSeq_Type.tp_name  = "BMEditSelSeq";
352         BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
353
354         BPy_BMEditSelSeq_Type.tp_doc   = NULL; // todo
355         BPy_BMEditSelIter_Type.tp_doc  = NULL;
356
357         BPy_BMEditSelSeq_Type.tp_repr  = (reprfunc)NULL;
358         BPy_BMEditSelIter_Type.tp_repr = (reprfunc)NULL;
359
360         BPy_BMEditSelSeq_Type.tp_getset     = bpy_bmeditselseq_getseters;
361         BPy_BMEditSelIter_Type.tp_getset = NULL;
362
363         BPy_BMEditSelSeq_Type.tp_methods     = bpy_bmeditselseq_methods;
364         BPy_BMEditSelIter_Type.tp_methods = NULL;
365
366         BPy_BMEditSelSeq_Type.tp_as_sequence = &bpy_bmeditselseq_as_sequence;
367
368         BPy_BMEditSelSeq_Type.tp_as_mapping = &bpy_bmeditselseq_as_mapping;
369
370         BPy_BMEditSelSeq_Type.tp_iter = (getiterfunc)bpy_bmeditselseq_iter;
371
372         /* only 1 iteratir so far */
373         BPy_BMEditSelIter_Type.tp_iternext = (iternextfunc)bpy_bmeditseliter_next;
374
375         BPy_BMEditSelSeq_Type.tp_dealloc     = NULL; //(destructor)bpy_bmeditselseq_dealloc;
376         BPy_BMEditSelIter_Type.tp_dealloc = NULL; //(destructor)bpy_bmvert_dealloc;
377
378         BPy_BMEditSelSeq_Type.tp_flags     = Py_TPFLAGS_DEFAULT;
379         BPy_BMEditSelIter_Type.tp_flags    = Py_TPFLAGS_DEFAULT;
380
381         PyType_Ready(&BPy_BMEditSelSeq_Type);
382         PyType_Ready(&BPy_BMEditSelIter_Type);
383 }