Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release/scripts/freestyle/style_modules/shaders.py')
-rw-r--r--release/scripts/freestyle/style_modules/shaders.py1298
1 files changed, 1298 insertions, 0 deletions
diff --git a/release/scripts/freestyle/style_modules/shaders.py b/release/scripts/freestyle/style_modules/shaders.py
new file mode 100644
index 00000000000..7a363e3273f
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/shaders.py
@@ -0,0 +1,1298 @@
+from freestyle_init import *
+from PredicatesU0D import *
+from PredicatesB1D import *
+from PredicatesU1D import *
+from logical_operators import *
+from ChainingIterators import *
+from random import *
+from math import *
+
+## thickness modifiers
+######################
+
+class pyDepthDiscontinuityThicknessShader(StrokeShader):
+ def __init__(self, min, max):
+ StrokeShader.__init__(self)
+ self.__min = float(min)
+ self.__max = float(max)
+ self.__func = ZDiscontinuityF0D()
+ def getName(self):
+ return "pyDepthDiscontinuityThicknessShader"
+ def shade(self, stroke):
+ z_min=0.0
+ z_max=1.0
+ a = (self.__max - self.__min)/(z_max-z_min)
+ b = (self.__min*z_max-self.__max*z_min)/(z_max-z_min)
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ z = self.__func(it.cast_to_interface0diterator())
+ thickness = a*z+b
+ it.object.attribute.thickness = (thickness, thickness)
+ it.increment()
+
+class pyConstantThicknessShader(StrokeShader):
+ def __init__(self, thickness):
+ StrokeShader.__init__(self)
+ self._thickness = thickness
+ def getName(self):
+ return "pyConstantThicknessShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ t = self._thickness/2.0
+ it.object.attribute.thickness = (t, t)
+ it.increment()
+
+class pyFXSThicknessShader(StrokeShader):
+ def __init__(self, thickness):
+ StrokeShader.__init__(self)
+ self._thickness = thickness
+ def getName(self):
+ return "pyFXSThicknessShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ t = self._thickness/2.0
+ it.object.attribute.thickness = (t, t)
+ it.increment()
+
+class pyFXSVaryingThicknessWithDensityShader(StrokeShader):
+ def __init__(self, wsize, threshold_min, threshold_max, thicknessMin, thicknessMax):
+ StrokeShader.__init__(self)
+ self.wsize= wsize
+ self.threshold_min= threshold_min
+ self.threshold_max= threshold_max
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ def getName(self):
+ return "pyVaryingThicknessWithDensityShader"
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ func = DensityF0D(self.wsize)
+ while not it.is_end:
+ toto = it.cast_to_interface0diterator()
+ c= func(toto)
+ if c < self.threshold_min:
+ c = self.threshold_min
+ if c > self.threshold_max:
+ c = self.threshold_max
+## t = (c - self.threshold_min)/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
+ t = (self.threshold_max - c )/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+class pyIncreasingThicknessShader(StrokeShader):
+ def __init__(self, thicknessMin, thicknessMax):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ def getName(self):
+ return "pyIncreasingThicknessShader"
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ c = float(i)/float(n)
+ if i < float(n)/2.0:
+ t = (1.0 - c)*self._thicknessMin + c * self._thicknessMax
+ else:
+ t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+class pyConstrainedIncreasingThicknessShader(StrokeShader):
+ def __init__(self, thicknessMin, thicknessMax, ratio):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ self._ratio = ratio
+ def getName(self):
+ return "pyConstrainedIncreasingThicknessShader"
+ def shade(self, stroke):
+ slength = stroke.length_2d
+ tmp = self._ratio*slength
+ maxT = 0.0
+ if tmp < self._thicknessMax:
+ maxT = tmp
+ else:
+ maxT = self._thicknessMax
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ c = float(i)/float(n)
+ if i < float(n)/2.0:
+ t = (1.0 - c)*self._thicknessMin + c * maxT
+ else:
+ t = (1.0 - c)*maxT + c * self._thicknessMin
+ att.thickness = (t/2.0, t/2.0)
+ if i == n-1:
+ att.thickness = (self._thicknessMin/2.0, self._thicknessMin/2.0)
+ i = i+1
+ it.increment()
+
+class pyDecreasingThicknessShader(StrokeShader):
+ def __init__(self, thicknessMax, thicknessMin):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ def getName(self):
+ return "pyDecreasingThicknessShader"
+ def shade(self, stroke):
+ l = stroke.length_2d
+ tMax = self._thicknessMax
+ if self._thicknessMax > 0.33*l:
+ tMax = 0.33*l
+ tMin = self._thicknessMin
+ if self._thicknessMin > 0.1*l:
+ tMin = 0.1*l
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ c = float(i)/float(n)
+ t = (1.0 - c)*tMax +c*tMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+def smoothC(a, exp):
+ c = pow(float(a),exp)*pow(2.0,exp)
+ return c
+
+class pyNonLinearVaryingThicknessShader(StrokeShader):
+ def __init__(self, thicknessExtremity, thicknessMiddle, exponent):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMiddle
+ self._thicknessMax = thicknessExtremity
+ self._exponent = exponent
+ def getName(self):
+ return "pyNonLinearVaryingThicknessShader"
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ if i < float(n)/2.0:
+ c = float(i)/float(n)
+ else:
+ c = float(n-i)/float(n)
+ c = smoothC(c, self._exponent)
+ t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+## Spherical linear interpolation (cos)
+class pySLERPThicknessShader(StrokeShader):
+ def __init__(self, thicknessMin, thicknessMax, omega=1.2):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ self._omega = omega
+ def getName(self):
+ return "pySLERPThicknessShader"
+ def shade(self, stroke):
+ slength = stroke.length_2d
+ tmp = 0.33*slength
+ maxT = self._thicknessMax
+ if tmp < self._thicknessMax:
+ maxT = tmp
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ c = float(i)/float(n)
+ if i < float(n)/2.0:
+ t = sin((1-c)*self._omega)/sinh(self._omega)*self._thicknessMin + sin(c*self._omega)/sinh(self._omega) * maxT
+ else:
+ t = sin((1-c)*self._omega)/sinh(self._omega)*maxT + sin(c*self._omega)/sinh(self._omega) * self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+class pyTVertexThickenerShader(StrokeShader): ## FIXME
+ def __init__(self, a=1.5, n=3):
+ StrokeShader.__init__(self)
+ self._a = a
+ self._n = n
+ def getName(self):
+ return "pyTVertexThickenerShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
+ while not it.is_end:
+ if predTVertex(it) == 1:
+ it2 = StrokeVertexIterator(it)
+ it2.increment()
+ if not (it.is_begin or it2.is_end):
+ it.increment()
+ continue
+ n = self._n
+ a = self._a
+ if it.is_begin:
+ it3 = StrokeVertexIterator(it)
+ count = 0
+ while (not it3.is_end) and count < n:
+ att = it3.object.attribute
+ (tr, tl) = att.thickness
+ r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
+ #r = (1.0-a)/float(n-1)*count + a
+ att.thickness = (r*tr, r*tl)
+ it3.increment()
+ count = count + 1
+ if it2.is_end:
+ it4 = StrokeVertexIterator(it)
+ count = 0
+ while (not it4.is_begin) and count < n:
+ att = it4.object.attribute
+ (tr, tl) = att.thickness
+ r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
+ #r = (1.0-a)/float(n-1)*count + a
+ att.thickness = (r*tr, r*tl)
+ it4.decrement()
+ count = count + 1
+ if it4.is_begin:
+ att = it4.object.attribute
+ (tr, tl) = att.thickness
+ r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
+ #r = (1.0-a)/float(n-1)*count + a
+ att.thickness = (r*tr, r*tl)
+ it.increment()
+
+class pyImportance2DThicknessShader(StrokeShader):
+ def __init__(self, x, y, w, kmin, kmax):
+ StrokeShader.__init__(self)
+ self._x = x
+ self._y = y
+ self._w = float(w)
+ self._kmin = float(kmin)
+ self._kmax = float(kmax)
+ def getName(self):
+ return "pyImportanceThicknessShader"
+ def shade(self, stroke):
+ origin = Vector([self._x, self._y])
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ p = Vector([v.projected_x, v.projected_y])
+ d = (p-origin).length
+ if d > self._w:
+ k = self._kmin
+ else:
+ k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
+ att = v.attribute
+ (tr, tl) = att.thickness
+ att.thickness = (k*tr/2.0, k*tl/2.0)
+ it.increment()
+
+class pyImportance3DThicknessShader(StrokeShader):
+ def __init__(self, x, y, z, w, kmin, kmax):
+ StrokeShader.__init__(self)
+ self._x = x
+ self._y = y
+ self._z = z
+ self._w = float(w)
+ self._kmin = float(kmin)
+ self._kmax = float(kmax)
+ def getName(self):
+ return "pyImportance3DThicknessShader"
+ def shade(self, stroke):
+ origin = Vector([self._x, self._y, self._z])
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ p = v.point_3d
+ d = (p-origin).length
+ if d > self._w:
+ k = self._kmin
+ else:
+ k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
+ att = v.attribute
+ (tr, tl) = att.thickness
+ att.thickness = (k*tr/2.0, k*tl/2.0)
+ it.increment()
+
+class pyZDependingThicknessShader(StrokeShader):
+ def __init__(self, min, max):
+ StrokeShader.__init__(self)
+ self.__min = min
+ self.__max = max
+ self.__func = GetProjectedZF0D()
+ def getName(self):
+ return "pyZDependingThicknessShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ z_min = 1
+ z_max = 0
+ while not it.is_end:
+ z = self.__func(it.cast_to_interface0diterator())
+ if z < z_min:
+ z_min = z
+ if z > z_max:
+ z_max = z
+ it.increment()
+ z_diff = 1 / (z_max - z_min)
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ z = (self.__func(it.cast_to_interface0diterator()) - z_min) * z_diff
+ thickness = (1 - z) * self.__max + z * self.__min
+ it.object.attribute.thickness = (thickness, thickness)
+ it.increment()
+
+
+## color modifiers
+##################
+
+class pyConstantColorShader(StrokeShader):
+ def __init__(self,r,g,b, a = 1):
+ StrokeShader.__init__(self)
+ self._r = r
+ self._g = g
+ self._b = b
+ self._a = a
+ def getName(self):
+ return "pyConstantColorShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ att.color = (self._r, self._g, self._b)
+ att.alpha = self._a
+ it.increment()
+
+#c1->c2
+class pyIncreasingColorShader(StrokeShader):
+ def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
+ StrokeShader.__init__(self)
+ self._c1 = [r1,g1,b1,a1]
+ self._c2 = [r2,g2,b2,a2]
+ def getName(self):
+ return "pyIncreasingColorShader"
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size() - 1
+ inc = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ c = float(inc)/float(n)
+
+ att.color = ((1-c)*self._c1[0] + c*self._c2[0],
+ (1-c)*self._c1[1] + c*self._c2[1],
+ (1-c)*self._c1[2] + c*self._c2[2])
+ att.alpha = (1-c)*self._c1[3] + c*self._c2[3]
+ inc = inc+1
+ it.increment()
+
+# c1->c2->c1
+class pyInterpolateColorShader(StrokeShader):
+ def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
+ StrokeShader.__init__(self)
+ self._c1 = [r1,g1,b1,a1]
+ self._c2 = [r2,g2,b2,a2]
+ def getName(self):
+ return "pyInterpolateColorShader"
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size() - 1
+ inc = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ u = float(inc)/float(n)
+ c = 1-2*(fabs(u-0.5))
+ att.color = ((1-c)*self._c1[0] + c*self._c2[0],
+ (1-c)*self._c1[1] + c*self._c2[1],
+ (1-c)*self._c1[2] + c*self._c2[2])
+ att.alpha = (1-c)*self._c1[3] + c*self._c2[3]
+ inc = inc+1
+ it.increment()
+
+class pyMaterialColorShader(StrokeShader):
+ def __init__(self, threshold=50):
+ StrokeShader.__init__(self)
+ self._threshold = threshold
+ def getName(self):
+ return "pyMaterialColorShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ func = MaterialF0D()
+ xn = 0.312713
+ yn = 0.329016
+ Yn = 1.0
+ un = 4.* xn/ ( -2.*xn + 12.*yn + 3. )
+ vn= 9.* yn/ ( -2.*xn + 12.*yn +3. )
+ while not it.is_end:
+ toto = it.cast_to_interface0diterator()
+ mat = func(toto)
+
+ r = mat.diffuse[0]
+ g = mat.diffuse[1]
+ b = mat.diffuse[2]
+
+ X = 0.412453*r + 0.35758 *g + 0.180423*b
+ Y = 0.212671*r + 0.71516 *g + 0.072169*b
+ Z = 0.019334*r + 0.119193*g + 0.950227*b
+
+ if X == 0 and Y == 0 and Z == 0:
+ X = 0.01
+ Y = 0.01
+ Z = 0.01
+ u = 4.*X / (X + 15.*Y + 3.*Z)
+ v = 9.*Y / (X + 15.*Y + 3.*Z)
+
+ L= 116. * pow((Y/Yn),(1./3.)) -16
+ U = 13. * L * (u - un)
+ V = 13. * L * (v - vn)
+
+ if L > self._threshold:
+ L = L/1.3
+ U = U+10
+ else:
+ L = L +2.5*(100-L)/5.
+ U = U/3.0
+ V = V/3.0
+ u = U / (13. * L) + un
+ v = V / (13. * L) + vn
+
+ Y = Yn * pow( ((L+16.)/116.), 3.)
+ X = -9. * Y * u / ((u - 4.)* v - u * v)
+ Z = (9. * Y - 15*v*Y - v*X) /( 3. * v)
+
+ r = 3.240479 * X - 1.53715 * Y - 0.498535 * Z
+ g = -0.969256 * X + 1.875991 * Y + 0.041556 * Z
+ b = 0.055648 * X - 0.204043 * Y + 1.057311 * Z
+
+ r = max(0,r)
+ g = max(0,g)
+ b = max(0,b)
+
+ it.object.attribute.color = (r, g, b)
+ it.increment()
+
+class pyRandomColorShader(StrokeShader):
+ def __init__(self, s=1):
+ StrokeShader.__init__(self)
+ seed(s)
+ def getName(self):
+ return "pyRandomColorShader"
+ def shade(self, stroke):
+ ## pick a random color
+ c0 = float(uniform(15,75))/100.0
+ c1 = float(uniform(15,75))/100.0
+ c2 = float(uniform(15,75))/100.0
+ print(c0, c1, c2)
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ it.object.attribute.color = (c0,c1,c2)
+ it.increment()
+
+class py2DCurvatureColorShader(StrokeShader):
+ def getName(self):
+ return "py2DCurvatureColorShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ func = Curvature2DAngleF0D()
+ while not it.is_end:
+ c = func(it.cast_to_interface0diterator())
+ if c < 0:
+ print("negative 2D curvature")
+ color = 10.0 * c/3.1415
+ it.object.attribute.color = (color, color, color)
+ it.increment()
+
+class pyTimeColorShader(StrokeShader):
+ def __init__(self, step=0.01):
+ StrokeShader.__init__(self)
+ self._t = 0
+ self._step = step
+ def shade(self, stroke):
+ c = self._t*1.0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ it.object.attribute.color = (c,c,c)
+ it.increment()
+ self._t = self._t+self._step
+
+## geometry modifiers
+
+class pySamplingShader(StrokeShader):
+ def __init__(self, sampling):
+ StrokeShader.__init__(self)
+ self._sampling = sampling
+ def getName(self):
+ return "pySamplingShader"
+ def shade(self, stroke):
+ stroke.resample(float(self._sampling))
+ stroke.update_length()
+
+class pyBackboneStretcherShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def getName(self):
+ return "pyBackboneStretcherShader"
+ def shade(self, stroke):
+ it0 = stroke.stroke_vertices_begin()
+ it1 = StrokeVertexIterator(it0)
+ it1.increment()
+ itn = stroke.stroke_vertices_end()
+ itn.decrement()
+ itn_1 = StrokeVertexIterator(itn)
+ itn_1.decrement()
+ v0 = it0.object
+ v1 = it1.object
+ vn_1 = itn_1.object
+ vn = itn.object
+ p0 = Vector([v0.projected_x, v0.projected_y])
+ pn = Vector([vn.projected_x, vn.projected_y])
+ p1 = Vector([v1.projected_x, v1.projected_y])
+ pn_1 = Vector([vn_1.projected_x, vn_1.projected_y])
+ d1 = p0-p1
+ d1.normalize()
+ dn = pn-pn_1
+ dn.normalize()
+ newFirst = p0+d1*float(self._l)
+ newLast = pn+dn*float(self._l)
+ v0.point = newFirst
+ vn.point = newLast
+ stroke.update_length()
+
+class pyLengthDependingBackboneStretcherShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def getName(self):
+ return "pyBackboneStretcherShader"
+ def shade(self, stroke):
+ l = stroke.length_2d
+ stretch = self._l*l
+ it0 = stroke.stroke_vertices_begin()
+ it1 = StrokeVertexIterator(it0)
+ it1.increment()
+ itn = stroke.stroke_vertices_end()
+ itn.decrement()
+ itn_1 = StrokeVertexIterator(itn)
+ itn_1.decrement()
+ v0 = it0.object
+ v1 = it1.object
+ vn_1 = itn_1.object
+ vn = itn.object
+ p0 = Vector([v0.projected_x, v0.projected_y])
+ pn = Vector([vn.projected_x, vn.projected_y])
+ p1 = Vector([v1.projected_x, v1.projected_y])
+ pn_1 = Vector([vn_1.projected_x, vn_1.projected_y])
+ d1 = p0-p1
+ d1.normalize()
+ dn = pn-pn_1
+ dn.normalize()
+ newFirst = p0+d1*float(stretch)
+ newLast = pn+dn*float(stretch)
+ v0.point = newFirst
+ vn.point = newLast
+ stroke.update_length()
+
+
+## Shader to replace a stroke by its corresponding tangent
+class pyGuidingLineShader(StrokeShader):
+ def getName(self):
+ return "pyGuidingLineShader"
+ ## shading method
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin() ## get the first vertex
+ itlast = stroke.stroke_vertices_end() ##
+ itlast.decrement() ## get the last one
+ t = itlast.object.point - it.object.point ## tangent direction
+ itmiddle = StrokeVertexIterator(it) ##
+ while itmiddle.object.u < 0.5: ## look for the stroke middle vertex
+ itmiddle.increment() ##
+ it = StrokeVertexIterator(itmiddle)
+ it.increment()
+ while not it.is_end: ## position all the vertices along the tangent for the right part
+ it.object.point = itmiddle.object.point \
+ +t*(it.object.u-itmiddle.object.u)
+ it.increment()
+ it = StrokeVertexIterator(itmiddle)
+ it.decrement()
+ while not it.is_begin: ## position all the vertices along the tangent for the left part
+ it.object.point = itmiddle.object.point \
+ -t*(itmiddle.object.u-it.object.u)
+ it.decrement()
+ it.object.point = itmiddle.object.point-t*itmiddle.object.u ## first vertex
+ stroke.update_length()
+
+
+class pyBackboneStretcherNoCuspShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def getName(self):
+ return "pyBackboneStretcherNoCuspShader"
+ def shade(self, stroke):
+ it0 = stroke.stroke_vertices_begin()
+ it1 = StrokeVertexIterator(it0)
+ it1.increment()
+ itn = stroke.stroke_vertices_end()
+ itn.decrement()
+ itn_1 = StrokeVertexIterator(itn)
+ itn_1.decrement()
+ v0 = it0.object
+ v1 = it1.object
+ if (v0.nature & Nature.CUSP) == 0 and (v1.nature & Nature.CUSP) == 0:
+ p0 = v0.point
+ p1 = v1.point
+ d1 = p0-p1
+ d1.normalize()
+ newFirst = p0+d1*float(self._l)
+ v0.point = newFirst
+ vn_1 = itn_1.object
+ vn = itn.object
+ if (vn.nature & Nature.CUSP) == 0 and (vn_1.nature & Nature.CUSP) == 0:
+ pn = vn.point
+ pn_1 = vn_1.point
+ dn = pn-pn_1
+ dn.normalize()
+ newLast = pn+dn*float(self._l)
+ vn.point = newLast
+ stroke.update_length()
+
+class pyDiffusion2Shader(StrokeShader):
+ """This shader iteratively adds an offset to the position of each
+ stroke vertex in the direction perpendicular to the stroke direction
+ at the point. The offset is scaled by the 2D curvature (i.e., how
+ quickly the stroke curve is) at the point."""
+ def __init__(self, lambda1, nbIter):
+ StrokeShader.__init__(self)
+ self._lambda = lambda1
+ self._nbIter = nbIter
+ self._normalInfo = Normal2DF0D()
+ self._curvatureInfo = Curvature2DAngleF0D()
+ def getName(self):
+ return "pyDiffusionShader"
+ def shade(self, stroke):
+ for i in range (1, self._nbIter):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ p1 = v.point
+ p2 = self._normalInfo(it.cast_to_interface0diterator())*self._lambda*self._curvatureInfo(it.cast_to_interface0diterator())
+ v.point = p1+p2
+ it.increment()
+ stroke.update_length()
+
+class pyTipRemoverShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def getName(self):
+ return "pyTipRemoverShader"
+ def shade(self, stroke):
+ originalSize = stroke.stroke_vertices_size()
+ if originalSize < 4:
+ return
+ verticesToRemove = []
+ oldAttributes = []
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ if v.curvilinear_abscissa < self._l or v.stroke_length-v.curvilinear_abscissa < self._l:
+ verticesToRemove.append(v)
+ oldAttributes.append(StrokeAttribute(v.attribute))
+ it.increment()
+ if originalSize-len(verticesToRemove) < 2:
+ return
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+ stroke.resample(originalSize)
+ if stroke.stroke_vertices_size() != originalSize:
+ print("pyTipRemover: Warning: resampling problem")
+ it = stroke.stroke_vertices_begin()
+ for a in oldAttributes:
+ if it.is_end:
+ break
+ it.object.attribute = a
+ it.increment()
+ stroke.update_length()
+
+class pyTVertexRemoverShader(StrokeShader):
+ def getName(self):
+ return "pyTVertexRemoverShader"
+ def shade(self, stroke):
+ if stroke.stroke_vertices_size() <= 3:
+ return
+ predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
+ it = stroke.stroke_vertices_begin()
+ itlast = stroke.stroke_vertices_end()
+ itlast.decrement()
+ if predTVertex(it):
+ stroke.remove_vertex(it.object)
+ if predTVertex(itlast):
+ stroke.remove_vertex(itlast.object)
+ stroke.update_length()
+
+class pyExtremitiesOrientationShader(StrokeShader):
+ def __init__(self, x1,y1,x2=0,y2=0):
+ StrokeShader.__init__(self)
+ self._v1 = Vector([x1,y1])
+ self._v2 = Vector([x2,y2])
+ def getName(self):
+ return "pyExtremitiesOrientationShader"
+ def shade(self, stroke):
+ #print(self._v1.x,self._v1.y)
+ stroke.setBeginningOrientation(self._v1.x,self._v1.y)
+ stroke.setEndingOrientation(self._v2.x,self._v2.y)
+
+def get_fedge(it1, it2):
+ return it1.get_fedge(it2)
+
+class pyHLRShader(StrokeShader):
+ def getName(self):
+ return "pyHLRShader"
+ def shade(self, stroke):
+ originalSize = stroke.stroke_vertices_size()
+ if originalSize < 4:
+ return
+ it = stroke.stroke_vertices_begin()
+ invisible = 0
+ it2 = StrokeVertexIterator(it)
+ it2.increment()
+ fe = get_fedge(it.object, it2.object)
+ if fe.viewedge.qi != 0:
+ invisible = 1
+ while not it2.is_end:
+ v = it.object
+ vnext = it2.object
+ if (v.nature & Nature.VIEW_VERTEX) != 0:
+ #if (v.nature & Nature.T_VERTEX) != 0:
+ fe = get_fedge(v, vnext)
+ qi = fe.viewedge.qi
+ if qi != 0:
+ invisible = 1
+ else:
+ invisible = 0
+ if invisible:
+ v.attribute.visible = False
+ it.increment()
+ it2.increment()
+
+class pyTVertexOrientationShader(StrokeShader):
+ def __init__(self):
+ StrokeShader.__init__(self)
+ self._Get2dDirection = Orientation2DF1D()
+ def getName(self):
+ return "pyTVertexOrientationShader"
+ ## finds the TVertex orientation from the TVertex and
+ ## the previous or next edge
+ def findOrientation(self, tv, ve):
+ mateVE = tv.get_mate(ve)
+ if ve.qi != 0 or mateVE.qi != 0:
+ ait = AdjacencyIterator(tv,1,0)
+ winner = None
+ incoming = True
+ while not ait.is_end:
+ ave = ait.object
+ if ave.id != ve.id and ave.id != mateVE.id:
+ winner = ait.object
+ if not ait.isIncoming(): # FIXME
+ incoming = False
+ break
+ ait.increment()
+ if winner is not None:
+ if not incoming:
+ direction = self._Get2dDirection(winner.last_fedge)
+ else:
+ direction = self._Get2dDirection(winner.first_fedge)
+ return direction
+ return None
+ def castToTVertex(self, cp):
+ if cp.t2d() == 0.0:
+ return cp.first_svertex.viewvertex
+ elif cp.t2d() == 1.0:
+ return cp.second_svertex.viewvertex
+ return None
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ it2 = StrokeVertexIterator(it)
+ it2.increment()
+ ## case where the first vertex is a TVertex
+ v = it.object
+ if (v.nature & Nature.T_VERTEX) != 0:
+ tv = self.castToTVertex(v)
+ if tv is not None:
+ ve = get_fedge(v, it2.object).viewedge
+ dir = self.findOrientation(tv, ve)
+ if dir is not None:
+ #print(dir.x, dir.y)
+ v.attribute.set_attribute_vec2("orientation", dir)
+ while not it2.is_end:
+ vprevious = it.object
+ v = it2.object
+ if (v.nature & Nature.T_VERTEX) != 0:
+ tv = self.castToTVertex(v)
+ if tv is not None:
+ ve = get_fedge(vprevious, v).viewedge
+ dir = self.findOrientation(tv, ve)
+ if dir is not None:
+ #print(dir.x, dir.y)
+ v.attribute.set_attribute_vec2("orientation", dir)
+ it.increment()
+ it2.increment()
+ ## case where the last vertex is a TVertex
+ v = it.object
+ if (v.nature & Nature.T_VERTEX) != 0:
+ itPrevious = StrokeVertexIterator(it)
+ itPrevious.decrement()
+ tv = self.castToTVertex(v)
+ if tv is not None:
+ ve = get_fedge(itPrevious.object, v).viewedge
+ dir = self.findOrientation(tv, ve)
+ if dir is not None:
+ #print(dir.x, dir.y)
+ v.attribute.set_attribute_vec2("orientation", dir)
+
+class pySinusDisplacementShader(StrokeShader):
+ def __init__(self, f, a):
+ StrokeShader.__init__(self)
+ self._f = f
+ self._a = a
+ self._getNormal = Normal2DF0D()
+ def getName(self):
+ return "pySinusDisplacementShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ #print(self._getNormal.getName())
+ n = self._getNormal(it.cast_to_interface0diterator())
+ p = v.point
+ u = v.u
+ a = self._a*(1-2*(fabs(u-0.5)))
+ n = n*a*cos(self._f*u*6.28)
+ #print(n.x, n.y)
+ v.point = p+n
+ #v.point = v.point+n*a*cos(f*v.u)
+ it.increment()
+ stroke.update_length()
+
+class pyPerlinNoise1DShader(StrokeShader):
+ def __init__(self, freq = 10, amp = 10, oct = 4, seed = -1):
+ StrokeShader.__init__(self)
+ self.__noise = Noise(seed)
+ self.__freq = freq
+ self.__amp = amp
+ self.__oct = oct
+ def getName(self):
+ return "pyPerlinNoise1DShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ i = v.projected_x + v.projected_y
+ nres = self.__noise.turbulence1(i, self.__freq, self.__amp, self.__oct)
+ v.point = (v.projected_x + nres, v.projected_y + nres)
+ it.increment()
+ stroke.update_length()
+
+class pyPerlinNoise2DShader(StrokeShader):
+ def __init__(self, freq = 10, amp = 10, oct = 4, seed = -1):
+ StrokeShader.__init__(self)
+ self.__noise = Noise(seed)
+ self.__freq = freq
+ self.__amp = amp
+ self.__oct = oct
+ def getName(self):
+ return "pyPerlinNoise2DShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ vec = Vector([v.projected_x, v.projected_y])
+ nres = self.__noise.turbulence2(vec, self.__freq, self.__amp, self.__oct)
+ v.point = (v.projected_x + nres, v.projected_y + nres)
+ it.increment()
+ stroke.update_length()
+
+class pyBluePrintCirclesShader(StrokeShader):
+ def __init__(self, turns = 1, random_radius = 3, random_center = 5):
+ StrokeShader.__init__(self)
+ self.__turns = turns
+ self.__random_center = random_center
+ self.__random_radius = random_radius
+ def getName(self):
+ return "pyBluePrintCirclesShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ if it.is_end:
+ return
+ p_min = it.object.point.copy()
+ p_max = it.object.point.copy()
+ while not it.is_end:
+ p = it.object.point
+ if p.x < p_min.x:
+ p_min.x = p.x
+ if p.x > p_max.x:
+ p_max.x = p.x
+ if p.y < p_min.y:
+ p_min.y = p.y
+ if p.y > p_max.y:
+ p_max.y = p.y
+ it.increment()
+ stroke.resample(32 * self.__turns)
+ sv_nb = stroke.stroke_vertices_size()
+# print("min :", p_min.x, p_min.y) # DEBUG
+# print("mean :", p_sum.x, p_sum.y) # DEBUG
+# print("max :", p_max.x, p_max.y) # DEBUG
+# print("----------------------") # DEBUG
+#######################################################
+ sv_nb = sv_nb // self.__turns
+ center = (p_min + p_max) / 2
+ radius = (center.x - p_min.x + center.y - p_min.y) / 2
+ p_new = Vector([0, 0])
+#######################################################
+ R = self.__random_radius
+ C = self.__random_center
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ for j in range(self.__turns):
+ prev_radius = radius
+ prev_center = center
+ radius = radius + randint(-R, R)
+ center = center + Vector([randint(-C, C), randint(-C, C)])
+ while i < sv_nb and not it.is_end:
+ t = float(i) / float(sv_nb - 1)
+ r = prev_radius + (radius - prev_radius) * t
+ c = prev_center + (center - prev_center) * t
+ p_new.x = c.x + r * cos(2 * pi * t)
+ p_new.y = c.y + r * sin(2 * pi * t)
+ it.object.point = p_new
+ i = i + 1
+ it.increment()
+ i = 1
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+class pyBluePrintEllipsesShader(StrokeShader):
+ def __init__(self, turns = 1, random_radius = 3, random_center = 5):
+ StrokeShader.__init__(self)
+ self.__turns = turns
+ self.__random_center = random_center
+ self.__random_radius = random_radius
+ def getName(self):
+ return "pyBluePrintEllipsesShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ if it.is_end:
+ return
+ p_min = it.object.point.copy()
+ p_max = it.object.point.copy()
+ while not it.is_end:
+ p = it.object.point
+ if p.x < p_min.x:
+ p_min.x = p.x
+ if p.x > p_max.x:
+ p_max.x = p.x
+ if p.y < p_min.y:
+ p_min.y = p.y
+ if p.y > p_max.y:
+ p_max.y = p.y
+ it.increment()
+ stroke.resample(32 * self.__turns)
+ sv_nb = stroke.stroke_vertices_size()
+ sv_nb = sv_nb // self.__turns
+ center = (p_min + p_max) / 2
+ radius = center - p_min
+ p_new = Vector([0, 0])
+#######################################################
+ R = self.__random_radius
+ C = self.__random_center
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ for j in range(self.__turns):
+ prev_radius = radius
+ prev_center = center
+ radius = radius + Vector([randint(-R, R), randint(-R, R)])
+ center = center + Vector([randint(-C, C), randint(-C, C)])
+ while i < sv_nb and not it.is_end:
+ t = float(i) / float(sv_nb - 1)
+ r = prev_radius + (radius - prev_radius) * t
+ c = prev_center + (center - prev_center) * t
+ p_new.x = c.x + r.x * cos(2 * pi * t)
+ p_new.y = c.y + r.y * sin(2 * pi * t)
+ it.object.point = p_new
+ i = i + 1
+ it.increment()
+ i = 1
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+
+class pyBluePrintSquaresShader(StrokeShader):
+ def __init__(self, turns = 1, bb_len = 10, bb_rand = 0):
+ StrokeShader.__init__(self)
+ self.__turns = turns
+ self.__bb_len = bb_len
+ self.__bb_rand = bb_rand
+ def getName(self):
+ return "pyBluePrintSquaresShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ if it.is_end:
+ return
+ p_min = it.object.point.copy()
+ p_max = it.object.point.copy()
+ while not it.is_end:
+ p = it.object.point
+ if p.x < p_min.x:
+ p_min.x = p.x
+ if p.x > p_max.x:
+ p_max.x = p.x
+ if p.y < p_min.y:
+ p_min.y = p.y
+ if p.y > p_max.y:
+ p_max.y = p.y
+ it.increment()
+ stroke.resample(32 * self.__turns)
+ sv_nb = stroke.stroke_vertices_size()
+#######################################################
+ sv_nb = sv_nb // self.__turns
+ first = sv_nb // 4
+ second = 2 * first
+ third = 3 * first
+ fourth = sv_nb
+ p_first = Vector([p_min.x - self.__bb_len, p_min.y])
+ p_first_end = Vector([p_max.x + self.__bb_len, p_min.y])
+ p_second = Vector([p_max.x, p_min.y - self.__bb_len])
+ p_second_end = Vector([p_max.x, p_max.y + self.__bb_len])
+ p_third = Vector([p_max.x + self.__bb_len, p_max.y])
+ p_third_end = Vector([p_min.x - self.__bb_len, p_max.y])
+ p_fourth = Vector([p_min.x, p_max.y + self.__bb_len])
+ p_fourth_end = Vector([p_min.x, p_min.y - self.__bb_len])
+#######################################################
+ R = self.__bb_rand
+ r = self.__bb_rand // 2
+ it = stroke.stroke_vertices_begin()
+ visible = True
+ for j in range(self.__turns):
+ p_first = p_first + Vector([randint(-R, R), randint(-r, r)])
+ p_first_end = p_first_end + Vector([randint(-R, R), randint(-r, r)])
+ p_second = p_second + Vector([randint(-r, r), randint(-R, R)])
+ p_second_end = p_second_end + Vector([randint(-r, r), randint(-R, R)])
+ p_third = p_third + Vector([randint(-R, R), randint(-r, r)])
+ p_third_end = p_third_end + Vector([randint(-R, R), randint(-r, r)])
+ p_fourth = p_fourth + Vector([randint(-r, r), randint(-R, R)])
+ p_fourth_end = p_fourth_end + Vector([randint(-r, r), randint(-R, R)])
+ vec_first = p_first_end - p_first
+ vec_second = p_second_end - p_second
+ vec_third = p_third_end - p_third
+ vec_fourth = p_fourth_end - p_fourth
+ i = 0
+ while i < sv_nb and not it.is_end:
+ if i < first:
+ p_new = p_first + vec_first * float(i)/float(first - 1)
+ if i == first - 1:
+ visible = False
+ elif i < second:
+ p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
+ if i == second - 1:
+ visible = False
+ elif i < third:
+ p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
+ if i == third - 1:
+ visible = False
+ else:
+ p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
+ if i == fourth - 1:
+ visible = False
+ if it.object == None:
+ i = i + 1
+ it.increment()
+ if not visible:
+ visible = True
+ continue
+ it.object.point = p_new
+ it.object.attribute.visible = visible
+ if not visible:
+ visible = True
+ i = i + 1
+ it.increment()
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+
+class pyBluePrintDirectedSquaresShader(StrokeShader):
+ def __init__(self, turns = 1, bb_len = 10, mult = 1):
+ StrokeShader.__init__(self)
+ self.__mult = mult
+ self.__turns = turns
+ self.__bb_len = 1 + float(bb_len) / 100
+ def getName(self):
+ return "pyBluePrintDirectedSquaresShader"
+ def shade(self, stroke):
+ stroke.resample(32 * self.__turns)
+ p_mean = Vector([0, 0])
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ p = it.object.point
+ p_mean = p_mean + p
+ it.increment()
+ sv_nb = stroke.stroke_vertices_size()
+ p_mean = p_mean / sv_nb
+ p_var_xx = 0
+ p_var_yy = 0
+ p_var_xy = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ p = it.object.point
+ p_var_xx = p_var_xx + pow(p.x - p_mean.x, 2)
+ p_var_yy = p_var_yy + pow(p.y - p_mean.y, 2)
+ p_var_xy = p_var_xy + (p.x - p_mean.x) * (p.y - p_mean.y)
+ it.increment()
+ p_var_xx = p_var_xx / sv_nb
+ p_var_yy = p_var_yy / sv_nb
+ p_var_xy = p_var_xy / sv_nb
+## print(p_var_xx, p_var_yy, p_var_xy)
+ trace = p_var_xx + p_var_yy
+ det = p_var_xx * p_var_yy - p_var_xy * p_var_xy
+ sqrt_coeff = sqrt(trace * trace - 4 * det)
+ lambda1 = (trace + sqrt_coeff) / 2
+ lambda2 = (trace - sqrt_coeff) / 2
+## print(lambda1, lambda2)
+ theta = atan(2 * p_var_xy / (p_var_xx - p_var_yy)) / 2
+## print(theta)
+ if p_var_yy > p_var_xx:
+ e1 = Vector([cos(theta + pi / 2), sin(theta + pi / 2)]) * sqrt(lambda1) * self.__mult
+ e2 = Vector([cos(theta + pi), sin(theta + pi)]) * sqrt(lambda2) * self.__mult
+ else:
+ e1 = Vector([cos(theta), sin(theta)]) * sqrt(lambda1) * self.__mult
+ e2 = Vector([cos(theta + pi / 2), sin(theta + pi / 2)]) * sqrt(lambda2) * self.__mult
+#######################################################
+ sv_nb = sv_nb // self.__turns
+ first = sv_nb // 4
+ second = 2 * first
+ third = 3 * first
+ fourth = sv_nb
+ bb_len1 = self.__bb_len
+ bb_len2 = 1 + (bb_len1 - 1) * sqrt(lambda1 / lambda2)
+ p_first = p_mean - e1 - e2 * bb_len2
+ p_second = p_mean - e1 * bb_len1 + e2
+ p_third = p_mean + e1 + e2 * bb_len2
+ p_fourth = p_mean + e1 * bb_len1 - e2
+ vec_first = e2 * bb_len2 * 2
+ vec_second = e1 * bb_len1 * 2
+ vec_third = vec_first * -1
+ vec_fourth = vec_second * -1
+#######################################################
+ it = stroke.stroke_vertices_begin()
+ visible = True
+ for j in range(self.__turns):
+ i = 0
+ while i < sv_nb:
+ if i < first:
+ p_new = p_first + vec_first * float(i)/float(first - 1)
+ if i == first - 1:
+ visible = False
+ elif i < second:
+ p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
+ if i == second - 1:
+ visible = False
+ elif i < third:
+ p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
+ if i == third - 1:
+ visible = False
+ else:
+ p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
+ if i == fourth - 1:
+ visible = False
+ it.object.point = p_new
+ it.object.attribute.visible = visible
+ if not visible:
+ visible = True
+ i = i + 1
+ it.increment()
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+class pyModulateAlphaShader(StrokeShader):
+ def __init__(self, min = 0, max = 1):
+ StrokeShader.__init__(self)
+ self.__min = min
+ self.__max = max
+ def getName(self):
+ return "pyModulateAlphaShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ alpha = it.object.attribute.alpha
+ p = it.object.point
+ alpha = alpha * p.y / 400
+ if alpha < self.__min:
+ alpha = self.__min
+ elif alpha > self.__max:
+ alpha = self.__max
+ it.object.attribute.alpha = alpha
+ it.increment()
+
+## various
+class pyDummyShader(StrokeShader):
+ def getName(self):
+ return "pyDummyShader"
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ toto = it.cast_to_interface0diterator()
+ att = it.object.attribute
+ att.color = (0.3, 0.4, 0.4)
+ att.thickness = (0, 5)
+ it.increment()
+
+class pyDebugShader(StrokeShader):
+ def getName(self):
+ return "pyDebugShader"
+ def shade(self, stroke):
+ fe = GetSelectedFEdgeCF()
+ id1 = fe.first_svertex.id
+ id2 = fe.second_svertex.id
+ #print(id1.first, id1.second)
+ #print(id2.first, id2.second)
+ it = stroke.stroke_vertices_begin()
+ found = True
+ foundfirst = True
+ foundsecond = False
+ while not it.is_end:
+ cp = it.object
+ if cp.first_svertex.id == id1 or cp.second_svertex.id == id1:
+ foundfirst = True
+ if cp.first_svertex.id == id2 or cp.second_svertex.id == id2:
+ foundsecond = True
+ if foundfirst and foundsecond:
+ found = True
+ break
+ it.increment()
+ if found:
+ print("The selected Stroke id is: ", stroke.id.first, stroke.id.second)