Fix for a bug in the Distance from Object modifiers.
[blender-staging.git] / release / scripts / freestyle / style_modules / parameter_editor.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 import Freestyle
20 import math
21
22 from freestyle_init import *
23 from logical_operators import *
24 from ChainingIterators import *
25 from shaders import *
26
27 class ColorRampModifier(StrokeShader):
28     def __init__(self, blend, influence, ramp):
29         StrokeShader.__init__(self)
30         self.__blend = blend
31         self.__influence = influence
32         self.__ramp = ramp
33     def evaluate(self, t):
34         col = Freestyle.evaluateColorRamp(self.__ramp, t)
35         col = col.xyz # omit alpha
36         return col
37     def blend_ramp(self, a, b):
38         return Freestyle.blendRamp(self.__blend, a, self.__influence, b)
39
40 class CurveMappingModifier(StrokeShader):
41     def __init__(self, blend, influence, mapping, invert, curve):
42         StrokeShader.__init__(self)
43         self.__blend = blend
44         self.__influence = influence
45         assert mapping in ("LINEAR", "CURVE")
46         self.__mapping = getattr(self, mapping)
47         self.__invert = invert
48         self.__curve = curve
49     def LINEAR(self, t):
50         if self.__invert:
51             return 1.0 - t
52         return t
53     def CURVE(self, t):
54         return Freestyle.evaluateCurveMappingF(self.__curve, 0, t)
55     def evaluate(self, t):
56         return self.__mapping(t)
57     def blend_curve(self, v1, v2):
58         fac = self.__influence
59         facm = 1.0 - fac
60         if self.__blend == "MIX":
61             v1 = facm * v1 + fac * v2
62         elif self.__blend == "ADD":
63             v1 += fac * v2
64         elif self.__blend == "MULTIPLY":
65             v1 *= facm + fac * v2;
66         elif self.__blend == "SUBTRACT":
67             v1 -= fac * v2
68         elif self.__blend == "DIVIDE":
69             if v2 != 0.0:
70                 v1 = facm * v1 + fac * v1 / v2
71         elif self.__blend == "DIFFERENCE":
72             v1 = facm * v1 + fac * abs(v1 - v2)
73         elif self.__blend == "MININUM":
74             tmp = fac * v1
75             if v1 > tmp:
76                 v1 = tmp
77         elif self.__blend == "MAXIMUM":
78             tmp = fac * v1
79             if v1 < tmp:
80                 v1 = tmp
81         else:
82             raise ValueError("unknown curve blend type: " + self.__blend)
83         return v1
84
85 # Along Stroke modifiers
86
87 def iter_t2d_along_stroke(stroke):
88     total = stroke.getLength2D()
89     distance = 0.0
90     it = stroke.strokeVerticesBegin()
91     while not it.isEnd():
92         p = it.getObject().getPoint()
93         if not it.isBegin():
94             distance += (prev - p).length
95         prev = p
96         t = min(distance / total, 1.0)
97         yield it, t
98         it.increment()
99
100 class ColorAlongStrokeShader(ColorRampModifier):
101     def getName(self):
102         return "ColorAlongStrokeShader"
103     def shade(self, stroke):
104         for it, t in iter_t2d_along_stroke(stroke):
105             attr = it.getObject().attribute()
106             a = attr.getColorRGB()
107             b = self.evaluate(t)
108             c = self.blend_ramp(a, b)
109             attr.setColor(c)
110
111 class AlphaAlongStrokeShader(CurveMappingModifier):
112     def getName(self):
113         return "AlphaAlongStrokeShader"
114     def shade(self, stroke):
115         for it, t in iter_t2d_along_stroke(stroke):
116             attr = it.getObject().attribute()
117             a = attr.getAlpha()
118             b = self.evaluate(t)
119             c = self.blend_curve(a, b)
120             attr.setAlpha(c)
121
122 class ThicknessAlongStrokeShader(CurveMappingModifier):
123     def __init__(self, blend, influence, mapping, invert, curve, value_min, value_max):
124         CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
125         self.__value_min = value_min
126         self.__value_max = value_max
127     def getName(self):
128         return "ThicknessAlongStrokeShader"
129     def shade(self, stroke):
130         for it, t in iter_t2d_along_stroke(stroke):
131             attr = it.getObject().attribute()
132             a = attr.getThicknessRL()
133             a = a[0] + a[1]
134             b = self.__value_min + self.evaluate(t) * (self.__value_max - self.__value_min)
135             c = self.blend_curve(a, b)
136             attr.setThickness(c/2, c/2)
137
138 # Distance from Camera modifiers
139
140 def iter_distance_from_camera(stroke, range_min, range_max):
141     normfac = range_max - range_min # normalization factor
142     it = stroke.strokeVerticesBegin()
143     while not it.isEnd():
144         p = it.getObject().getPoint3D() # in the camera coordinate
145         distance = p.length
146         if distance < range_min:
147             t = 0.0
148         elif distance > range_max:
149             t = 1.0
150         else:
151             t = (distance - range_min) / normfac
152         yield it, t
153         it.increment()
154
155 class ColorDistanceFromCameraShader(ColorRampModifier):
156     def __init__(self, blend, influence, ramp, range_min, range_max):
157         ColorRampModifier.__init__(self, blend, influence, ramp)
158         self.__range_min = range_min
159         self.__range_max = range_max
160     def getName(self):
161         return "ColorDistanceFromCameraShader"
162     def shade(self, stroke):
163         for it, t in iter_distance_from_camera(stroke, self.__range_min, self.__range_max):
164             attr = it.getObject().attribute()
165             a = attr.getColorRGB()
166             b = self.evaluate(t)
167             c = self.blend_ramp(a, b)
168             attr.setColor(c)
169
170 class AlphaDistanceFromCameraShader(CurveMappingModifier):
171     def __init__(self, blend, influence, mapping, invert, curve, range_min, range_max):
172         CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
173         self.__range_min = range_min
174         self.__range_max = range_max
175     def getName(self):
176         return "AlphaDistanceFromCameraShader"
177     def shade(self, stroke):
178         for it, t in iter_distance_from_camera(stroke, self.__range_min, self.__range_max):
179             attr = it.getObject().attribute()
180             a = attr.getAlpha()
181             b = self.evaluate(t)
182             c = self.blend_curve(a, b)
183             attr.setAlpha(c)
184
185 class ThicknessDistanceFromCameraShader(CurveMappingModifier):
186     def __init__(self, blend, influence, mapping, invert, curve, range_min, range_max, value_min, value_max):
187         CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
188         self.__range_min = range_min
189         self.__range_max = range_max
190         self.__value_min = value_min
191         self.__value_max = value_max
192     def getName(self):
193         return "ThicknessDistanceFromCameraShader"
194     def shade(self, stroke):
195         for it, t in iter_distance_from_camera(stroke, self.__range_min, self.__range_max):
196             attr = it.getObject().attribute()
197             a = attr.getThicknessRL()
198             a = a[0] + a[1]
199             b = self.__value_min + self.evaluate(t) * (self.__value_max - self.__value_min)
200             c = self.blend_curve(a, b)
201             attr.setThickness(c/2, c/2)
202
203 # Distance from Object modifiers
204
205 def iter_distance_from_object(stroke, object, range_min, range_max):
206     scene = Freestyle.getCurrentScene()
207     mv = scene.camera.matrix_world.copy().invert() # model-view matrix
208     loc = object.location * mv # loc in the camera coordinate
209     normfac = range_max - range_min # normalization factor
210     it = stroke.strokeVerticesBegin()
211     while not it.isEnd():
212         p = it.getObject().getPoint3D() # in the camera coordinate
213         distance = (p - loc).length
214         if distance < range_min:
215             t = 0.0
216         elif distance > range_max:
217             t = 1.0
218         else:
219             t = (distance - range_min) / normfac
220         yield it, t
221         it.increment()
222
223 class ColorDistanceFromObjectShader(ColorRampModifier):
224     def __init__(self, blend, influence, ramp, target, range_min, range_max):
225         ColorRampModifier.__init__(self, blend, influence, ramp)
226         self.__target = target
227         self.__range_min = range_min
228         self.__range_max = range_max
229     def getName(self):
230         return "ColorDistanceFromObjectShader"
231     def shade(self, stroke):
232         if self.__target is None:
233             return
234         for it, t in iter_distance_from_object(stroke, self.__target, self.__range_min, self.__range_max):
235             attr = it.getObject().attribute()
236             a = attr.getColorRGB()
237             b = self.evaluate(t)
238             c = self.blend_ramp(a, b)
239             attr.setColor(c)
240
241 class AlphaDistanceFromObjectShader(CurveMappingModifier):
242     def __init__(self, blend, influence, mapping, invert, curve, target, range_min, range_max):
243         CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
244         self.__target = target
245         self.__range_min = range_min
246         self.__range_max = range_max
247     def getName(self):
248         return "AlphaDistanceFromObjectShader"
249     def shade(self, stroke):
250         if self.__target is None:
251             return
252         for it, t in iter_distance_from_object(stroke, self.__target, self.__range_min, self.__range_max):
253             attr = it.getObject().attribute()
254             a = attr.getAlpha()
255             b = self.evaluate(t)
256             c = self.blend_curve(a, b)
257             attr.setAlpha(c)
258
259 class ThicknessDistanceFromObjectShader(CurveMappingModifier):
260     def __init__(self, blend, influence, mapping, invert, curve, target, range_min, range_max, value_min, value_max):
261         CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
262         self.__target = target
263         self.__range_min = range_min
264         self.__range_max = range_max
265         self.__value_min = value_min
266         self.__value_max = value_max
267     def getName(self):
268         return "ThicknessDistanceFromObjectShader"
269     def shade(self, stroke):
270         if self.__target is None:
271             return
272         for it, t in iter_distance_from_object(stroke, self.__target, self.__range_min, self.__range_max):
273             attr = it.getObject().attribute()
274             a = attr.getThicknessRL()
275             a = a[0] + a[1]
276             b = self.__value_min + self.evaluate(t) * (self.__value_max - self.__value_min)
277             c = self.blend_curve(a, b)
278             attr.setThickness(c/2, c/2)
279
280 # Predicates and helper functions
281
282 class QuantitativeInvisibilityRangeUP1D(UnaryPredicate1D):
283     def __init__(self, qi_start, qi_end):
284         UnaryPredicate1D.__init__(self)
285         self.__getQI = QuantitativeInvisibilityF1D()
286         self.__qi_start = qi_start
287         self.__qi_end = qi_end
288     def getName(self):
289         return "QuantitativeInvisibilityRangeUP1D"
290     def __call__(self, inter):
291         qi = self.__getQI(inter)
292         return self.__qi_start <= qi <= self.__qi_end
293
294 def join_unary_predicates(upred_list, bpred):
295     if not upred_list:
296         return TrueUP1D()
297     upred = upred_list[0]
298     for p in upred_list[1:]:
299         upred = bpred(upred, p)
300     return upred
301
302 class ObjectNamesUP1D(UnaryPredicate1D):
303     def __init__(self, names, negative):
304         UnaryPredicate1D.__init__(self)
305         self._names = names
306         self._negative = negative
307     def getName(self):
308         return "ObjectNamesUP1D"
309     def __call__(self, viewEdge):
310         found = viewEdge.viewShape().getName() in self._names
311         if self._negative:
312             return not found
313         return found
314
315 class WithinImageBorderUP1D(UnaryPredicate1D):
316     def __init__(self, xmin, xmax, ymin, ymax):
317         UnaryPredicate1D.__init__(self)
318         self._xmin = xmin
319         self._xmax = xmax
320         self._ymin = ymin
321         self._ymax = ymax
322     def getName(self):
323         return "WithinImageBorderUP1D"
324     def __call__(self, inter):
325         return self.withinBorder(inter.A()) or self.withinBorder(inter.B())
326     def withinBorder(self, vert):
327         x = vert.getProjectedX()
328         y = vert.getProjectedY()
329         return self._xmin <= x <= self._xmax and self._ymin <= y <= self._ymax
330
331 # Stroke caps
332
333 def iter_stroke_vertices(stroke):
334     it = stroke.strokeVerticesBegin()
335     while not it.isEnd():
336         yield it.getObject()
337         it.increment()
338
339 class RoundCapShader(StrokeShader):
340     def round_cap_thickness(self, x):
341         x = max(0.0, min(x, 1.0))
342         return math.sqrt(1.0 - (x ** 2))
343     def shade(self, stroke):
344         # save the location and attribute of stroke vertices
345         buffer = []
346         for sv in iter_stroke_vertices(stroke):
347             buffer.append((sv.getPoint(), sv.attribute()))
348         # calculate the number of additional vertices to form caps
349         R, L = stroke[0].attribute().getThicknessRL()
350         caplen_beg = (R + L) / 2.0
351         nverts_beg = max(5, int(R + L))
352         R, L = stroke[-1].attribute().getThicknessRL()
353         caplen_end = (R + L) / 2.0
354         nverts_end = max(5, int(R + L))
355         # increase the total number of stroke vertices
356         nverts = stroke.strokeVerticesSize()
357         stroke.Resample(nverts + nverts_beg + nverts_end)
358         # restore the location and attribute of the original vertices
359         for i in range(nverts):
360             p, attr = buffer[i]
361             stroke[nverts_beg + i].setPoint(p)
362             stroke[nverts_beg + i].setAttribute(attr)
363         # reshape the cap at the beginning of the stroke
364         q, attr = buffer[1]
365         p, attr = buffer[0]
366         d = p - q
367         d = d / d.length * caplen_beg
368         n = 1.0 / nverts_beg
369         R, L = attr.getThicknessRL()
370         for i in range(nverts_beg):
371             t = (nverts_beg - i) * n
372             stroke[i].setPoint(p + d * t)
373             r = self.round_cap_thickness((nverts_beg - i + 1) * n)
374             stroke[i].setAttribute(attr)
375             stroke[i].attribute().setThickness(R * r, L * r)
376         # reshape the cap at the end of the stroke
377         q, attr = buffer[-2]
378         p, attr = buffer[-1]
379         d = p - q
380         d = d / d.length * caplen_end
381         n = 1.0 / nverts_end
382         R, L = attr.getThicknessRL()
383         for i in range(nverts_end):
384             t = (nverts_end - i) * n
385             stroke[-i-1].setPoint(p + d * t)
386             r = self.round_cap_thickness((nverts_end - i + 1) * n)
387             stroke[-i-1].setAttribute(attr)
388             stroke[-i-1].attribute().setThickness(R * r, L * r)
389
390 class SquareCapShader(StrokeShader):
391     def shade(self, stroke):
392         # save the location and attribute of stroke vertices
393         buffer = []
394         for sv in iter_stroke_vertices(stroke):
395             buffer.append((sv.getPoint(), sv.attribute()))
396         # calculate the number of additional vertices to form caps
397         R, L = stroke[0].attribute().getThicknessRL()
398         caplen_beg = (R + L) / 2.0
399         nverts_beg = 1
400         R, L = stroke[-1].attribute().getThicknessRL()
401         caplen_end = (R + L) / 2.0
402         nverts_end = 1
403         # increase the total number of stroke vertices
404         nverts = stroke.strokeVerticesSize()
405         stroke.Resample(nverts + nverts_beg + nverts_end)
406         # restore the location and attribute of the original vertices
407         for i in range(nverts):
408             p, attr = buffer[i]
409             stroke[nverts_beg + i].setPoint(p)
410             stroke[nverts_beg + i].setAttribute(attr)
411         # reshape the cap at the beginning of the stroke
412         q, attr = buffer[1]
413         p, attr = buffer[0]
414         d = p - q
415         stroke[0].setPoint(p + d / d.length * caplen_beg)
416         stroke[0].setAttribute(attr)
417         # reshape the cap at the end of the stroke
418         q, attr = buffer[-2]
419         p, attr = buffer[-1]
420         d = p - q
421         stroke[-1].setPoint(p + d / d.length * caplen_beg)
422         stroke[-1].setAttribute(attr)
423
424 # dashed line
425
426 class DashedLineStartingUP0D(UnaryPredicate0D):
427     def __init__(self, controller):
428         UnaryPredicate0D.__init__(self)
429         self._controller = controller
430     def __call__(self, inter):
431         return self._controller.start()
432
433 class DashedLineStoppingUP0D(UnaryPredicate0D):
434     def __init__(self, controller):
435         UnaryPredicate0D.__init__(self)
436         self._controller = controller
437     def __call__(self, inter):
438         return self._controller.stop()
439
440 class DashedLineController:
441     def __init__(self, pattern, sampling):
442         self.sampling = float(sampling)
443         k = len(pattern) // 2
444         n = k * 2
445         self.start_pos = [pattern[i] + pattern[i+1] for i in range(0, n, 2)]
446         self.stop_pos = [pattern[i] for i in range(0, n, 2)]
447         self.init()
448     def init(self):
449         self.start_len = 0.0
450         self.start_idx = 0
451         self.stop_len = self.sampling
452         self.stop_idx = 0
453     def start(self):
454         self.start_len += self.sampling
455         if abs(self.start_len - self.start_pos[self.start_idx]) < self.sampling / 2.0:
456             self.start_len = 0.0
457             self.start_idx = (self.start_idx + 1) % len(self.start_pos)
458             return True
459         return False
460     def stop(self):
461         if self.start_len > 0.0:
462             self.init()
463         self.stop_len += self.sampling
464         if abs(self.stop_len - self.stop_pos[self.stop_idx]) < self.sampling / 2.0:
465             self.stop_len = self.sampling
466             self.stop_idx = (self.stop_idx + 1) % len(self.stop_pos)
467             return True
468         return False
469
470 # main function for parameter processing
471
472 def process(layer_name, lineset_name):
473     scene = Freestyle.getCurrentScene()
474     layer = scene.render.layers[layer_name]
475     lineset = layer.freestyle_settings.linesets[lineset_name]
476     linestyle = lineset.linestyle
477
478     selection_criteria = []
479     # prepare selection criteria by visibility
480     if lineset.select_by_visibility:
481         if lineset.visibility == "VISIBLE":
482             selection_criteria.append(
483                 QuantitativeInvisibilityUP1D(0))
484         elif lineset.visibility == "HIDDEN":
485             selection_criteria.append(
486                 NotUP1D(QuantitativeInvisibilityUP1D(0)))
487         elif lineset.visibility == "RANGE":
488             selection_criteria.append(
489                 QuantitativeInvisibilityRangeUP1D(lineset.qi_start, lineset.qi_end))
490     # prepare selection criteria by edge types
491     if lineset.select_by_edge_types:
492         edge_type_criteria = []
493         if lineset.edge_type_combination == "OR":
494             flags = Nature.NO_FEATURE
495             if lineset.select_silhouette:
496                 flags |= Nature.SILHOUETTE
497             if lineset.select_border:
498                 flags |= Nature.BORDER
499             if lineset.select_crease:
500                 flags |= Nature.CREASE
501             if lineset.select_ridge:
502                 flags |= Nature.RIDGE
503             if lineset.select_valley:
504                 flags |= Nature.VALLEY
505             if lineset.select_suggestive_contour:
506                 flags |= Nature.SUGGESTIVE_CONTOUR
507             if lineset.select_material_boundary:
508                 flags |= Nature.MATERIAL_BOUNDARY
509             if flags != Nature.NO_FEATURE:
510                 edge_type_criteria.append(pyNatureUP1D(flags))
511         else:
512             if lineset.select_silhouette:
513                 edge_type_criteria.append(pyNatureUP1D(Nature.SILHOUETTE))
514             if lineset.select_border:
515                 edge_type_criteria.append(pyNatureUP1D(Nature.BORDER))
516             if lineset.select_crease:
517                 edge_type_criteria.append(pyNatureUP1D(Nature.CREASE))
518             if lineset.select_ridge:
519                 edge_type_criteria.append(pyNatureUP1D(Nature.RIDGE))
520             if lineset.select_valley:
521                 edge_type_criteria.append(pyNatureUP1D(Nature.VALLEY))
522             if lineset.select_suggestive_contour:
523                 edge_type_criteria.append(pyNatureUP1D(Nature.SUGGESTIVE_CONTOUR))
524             if lineset.select_material_boundary:
525                 edge_type_criteria.append(pyNatureUP1D(Nature.MATERIAL_BOUNDARY))
526         if lineset.select_contour:
527             edge_type_criteria.append(ContourUP1D())
528         if lineset.select_external_contour:
529             edge_type_criteria.append(ExternalContourUP1D())
530         if lineset.edge_type_combination == "OR":
531             upred = join_unary_predicates(edge_type_criteria, OrUP1D)
532         else:
533             upred = join_unary_predicates(edge_type_criteria, AndUP1D)
534         if upred is not None:
535             if lineset.edge_type_negation == "EXCLUSIVE":
536                 upred = NotUP1D(upred)
537             selection_criteria.append(upred)
538     # prepare selection criteria by group of objects
539     if lineset.select_by_group:
540         if lineset.group is not None and len(lineset.group.objects) > 0:
541             names = dict((ob.name, True) for ob in lineset.group.objects)
542             upred = ObjectNamesUP1D(names, lineset.group_negation == 'EXCLUSIVE')
543             selection_criteria.append(upred)
544     # prepare selection criteria by image border
545     if lineset.select_by_image_border:
546         w = scene.render.resolution_x
547         h = scene.render.resolution_y
548         if scene.render.use_border:
549             xmin = scene.render.border_min_x * w
550             xmax = scene.render.border_max_x * w
551             ymin = scene.render.border_min_y * h
552             ymax = scene.render.border_max_y * h
553         else:
554             xmin, xmax = 0.0, float(w)
555             ymin, ymax = 0.0, float(h)
556         upred = WithinImageBorderUP1D(xmin, xmax, ymin, ymax)
557         selection_criteria.append(upred)
558     # do feature edge selection
559     upred = join_unary_predicates(selection_criteria, AndUP1D)
560     if upred is None:
561         upred = TrueUP1D()
562     Operators.select(upred)
563     # join feature edges
564     if linestyle.same_object:
565         bpred = SameShapeIdBP1D()
566         chaining_iterator = ChainPredicateIterator(upred, bpred)
567     else:
568         chaining_iterator = ChainSilhouetteIterator()
569     Operators.bidirectionalChain(chaining_iterator, NotUP1D(upred))
570     # dashed line
571     if linestyle.use_dashed_line:
572         pattern = []
573         if linestyle.dash1 > 0 and linestyle.gap1 > 0:
574             pattern.append(linestyle.dash1)
575             pattern.append(linestyle.gap1)
576         if linestyle.dash2 > 0 and linestyle.gap2 > 0:
577             pattern.append(linestyle.dash2)
578             pattern.append(linestyle.gap2)
579         if linestyle.dash3 > 0 and linestyle.gap3 > 0:
580             pattern.append(linestyle.dash3)
581             pattern.append(linestyle.gap3)
582         if len(pattern) > 0:
583             sampling = 1.0
584             controller = DashedLineController(pattern, sampling)
585             Operators.sequentialSplit(DashedLineStartingUP0D(controller),
586                                       DashedLineStoppingUP0D(controller),
587                                       sampling)
588     # prepare a list of stroke shaders
589     color = linestyle.color
590     shaders_list = [
591         SamplingShader(5.0),
592         ConstantThicknessShader(linestyle.thickness),
593         ConstantColorShader(color.r, color.g, color.b, linestyle.alpha)]
594     for m in linestyle.color_modifiers:
595         if not m.use:
596             continue
597         if m.type == "ALONG_STROKE":
598             shaders_list.append(ColorAlongStrokeShader(
599                 m.blend, m.influence, m.color_ramp))
600         elif m.type == "DISTANCE_FROM_CAMERA":
601             shaders_list.append(ColorDistanceFromCameraShader(
602                 m.blend, m.influence, m.color_ramp,
603                 m.range_min, m.range_max))
604         elif m.type == "DISTANCE_FROM_OBJECT":
605             shaders_list.append(ColorDistanceFromObjectShader(
606                 m.blend, m.influence, m.color_ramp, m.target,
607                 m.range_min, m.range_max))
608     for m in linestyle.alpha_modifiers:
609         if not m.use:
610             continue
611         if m.type == "ALONG_STROKE":
612             shaders_list.append(AlphaAlongStrokeShader(
613                 m.blend, m.influence, m.mapping, m.invert, m.curve))
614         elif m.type == "DISTANCE_FROM_CAMERA":
615             shaders_list.append(AlphaDistanceFromCameraShader(
616                 m.blend, m.influence, m.mapping, m.invert, m.curve,
617                 m.range_min, m.range_max))
618         elif m.type == "DISTANCE_FROM_OBJECT":
619             shaders_list.append(AlphaDistanceFromObjectShader(
620                 m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
621                 m.range_min, m.range_max))
622     for m in linestyle.thickness_modifiers:
623         if not m.use:
624             continue
625         if m.type == "ALONG_STROKE":
626             shaders_list.append(ThicknessAlongStrokeShader(
627                 m.blend, m.influence, m.mapping, m.invert, m.curve,
628                 m.value_min, m.value_max))
629         elif m.type == "DISTANCE_FROM_CAMERA":
630             shaders_list.append(ThicknessDistanceFromCameraShader(
631                 m.blend, m.influence, m.mapping, m.invert, m.curve,
632                 m.range_min, m.range_max, m.value_min, m.value_max))
633         elif m.type == "DISTANCE_FROM_OBJECT":
634             shaders_list.append(ThicknessDistanceFromObjectShader(
635                 m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
636                 m.range_min, m.range_max, m.value_min, m.value_max))
637     if linestyle.caps == "ROUND":
638         shaders_list.append(RoundCapShader())
639     elif linestyle.caps == "SQUARE":
640         shaders_list.append(SquareCapShader())
641     # create strokes using the shaders list
642     Operators.create(TrueUP1D(), shaders_list)