fd9f329f7c0267d69d1b43b2cbb02424e346f9a6
[blender-staging.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, (void *)BM_VERTS_OF_MESH},
69     {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
70 };
71
72 static struct PyMethodDef bpy_bmeditselseq_methods[] = {
73     // {"select_flush_mode", (PyCFunction)bpy_bmesh_select_flush_mode, METH_NOARGS, bpy_bmesh_select_flush_mode_doc},
74     {NULL, NULL, 0, NULL}
75 };
76
77
78 /* Sequences
79  * ========= */
80
81 static Py_ssize_t bpy_bmeditselseq_length(BPy_BMEditSelSeq *self)
82 {
83         BPY_BM_CHECK_INT(self);
84
85         return BLI_countlist(&self->bm->selected);
86 }
87
88 static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, int keynum)
89 {
90         BMEditSelection *ese;
91
92         BPY_BM_CHECK_OBJ(self);
93
94         if (keynum < 0) {
95                 ese = BLI_rfindlink(&self->bm->selected, -1 - keynum);
96         }
97         else {
98                 ese = BLI_findlink(&self->bm->selected, keynum);
99         }
100
101         if (ese) {
102                 return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
103         }
104         else {
105                 PyErr_Format(PyExc_IndexError,
106                                  "BMElemSeq[index]: index %d out of range", keynum);
107                 return NULL;
108         }
109 }
110
111 static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, Py_ssize_t start, Py_ssize_t stop)
112 {
113         int count = 0;
114         int ok;
115
116         PyObject *list;
117         PyObject *item;
118         BMEditSelection *ese;
119
120         BPY_BM_CHECK_OBJ(self);
121
122         list = PyList_New(0);
123
124         ese = self->bm->selected.first;
125
126         ok = (ese != NULL);
127
128         if (UNLIKELY(ok == FALSE)) {
129                 return list;
130         }
131
132         /* first loop up-until the start */
133         for (ok = TRUE; ok; ok = ((ese = ese->next) != NULL)) {
134                 if (count == start) {
135                         break;
136                 }
137                 count++;
138         }
139
140         /* add items until stop */
141         while ((ese = ese->next)) {
142                 item = BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
143                 PyList_Append(list, item);
144                 Py_DECREF(item);
145
146                 count++;
147                 if (count == stop) {
148                         break;
149                 }
150         }
151
152         return list;
153 }
154
155 static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
156 {
157         /* dont need error check here */
158         if (PyIndex_Check(key)) {
159                 Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
160                 if (i == -1 && PyErr_Occurred())
161                         return NULL;
162                 return bpy_bmeditselseq_subscript_int(self, i);
163         }
164         else if (PySlice_Check(key)) {
165                 PySliceObject *key_slice = (PySliceObject *)key;
166                 Py_ssize_t step = 1;
167
168                 if (key_slice->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
169                         return NULL;
170                 }
171                 else if (step != 1) {
172                         PyErr_SetString(PyExc_TypeError,
173                                         "BMElemSeq[slice]: slice steps not supported");
174                         return NULL;
175                 }
176                 else if (key_slice->start == Py_None && key_slice->stop == Py_None) {
177                         return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
178                 }
179                 else {
180                         Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
181
182                         /* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
183                         if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) return NULL;
184                         if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop))    return NULL;
185
186                         if (start < 0 || stop < 0) {
187                                 /* only get the length for negative values */
188                                 Py_ssize_t len = bpy_bmeditselseq_length(self);
189                                 if (start < 0) start += len;
190                                 if (stop < 0) start += len;
191                         }
192
193                         if (stop - start <= 0) {
194                                 return PyList_New(0);
195                         }
196                         else {
197                                 return bpy_bmeditselseq_subscript_slice(self, start, stop);
198                         }
199                 }
200         }
201         else {
202                 PyErr_SetString(PyExc_AttributeError,
203                                 "BMElemSeq[key]: invalid key, key must be an int");
204                 return NULL;
205         }
206 }
207
208 static int bpy_bmeditselseq_contains(BPy_BMEditSelSeq *self, PyObject *value)
209 {
210         BPy_BMElem *value_bm_ele;
211
212         BPY_BM_CHECK_INT(self);
213
214         value_bm_ele = (BPy_BMElem *)value;
215         if (value_bm_ele->bm == self->bm) {
216                 BMEditSelection *ese_test;
217                 BMElem *ele;
218
219                 ele = value_bm_ele->ele;
220                 for (ese_test = self->bm->selected.first; ese_test; ese_test = ese_test->next) {
221                         if (ele == ese_test->ele) {
222                                 return 1;
223                         }
224                 }
225         }
226
227         return 0;
228 }
229
230 static PySequenceMethods bpy_bmeditselseq_as_sequence = {
231     (lenfunc)bpy_bmeditselseq_length,                  /* sq_length */
232     NULL,                                        /* sq_concat */
233     NULL,                                        /* sq_repeat */
234     (ssizeargfunc)bpy_bmeditselseq_subscript_int,      /* sq_item */ /* Only set this so PySequence_Check() returns True */
235     NULL,                                        /* sq_slice */
236     (ssizeobjargproc)NULL,                       /* sq_ass_item */
237     NULL,                                        /* *was* sq_ass_slice */
238     (objobjproc)bpy_bmeditselseq_contains,             /* sq_contains */
239     (binaryfunc) NULL,                           /* sq_inplace_concat */
240     (ssizeargfunc) NULL,                         /* sq_inplace_repeat */
241 };
242
243 static PyMappingMethods bpy_bmeditselseq_as_mapping = {
244     (lenfunc)bpy_bmeditselseq_length,                  /* mp_length */
245     (binaryfunc)bpy_bmeditselseq_subscript,            /* mp_subscript */
246     (objobjargproc)NULL,                         /* mp_ass_subscript */
247 };
248
249
250 /* Iterator
251  * -------- */
252
253 static PyObject *bpy_bmeditselseq_iter(BPy_BMEditSelSeq *self)
254 {
255         BPy_BMEditSelIter *py_iter;
256
257         BPY_BM_CHECK_OBJ(self);
258         py_iter = (BPy_BMEditSelIter *)BPy_BMEditSelIter_CreatePyObject(self->bm);
259         py_iter->ese = self->bm->selected.first;
260         return (PyObject *)py_iter;
261 }
262
263 static PyObject *bpy_bmeditseliter_next(BPy_BMEditSelIter *self)
264 {
265         BMEditSelection *ese = self->ese;
266         if (ese == NULL) {
267                 PyErr_SetString(PyExc_StopIteration,
268                                 "bpy_bmiter_next stop");
269                 return NULL;
270         }
271         else {
272                 self->ese = ese->next;
273                 return (PyObject *)BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
274         }
275 }
276
277 PyTypeObject BPy_BMEditSelSeq_Type     = {{{0}}};
278 PyTypeObject BPy_BMEditSelIter_Type = {{{0}}};
279
280
281 PyObject *BPy_BMEditSel_CreatePyObject(BMesh *bm)
282 {
283         BPy_BMEditSelSeq *self = PyObject_New(BPy_BMEditSelSeq, &BPy_BMEditSelSeq_Type);
284         self->bm = bm;
285         /* caller must initialize 'iter' member */
286         return (PyObject *)self;
287 }
288
289 PyObject *BPy_BMEditSelIter_CreatePyObject(BMesh *bm)
290 {
291         BPy_BMEditSelIter *self = PyObject_New(BPy_BMEditSelIter, &BPy_BMEditSelIter_Type);
292         self->bm = bm;
293         /* caller must initialize 'iter' member */
294         return (PyObject *)self;
295 }
296
297 void BPy_BM_init_select_types(void)
298 {
299         BPy_BMEditSelSeq_Type.tp_basicsize     = sizeof(BPy_BMEditSelSeq);
300         BPy_BMEditSelIter_Type.tp_basicsize    = sizeof(BPy_BMEditSelIter);
301
302         BPy_BMEditSelSeq_Type.tp_name  = "BMEditSelSeq";
303         BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
304
305         BPy_BMEditSelSeq_Type.tp_doc   = NULL; // todo
306         BPy_BMEditSelIter_Type.tp_doc  = NULL;
307
308         BPy_BMEditSelSeq_Type.tp_repr  = (reprfunc)NULL;
309         BPy_BMEditSelIter_Type.tp_repr = (reprfunc)NULL;
310
311         BPy_BMEditSelSeq_Type.tp_getset     = bpy_bmeditselseq_getseters;
312         BPy_BMEditSelIter_Type.tp_getset = NULL;
313
314         BPy_BMEditSelSeq_Type.tp_methods     = bpy_bmeditselseq_methods;
315         BPy_BMEditSelIter_Type.tp_methods = NULL;
316
317         BPy_BMEditSelSeq_Type.tp_as_sequence = &bpy_bmeditselseq_as_sequence;
318
319         BPy_BMEditSelSeq_Type.tp_as_mapping = &bpy_bmeditselseq_as_mapping;
320
321         BPy_BMEditSelSeq_Type.tp_iter = (getiterfunc)bpy_bmeditselseq_iter;
322
323         /* only 1 iteratir so far */
324         BPy_BMEditSelIter_Type.tp_iternext = (iternextfunc)bpy_bmeditseliter_next;
325
326         BPy_BMEditSelSeq_Type.tp_dealloc     = NULL; //(destructor)bpy_bmeditselseq_dealloc;
327         BPy_BMEditSelIter_Type.tp_dealloc = NULL; //(destructor)bpy_bmvert_dealloc;
328
329         BPy_BMEditSelSeq_Type.tp_flags     = Py_TPFLAGS_DEFAULT;
330         BPy_BMEditSelIter_Type.tp_flags    = Py_TPFLAGS_DEFAULT;
331
332         PyType_Ready(&BPy_BMEditSelSeq_Type);
333         PyType_Ready(&BPy_BMEditSelIter_Type);
334 }