# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

#  Filename : shaders.py
#  Authors  : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin 
#  Date     : 11/08/2005
#  Purpose  : Stroke shaders to be used for creation of stylized strokes

from freestyle import AdjacencyIterator, Curvature2DAngleF0D, DensityF0D, GetProjectedZF0D, \
    Interface0DIterator, MaterialF0D, Nature, Noise, Normal2DF0D, Orientation2DF1D, \
    StrokeAttribute, StrokeShader, StrokeVertexIterator, ZDiscontinuityF0D
from freestyle import ContextFunctions as CF
from PredicatesU0D import pyVertexNatureUP0D

import math
import mathutils
import random

## 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 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(Interface0DIterator(it))
			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 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 shade(self, stroke):
		n = stroke.stroke_vertices_size()
		i = 0
		it = stroke.stroke_vertices_begin()
		func = DensityF0D(self.wsize)
		while not it.is_end:
			c = func(Interface0DIterator(it))
			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 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 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, thicknessMin, thicknessMax):
		StrokeShader.__init__(self)
		self._thicknessMin = thicknessMin
		self._thicknessMax = thicknessMax
	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()

class pyNonLinearVaryingThicknessShader(StrokeShader):
	def __init__(self, thicknessExtremity, thicknessMiddle, exponent):
		StrokeShader.__init__(self)
		self._thicknessMin = thicknessMiddle
		self._thicknessMax = thicknessExtremity
		self._exponent = exponent
	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 = self.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()
	def smoothC(self, a, exp):
		return math.pow(float(a), exp) * math.pow(2.0, exp)

## 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 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 = math.sin((1-c)*self._omega)/math.sinh(self._omega)*self._thicknessMin + math.sin(c*self._omega)/math.sinh(self._omega) * maxT
			else:
				t = math.sin((1-c)*self._omega)/math.sinh(self._omega)*maxT + math.sin(c*self._omega)/math.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 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 shade(self, stroke):
		origin = mathutils.Vector([self._x, self._y])
		it = stroke.stroke_vertices_begin()
		while not it.is_end:
			v = it.object
			p = mathutils.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 shade(self, stroke):
		origin = mathutils.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 shade(self, stroke):
		it = stroke.stroke_vertices_begin()
		z_min = 1
		z_max = 0
		while not it.is_end:
			z = self.__func(Interface0DIterator(it))
			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(Interface0DIterator(it)) - 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 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 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 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*(math.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 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:
			mat = func(Interface0DIterator(it))
			
			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. * math.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 * math.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)
		random.seed(s)
	def shade(self, stroke):
		## pick a random color
		c0 = float(random.uniform(15,75))/100.0
		c1 = float(random.uniform(15,75))/100.0
		c2 = float(random.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 shade(self, stroke):
		it = stroke.stroke_vertices_begin()
		func = Curvature2DAngleF0D()
		while not it.is_end:
			c = func(Interface0DIterator(it))
			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 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 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 = mathutils.Vector([v0.projected_x, v0.projected_y])
		pn = mathutils.Vector([vn.projected_x, vn.projected_y])
		p1 = mathutils.Vector([v1.projected_x, v1.projected_y])
		pn_1 = mathutils.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 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 = mathutils.Vector([v0.projected_x, v0.projected_y])
		pn = mathutils.Vector([vn.projected_x, vn.projected_y])
		p1 = mathutils.Vector([v1.projected_x, v1.projected_y])
		pn_1 = mathutils.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 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 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 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(Interface0DIterator(it))*self._lambda*self._curvatureInfo(Interface0DIterator(it))
				v.point = p1+p2
				it.increment()
		stroke.update_length()

class pyTipRemoverShader(StrokeShader):
	def __init__(self, l):
		StrokeShader.__init__(self)
		self._l = l
	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 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 = mathutils.Vector([x1,y1])
#		self._v2 = mathutils.Vector([x2,y2])
#	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)

class pyHLRShader(StrokeShader):
	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 = self.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 = self.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()
	def get_fedge(self, it1, it2):
		return it1.get_fedge(it2)

class pyTVertexOrientationShader(StrokeShader):
	def __init__(self):
		StrokeShader.__init__(self)
		self._Get2dDirection = Orientation2DF1D()
	## 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 = self.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 = self.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 = self.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)
	def get_fedge(self, it1, it2):
		return it1.get_fedge(it2)

class pySinusDisplacementShader(StrokeShader):
	def __init__(self, f, a):
		StrokeShader.__init__(self)
		self._f = f
		self._a = a
		self._getNormal = Normal2DF0D()
	def shade(self, stroke):
		it = stroke.stroke_vertices_begin()
		while not it.is_end:
			v = it.object
			#print(self._getNormal.name)
			n = self._getNormal(Interface0DIterator(it))
			p = v.point
			u = v.u
			a = self._a*(1-2*(math.fabs(u-0.5)))
			n = n*a*math.cos(self._f*u*6.28)
			#print(n.x, n.y)
			v.point = p+n
			#v.point = v.point+n*a*math.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 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 shade(self, stroke):
		it = stroke.stroke_vertices_begin()
		while not it.is_end:
			v = it.object
			vec = mathutils.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 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 = mathutils.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 + random.randint(-R, R)
			center = center + mathutils.Vector([random.randint(-C, C), random.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 * math.cos(2 * math.pi * t)
				p_new.y = c.y + r * math.sin(2 * math.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 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 = mathutils.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 + mathutils.Vector([random.randint(-R, R), random.randint(-R, R)])
			center = center + mathutils.Vector([random.randint(-C, C), random.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 * math.cos(2 * math.pi * t)
				p_new.y = c.y + r.y * math.sin(2 * math.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 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 = mathutils.Vector([p_min.x - self.__bb_len, p_min.y])
		p_first_end = mathutils.Vector([p_max.x + self.__bb_len, p_min.y])
		p_second = mathutils.Vector([p_max.x, p_min.y - self.__bb_len])
		p_second_end = mathutils.Vector([p_max.x, p_max.y + self.__bb_len])
		p_third = mathutils.Vector([p_max.x + self.__bb_len, p_max.y])
		p_third_end = mathutils.Vector([p_min.x - self.__bb_len, p_max.y])
		p_fourth = mathutils.Vector([p_min.x, p_max.y + self.__bb_len])
		p_fourth_end = mathutils.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 + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
			p_first_end = p_first_end + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
			p_second = p_second + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
			p_second_end = p_second_end + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
			p_third = p_third + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
			p_third_end = p_third_end + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
			p_fourth = p_fourth + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
			p_fourth_end = p_fourth_end + mathutils.Vector([random.randint(-r, r), random.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 shade(self, stroke):
		stroke.resample(32 * self.__turns)
		p_mean = mathutils.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 + math.pow(p.x - p_mean.x, 2)
			p_var_yy = p_var_yy + math.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 = math.sqrt(trace * trace - 4 * det)
		lambda1 = (trace + sqrt_coeff) / 2
		lambda2 = (trace - sqrt_coeff) / 2
##		print(lambda1, lambda2)
		theta = math.atan(2 * p_var_xy / (p_var_xx - p_var_yy)) / 2
##		print(theta)
		if p_var_yy > p_var_xx:
			e1 = mathutils.Vector([math.cos(theta + math.pi / 2), math.sin(theta + math.pi / 2)]) * math.sqrt(lambda1) * self.__mult
			e2 = mathutils.Vector([math.cos(theta + math.pi), math.sin(theta + math.pi)]) *  math.sqrt(lambda2) * self.__mult
		else:
			e1 = mathutils.Vector([math.cos(theta), math.sin(theta)]) * math.sqrt(lambda1) * self.__mult
			e2 = mathutils.Vector([math.cos(theta + math.pi / 2), math.sin(theta + math.pi / 2)]) * math.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) * math.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 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 shade(self, stroke):
		it = stroke.stroke_vertices_begin()
		while not it.is_end:
			toto = Interface0DIterator(it)
			att = it.object.attribute
			att.color = (0.3, 0.4, 0.4)
			att.thickness = (0, 5)
			it.increment()

class pyDebugShader(StrokeShader):
	def shade(self, stroke):
		fe = CF.get_selected_fedge()
		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)
