c9b022d46dc8911d4fcbf5ac96cd30b47103a0b8
[blender.git] / release / scripts / freestyle / style_modules / shaders.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 #  Filename : shaders.py
20 #  Authors  : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin 
21 #  Date     : 11/08/2005
22 #  Purpose  : Stroke shaders to be used for creation of stylized strokes
23
24 from freestyle import AdjacencyIterator, Curvature2DAngleF0D, DensityF0D, GetProjectedZF0D, \
25     Interface0DIterator, MaterialF0D, Nature, Noise, Normal2DF0D, Orientation2DF1D, \
26     StrokeAttribute, StrokeShader, StrokeVertexIterator, ZDiscontinuityF0D
27 from freestyle import ContextFunctions as CF
28 from PredicatesU0D import pyVertexNatureUP0D
29
30 import math
31 import mathutils
32 import random
33
34 ## thickness modifiers
35 ######################
36
37 class pyDepthDiscontinuityThicknessShader(StrokeShader):
38         def __init__(self, min, max):
39                 StrokeShader.__init__(self)
40                 self.__min = float(min)
41                 self.__max = float(max)
42                 self.__func = ZDiscontinuityF0D()
43         def shade(self, stroke):
44                 z_min=0.0
45                 z_max=1.0
46                 a = (self.__max - self.__min)/(z_max-z_min)
47                 b = (self.__min*z_max-self.__max*z_min)/(z_max-z_min)
48                 it = stroke.stroke_vertices_begin()
49                 while not it.is_end:
50                         z = self.__func(Interface0DIterator(it))
51                         thickness = a*z+b
52                         it.object.attribute.thickness = (thickness, thickness)
53                         it.increment()
54
55 class pyConstantThicknessShader(StrokeShader):
56         def __init__(self, thickness):
57                 StrokeShader.__init__(self)
58                 self._thickness = thickness
59         def shade(self, stroke):
60                 it = stroke.stroke_vertices_begin()
61                 while not it.is_end:
62                         t = self._thickness/2.0
63                         it.object.attribute.thickness = (t, t)
64                         it.increment()
65
66 class pyFXSVaryingThicknessWithDensityShader(StrokeShader):
67         def __init__(self, wsize, threshold_min, threshold_max, thicknessMin, thicknessMax):
68                 StrokeShader.__init__(self)
69                 self.wsize= wsize
70                 self.threshold_min= threshold_min
71                 self.threshold_max= threshold_max
72                 self._thicknessMin = thicknessMin
73                 self._thicknessMax = thicknessMax
74         def shade(self, stroke):
75                 n = stroke.stroke_vertices_size()
76                 i = 0
77                 it = stroke.stroke_vertices_begin()
78                 func = DensityF0D(self.wsize)
79                 while not it.is_end:
80                         c = func(Interface0DIterator(it))
81                         if c < self.threshold_min:
82                                 c = self.threshold_min
83                         if c > self.threshold_max:
84                                 c = self.threshold_max
85 ##                      t = (c - self.threshold_min)/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
86                         t = (self.threshold_max - c  )/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
87                         it.object.attribute.thickness = (t/2.0, t/2.0)
88                         i = i+1
89                         it.increment()
90
91 class pyIncreasingThicknessShader(StrokeShader):
92         def __init__(self, thicknessMin, thicknessMax):
93                 StrokeShader.__init__(self)
94                 self._thicknessMin = thicknessMin
95                 self._thicknessMax = thicknessMax
96         def shade(self, stroke):
97                 n = stroke.stroke_vertices_size()
98                 i = 0
99                 it = stroke.stroke_vertices_begin()
100                 while not it.is_end:
101                         c = float(i)/float(n)
102                         if i < float(n)/2.0:
103                                 t = (1.0 - c)*self._thicknessMin + c * self._thicknessMax
104                         else:
105                                 t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
106                         it.object.attribute.thickness = (t/2.0, t/2.0)
107                         i = i+1
108                         it.increment()
109
110 class pyConstrainedIncreasingThicknessShader(StrokeShader):
111         def __init__(self, thicknessMin, thicknessMax, ratio):
112                 StrokeShader.__init__(self)
113                 self._thicknessMin = thicknessMin
114                 self._thicknessMax = thicknessMax
115                 self._ratio = ratio
116         def shade(self, stroke):
117                 slength = stroke.length_2d
118                 tmp = self._ratio*slength
119                 maxT = 0.0
120                 if tmp < self._thicknessMax:
121                         maxT = tmp
122                 else:
123                         maxT = self._thicknessMax
124                 n = stroke.stroke_vertices_size()
125                 i = 0
126                 it = stroke.stroke_vertices_begin()
127                 while not it.is_end:
128                         att = it.object.attribute
129                         c = float(i)/float(n)
130                         if i < float(n)/2.0:
131                                 t = (1.0 - c)*self._thicknessMin + c * maxT
132                         else:
133                                 t = (1.0 - c)*maxT + c * self._thicknessMin
134                         att.thickness = (t/2.0, t/2.0)
135                         if i == n-1:
136                                 att.thickness = (self._thicknessMin/2.0, self._thicknessMin/2.0)
137                         i = i+1
138                         it.increment()
139
140 class pyDecreasingThicknessShader(StrokeShader):
141         def __init__(self, thicknessMin, thicknessMax):
142                 StrokeShader.__init__(self)
143                 self._thicknessMin = thicknessMin
144                 self._thicknessMax = thicknessMax
145         def shade(self, stroke):
146                 l = stroke.length_2d
147                 tMax = self._thicknessMax
148                 if self._thicknessMax > 0.33*l:
149                         tMax = 0.33*l
150                 tMin = self._thicknessMin
151                 if self._thicknessMin > 0.1*l:
152                         tMin = 0.1*l
153                 n = stroke.stroke_vertices_size()
154                 i = 0
155                 it = stroke.stroke_vertices_begin()
156                 while not it.is_end:
157                         c = float(i)/float(n)
158                         t = (1.0 - c)*tMax +c*tMin
159                         it.object.attribute.thickness = (t/2.0, t/2.0)
160                         i = i+1
161                         it.increment()
162
163 class pyNonLinearVaryingThicknessShader(StrokeShader):
164         def __init__(self, thicknessExtremity, thicknessMiddle, exponent):
165                 StrokeShader.__init__(self)
166                 self._thicknessMin = thicknessMiddle
167                 self._thicknessMax = thicknessExtremity
168                 self._exponent = exponent
169         def shade(self, stroke):
170                 n = stroke.stroke_vertices_size()
171                 i = 0
172                 it = stroke.stroke_vertices_begin()
173                 while not it.is_end:
174                         if i < float(n)/2.0:
175                                 c = float(i)/float(n)
176                         else:
177                                 c = float(n-i)/float(n)
178                         c = self.smoothC(c, self._exponent)
179                         t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
180                         it.object.attribute.thickness = (t/2.0, t/2.0)
181                         i = i+1
182                         it.increment()
183         def smoothC(self, a, exp):
184                 return math.pow(float(a), exp) * math.pow(2.0, exp)
185
186 ## Spherical linear interpolation (cos)
187 class pySLERPThicknessShader(StrokeShader):
188         def __init__(self, thicknessMin, thicknessMax, omega=1.2):
189                 StrokeShader.__init__(self)
190                 self._thicknessMin = thicknessMin
191                 self._thicknessMax = thicknessMax
192                 self._omega = omega
193         def shade(self, stroke):
194                 slength = stroke.length_2d
195                 tmp = 0.33*slength
196                 maxT = self._thicknessMax
197                 if tmp < self._thicknessMax:
198                         maxT = tmp
199                 n = stroke.stroke_vertices_size()
200                 i = 0
201                 it = stroke.stroke_vertices_begin()
202                 while not it.is_end:
203                         c = float(i)/float(n)
204                         if i < float(n)/2.0:
205                                 t = math.sin((1-c)*self._omega)/math.sinh(self._omega)*self._thicknessMin + math.sin(c*self._omega)/math.sinh(self._omega) * maxT
206                         else:
207                                 t = math.sin((1-c)*self._omega)/math.sinh(self._omega)*maxT + math.sin(c*self._omega)/math.sinh(self._omega) * self._thicknessMin
208                         it.object.attribute.thickness = (t/2.0, t/2.0)
209                         i = i+1
210                         it.increment()
211
212 class pyTVertexThickenerShader(StrokeShader): ## FIXME
213         def __init__(self, a=1.5, n=3):
214                 StrokeShader.__init__(self)
215                 self._a = a
216                 self._n = n
217         def shade(self, stroke):
218                 it = stroke.stroke_vertices_begin()
219                 predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
220                 while not it.is_end:
221                         if predTVertex(it) == 1:
222                                 it2 = StrokeVertexIterator(it)
223                                 it2.increment()
224                                 if not (it.is_begin or it2.is_end):
225                                         it.increment()
226                                         continue
227                                 n = self._n
228                                 a = self._a
229                                 if it.is_begin:
230                                         it3 = StrokeVertexIterator(it)
231                                         count = 0
232                                         while (not it3.is_end) and count < n:
233                                                 att = it3.object.attribute
234                                                 (tr, tl) = att.thickness
235                                                 r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
236                                                 #r = (1.0-a)/float(n-1)*count + a
237                                                 att.thickness = (r*tr, r*tl)    
238                                                 it3.increment()
239                                                 count = count + 1
240                                 if it2.is_end:
241                                         it4 = StrokeVertexIterator(it)
242                                         count = 0
243                                         while (not it4.is_begin) and count < n:
244                                                 att = it4.object.attribute
245                                                 (tr, tl) = att.thickness
246                                                 r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
247                                                 #r = (1.0-a)/float(n-1)*count + a
248                                                 att.thickness = (r*tr, r*tl)    
249                                                 it4.decrement()
250                                                 count = count + 1
251                                         if it4.is_begin:
252                                                 att = it4.object.attribute
253                                                 (tr, tl) = att.thickness
254                                                 r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
255                                                 #r = (1.0-a)/float(n-1)*count + a
256                                                 att.thickness = (r*tr, r*tl)    
257                         it.increment()
258
259 class pyImportance2DThicknessShader(StrokeShader):
260         def __init__(self, x, y, w, kmin, kmax):
261                 StrokeShader.__init__(self)
262                 self._x = x
263                 self._y = y
264                 self._w = float(w)
265                 self._kmin = float(kmin)
266                 self._kmax = float(kmax)
267         def shade(self, stroke):
268                 origin = mathutils.Vector([self._x, self._y])
269                 it = stroke.stroke_vertices_begin()
270                 while not it.is_end:
271                         v = it.object
272                         p = mathutils.Vector([v.projected_x, v.projected_y])
273                         d = (p-origin).length
274                         if d > self._w:
275                                 k = self._kmin
276                         else:
277                                 k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
278                         att = v.attribute
279                         (tr, tl) = att.thickness
280                         att.thickness = (k*tr/2.0, k*tl/2.0)
281                         it.increment()
282
283 class pyImportance3DThicknessShader(StrokeShader):
284         def __init__(self, x, y, z, w, kmin, kmax):
285                 StrokeShader.__init__(self)
286                 self._x = x
287                 self._y = y
288                 self._z = z
289                 self._w = float(w)
290                 self._kmin = float(kmin)
291                 self._kmax = float(kmax)
292         def shade(self, stroke):
293                 origin = mathutils.Vector([self._x, self._y, self._z])
294                 it = stroke.stroke_vertices_begin()
295                 while not it.is_end:
296                         v = it.object
297                         p = v.point_3d
298                         d = (p-origin).length
299                         if d > self._w:
300                                 k = self._kmin
301                         else:
302                                 k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
303                         att = v.attribute
304                         (tr, tl) = att.thickness
305                         att.thickness = (k*tr/2.0, k*tl/2.0)
306                         it.increment()
307
308 class pyZDependingThicknessShader(StrokeShader):
309         def __init__(self, min, max):
310                 StrokeShader.__init__(self)
311                 self.__min = min
312                 self.__max = max
313                 self.__func = GetProjectedZF0D()
314         def shade(self, stroke):
315                 it = stroke.stroke_vertices_begin()
316                 z_min = 1
317                 z_max = 0
318                 while not it.is_end:
319                         z = self.__func(Interface0DIterator(it))
320                         if z < z_min:
321                                 z_min = z
322                         if z > z_max:
323                                 z_max = z
324                         it.increment()
325                 z_diff = 1 / (z_max - z_min)
326                 it = stroke.stroke_vertices_begin()
327                 while not it.is_end:
328                         z = (self.__func(Interface0DIterator(it)) - z_min) * z_diff
329                         thickness = (1 - z) * self.__max + z * self.__min
330                         it.object.attribute.thickness = (thickness, thickness)
331                         it.increment()
332
333
334 ## color modifiers
335 ##################
336
337 class pyConstantColorShader(StrokeShader):
338         def __init__(self,r,g,b, a = 1):
339                 StrokeShader.__init__(self)
340                 self._r = r
341                 self._g = g
342                 self._b = b
343                 self._a = a
344         def shade(self, stroke):
345                 it = stroke.stroke_vertices_begin()
346                 while not it.is_end:
347                         att = it.object.attribute
348                         att.color = (self._r, self._g, self._b)
349                         att.alpha = self._a
350                         it.increment()
351
352 #c1->c2
353 class pyIncreasingColorShader(StrokeShader):
354         def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
355                 StrokeShader.__init__(self)
356                 self._c1 = [r1,g1,b1,a1]
357                 self._c2 = [r2,g2,b2,a2]
358         def shade(self, stroke):
359                 n = stroke.stroke_vertices_size() - 1
360                 inc = 0
361                 it = stroke.stroke_vertices_begin()
362                 while not it.is_end:
363                         att = it.object.attribute
364                         c = float(inc)/float(n)
365
366                         att.color = ((1-c)*self._c1[0] + c*self._c2[0], 
367                                      (1-c)*self._c1[1] + c*self._c2[1],
368                                      (1-c)*self._c1[2] + c*self._c2[2])
369                         att.alpha = (1-c)*self._c1[3] + c*self._c2[3]
370                         inc = inc+1
371                         it.increment()
372
373 # c1->c2->c1
374 class pyInterpolateColorShader(StrokeShader):
375         def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
376                 StrokeShader.__init__(self)
377                 self._c1 = [r1,g1,b1,a1]
378                 self._c2 = [r2,g2,b2,a2]
379         def shade(self, stroke):
380                 n = stroke.stroke_vertices_size() - 1
381                 inc = 0
382                 it = stroke.stroke_vertices_begin()
383                 while not it.is_end:
384                         att = it.object.attribute
385                         u = float(inc)/float(n)
386                         c = 1-2*(math.fabs(u-0.5))
387                         att.color = ((1-c)*self._c1[0] + c*self._c2[0], 
388                                      (1-c)*self._c1[1] + c*self._c2[1],
389                                      (1-c)*self._c1[2] + c*self._c2[2])
390                         att.alpha = (1-c)*self._c1[3] + c*self._c2[3]
391                         inc = inc+1
392                         it.increment()
393
394 class pyMaterialColorShader(StrokeShader):
395         def __init__(self, threshold=50):
396                 StrokeShader.__init__(self)
397                 self._threshold = threshold
398         def shade(self, stroke):
399                 it = stroke.stroke_vertices_begin()
400                 func = MaterialF0D()
401                 xn = 0.312713
402                 yn = 0.329016
403                 Yn = 1.0
404                 un = 4.* xn/ ( -2.*xn + 12.*yn + 3. )
405                 vn= 9.* yn/ ( -2.*xn + 12.*yn +3. )     
406                 while not it.is_end:
407                         mat = func(Interface0DIterator(it))
408                         
409                         r = mat.diffuse[0]
410                         g = mat.diffuse[1]
411                         b = mat.diffuse[2]
412
413                         X = 0.412453*r + 0.35758 *g + 0.180423*b
414                         Y = 0.212671*r + 0.71516 *g + 0.072169*b
415                         Z = 0.019334*r + 0.119193*g + 0.950227*b
416
417                         if X == 0 and Y == 0 and Z == 0:
418                                 X = 0.01
419                                 Y = 0.01
420                                 Z = 0.01
421                         u = 4.*X / (X + 15.*Y + 3.*Z)
422                         v = 9.*Y / (X + 15.*Y + 3.*Z)
423                         
424                         L= 116. * math.pow((Y/Yn),(1./3.)) -16
425                         U = 13. * L * (u - un)
426                         V = 13. * L * (v - vn)
427                         
428                         if L > self._threshold:
429                                 L = L/1.3
430                                 U = U+10
431                         else:
432                                 L = L +2.5*(100-L)/5.
433                                 U = U/3.0
434                                 V = V/3.0                               
435                         u = U / (13. * L) + un
436                         v = V / (13. * L) + vn
437                         
438                         Y = Yn * math.pow( ((L+16.)/116.), 3.)
439                         X = -9. * Y * u / ((u - 4.)* v - u * v)
440                         Z = (9. * Y - 15*v*Y - v*X) /( 3. * v)
441                         
442                         r = 3.240479 * X - 1.53715 * Y - 0.498535 * Z
443                         g = -0.969256 * X + 1.875991 * Y + 0.041556 * Z
444                         b = 0.055648 * X - 0.204043 * Y + 1.057311 * Z
445
446                         r = max(0,r)
447                         g = max(0,g)
448                         b = max(0,b)
449
450                         it.object.attribute.color = (r, g, b)
451                         it.increment()
452
453 class pyRandomColorShader(StrokeShader):
454         def __init__(self, s=1):
455                 StrokeShader.__init__(self)
456                 random.seed(s)
457         def shade(self, stroke):
458                 ## pick a random color
459                 c0 = float(random.uniform(15,75))/100.0
460                 c1 = float(random.uniform(15,75))/100.0
461                 c2 = float(random.uniform(15,75))/100.0
462                 print(c0, c1, c2)
463                 it = stroke.stroke_vertices_begin()
464                 while not it.is_end:
465                         it.object.attribute.color = (c0,c1,c2)
466                         it.increment()
467
468 class py2DCurvatureColorShader(StrokeShader):
469         def shade(self, stroke):
470                 it = stroke.stroke_vertices_begin()
471                 func = Curvature2DAngleF0D()
472                 while not it.is_end:
473                         c = func(Interface0DIterator(it))
474                         if c < 0:
475                                 print("negative 2D curvature")
476                         color = 10.0 * c/3.1415
477                         it.object.attribute.color = (color, color, color)
478                         it.increment()
479
480 class pyTimeColorShader(StrokeShader):
481         def __init__(self, step=0.01):
482                 StrokeShader.__init__(self)
483                 self._t = 0
484                 self._step = step
485         def shade(self, stroke):
486                 c = self._t*1.0
487                 it = stroke.stroke_vertices_begin()
488                 while not it.is_end:
489                         it.object.attribute.color = (c,c,c)
490                         it.increment()
491                 self._t = self._t+self._step
492
493 ## geometry modifiers
494
495 class pySamplingShader(StrokeShader):
496         def __init__(self, sampling):
497                 StrokeShader.__init__(self)
498                 self._sampling = sampling
499         def shade(self, stroke):
500                 stroke.resample(float(self._sampling)) 
501                 stroke.update_length()
502
503 class pyBackboneStretcherShader(StrokeShader):
504         def __init__(self, l):
505                 StrokeShader.__init__(self)
506                 self._l = l
507         def shade(self, stroke):
508                 it0 = stroke.stroke_vertices_begin()
509                 it1 = StrokeVertexIterator(it0)
510                 it1.increment()
511                 itn = stroke.stroke_vertices_end()
512                 itn.decrement()
513                 itn_1 = StrokeVertexIterator(itn)
514                 itn_1.decrement()
515                 v0 = it0.object
516                 v1 = it1.object
517                 vn_1 = itn_1.object
518                 vn = itn.object
519                 p0 = mathutils.Vector([v0.projected_x, v0.projected_y])
520                 pn = mathutils.Vector([vn.projected_x, vn.projected_y])
521                 p1 = mathutils.Vector([v1.projected_x, v1.projected_y])
522                 pn_1 = mathutils.Vector([vn_1.projected_x, vn_1.projected_y])
523                 d1 = p0-p1
524                 d1.normalize()
525                 dn = pn-pn_1
526                 dn.normalize()
527                 newFirst = p0+d1*float(self._l)
528                 newLast = pn+dn*float(self._l)
529                 v0.point = newFirst
530                 vn.point = newLast
531                 stroke.update_length()
532                 
533 class pyLengthDependingBackboneStretcherShader(StrokeShader):
534         def __init__(self, l):
535                 StrokeShader.__init__(self)
536                 self._l = l
537         def shade(self, stroke):
538                 l = stroke.length_2d
539                 stretch = self._l*l 
540                 it0 = stroke.stroke_vertices_begin()
541                 it1 = StrokeVertexIterator(it0)
542                 it1.increment()
543                 itn = stroke.stroke_vertices_end()
544                 itn.decrement()
545                 itn_1 = StrokeVertexIterator(itn)
546                 itn_1.decrement()
547                 v0 = it0.object
548                 v1 = it1.object
549                 vn_1 = itn_1.object
550                 vn = itn.object
551                 p0 = mathutils.Vector([v0.projected_x, v0.projected_y])
552                 pn = mathutils.Vector([vn.projected_x, vn.projected_y])
553                 p1 = mathutils.Vector([v1.projected_x, v1.projected_y])
554                 pn_1 = mathutils.Vector([vn_1.projected_x, vn_1.projected_y])
555                 d1 = p0-p1
556                 d1.normalize()
557                 dn = pn-pn_1
558                 dn.normalize()
559                 newFirst = p0+d1*float(stretch)
560                 newLast = pn+dn*float(stretch)
561                 v0.point = newFirst
562                 vn.point = newLast
563                 stroke.update_length()
564
565
566 ## Shader to replace a stroke by its corresponding tangent
567 class pyGuidingLineShader(StrokeShader):
568         def shade(self, stroke):
569                 it = stroke.stroke_vertices_begin()     ## get the first vertex
570                 itlast = stroke.stroke_vertices_end()   ## 
571                 itlast.decrement()                      ## get the last one
572                 t = itlast.object.point - it.object.point       ## tangent direction
573                 itmiddle = StrokeVertexIterator(it)     ## 
574                 while itmiddle.object.u < 0.5:  ## look for the stroke middle vertex
575                         itmiddle.increment()            ##
576                 it = StrokeVertexIterator(itmiddle)     
577                 it.increment()
578                 while not it.is_end:                    ## position all the vertices along the tangent for the right part
579                         it.object.point = itmiddle.object.point \
580                             +t*(it.object.u-itmiddle.object.u)
581                         it.increment()
582                 it = StrokeVertexIterator(itmiddle)
583                 it.decrement()
584                 while not it.is_begin:                  ## position all the vertices along the tangent for the left part
585                         it.object.point = itmiddle.object.point \
586                             -t*(itmiddle.object.u-it.object.u)
587                         it.decrement()
588                 it.object.point = itmiddle.object.point-t*itmiddle.object.u ## first vertex
589                 stroke.update_length()
590
591
592 class pyBackboneStretcherNoCuspShader(StrokeShader):
593         def __init__(self, l):
594                 StrokeShader.__init__(self)
595                 self._l = l
596         def shade(self, stroke):
597                 it0 = stroke.stroke_vertices_begin()
598                 it1 = StrokeVertexIterator(it0)
599                 it1.increment()
600                 itn = stroke.stroke_vertices_end()
601                 itn.decrement()
602                 itn_1 = StrokeVertexIterator(itn)
603                 itn_1.decrement()
604                 v0 = it0.object
605                 v1 = it1.object
606                 if (v0.nature & Nature.CUSP) == 0 and (v1.nature & Nature.CUSP) == 0:
607                         p0 = v0.point
608                         p1 = v1.point
609                         d1 = p0-p1
610                         d1.normalize()
611                         newFirst = p0+d1*float(self._l)
612                         v0.point = newFirst
613                 vn_1 = itn_1.object
614                 vn = itn.object
615                 if (vn.nature & Nature.CUSP) == 0 and (vn_1.nature & Nature.CUSP) == 0:
616                         pn = vn.point
617                         pn_1 = vn_1.point
618                         dn = pn-pn_1
619                         dn.normalize()
620                         newLast = pn+dn*float(self._l)  
621                         vn.point = newLast
622                 stroke.update_length()
623
624 class pyDiffusion2Shader(StrokeShader):
625         """This shader iteratively adds an offset to the position of each
626         stroke vertex in the direction perpendicular to the stroke direction
627         at the point.  The offset is scaled by the 2D curvature (i.e., how
628         quickly the stroke curve is) at the point."""
629         def __init__(self, lambda1, nbIter):
630                 StrokeShader.__init__(self)
631                 self._lambda = lambda1
632                 self._nbIter = nbIter
633                 self._normalInfo = Normal2DF0D()
634                 self._curvatureInfo = Curvature2DAngleF0D()
635         def shade(self, stroke):
636                 for i in range (1, self._nbIter):
637                         it = stroke.stroke_vertices_begin()
638                         while not it.is_end:
639                                 v = it.object
640                                 p1 = v.point
641                                 p2 = self._normalInfo(Interface0DIterator(it))*self._lambda*self._curvatureInfo(Interface0DIterator(it))
642                                 v.point = p1+p2
643                                 it.increment()
644                 stroke.update_length()
645
646 class pyTipRemoverShader(StrokeShader):
647         def __init__(self, l):
648                 StrokeShader.__init__(self)
649                 self._l = l
650         def shade(self, stroke):
651                 originalSize = stroke.stroke_vertices_size()
652                 if originalSize < 4:
653                         return
654                 verticesToRemove = []
655                 oldAttributes = []
656                 it = stroke.stroke_vertices_begin()
657                 while not it.is_end:
658                         v = it.object
659                         if v.curvilinear_abscissa < self._l or v.stroke_length-v.curvilinear_abscissa < self._l:
660                                 verticesToRemove.append(v)
661                         oldAttributes.append(StrokeAttribute(v.attribute))
662                         it.increment()
663                 if originalSize-len(verticesToRemove) < 2:
664                         return
665                 for sv in verticesToRemove:
666                         stroke.remove_vertex(sv)
667                 stroke.update_length()
668                 stroke.resample(originalSize)
669                 if stroke.stroke_vertices_size() != originalSize:
670                         print("pyTipRemover: Warning: resampling problem")
671                 it = stroke.stroke_vertices_begin()
672                 for a in oldAttributes:
673                         if it.is_end:
674                                 break
675                         it.object.attribute = a
676                         it.increment()
677                 stroke.update_length()
678
679 class pyTVertexRemoverShader(StrokeShader):
680         def shade(self, stroke):
681                 if stroke.stroke_vertices_size() <= 3:
682                         return
683                 predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
684                 it = stroke.stroke_vertices_begin()
685                 itlast = stroke.stroke_vertices_end()
686                 itlast.decrement()
687                 if predTVertex(it):
688                         stroke.remove_vertex(it.object)
689                 if predTVertex(itlast):
690                         stroke.remove_vertex(itlast.object)
691                 stroke.update_length()
692
693 #class pyExtremitiesOrientationShader(StrokeShader):
694 #       def __init__(self, x1,y1,x2=0,y2=0):
695 #               StrokeShader.__init__(self)
696 #               self._v1 = mathutils.Vector([x1,y1])
697 #               self._v2 = mathutils.Vector([x2,y2])
698 #       def shade(self, stroke):
699 #               #print(self._v1.x,self._v1.y)
700 #               stroke.setBeginningOrientation(self._v1.x,self._v1.y)
701 #               stroke.setEndingOrientation(self._v2.x,self._v2.y)
702
703 class pyHLRShader(StrokeShader):
704         def shade(self, stroke):
705                 originalSize = stroke.stroke_vertices_size()
706                 if originalSize < 4:
707                         return
708                 it = stroke.stroke_vertices_begin()
709                 invisible = 0
710                 it2 = StrokeVertexIterator(it)
711                 it2.increment()
712                 fe = self.get_fedge(it.object, it2.object)
713                 if fe.viewedge.qi != 0:
714                         invisible = 1
715                 while not it2.is_end:
716                         v = it.object
717                         vnext = it2.object
718                         if (v.nature & Nature.VIEW_VERTEX) != 0:
719                                 #if (v.nature & Nature.T_VERTEX) != 0:
720                                 fe = self.get_fedge(v, vnext)
721                                 qi = fe.viewedge.qi
722                                 if qi != 0:
723                                         invisible = 1
724                                 else:
725                                         invisible = 0
726                         if invisible:
727                                 v.attribute.visible = False
728                         it.increment()
729                         it2.increment()
730         def get_fedge(self, it1, it2):
731                 return it1.get_fedge(it2)
732
733 class pyTVertexOrientationShader(StrokeShader):
734         def __init__(self):
735                 StrokeShader.__init__(self)
736                 self._Get2dDirection = Orientation2DF1D()
737         ## finds the TVertex orientation from the TVertex and 
738         ## the previous or next edge
739         def findOrientation(self, tv, ve):
740                 mateVE = tv.get_mate(ve)
741                 if ve.qi != 0 or mateVE.qi != 0:
742                         ait = AdjacencyIterator(tv,1,0)
743                         winner = None
744                         incoming = True
745                         while not ait.is_end:
746                                 ave = ait.object
747                                 if  ave.id != ve.id and ave.id != mateVE.id:
748                                         winner = ait.object
749                                         if not ait.isIncoming(): # FIXME
750                                                 incoming = False
751                                                 break
752                                 ait.increment()
753                         if winner is not None:
754                                 if not incoming:
755                                         direction = self._Get2dDirection(winner.last_fedge)
756                                 else:
757                                         direction = self._Get2dDirection(winner.first_fedge)
758                                 return direction
759                 return None
760         def castToTVertex(self, cp):
761                 if cp.t2d() == 0.0:
762                         return cp.first_svertex.viewvertex
763                 elif cp.t2d() == 1.0:
764                         return cp.second_svertex.viewvertex
765                 return None
766         def shade(self, stroke):
767                 it = stroke.stroke_vertices_begin()
768                 it2 = StrokeVertexIterator(it)
769                 it2.increment()
770                 ## case where the first vertex is a TVertex
771                 v = it.object
772                 if (v.nature & Nature.T_VERTEX) != 0:
773                         tv = self.castToTVertex(v)
774                         if tv is not None:
775                                 ve = self.get_fedge(v, it2.object).viewedge
776                                 dir = self.findOrientation(tv, ve)
777                                 if dir is not None:
778                                         #print(dir.x, dir.y)
779                                         v.attribute.set_attribute_vec2("orientation", dir)
780                 while not it2.is_end:
781                         vprevious = it.object
782                         v = it2.object
783                         if (v.nature & Nature.T_VERTEX) != 0:
784                                 tv = self.castToTVertex(v)
785                                 if tv is not None:
786                                         ve = self.get_fedge(vprevious, v).viewedge
787                                         dir = self.findOrientation(tv, ve)
788                                         if dir is not None:
789                                                 #print(dir.x, dir.y)
790                                                 v.attribute.set_attribute_vec2("orientation", dir)
791                         it.increment()
792                         it2.increment()
793                 ## case where the last vertex is a TVertex
794                 v = it.object 
795                 if (v.nature & Nature.T_VERTEX) != 0:
796                         itPrevious = StrokeVertexIterator(it)
797                         itPrevious.decrement()
798                         tv = self.castToTVertex(v)
799                         if tv is not None:
800                                 ve = self.get_fedge(itPrevious.object, v).viewedge
801                                 dir = self.findOrientation(tv, ve)
802                                 if dir is not None:
803                                         #print(dir.x, dir.y)
804                                         v.attribute.set_attribute_vec2("orientation", dir)
805         def get_fedge(self, it1, it2):
806                 return it1.get_fedge(it2)
807
808 class pySinusDisplacementShader(StrokeShader):
809         def __init__(self, f, a):
810                 StrokeShader.__init__(self)
811                 self._f = f
812                 self._a = a
813                 self._getNormal = Normal2DF0D()
814         def shade(self, stroke):
815                 it = stroke.stroke_vertices_begin()
816                 while not it.is_end:
817                         v = it.object
818                         #print(self._getNormal.name)
819                         n = self._getNormal(Interface0DIterator(it))
820                         p = v.point
821                         u = v.u
822                         a = self._a*(1-2*(math.fabs(u-0.5)))
823                         n = n*a*math.cos(self._f*u*6.28)
824                         #print(n.x, n.y)
825                         v.point = p+n
826                         #v.point = v.point+n*a*math.cos(f*v.u)
827                         it.increment()
828                 stroke.update_length()
829
830 class pyPerlinNoise1DShader(StrokeShader):
831         def __init__(self, freq = 10, amp = 10, oct = 4, seed = -1):
832                 StrokeShader.__init__(self)
833                 self.__noise = Noise(seed)
834                 self.__freq = freq
835                 self.__amp = amp
836                 self.__oct = oct
837         def shade(self, stroke):
838                 it = stroke.stroke_vertices_begin()
839                 while not it.is_end:
840                         v = it.object
841                         i = v.projected_x + v.projected_y
842                         nres = self.__noise.turbulence1(i, self.__freq, self.__amp, self.__oct)
843                         v.point = (v.projected_x + nres, v.projected_y + nres)
844                         it.increment()
845                 stroke.update_length()
846
847 class pyPerlinNoise2DShader(StrokeShader):
848         def __init__(self, freq = 10, amp = 10, oct = 4, seed = -1):
849                 StrokeShader.__init__(self)
850                 self.__noise = Noise(seed)
851                 self.__freq = freq
852                 self.__amp = amp
853                 self.__oct = oct
854         def shade(self, stroke):
855                 it = stroke.stroke_vertices_begin()
856                 while not it.is_end:
857                         v = it.object
858                         vec = mathutils.Vector([v.projected_x, v.projected_y])
859                         nres = self.__noise.turbulence2(vec, self.__freq, self.__amp, self.__oct)
860                         v.point = (v.projected_x + nres, v.projected_y + nres)
861                         it.increment()
862                 stroke.update_length()
863
864 class pyBluePrintCirclesShader(StrokeShader):
865         def __init__(self, turns = 1, random_radius = 3, random_center = 5):
866                 StrokeShader.__init__(self)
867                 self.__turns = turns
868                 self.__random_center = random_center
869                 self.__random_radius = random_radius
870         def shade(self, stroke):
871                 it = stroke.stroke_vertices_begin()
872                 if it.is_end:
873                         return
874                 p_min = it.object.point.copy()
875                 p_max = it.object.point.copy()
876                 while not it.is_end:
877                         p = it.object.point
878                         if p.x < p_min.x:
879                                 p_min.x = p.x
880                         if p.x > p_max.x:
881                                 p_max.x = p.x
882                         if p.y < p_min.y:
883                                 p_min.y = p.y
884                         if p.y > p_max.y:
885                                 p_max.y = p.y
886                         it.increment()
887                 stroke.resample(32 * self.__turns)
888                 sv_nb = stroke.stroke_vertices_size()
889 #               print("min  :", p_min.x, p_min.y) # DEBUG
890 #               print("mean :", p_sum.x, p_sum.y) # DEBUG
891 #               print("max  :", p_max.x, p_max.y) # DEBUG
892 #               print("----------------------") # DEBUG
893 ####################################################### 
894                 sv_nb = sv_nb // self.__turns
895                 center = (p_min + p_max) / 2
896                 radius = (center.x - p_min.x + center.y - p_min.y) / 2
897                 p_new = mathutils.Vector([0, 0])
898 #######################################################
899                 R = self.__random_radius
900                 C = self.__random_center
901                 i = 0
902                 it = stroke.stroke_vertices_begin()
903                 for j in range(self.__turns):
904                         prev_radius = radius
905                         prev_center = center
906                         radius = radius + random.randint(-R, R)
907                         center = center + mathutils.Vector([random.randint(-C, C), random.randint(-C, C)])
908                         while i < sv_nb and not it.is_end:
909                                 t = float(i) / float(sv_nb - 1)
910                                 r = prev_radius + (radius - prev_radius) * t
911                                 c = prev_center + (center - prev_center) * t
912                                 p_new.x = c.x + r * math.cos(2 * math.pi * t)
913                                 p_new.y = c.y + r * math.sin(2 * math.pi * t)
914                                 it.object.point = p_new
915                                 i = i + 1
916                                 it.increment()
917                         i = 1
918                 verticesToRemove = []
919                 while not it.is_end:
920                         verticesToRemove.append(it.object)
921                         it.increment()
922                 for sv in verticesToRemove:
923                         stroke.remove_vertex(sv)
924                 stroke.update_length()
925
926 class pyBluePrintEllipsesShader(StrokeShader):
927         def __init__(self, turns = 1, random_radius = 3, random_center = 5):
928                 StrokeShader.__init__(self)
929                 self.__turns = turns
930                 self.__random_center = random_center
931                 self.__random_radius = random_radius
932         def shade(self, stroke):
933                 it = stroke.stroke_vertices_begin()
934                 if it.is_end:
935                         return
936                 p_min = it.object.point.copy()
937                 p_max = it.object.point.copy()
938                 while not it.is_end:
939                         p = it.object.point
940                         if p.x < p_min.x:
941                                 p_min.x = p.x
942                         if p.x > p_max.x:
943                                 p_max.x = p.x
944                         if p.y < p_min.y:
945                                 p_min.y = p.y
946                         if p.y > p_max.y:
947                                 p_max.y = p.y
948                         it.increment()
949                 stroke.resample(32 * self.__turns)
950                 sv_nb = stroke.stroke_vertices_size()
951                 sv_nb = sv_nb // self.__turns
952                 center = (p_min + p_max) / 2
953                 radius = center - p_min
954                 p_new = mathutils.Vector([0, 0])
955 #######################################################
956                 R = self.__random_radius
957                 C = self.__random_center
958                 i = 0
959                 it = stroke.stroke_vertices_begin()
960                 for j in range(self.__turns):
961                         prev_radius = radius
962                         prev_center = center
963                         radius = radius + mathutils.Vector([random.randint(-R, R), random.randint(-R, R)])
964                         center = center + mathutils.Vector([random.randint(-C, C), random.randint(-C, C)])
965                         while i < sv_nb and not it.is_end:
966                                 t = float(i) / float(sv_nb - 1)
967                                 r = prev_radius + (radius - prev_radius) * t
968                                 c = prev_center + (center - prev_center) * t
969                                 p_new.x = c.x + r.x * math.cos(2 * math.pi * t)
970                                 p_new.y = c.y + r.y * math.sin(2 * math.pi * t)
971                                 it.object.point = p_new
972                                 i = i + 1
973                                 it.increment()
974                         i = 1
975                 verticesToRemove = []
976                 while not it.is_end:
977                         verticesToRemove.append(it.object)
978                         it.increment()
979                 for sv in verticesToRemove:
980                         stroke.remove_vertex(sv)
981                 stroke.update_length()
982
983
984 class pyBluePrintSquaresShader(StrokeShader):
985         def __init__(self, turns = 1, bb_len = 10, bb_rand = 0):
986                 StrokeShader.__init__(self)
987                 self.__turns = turns
988                 self.__bb_len = bb_len
989                 self.__bb_rand = bb_rand
990         def shade(self, stroke):
991                 it = stroke.stroke_vertices_begin()
992                 if it.is_end:
993                         return
994                 p_min = it.object.point.copy()
995                 p_max = it.object.point.copy()
996                 while not it.is_end:
997                         p = it.object.point
998                         if p.x < p_min.x:
999                                 p_min.x = p.x
1000                         if p.x > p_max.x:
1001                                 p_max.x = p.x
1002                         if p.y < p_min.y:
1003                                 p_min.y = p.y
1004                         if p.y > p_max.y:
1005                                 p_max.y = p.y
1006                         it.increment()
1007                 stroke.resample(32 * self.__turns)
1008                 sv_nb = stroke.stroke_vertices_size()
1009 ####################################################### 
1010                 sv_nb = sv_nb // self.__turns
1011                 first = sv_nb // 4
1012                 second = 2 * first
1013                 third = 3 * first
1014                 fourth = sv_nb
1015                 p_first = mathutils.Vector([p_min.x - self.__bb_len, p_min.y])
1016                 p_first_end = mathutils.Vector([p_max.x + self.__bb_len, p_min.y])
1017                 p_second = mathutils.Vector([p_max.x, p_min.y - self.__bb_len])
1018                 p_second_end = mathutils.Vector([p_max.x, p_max.y + self.__bb_len])
1019                 p_third = mathutils.Vector([p_max.x + self.__bb_len, p_max.y])
1020                 p_third_end = mathutils.Vector([p_min.x - self.__bb_len, p_max.y])
1021                 p_fourth = mathutils.Vector([p_min.x, p_max.y + self.__bb_len])
1022                 p_fourth_end = mathutils.Vector([p_min.x, p_min.y - self.__bb_len])
1023 #######################################################
1024                 R = self.__bb_rand
1025                 r = self.__bb_rand // 2
1026                 it = stroke.stroke_vertices_begin()
1027                 visible = True
1028                 for j in range(self.__turns):
1029                         p_first = p_first + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
1030                         p_first_end = p_first_end + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
1031                         p_second = p_second + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
1032                         p_second_end = p_second_end + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
1033                         p_third = p_third + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
1034                         p_third_end = p_third_end + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
1035                         p_fourth = p_fourth + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
1036                         p_fourth_end = p_fourth_end + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
1037                         vec_first = p_first_end - p_first
1038                         vec_second = p_second_end - p_second
1039                         vec_third = p_third_end - p_third
1040                         vec_fourth = p_fourth_end - p_fourth
1041                         i = 0
1042                         while i < sv_nb and not it.is_end:
1043                                 if i < first:
1044                                         p_new = p_first + vec_first * float(i)/float(first - 1)
1045                                         if i == first - 1:
1046                                                 visible = False
1047                                 elif i < second:
1048                                         p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
1049                                         if i == second - 1:
1050                                                 visible = False
1051                                 elif i < third:
1052                                         p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
1053                                         if i == third - 1:
1054                                                 visible = False
1055                                 else:
1056                                         p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
1057                                         if i == fourth - 1:
1058                                                 visible = False
1059                                 if it.object == None:
1060                                         i = i + 1
1061                                         it.increment()
1062                                         if not visible:
1063                                                 visible = True
1064                                         continue
1065                                 it.object.point = p_new
1066                                 it.object.attribute.visible = visible
1067                                 if not visible:
1068                                         visible = True
1069                                 i = i + 1
1070                                 it.increment()
1071                 verticesToRemove = []
1072                 while not it.is_end:
1073                         verticesToRemove.append(it.object)
1074                         it.increment()
1075                 for sv in verticesToRemove:
1076                         stroke.remove_vertex(sv)
1077                 stroke.update_length()
1078
1079
1080 class pyBluePrintDirectedSquaresShader(StrokeShader):
1081         def __init__(self, turns = 1, bb_len = 10, mult = 1):
1082                 StrokeShader.__init__(self)
1083                 self.__mult = mult
1084                 self.__turns = turns
1085                 self.__bb_len = 1 + float(bb_len) / 100
1086         def shade(self, stroke):
1087                 stroke.resample(32 * self.__turns)
1088                 p_mean = mathutils.Vector([0, 0])
1089                 it = stroke.stroke_vertices_begin()
1090                 while not it.is_end:
1091                         p = it.object.point
1092                         p_mean = p_mean + p
1093                         it.increment()
1094                 sv_nb = stroke.stroke_vertices_size()
1095                 p_mean = p_mean / sv_nb
1096                 p_var_xx = 0
1097                 p_var_yy = 0
1098                 p_var_xy = 0
1099                 it = stroke.stroke_vertices_begin()
1100                 while not it.is_end:
1101                         p = it.object.point
1102                         p_var_xx = p_var_xx + math.pow(p.x - p_mean.x, 2)
1103                         p_var_yy = p_var_yy + math.pow(p.y - p_mean.y, 2)
1104                         p_var_xy = p_var_xy + (p.x - p_mean.x) * (p.y - p_mean.y)
1105                         it.increment()
1106                 p_var_xx = p_var_xx / sv_nb
1107                 p_var_yy = p_var_yy / sv_nb
1108                 p_var_xy = p_var_xy / sv_nb
1109 ##              print(p_var_xx, p_var_yy, p_var_xy)
1110                 trace = p_var_xx + p_var_yy
1111                 det = p_var_xx * p_var_yy - p_var_xy * p_var_xy
1112                 sqrt_coeff = math.sqrt(trace * trace - 4 * det)
1113                 lambda1 = (trace + sqrt_coeff) / 2
1114                 lambda2 = (trace - sqrt_coeff) / 2
1115 ##              print(lambda1, lambda2)
1116                 theta = math.atan(2 * p_var_xy / (p_var_xx - p_var_yy)) / 2
1117 ##              print(theta)
1118                 if p_var_yy > p_var_xx:
1119                         e1 = mathutils.Vector([math.cos(theta + math.pi / 2), math.sin(theta + math.pi / 2)]) * math.sqrt(lambda1) * self.__mult
1120                         e2 = mathutils.Vector([math.cos(theta + math.pi), math.sin(theta + math.pi)]) *  math.sqrt(lambda2) * self.__mult
1121                 else:
1122                         e1 = mathutils.Vector([math.cos(theta), math.sin(theta)]) * math.sqrt(lambda1) * self.__mult
1123                         e2 = mathutils.Vector([math.cos(theta + math.pi / 2), math.sin(theta + math.pi / 2)]) * math.sqrt(lambda2) * self.__mult
1124 ####################################################### 
1125                 sv_nb = sv_nb // self.__turns
1126                 first = sv_nb // 4
1127                 second = 2 * first
1128                 third = 3 * first
1129                 fourth = sv_nb
1130                 bb_len1 = self.__bb_len
1131                 bb_len2 = 1 + (bb_len1 - 1) * math.sqrt(lambda1 / lambda2)
1132                 p_first = p_mean - e1 - e2 * bb_len2
1133                 p_second = p_mean - e1 * bb_len1 + e2
1134                 p_third = p_mean + e1 + e2 * bb_len2
1135                 p_fourth = p_mean + e1 * bb_len1 - e2
1136                 vec_first = e2 * bb_len2 * 2
1137                 vec_second = e1 * bb_len1 * 2
1138                 vec_third = vec_first * -1
1139                 vec_fourth = vec_second * -1
1140 #######################################################
1141                 it = stroke.stroke_vertices_begin()
1142                 visible = True
1143                 for j in range(self.__turns):
1144                         i = 0
1145                         while i < sv_nb:
1146                                 if i < first:
1147                                         p_new = p_first + vec_first * float(i)/float(first - 1)
1148                                         if i == first - 1:
1149                                                 visible = False
1150                                 elif i < second:
1151                                         p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
1152                                         if i == second - 1:
1153                                                 visible = False
1154                                 elif i < third:
1155                                         p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
1156                                         if i == third - 1:
1157                                                 visible = False
1158                                 else:
1159                                         p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
1160                                         if i == fourth - 1:
1161                                                 visible = False
1162                                 it.object.point = p_new
1163                                 it.object.attribute.visible = visible
1164                                 if not visible:
1165                                         visible = True
1166                                 i = i + 1
1167                                 it.increment()
1168                 verticesToRemove = []
1169                 while not it.is_end:
1170                         verticesToRemove.append(it.object)
1171                         it.increment()
1172                 for sv in verticesToRemove:
1173                         stroke.remove_vertex(sv)
1174                 stroke.update_length()
1175
1176 class pyModulateAlphaShader(StrokeShader):
1177         def __init__(self, min = 0, max = 1):
1178                 StrokeShader.__init__(self)
1179                 self.__min = min
1180                 self.__max = max
1181         def shade(self, stroke):
1182                 it = stroke.stroke_vertices_begin()
1183                 while not it.is_end:
1184                         alpha = it.object.attribute.alpha
1185                         p = it.object.point
1186                         alpha = alpha * p.y / 400
1187                         if alpha < self.__min:
1188                                 alpha = self.__min
1189                         elif alpha > self.__max:
1190                                 alpha = self.__max
1191                         it.object.attribute.alpha = alpha
1192                         it.increment()
1193
1194 ## various
1195 class pyDummyShader(StrokeShader):
1196         def shade(self, stroke):
1197                 it = stroke.stroke_vertices_begin()
1198                 while not it.is_end:
1199                         toto = Interface0DIterator(it)
1200                         att = it.object.attribute
1201                         att.color = (0.3, 0.4, 0.4)
1202                         att.thickness = (0, 5)
1203                         it.increment()
1204
1205 class pyDebugShader(StrokeShader):
1206         def shade(self, stroke):
1207                 fe = CF.get_selected_fedge()
1208                 id1 = fe.first_svertex.id
1209                 id2 = fe.second_svertex.id
1210                 #print(id1.first, id1.second)
1211                 #print(id2.first, id2.second)
1212                 it = stroke.stroke_vertices_begin()
1213                 found = True
1214                 foundfirst = True
1215                 foundsecond = False
1216                 while not it.is_end:
1217                         cp = it.object
1218                         if cp.first_svertex.id == id1 or cp.second_svertex.id == id1:
1219                                 foundfirst = True
1220                         if cp.first_svertex.id == id2 or cp.second_svertex.id == id2:
1221                                 foundsecond = True
1222                         if foundfirst and foundsecond:
1223                                 found = True
1224                                 break
1225                         it.increment()
1226                 if found:
1227                         print("The selected Stroke id is: ", stroke.id.first, stroke.id.second)