83c5bcaef805cc8055bf24c54bf290b92834d140
[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 = mv * object.location # 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 # Stroke caps
316
317 def iter_stroke_vertices(stroke):
318     it = stroke.strokeVerticesBegin()
319     while not it.isEnd():
320         yield it.getObject()
321         it.increment()
322
323 class RoundCapShader(StrokeShader):
324     def round_cap_thickness(self, x):
325         x = max(0.0, min(x, 1.0))
326         return math.sqrt(1.0 - (x ** 2))
327     def shade(self, stroke):
328         # save the location and attribute of stroke vertices
329         buffer = []
330         for sv in iter_stroke_vertices(stroke):
331             buffer.append((sv.getPoint(), sv.attribute()))
332         # calculate the number of additional vertices to form caps
333         R, L = stroke[0].attribute().getThicknessRL()
334         caplen_beg = (R + L) / 2.0
335         nverts_beg = max(5, int(R + L))
336         R, L = stroke[-1].attribute().getThicknessRL()
337         caplen_end = (R + L) / 2.0
338         nverts_end = max(5, int(R + L))
339         # increase the total number of stroke vertices
340         nverts = stroke.strokeVerticesSize()
341         stroke.Resample(nverts + nverts_beg + nverts_end)
342         # restore the location and attribute of the original vertices
343         for i in range(nverts):
344             p, attr = buffer[i]
345             stroke[nverts_beg + i].setPoint(p)
346             stroke[nverts_beg + i].setAttribute(attr)
347         # reshape the cap at the beginning of the stroke
348         q, attr = buffer[1]
349         p, attr = buffer[0]
350         d = p - q
351         d = d / d.length * caplen_beg
352         n = 1.0 / nverts_beg
353         R, L = attr.getThicknessRL()
354         for i in range(nverts_beg):
355             t = (nverts_beg - i) * n
356             stroke[i].setPoint(p + d * t)
357             r = self.round_cap_thickness((nverts_beg - i + 1) * n)
358             stroke[i].setAttribute(attr)
359             stroke[i].attribute().setThickness(R * r, L * r)
360         # reshape the cap at the end of the stroke
361         q, attr = buffer[-2]
362         p, attr = buffer[-1]
363         d = p - q
364         d = d / d.length * caplen_end
365         n = 1.0 / nverts_end
366         R, L = attr.getThicknessRL()
367         for i in range(nverts_end):
368             t = (nverts_end - i) * n
369             stroke[-i-1].setPoint(p + d * t)
370             r = self.round_cap_thickness((nverts_end - i + 1) * n)
371             stroke[-i-1].setAttribute(attr)
372             stroke[-i-1].attribute().setThickness(R * r, L * r)
373
374 class SquareCapShader(StrokeShader):
375     def shade(self, stroke):
376         # save the location and attribute of stroke vertices
377         buffer = []
378         for sv in iter_stroke_vertices(stroke):
379             buffer.append((sv.getPoint(), sv.attribute()))
380         # calculate the number of additional vertices to form caps
381         R, L = stroke[0].attribute().getThicknessRL()
382         caplen_beg = (R + L) / 2.0
383         nverts_beg = 1
384         R, L = stroke[-1].attribute().getThicknessRL()
385         caplen_end = (R + L) / 2.0
386         nverts_end = 1
387         # increase the total number of stroke vertices
388         nverts = stroke.strokeVerticesSize()
389         stroke.Resample(nverts + nverts_beg + nverts_end)
390         # restore the location and attribute of the original vertices
391         for i in range(nverts):
392             p, attr = buffer[i]
393             stroke[nverts_beg + i].setPoint(p)
394             stroke[nverts_beg + i].setAttribute(attr)
395         # reshape the cap at the beginning of the stroke
396         q, attr = buffer[1]
397         p, attr = buffer[0]
398         d = p - q
399         stroke[0].setPoint(p + d / d.length * caplen_beg)
400         stroke[0].setAttribute(attr)
401         # reshape the cap at the end of the stroke
402         q, attr = buffer[-2]
403         p, attr = buffer[-1]
404         d = p - q
405         stroke[-1].setPoint(p + d / d.length * caplen_beg)
406         stroke[-1].setAttribute(attr)
407
408 # dashed line
409
410 class DashedLineStartingUP0D(UnaryPredicate0D):
411     def __init__(self, controller):
412         UnaryPredicate0D.__init__(self)
413         self._controller = controller
414     def __call__(self, inter):
415         return self._controller.start()
416
417 class DashedLineStoppingUP0D(UnaryPredicate0D):
418     def __init__(self, controller):
419         UnaryPredicate0D.__init__(self)
420         self._controller = controller
421     def __call__(self, inter):
422         return self._controller.stop()
423
424 class DashedLineController:
425     def __init__(self, pattern, sampling):
426         self.sampling = float(sampling)
427         k = len(pattern) // 2
428         n = k * 2
429         self.start_pos = [pattern[i] + pattern[i+1] for i in range(0, n, 2)]
430         self.stop_pos = [pattern[i] for i in range(0, n, 2)]
431         self.init()
432     def init(self):
433         self.start_len = 0.0
434         self.start_idx = 0
435         self.stop_len = self.sampling
436         self.stop_idx = 0
437     def start(self):
438         self.start_len += self.sampling
439         if abs(self.start_len - self.start_pos[self.start_idx]) < self.sampling / 2.0:
440             self.start_len = 0.0
441             self.start_idx = (self.start_idx + 1) % len(self.start_pos)
442             return True
443         return False
444     def stop(self):
445         if self.start_len > 0.0:
446             self.init()
447         self.stop_len += self.sampling
448         if abs(self.stop_len - self.stop_pos[self.stop_idx]) < self.sampling / 2.0:
449             self.stop_len = self.sampling
450             self.stop_idx = (self.stop_idx + 1) % len(self.stop_pos)
451             return True
452         return False
453
454 # main function for parameter processing
455
456 def process(layer_name, lineset_name):
457     scene = Freestyle.getCurrentScene()
458     layer = scene.render.layers[layer_name]
459     lineset = layer.freestyle_settings.linesets[lineset_name]
460     linestyle = lineset.linestyle
461
462     selection_criteria = []
463     # prepare selection criteria by visibility
464     if lineset.select_by_visibility:
465         if lineset.visibility == "VISIBLE":
466             selection_criteria.append(
467                 QuantitativeInvisibilityUP1D(0))
468         elif lineset.visibility == "HIDDEN":
469             selection_criteria.append(
470                 NotUP1D(QuantitativeInvisibilityUP1D(0)))
471         elif lineset.visibility == "RANGE":
472             selection_criteria.append(
473                 QuantitativeInvisibilityRangeUP1D(lineset.qi_start, lineset.qi_end))
474     # prepare selection criteria by edge types
475     if lineset.select_by_edge_types:
476         edge_type_criteria = []
477         if lineset.edge_type_combination == "OR":
478             flags = Nature.NO_FEATURE
479             if lineset.select_silhouette:
480                 flags |= Nature.SILHOUETTE
481             if lineset.select_border:
482                 flags |= Nature.BORDER
483             if lineset.select_crease:
484                 flags |= Nature.CREASE
485             if lineset.select_ridge:
486                 flags |= Nature.RIDGE
487             if lineset.select_valley:
488                 flags |= Nature.VALLEY
489             if lineset.select_suggestive_contour:
490                 flags |= Nature.SUGGESTIVE_CONTOUR
491             if lineset.select_material_boundary:
492                 flags |= Nature.MATERIAL_BOUNDARY
493             if flags != Nature.NO_FEATURE:
494                 edge_type_criteria.append(pyNatureUP1D(flags))
495         else:
496             if lineset.select_silhouette:
497                 edge_type_criteria.append(pyNatureUP1D(Nature.SILHOUETTE))
498             if lineset.select_border:
499                 edge_type_criteria.append(pyNatureUP1D(Nature.BORDER))
500             if lineset.select_crease:
501                 edge_type_criteria.append(pyNatureUP1D(Nature.CREASE))
502             if lineset.select_ridge:
503                 edge_type_criteria.append(pyNatureUP1D(Nature.RIDGE))
504             if lineset.select_valley:
505                 edge_type_criteria.append(pyNatureUP1D(Nature.VALLEY))
506             if lineset.select_suggestive_contour:
507                 edge_type_criteria.append(pyNatureUP1D(Nature.SUGGESTIVE_CONTOUR))
508             if lineset.select_material_boundary:
509                 edge_type_criteria.append(pyNatureUP1D(Nature.MATERIAL_BOUNDARY))
510         if lineset.select_contour:
511             edge_type_criteria.append(ContourUP1D())
512         if lineset.select_external_contour:
513             edge_type_criteria.append(ExternalContourUP1D())
514         if lineset.edge_type_combination == "OR":
515             upred = join_unary_predicates(edge_type_criteria, OrUP1D)
516         else:
517             upred = join_unary_predicates(edge_type_criteria, AndUP1D)
518         if upred is not None:
519             if lineset.edge_type_negation == "EXCLUSIVE":
520                 upred = NotUP1D(upred)
521             selection_criteria.append(upred)
522     # prepare selection criteria by group of objects
523     if lineset.select_by_group:
524         if lineset.group is not None and len(lineset.group.objects) > 0:
525             names = dict((ob.name, True) for ob in lineset.group.objects)
526             upred = ObjectNamesUP1D(names, lineset.group_negation == 'EXCLUSIVE')
527             selection_criteria.append(upred)
528     # do feature edge selection
529     upred = join_unary_predicates(selection_criteria, AndUP1D)
530     if upred is None:
531         upred = TrueUP1D()
532     Operators.select(upred)
533     # join feature edges
534     if linestyle.same_object:
535         bpred = SameShapeIdBP1D()
536         chaining_iterator = ChainPredicateIterator(upred, bpred)
537     else:
538         chaining_iterator = ChainSilhouetteIterator()
539     Operators.bidirectionalChain(chaining_iterator, NotUP1D(upred))
540     # dashed line
541     if linestyle.use_dashed_line:
542         pattern = []
543         if linestyle.dash1 > 0 and linestyle.gap1 > 0:
544             pattern.append(linestyle.dash1)
545             pattern.append(linestyle.gap1)
546         if linestyle.dash2 > 0 and linestyle.gap2 > 0:
547             pattern.append(linestyle.dash2)
548             pattern.append(linestyle.gap2)
549         if linestyle.dash3 > 0 and linestyle.gap3 > 0:
550             pattern.append(linestyle.dash3)
551             pattern.append(linestyle.gap3)
552         if len(pattern) > 0:
553             sampling = 1.0
554             controller = DashedLineController(pattern, sampling)
555             Operators.sequentialSplit(DashedLineStartingUP0D(controller),
556                                       DashedLineStoppingUP0D(controller),
557                                       sampling)
558     # prepare a list of stroke shaders
559     color = linestyle.color
560     shaders_list = [
561         SamplingShader(5.0),
562         ConstantThicknessShader(linestyle.thickness),
563         ConstantColorShader(color.r, color.g, color.b, linestyle.alpha)]
564     for m in linestyle.color_modifiers:
565         if not m.use:
566             continue
567         if m.type == "ALONG_STROKE":
568             shaders_list.append(ColorAlongStrokeShader(
569                 m.blend, m.influence, m.color_ramp))
570         elif m.type == "DISTANCE_FROM_CAMERA":
571             shaders_list.append(ColorDistanceFromCameraShader(
572                 m.blend, m.influence, m.color_ramp,
573                 m.range_min, m.range_max))
574         elif m.type == "DISTANCE_FROM_OBJECT":
575             shaders_list.append(ColorDistanceFromObjectShader(
576                 m.blend, m.influence, m.color_ramp, m.target,
577                 m.range_min, m.range_max))
578     for m in linestyle.alpha_modifiers:
579         if not m.use:
580             continue
581         if m.type == "ALONG_STROKE":
582             shaders_list.append(AlphaAlongStrokeShader(
583                 m.blend, m.influence, m.mapping, m.invert, m.curve))
584         elif m.type == "DISTANCE_FROM_CAMERA":
585             shaders_list.append(AlphaDistanceFromCameraShader(
586                 m.blend, m.influence, m.mapping, m.invert, m.curve,
587                 m.range_min, m.range_max))
588         elif m.type == "DISTANCE_FROM_OBJECT":
589             shaders_list.append(AlphaDistanceFromObjectShader(
590                 m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
591                 m.range_min, m.range_max))
592     for m in linestyle.thickness_modifiers:
593         if not m.use:
594             continue
595         if m.type == "ALONG_STROKE":
596             shaders_list.append(ThicknessAlongStrokeShader(
597                 m.blend, m.influence, m.mapping, m.invert, m.curve,
598                 m.value_min, m.value_max))
599         elif m.type == "DISTANCE_FROM_CAMERA":
600             shaders_list.append(ThicknessDistanceFromCameraShader(
601                 m.blend, m.influence, m.mapping, m.invert, m.curve,
602                 m.range_min, m.range_max, m.value_min, m.value_max))
603         elif m.type == "DISTANCE_FROM_OBJECT":
604             shaders_list.append(ThicknessDistanceFromObjectShader(
605                 m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
606                 m.range_min, m.range_max, m.value_min, m.value_max))
607     if linestyle.caps == "ROUND":
608         shaders_list.append(RoundCapShader())
609     elif linestyle.caps == "SQUARE":
610         shaders_list.append(SquareCapShader())
611     # create strokes using the shaders list
612     Operators.create(TrueUP1D(), shaders_list)