# ##### 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)