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 
17 /** \file
18  * \ingroup freestyle
19  */
20 
21 #include "BPy_StrokeVertexIterator.h"
22 
23 #include "../BPy_Convert.h"
24 #include "../Interface1D/BPy_Stroke.h"
25 #include "BPy_Interface0DIterator.h"
26 
27 #ifdef __cplusplus
28 extern "C" {
29 #endif
30 
31 ///////////////////////////////////////////////////////////////////////////////////////////
32 
33 //------------------------INSTANCE METHODS ----------------------------------
34 
35 PyDoc_STRVAR(StrokeVertexIterator_doc,
36              "Class hierarchy: :class:`Iterator` > :class:`StrokeVertexIterator`\n"
37              "\n"
38              "Class defining an iterator designed to iterate over the\n"
39              ":class:`StrokeVertex` of a :class:`Stroke`.  An instance of a\n"
40              "StrokeVertexIterator can be obtained from a Stroke by calling\n"
41              "iter(), stroke_vertices_begin() or stroke_vertices_begin().  It is iterating\n"
42              "over the same vertices as an :class:`Interface0DIterator`.  The difference\n"
43              "resides in the object access: an Interface0DIterator only allows\n"
44              "access to an Interface0D while one might need to access the\n"
45              "specialized StrokeVertex type.  In this case, one should use a\n"
46              "StrokeVertexIterator.  To call functions of the UnaryFuntion0D type,\n"
47              "a StrokeVertexIterator can be converted to an Interface0DIterator by\n"
48              "by calling Interface0DIterator(it).\n"
49              "\n"
50              ".. method:: __init__()\n"
51              "            __init__(brother)\n"
52              "\n"
53              "   Creates a :class:`StrokeVertexIterator` using either the\n"
54              "   default constructor or the copy constructor.\n"
55              "\n"
56              "   :arg brother: A StrokeVertexIterator object.\n"
57              "   :type brother: :class:`StrokeVertexIterator`");
58 
StrokeVertexIterator_init(BPy_StrokeVertexIterator * self,PyObject * args,PyObject * kwds)59 static int StrokeVertexIterator_init(BPy_StrokeVertexIterator *self,
60                                      PyObject *args,
61                                      PyObject *kwds)
62 {
63   static const char *kwlist_1[] = {"brother", NULL};
64   static const char *kwlist_2[] = {"stroke", NULL};
65   PyObject *brother = 0, *stroke = 0;
66 
67   if (PyArg_ParseTupleAndKeywords(
68           args, kwds, "O!", (char **)kwlist_1, &StrokeVertexIterator_Type, &brother)) {
69     self->sv_it = new StrokeInternal::StrokeVertexIterator(
70         *(((BPy_StrokeVertexIterator *)brother)->sv_it));
71     self->reversed = ((BPy_StrokeVertexIterator *)brother)->reversed;
72     self->at_start = ((BPy_StrokeVertexIterator *)brother)->at_start;
73   }
74 
75   else if ((void)PyErr_Clear(),
76            PyArg_ParseTupleAndKeywords(
77                args, kwds, "|O!", (char **)kwlist_2, &Stroke_Type, &stroke)) {
78     if (!stroke) {
79       self->sv_it = new StrokeInternal::StrokeVertexIterator();
80     }
81     else {
82       self->sv_it = new StrokeInternal::StrokeVertexIterator(
83           ((BPy_Stroke *)stroke)->s->strokeVerticesBegin());
84     }
85     self->reversed = false;
86     self->at_start = true;
87   }
88   else {
89     PyErr_SetString(PyExc_TypeError, "argument 1 must be StrokeVertexIterator or Stroke");
90     return -1;
91   }
92   self->py_it.it = self->sv_it;
93   return 0;
94 }
95 
StrokeVertexIterator_iter(BPy_StrokeVertexIterator * self)96 static PyObject *StrokeVertexIterator_iter(BPy_StrokeVertexIterator *self)
97 {
98   Py_INCREF(self);
99   self->at_start = true;
100   return (PyObject *)self;
101 }
102 
StrokeVertexIterator_iternext(BPy_StrokeVertexIterator * self)103 static PyObject *StrokeVertexIterator_iternext(BPy_StrokeVertexIterator *self)
104 {
105   /* Because Freestyle iterators for which it.isEnd() holds true have no valid object
106    * (they point to the past-the-end element and can't be dereferenced), we have to check
107    * iterators for validity.
108    * Additionally, the at_start attribute is used to keep Freestyle iterator objects
109    * and Python for loops in sync. */
110 
111   if (self->reversed) {
112     if (self->sv_it->isBegin()) {
113       PyErr_SetNone(PyExc_StopIteration);
114       return NULL;
115     }
116     self->sv_it->decrement();
117   }
118   else {
119     /* If sv_it.isEnd() is true, the iterator can't be incremented. */
120     if (self->sv_it->isEnd()) {
121       PyErr_SetNone(PyExc_StopIteration);
122       return NULL;
123     }
124     /* If at the start of the iterator, only return the object
125      * and don't increment, to keep for-loops in sync */
126     if (self->at_start) {
127       self->at_start = false;
128     }
129     /* If sv_it.atLast() is true, the iterator is currently pointing to the final valid element.
130      * Incrementing it further would lead to a state that the iterator can't be dereferenced. */
131     else if (self->sv_it->atLast()) {
132       PyErr_SetNone(PyExc_StopIteration);
133       return NULL;
134     }
135     else {
136       self->sv_it->increment();
137     }
138   }
139   StrokeVertex *sv = self->sv_it->operator->();
140   return BPy_StrokeVertex_from_StrokeVertex(*sv);
141 }
142 
143 /*----------------------StrokeVertexIterator methods ----------------------------*/
144 
145 PyDoc_STRVAR(StrokeVertexIterator_incremented_doc,
146              ".. method:: incremented()\n"
147              "\n"
148              "   Returns a copy of an incremented StrokeVertexIterator.\n"
149              "\n"
150              "   :return: A StrokeVertexIterator pointing the next StrokeVertex.\n"
151              "   :rtype: :class:`StrokeVertexIterator`");
152 
StrokeVertexIterator_incremented(BPy_StrokeVertexIterator * self)153 static PyObject *StrokeVertexIterator_incremented(BPy_StrokeVertexIterator *self)
154 {
155   if (self->sv_it->isEnd()) {
156     PyErr_SetString(PyExc_RuntimeError, "cannot increment any more");
157     return NULL;
158   }
159   StrokeInternal::StrokeVertexIterator copy(*self->sv_it);
160   copy.increment();
161   return BPy_StrokeVertexIterator_from_StrokeVertexIterator(copy, self->reversed);
162 }
163 
164 PyDoc_STRVAR(StrokeVertexIterator_decremented_doc,
165              ".. method:: decremented()\n"
166              "\n"
167              "   Returns a copy of a decremented StrokeVertexIterator.\n"
168              "\n"
169              "   :return: A StrokeVertexIterator pointing the previous StrokeVertex.\n"
170              "   :rtype: :class:`StrokeVertexIterator`");
171 
StrokeVertexIterator_decremented(BPy_StrokeVertexIterator * self)172 static PyObject *StrokeVertexIterator_decremented(BPy_StrokeVertexIterator *self)
173 {
174   if (self->sv_it->isBegin()) {
175     PyErr_SetString(PyExc_RuntimeError, "cannot decrement any more");
176     return NULL;
177   }
178   StrokeInternal::StrokeVertexIterator copy(*self->sv_it);
179   copy.decrement();
180   return BPy_StrokeVertexIterator_from_StrokeVertexIterator(copy, self->reversed);
181 }
182 
183 PyDoc_STRVAR(StrokeVertexIterator_reversed_doc,
184              ".. method:: reversed()\n"
185              "\n"
186              "   Returns a StrokeVertexIterator that traverses stroke vertices in the\n"
187              "   reversed order.\n"
188              "\n"
189              "   :return: A StrokeVertexIterator traversing stroke vertices backward.\n"
190              "   :rtype: :class:`StrokeVertexIterator`");
191 
StrokeVertexIterator_reversed(BPy_StrokeVertexIterator * self)192 static PyObject *StrokeVertexIterator_reversed(BPy_StrokeVertexIterator *self)
193 {
194   return BPy_StrokeVertexIterator_from_StrokeVertexIterator(*self->sv_it, !self->reversed);
195 }
196 
197 static PyMethodDef BPy_StrokeVertexIterator_methods[] = {
198     {"incremented",
199      (PyCFunction)StrokeVertexIterator_incremented,
200      METH_NOARGS,
201      StrokeVertexIterator_incremented_doc},
202     {"decremented",
203      (PyCFunction)StrokeVertexIterator_decremented,
204      METH_NOARGS,
205      StrokeVertexIterator_decremented_doc},
206     {"reversed",
207      (PyCFunction)StrokeVertexIterator_reversed,
208      METH_NOARGS,
209      StrokeVertexIterator_reversed_doc},
210     {NULL, NULL, 0, NULL},
211 };
212 
213 /*----------------------StrokeVertexIterator get/setters ----------------------------*/
214 
215 PyDoc_STRVAR(StrokeVertexIterator_object_doc,
216              "The StrokeVertex object currently pointed to by this iterator.\n"
217              "\n"
218              ":type: :class:`StrokeVertex`");
219 
StrokeVertexIterator_object_get(BPy_StrokeVertexIterator * self,void * UNUSED (closure))220 static PyObject *StrokeVertexIterator_object_get(BPy_StrokeVertexIterator *self,
221                                                  void *UNUSED(closure))
222 {
223   if (self->sv_it->isEnd()) {
224     PyErr_SetString(PyExc_RuntimeError, "iteration has stopped");
225     return NULL;
226   }
227   StrokeVertex *sv = self->sv_it->operator->();
228   if (sv) {
229     return BPy_StrokeVertex_from_StrokeVertex(*sv);
230   }
231   Py_RETURN_NONE;
232 }
233 
234 PyDoc_STRVAR(StrokeVertexIterator_t_doc,
235              "The curvilinear abscissa of the current point.\n"
236              "\n"
237              ":type: float");
238 
StrokeVertexIterator_t_get(BPy_StrokeVertexIterator * self,void * UNUSED (closure))239 static PyObject *StrokeVertexIterator_t_get(BPy_StrokeVertexIterator *self, void *UNUSED(closure))
240 {
241   return PyFloat_FromDouble(self->sv_it->t());
242 }
243 
244 PyDoc_STRVAR(StrokeVertexIterator_u_doc,
245              "The point parameter at the current point in the stroke (0 <= u <= 1).\n"
246              "\n"
247              ":type: float");
248 
StrokeVertexIterator_u_get(BPy_StrokeVertexIterator * self,void * UNUSED (closure))249 static PyObject *StrokeVertexIterator_u_get(BPy_StrokeVertexIterator *self, void *UNUSED(closure))
250 {
251   return PyFloat_FromDouble(self->sv_it->u());
252 }
253 
254 PyDoc_STRVAR(StrokeVertexIterator_at_last_doc,
255              "True if the iterator points to the last valid element.\n"
256              "For its counterpart (pointing to the first valid element), use it.is_begin.\n"
257              "\n"
258              ":type: bool");
259 
StrokeVertexIterator_at_last_get(BPy_StrokeVertexIterator * self)260 static PyObject *StrokeVertexIterator_at_last_get(BPy_StrokeVertexIterator *self)
261 {
262   return PyBool_from_bool(self->sv_it->atLast());
263 }
264 
265 static PyGetSetDef BPy_StrokeVertexIterator_getseters[] = {
266     {"object",
267      (getter)StrokeVertexIterator_object_get,
268      (setter)NULL,
269      StrokeVertexIterator_object_doc,
270      NULL},
271     {"t", (getter)StrokeVertexIterator_t_get, (setter)NULL, StrokeVertexIterator_t_doc, NULL},
272     {"u", (getter)StrokeVertexIterator_u_get, (setter)NULL, StrokeVertexIterator_u_doc, NULL},
273     {"at_last",
274      (getter)StrokeVertexIterator_at_last_get,
275      (setter)NULL,
276      StrokeVertexIterator_at_last_doc,
277      NULL},
278     {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
279 };
280 
281 /*-----------------------BPy_StrokeVertexIterator type definition ------------------------------*/
282 
283 PyTypeObject StrokeVertexIterator_Type = {
284     PyVarObject_HEAD_INIT(NULL, 0) "StrokeVertexIterator", /* tp_name */
285     sizeof(BPy_StrokeVertexIterator),                      /* tp_basicsize */
286     0,                                                     /* tp_itemsize */
287     0,                                                     /* tp_dealloc */
288     0,                                                     /* tp_print */
289     0,                                                     /* tp_getattr */
290     0,                                                     /* tp_setattr */
291     0,                                                     /* tp_reserved */
292     0,                                                     /* tp_repr */
293     0,                                                     /* tp_as_number */
294     0,                                                     /* tp_as_sequence */
295     0,                                                     /* tp_as_mapping */
296     0,                                                     /* tp_hash  */
297     0,                                                     /* tp_call */
298     0,                                                     /* tp_str */
299     0,                                                     /* tp_getattro */
300     0,                                                     /* tp_setattro */
301     0,                                                     /* tp_as_buffer */
302     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,              /* tp_flags */
303     StrokeVertexIterator_doc,                              /* tp_doc */
304     0,                                                     /* tp_traverse */
305     0,                                                     /* tp_clear */
306     0,                                                     /* tp_richcompare */
307     0,                                                     /* tp_weaklistoffset */
308     (getiterfunc)StrokeVertexIterator_iter,                /* tp_iter */
309     (iternextfunc)StrokeVertexIterator_iternext,           /* tp_iternext */
310     BPy_StrokeVertexIterator_methods,                      /* tp_methods */
311     0,                                                     /* tp_members */
312     BPy_StrokeVertexIterator_getseters,                    /* tp_getset */
313     &Iterator_Type,                                        /* tp_base */
314     0,                                                     /* tp_dict */
315     0,                                                     /* tp_descr_get */
316     0,                                                     /* tp_descr_set */
317     0,                                                     /* tp_dictoffset */
318     (initproc)StrokeVertexIterator_init,                   /* tp_init */
319     0,                                                     /* tp_alloc */
320     0,                                                     /* tp_new */
321 };
322 
323 ///////////////////////////////////////////////////////////////////////////////////////////
324 
325 #ifdef __cplusplus
326 }
327 #endif
328