As a preliminary first step towards full support for Python’s native iterator protocol in Freestyle, Stroke and StrokeVertexIterator (used in the shade() method of a StrokeShader implementation) have been improved to adhere to the iterator protocol as follows:
- Stroke object has __len__() and __getitem__() methods so that subscript can be used, like stroke[0] and stroke[-1].
- stroke.StrokeVerticesBegin() and stroke.StrokeVerticesEnd() return iterable objects (having next() method).
- “for v in stroke.StrokeVerticesBegin()” is equivalent to “for v in stroke”.
The conventional C++/SWIG-based syntax is still usable, so programmers can choose a preferable syntax.
Simple iteration
C++/SWIG-based syntax:
it = stroke.StrokeVerticesBegin()
while it.isEnd() == 0:
v = it.getObject()
v.attribute().setThickness(...)
it.increment()
New syntax #1:
for v in stroke:
v.attribute().setThickness(...)
New syntax #2:
for v in stroke.strokeVerticesBegin():
v.attribute().setThickness(...)
The strokeVerticesBegin() method takes an optional argument for resampling (the default value is 0, which means no resampling). The second syntax is useful when the optional argument is specified.
Reverse iteration
C++/SWIG-based syntax:
it = stroke.StrokeVerticesEnd()
while it.isBegin() == 0:
it.decrement()
v = it.getObject()
v.attribute().setThickness(...)
New syntax:
for v in stroke.StrokeVerticesEnd():
v.attribute().setThickness(...)
Getting the first StrokeVertex
C++/SWIG-based syntax:
it = stroke.StrokeVerticesBegin()
v = it.getObject()
New syntax:
v = stroke[0]
Getting the last StrokeVertex
C++/SWIG-based syntax:
it = stroke.StrokeVerticesEnd()
it.decrement()
v = it.getObject()
New syntax:
v = stroke[-1]
Getting the number of vertices
C++/SWIG-based syntax:
n = stroke.StrokeVerticeSize()
New syntax:
n = len(stroke)
Removing vertices during iteration
There are needs to remove some vertices during iteration. However, the following code will not work, since the removal of vertices may confuse the iteration.
Incorrect code:
for vertex in stroke:
if some condition holds:
stroke.RemoveVertex(vertex)
If an API were designed so as to make this work, stroke.__iter__() would always have to return a copy of the sequence of vertices, which would be expensive in many cases where the removal of vertices is not necessary.
So, a compromised API design has been taken as follows:
- stroke.__iter__() returns a reference to the sequence of vertices.
- when removal of vertices is necessary, programmers must create a copy of the stroke explicitly before starting iteration over the vertices.
Correct code #1:
for vertex in Stroke(stroke):
if some condition holds:
stroke.RemoveVertex(vertex)
Correct code #2:
for vertex in list(stroke):
if some condition holds:
stroke.RemoveVertex(vertex)