Merge branch 'master' into blender2.8
[blender.git] / release / scripts / startup / bl_ui / properties_freestyle.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 # <pep8 compliant>
20 import bpy
21 from bpy.types import Menu, Panel, UIList
22
23
24 # Render properties
25
26 class RenderFreestyleButtonsPanel:
27     bl_space_type = 'PROPERTIES'
28     bl_region_type = 'WINDOW'
29     bl_context = "render"
30     # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
31
32     @classmethod
33     def poll(cls, context):
34         scene = context.scene
35         with_freestyle = bpy.app.build_options.freestyle
36         return scene and with_freestyle and(scene.view_render.engine in cls.COMPAT_ENGINES)
37
38
39 class RENDER_PT_freestyle(RenderFreestyleButtonsPanel, Panel):
40     bl_label = "Freestyle"
41     bl_options = {'DEFAULT_CLOSED'}
42     COMPAT_ENGINES = {'BLENDER_RENDER'}
43
44     def draw_header(self, context):
45         rd = context.scene.render
46         self.layout.prop(rd, "use_freestyle", text="")
47
48     def draw(self, context):
49         layout = self.layout
50
51         rd = context.scene.render
52
53         layout.active = rd.use_freestyle
54
55         row = layout.row()
56         row.label(text="Line Thickness:")
57         row.prop(rd, "line_thickness_mode", expand=True)
58
59         if (rd.line_thickness_mode == 'ABSOLUTE'):
60             layout.prop(rd, "line_thickness")
61
62
63 # Render layer properties
64
65 class RenderLayerFreestyleButtonsPanel:
66     bl_space_type = 'PROPERTIES'
67     bl_region_type = 'WINDOW'
68     bl_context = "render_layer"
69     # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
70
71     @classmethod
72     def poll(cls, context):
73         scene = context.scene
74         rd = context.scene.render
75         with_freestyle = bpy.app.build_options.freestyle
76
77         return (scene and with_freestyle and rd.use_freestyle and
78                 scene.render_layers.active and(scene.view_render.engine in cls.COMPAT_ENGINES))
79
80
81 class RenderLayerFreestyleEditorButtonsPanel(RenderLayerFreestyleButtonsPanel):
82     # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
83
84     @classmethod
85     def poll(cls, context):
86         if not super().poll(context):
87             return False
88         rl = context.scene.render_layers.active
89         return rl and rl.freestyle_settings.mode == 'EDITOR'
90
91
92 class RENDERLAYER_UL_linesets(UIList):
93     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
94         lineset = item
95         if self.layout_type in {'DEFAULT', 'COMPACT'}:
96             layout.prop(lineset, "name", text="", emboss=False, icon_value=icon)
97             layout.prop(lineset, "show_render", text="", index=index)
98         elif self.layout_type == 'GRID':
99             layout.alignment = 'CENTER'
100             layout.label("", icon_value=icon)
101
102
103 class RENDER_MT_lineset_specials(Menu):
104     bl_label = "Lineset Specials"
105
106     def draw(self, context):
107         layout = self.layout
108         layout.operator("scene.freestyle_lineset_copy", icon='COPYDOWN')
109         layout.operator("scene.freestyle_lineset_paste", icon='PASTEDOWN')
110
111
112 class RENDERLAYER_PT_freestyle(RenderLayerFreestyleButtonsPanel, Panel):
113     bl_label = "Freestyle"
114     COMPAT_ENGINES = {'BLENDER_RENDER'}
115
116     def draw(self, context):
117         layout = self.layout
118
119         scene = context.scene
120         rl = scene.render_layers.active
121         freestyle = rl.freestyle_settings
122
123         layout.active = rl.use_freestyle
124
125         row = layout.row()
126         layout.prop(freestyle, "mode", text="Control Mode")
127         layout.prop(freestyle, "use_view_map_cache", text="View Map Cache")
128         layout.label(text="Edge Detection Options:")
129
130         split = layout.split()
131
132         col = split.column()
133         col.prop(freestyle, "crease_angle")
134         col.prop(freestyle, "use_culling")
135         col.prop(freestyle, "use_advanced_options")
136
137         col = split.column()
138         col.prop(freestyle, "use_smoothness")
139         if freestyle.mode == 'SCRIPT':
140             col.prop(freestyle, "use_material_boundaries")
141
142         # Advanced options are hidden by default to warn new users
143         if freestyle.use_advanced_options:
144             if freestyle.mode == 'SCRIPT':
145                 row = layout.row()
146                 row.prop(freestyle, "use_ridges_and_valleys")
147                 row.prop(freestyle, "use_suggestive_contours")
148             row = layout.row()
149             row.prop(freestyle, "sphere_radius")
150             row.prop(freestyle, "kr_derivative_epsilon")
151
152         if freestyle.mode == 'SCRIPT':
153             row = layout.row()
154             row.label("Style modules:")
155             row.operator("scene.freestyle_module_add", text="Add")
156             for i, module in enumerate(freestyle.modules):
157                 box = layout.box()
158                 box.context_pointer_set("freestyle_module", module)
159                 row = box.row(align=True)
160                 row.prop(module, "use", text="")
161                 row.prop(module, "script", text="")
162                 row.operator("scene.freestyle_module_open", icon='FILESEL', text="")
163                 row.operator("scene.freestyle_module_remove", icon='X', text="")
164                 row.operator("scene.freestyle_module_move", icon='TRIA_UP', text="").direction = 'UP'
165                 row.operator("scene.freestyle_module_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
166
167
168 class RENDERLAYER_PT_freestyle_lineset(RenderLayerFreestyleEditorButtonsPanel, Panel):
169     bl_label = "Freestyle Line Set"
170     COMPAT_ENGINES = {'BLENDER_RENDER'}
171
172     def draw_edge_type_buttons(self, box, lineset, edge_type):
173         # property names
174         select_edge_type = "select_" + edge_type
175         exclude_edge_type = "exclude_" + edge_type
176         # draw edge type buttons
177         row = box.row(align=True)
178         row.prop(lineset, select_edge_type)
179         sub = row.column(align=True)
180         sub.prop(lineset, exclude_edge_type, text="")
181         sub.active = getattr(lineset, select_edge_type)
182
183     def draw(self, context):
184         layout = self.layout
185
186         scene = context.scene
187         rd = scene.render
188         view_render = scene.view_render
189
190         rl = scene.render_layers.active
191         freestyle = rl.freestyle_settings
192         lineset = freestyle.linesets.active
193
194         layout.active = rl.use_freestyle
195
196         row = layout.row()
197         rows = 4 if lineset else 2
198         row.template_list("RENDERLAYER_UL_linesets", "", freestyle, "linesets", freestyle.linesets, "active_index", rows=rows)
199
200         sub = row.column(align=True)
201         sub.operator("scene.freestyle_lineset_add", icon='ZOOMIN', text="")
202         sub.operator("scene.freestyle_lineset_remove", icon='ZOOMOUT', text="")
203         sub.menu("RENDER_MT_lineset_specials", icon='DOWNARROW_HLT', text="")
204         if lineset:
205             sub.separator()
206             sub.separator()
207             sub.operator("scene.freestyle_lineset_move", icon='TRIA_UP', text="").direction = 'UP'
208             sub.operator("scene.freestyle_lineset_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
209
210             col = layout.column()
211             col.label(text="Selection By:")
212             row = col.row(align=True)
213             row.prop(lineset, "select_by_visibility", text="Visibility", toggle=True)
214             row.prop(lineset, "select_by_edge_types", text="Edge Types", toggle=True)
215             row.prop(lineset, "select_by_face_marks", text="Face Marks", toggle=True)
216             row.prop(lineset, "select_by_group", text="Group", toggle=True)
217             row.prop(lineset, "select_by_image_border", text="Image Border", toggle=True)
218
219             if lineset.select_by_visibility:
220                 col.label(text="Visibility:")
221                 row = col.row(align=True)
222                 row.prop(lineset, "visibility", expand=True)
223                 if lineset.visibility == 'RANGE':
224                     row = col.row(align=True)
225                     row.prop(lineset, "qi_start")
226                     row.prop(lineset, "qi_end")
227
228             if lineset.select_by_edge_types:
229                 col.label(text="Edge Types:")
230                 row = col.row()
231                 row.prop(lineset, "edge_type_negation", expand=True)
232                 row.prop(lineset, "edge_type_combination", expand=True)
233
234                 split = col.split()
235
236                 sub = split.column()
237                 self.draw_edge_type_buttons(sub, lineset, "silhouette")
238                 self.draw_edge_type_buttons(sub, lineset, "border")
239                 self.draw_edge_type_buttons(sub, lineset, "contour")
240                 self.draw_edge_type_buttons(sub, lineset, "suggestive_contour")
241                 self.draw_edge_type_buttons(sub, lineset, "ridge_valley")
242
243                 sub = split.column()
244                 self.draw_edge_type_buttons(sub, lineset, "crease")
245                 self.draw_edge_type_buttons(sub, lineset, "edge_mark")
246                 self.draw_edge_type_buttons(sub, lineset, "external_contour")
247                 self.draw_edge_type_buttons(sub, lineset, "material_boundary")
248
249             if lineset.select_by_face_marks:
250                 col.label(text="Face Marks:")
251                 row = col.row()
252                 row.prop(lineset, "face_mark_negation", expand=True)
253                 row.prop(lineset, "face_mark_condition", expand=True)
254
255             if lineset.select_by_group:
256                 col.label(text="Group:")
257                 row = col.row()
258                 row.prop(lineset, "group", text="")
259                 row.prop(lineset, "group_negation", expand=True)
260
261
262 class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, Panel):
263     bl_label = "Freestyle Line Style"
264     bl_options = {'DEFAULT_CLOSED'}
265     COMPAT_ENGINES = {'BLENDER_RENDER'}
266
267     def draw_modifier_box_header(self, box, modifier):
268         row = box.row()
269         row.context_pointer_set("modifier", modifier)
270         if modifier.expanded:
271             icon = 'TRIA_DOWN'
272         else:
273             icon = 'TRIA_RIGHT'
274         row.prop(modifier, "expanded", text="", icon=icon, emboss=False)
275         # TODO: Use icons rather than text label, would save some room!
276         row.label(text=modifier.rna_type.name)
277         row.prop(modifier, "name", text="")
278         if modifier.use:
279             icon = 'RESTRICT_RENDER_OFF'
280         else:
281             icon = 'RESTRICT_RENDER_ON'
282         row.prop(modifier, "use", text="", icon=icon)
283         sub = row.row(align=True)
284         sub.operator("scene.freestyle_modifier_copy", icon='NONE', text="Copy")
285         sub.operator("scene.freestyle_modifier_move", icon='TRIA_UP', text="").direction = 'UP'
286         sub.operator("scene.freestyle_modifier_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
287         sub.operator("scene.freestyle_modifier_remove", icon='X', text="")
288
289     def draw_modifier_box_error(self, box, modifier, message):
290         row = box.row()
291         row.label(text=message, icon='ERROR')
292
293     def draw_modifier_common(self, box, modifier):
294         row = box.row()
295         row.prop(modifier, "blend", text="")
296         row.prop(modifier, "influence")
297
298     def draw_modifier_color_ramp_common(self, box, modifier, has_range):
299         box.template_color_ramp(modifier, "color_ramp", expand=True)
300         if has_range:
301             row = box.row(align=True)
302             row.prop(modifier, "range_min")
303             row.prop(modifier, "range_max")
304
305     def draw_modifier_curve_common(self, box, modifier, has_range, has_value):
306         row = box.row()
307         row.prop(modifier, "mapping", text="")
308         sub = row.column()
309         sub.prop(modifier, "invert")
310         if modifier.mapping == 'CURVE':
311             sub.active = False
312             box.template_curve_mapping(modifier, "curve")
313         if has_range:
314             row = box.row(align=True)
315             row.prop(modifier, "range_min")
316             row.prop(modifier, "range_max")
317         if has_value:
318             row = box.row(align=True)
319             row.prop(modifier, "value_min")
320             row.prop(modifier, "value_max")
321
322     def draw_color_modifier(self, context, modifier):
323         layout = self.layout
324
325         col = layout.column(align=True)
326         self.draw_modifier_box_header(col.box(), modifier)
327         if modifier.expanded:
328             box = col.box()
329             self.draw_modifier_common(box, modifier)
330
331             if modifier.type == 'ALONG_STROKE':
332                 self.draw_modifier_color_ramp_common(box, modifier, False)
333
334             elif modifier.type == 'DISTANCE_FROM_OBJECT':
335                 box.prop(modifier, "target")
336                 self.draw_modifier_color_ramp_common(box, modifier, True)
337                 prop = box.operator("scene.freestyle_fill_range_by_selection")
338                 prop.type = 'COLOR'
339                 prop.name = modifier.name
340
341             elif modifier.type == 'DISTANCE_FROM_CAMERA':
342                 self.draw_modifier_color_ramp_common(box, modifier, True)
343                 prop = box.operator("scene.freestyle_fill_range_by_selection")
344                 prop.type = 'COLOR'
345                 prop.name = modifier.name
346
347             elif modifier.type == 'MATERIAL':
348                 row = box.row()
349                 row.prop(modifier, "material_attribute", text="")
350                 sub = row.column()
351                 sub.prop(modifier, "use_ramp")
352                 if modifier.material_attribute in {'LINE', 'DIFF', 'SPEC'}:
353                     sub.active = True
354                     show_ramp = modifier.use_ramp
355                 else:
356                     sub.active = False
357                     show_ramp = True
358                 if show_ramp:
359                     self.draw_modifier_color_ramp_common(box, modifier, False)
360
361             elif modifier.type == 'TANGENT':
362                 self.draw_modifier_color_ramp_common(box, modifier, False)
363
364             elif modifier.type == 'NOISE':
365                 self.draw_modifier_color_ramp_common(box, modifier, False)
366                 row = box.row(align=False)
367                 row.prop(modifier, "amplitude")
368                 row.prop(modifier, "period")
369                 row.prop(modifier, "seed")
370
371             elif modifier.type == 'CREASE_ANGLE':
372                 self.draw_modifier_color_ramp_common(box, modifier, False)
373                 row = box.row(align=True)
374                 row.prop(modifier, "angle_min")
375                 row.prop(modifier, "angle_max")
376
377             elif modifier.type == 'CURVATURE_3D':
378                 self.draw_modifier_color_ramp_common(box, modifier, False)
379                 row = box.row(align=True)
380                 row.prop(modifier, "curvature_min")
381                 row.prop(modifier, "curvature_max")
382                 freestyle = context.scene.render_layers.active.freestyle_settings
383                 if not freestyle.use_smoothness:
384                     message = "Enable Face Smoothness to use this modifier"
385                     self.draw_modifier_box_error(col.box(), modifier, message)
386
387     def draw_alpha_modifier(self, context, modifier):
388         layout = self.layout
389
390         col = layout.column(align=True)
391         self.draw_modifier_box_header(col.box(), modifier)
392         if modifier.expanded:
393             box = col.box()
394             self.draw_modifier_common(box, modifier)
395
396             if modifier.type == 'ALONG_STROKE':
397                 self.draw_modifier_curve_common(box, modifier, False, False)
398
399             elif modifier.type == 'DISTANCE_FROM_OBJECT':
400                 box.prop(modifier, "target")
401                 self.draw_modifier_curve_common(box, modifier, True, False)
402                 prop = box.operator("scene.freestyle_fill_range_by_selection")
403                 prop.type = 'ALPHA'
404                 prop.name = modifier.name
405
406             elif modifier.type == 'DISTANCE_FROM_CAMERA':
407                 self.draw_modifier_curve_common(box, modifier, True, False)
408                 prop = box.operator("scene.freestyle_fill_range_by_selection")
409                 prop.type = 'ALPHA'
410                 prop.name = modifier.name
411
412             elif modifier.type == 'MATERIAL':
413                 box.prop(modifier, "material_attribute", text="")
414                 self.draw_modifier_curve_common(box, modifier, False, False)
415
416             elif modifier.type == 'TANGENT':
417                 self.draw_modifier_curve_common(box, modifier, False, False)
418
419             elif modifier.type == 'NOISE':
420                 self.draw_modifier_curve_common(box, modifier, False, False)
421                 row = box.row(align=False)
422                 row.prop(modifier, "amplitude")
423                 row.prop(modifier, "period")
424                 row.prop(modifier, "seed")
425
426             elif modifier.type == 'CREASE_ANGLE':
427                 self.draw_modifier_curve_common(box, modifier, False, False)
428                 row = box.row(align=True)
429                 row.prop(modifier, "angle_min")
430                 row.prop(modifier, "angle_max")
431
432             elif modifier.type == 'CURVATURE_3D':
433                 self.draw_modifier_curve_common(box, modifier, False, False)
434                 row = box.row(align=True)
435                 row.prop(modifier, "curvature_min")
436                 row.prop(modifier, "curvature_max")
437                 freestyle = context.scene.render_layers.active.freestyle_settings
438                 if not freestyle.use_smoothness:
439                     message = "Enable Face Smoothness to use this modifier"
440                     self.draw_modifier_box_error(col.box(), modifier, message)
441
442     def draw_thickness_modifier(self, context, modifier):
443         layout = self.layout
444
445         col = layout.column(align=True)
446         self.draw_modifier_box_header(col.box(), modifier)
447         if modifier.expanded:
448             box = col.box()
449             self.draw_modifier_common(box, modifier)
450
451             if modifier.type == 'ALONG_STROKE':
452                 self.draw_modifier_curve_common(box, modifier, False, True)
453
454             elif modifier.type == 'DISTANCE_FROM_OBJECT':
455                 box.prop(modifier, "target")
456                 self.draw_modifier_curve_common(box, modifier, True, True)
457                 prop = box.operator("scene.freestyle_fill_range_by_selection")
458                 prop.type = 'THICKNESS'
459                 prop.name = modifier.name
460
461             elif modifier.type == 'DISTANCE_FROM_CAMERA':
462                 self.draw_modifier_curve_common(box, modifier, True, True)
463                 prop = box.operator("scene.freestyle_fill_range_by_selection")
464                 prop.type = 'THICKNESS'
465                 prop.name = modifier.name
466
467             elif modifier.type == 'MATERIAL':
468                 box.prop(modifier, "material_attribute", text="")
469                 self.draw_modifier_curve_common(box, modifier, False, True)
470
471             elif modifier.type == 'CALLIGRAPHY':
472                 box.prop(modifier, "orientation")
473                 row = box.row(align=True)
474                 row.prop(modifier, "thickness_min")
475                 row.prop(modifier, "thickness_max")
476
477             elif modifier.type == 'TANGENT':
478                 self.draw_modifier_curve_common(box, modifier, False, False)
479                 self.mapping = 'CURVE'
480                 row = box.row(align=True)
481                 row.prop(modifier, "thickness_min")
482                 row.prop(modifier, "thickness_max")
483
484             elif modifier.type == 'NOISE':
485                 row = box.row(align=False)
486                 row.prop(modifier, "amplitude")
487                 row.prop(modifier, "period")
488                 row = box.row(align=False)
489                 row.prop(modifier, "seed")
490                 row.prop(modifier, "use_asymmetric")
491
492             elif modifier.type == 'CREASE_ANGLE':
493                 self.draw_modifier_curve_common(box, modifier, False, False)
494                 row = box.row(align=True)
495                 row.prop(modifier, "thickness_min")
496                 row.prop(modifier, "thickness_max")
497                 row = box.row(align=True)
498                 row.prop(modifier, "angle_min")
499                 row.prop(modifier, "angle_max")
500
501             elif modifier.type == 'CURVATURE_3D':
502                 self.draw_modifier_curve_common(box, modifier, False, False)
503                 row = box.row(align=True)
504                 row.prop(modifier, "thickness_min")
505                 row.prop(modifier, "thickness_max")
506                 row = box.row(align=True)
507                 row.prop(modifier, "curvature_min")
508                 row.prop(modifier, "curvature_max")
509                 freestyle = context.scene.render_layers.active.freestyle_settings
510                 if not freestyle.use_smoothness:
511                     message = "Enable Face Smoothness to use this modifier"
512                     self.draw_modifier_box_error(col.box(), modifier, message)
513
514     def draw_geometry_modifier(self, context, modifier):
515         layout = self.layout
516
517         col = layout.column(align=True)
518         self.draw_modifier_box_header(col.box(), modifier)
519         if modifier.expanded:
520             box = col.box()
521
522             if modifier.type == 'SAMPLING':
523                 box.prop(modifier, "sampling")
524
525             elif modifier.type == 'BEZIER_CURVE':
526                 box.prop(modifier, "error")
527
528             elif modifier.type == 'SINUS_DISPLACEMENT':
529                 split = box.split()
530                 col = split.column()
531                 col.prop(modifier, "wavelength")
532                 col.prop(modifier, "amplitude")
533                 col = split.column()
534                 col.prop(modifier, "phase")
535
536             elif modifier.type == 'SPATIAL_NOISE':
537                 split = box.split()
538                 col = split.column()
539                 col.prop(modifier, "amplitude")
540                 col.prop(modifier, "scale")
541                 col.prop(modifier, "octaves")
542                 col = split.column()
543                 col.prop(modifier, "smooth")
544                 col.prop(modifier, "use_pure_random")
545
546             elif modifier.type == 'PERLIN_NOISE_1D':
547                 split = box.split()
548                 col = split.column()
549                 col.prop(modifier, "frequency")
550                 col.prop(modifier, "amplitude")
551                 col.prop(modifier, "seed")
552                 col = split.column()
553                 col.prop(modifier, "octaves")
554                 col.prop(modifier, "angle")
555
556             elif modifier.type == 'PERLIN_NOISE_2D':
557                 split = box.split()
558                 col = split.column()
559                 col.prop(modifier, "frequency")
560                 col.prop(modifier, "amplitude")
561                 col.prop(modifier, "seed")
562                 col = split.column()
563                 col.prop(modifier, "octaves")
564                 col.prop(modifier, "angle")
565
566             elif modifier.type == 'BACKBONE_STRETCHER':
567                 box.prop(modifier, "backbone_length")
568
569             elif modifier.type == 'TIP_REMOVER':
570                 box.prop(modifier, "tip_length")
571
572             elif modifier.type == 'POLYGONIZATION':
573                 box.prop(modifier, "error")
574
575             elif modifier.type == 'GUIDING_LINES':
576                 box.prop(modifier, "offset")
577
578             elif modifier.type == 'BLUEPRINT':
579                 row = box.row()
580                 row.prop(modifier, "shape", expand=True)
581                 box.prop(modifier, "rounds")
582                 row = box.row()
583                 if modifier.shape in {'CIRCLES', 'ELLIPSES'}:
584                     row.prop(modifier, "random_radius")
585                     row.prop(modifier, "random_center")
586                 elif modifier.shape == 'SQUARES':
587                     row.prop(modifier, "backbone_length")
588                     row.prop(modifier, "random_backbone")
589
590             elif modifier.type == '2D_OFFSET':
591                 row = box.row(align=True)
592                 row.prop(modifier, "start")
593                 row.prop(modifier, "end")
594                 row = box.row(align=True)
595                 row.prop(modifier, "x")
596                 row.prop(modifier, "y")
597
598             elif modifier.type == '2D_TRANSFORM':
599                 box.prop(modifier, "pivot")
600                 if modifier.pivot == 'PARAM':
601                     box.prop(modifier, "pivot_u")
602                 elif modifier.pivot == 'ABSOLUTE':
603                     row = box.row(align=True)
604                     row.prop(modifier, "pivot_x")
605                     row.prop(modifier, "pivot_y")
606                 row = box.row(align=True)
607                 row.prop(modifier, "scale_x")
608                 row.prop(modifier, "scale_y")
609                 box.prop(modifier, "angle")
610
611             elif modifier.type == 'SIMPLIFICATION':
612                 box.prop(modifier, "tolerance")
613
614     def draw(self, context):
615         layout = self.layout
616
617         scene = context.scene
618         rl = scene.render_layers.active
619         lineset = rl.freestyle_settings.linesets.active
620
621         layout.active = rl.use_freestyle
622
623         if lineset is None:
624             return
625         linestyle = lineset.linestyle
626
627         layout.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
628         if linestyle is None:
629             return
630         row = layout.row(align=True)
631         row.prop(linestyle, "panel", expand=True)
632         if linestyle.panel == 'STROKES':
633             ## Chaining
634             layout.prop(linestyle, "use_chaining", text="Chaining:")
635             split = layout.split(align=True)
636             split.active = linestyle.use_chaining
637             # First column
638             col = split.column()
639             col.active = linestyle.use_chaining
640             col.prop(linestyle, "chaining", text="")
641             if linestyle.chaining == 'SKETCHY':
642                 col.prop(linestyle, "rounds")
643             # Second column
644             col = split.column()
645             col.prop(linestyle, "use_same_object")
646
647             ## Splitting
648             layout.label(text="Splitting:")
649             split = layout.split(align=True)
650             # First column
651             col = split.column()
652             row = col.row(align=True)
653             row.prop(linestyle, "use_angle_min", text="")
654             sub = row.row()
655             sub.active = linestyle.use_angle_min
656             sub.prop(linestyle, "angle_min")
657             row = col.row(align=True)
658             row.prop(linestyle, "use_angle_max", text="")
659             sub = row.row()
660             sub.active = linestyle.use_angle_max
661             sub.prop(linestyle, "angle_max")
662             # Second column
663             col = split.column()
664             row = col.row(align=True)
665             row.prop(linestyle, "use_split_length", text="")
666             sub = row.row()
667             sub.active = linestyle.use_split_length
668             sub.prop(linestyle, "split_length", text="2D Length")
669             row = col.row(align=True)
670             row.prop(linestyle, "material_boundary")
671             # End of columns
672             row = layout.row(align=True)
673             row.prop(linestyle, "use_split_pattern", text="")
674             sub = row.row(align=True)
675             sub.active = linestyle.use_split_pattern
676             sub.prop(linestyle, "split_dash1", text="D1")
677             sub.prop(linestyle, "split_gap1", text="G1")
678             sub.prop(linestyle, "split_dash2", text="D2")
679             sub.prop(linestyle, "split_gap2", text="G2")
680             sub.prop(linestyle, "split_dash3", text="D3")
681             sub.prop(linestyle, "split_gap3", text="G3")
682
683             ## Sorting
684             layout.prop(linestyle, "use_sorting", text="Sorting:")
685             col = layout.column()
686             col.active = linestyle.use_sorting
687             row = col.row(align=True)
688             row.prop(linestyle, "sort_key", text="")
689             sub = row.row()
690             sub.active = linestyle.sort_key in {'DISTANCE_FROM_CAMERA',
691                                                 'PROJECTED_X',
692                                                 'PROJECTED_Y'}
693             sub.prop(linestyle, "integration_type", text="")
694             row = col.row(align=True)
695             row.prop(linestyle, "sort_order", expand=True)
696
697             ## Selection
698             layout.label(text="Selection:")
699             split = layout.split(align=True)
700             # First column
701             col = split.column()
702             row = col.row(align=True)
703             row.prop(linestyle, "use_length_min", text="")
704             sub = row.row()
705             sub.active = linestyle.use_length_min
706             sub.prop(linestyle, "length_min")
707             row = col.row(align=True)
708             row.prop(linestyle, "use_length_max", text="")
709             sub = row.row()
710             sub.active = linestyle.use_length_max
711             sub.prop(linestyle, "length_max")
712             # Second column
713             col = split.column()
714             row = col.row(align=True)
715             row.prop(linestyle, "use_chain_count", text="")
716             sub = row.row()
717             sub.active = linestyle.use_chain_count
718             sub.prop(linestyle, "chain_count")
719
720             ## Caps
721             layout.label(text="Caps:")
722             row = layout.row(align=True)
723             row.prop(linestyle, "caps", expand=True)
724
725             ## Dashed lines
726             layout.prop(linestyle, "use_dashed_line", text="Dashed Line:")
727             row = layout.row(align=True)
728             row.active = linestyle.use_dashed_line
729             row.prop(linestyle, "dash1", text="D1")
730             row.prop(linestyle, "gap1", text="G1")
731             row.prop(linestyle, "dash2", text="D2")
732             row.prop(linestyle, "gap2", text="G2")
733             row.prop(linestyle, "dash3", text="D3")
734             row.prop(linestyle, "gap3", text="G3")
735
736         elif linestyle.panel == 'COLOR':
737             col = layout.column()
738             row = col.row()
739             row.label(text="Base Color:")
740             row.prop(linestyle, "color", text="")
741             col.label(text="Modifiers:")
742             col.operator_menu_enum("scene.freestyle_color_modifier_add", "type", text="Add Modifier")
743             for modifier in linestyle.color_modifiers:
744                 self.draw_color_modifier(context, modifier)
745
746         elif linestyle.panel == 'ALPHA':
747             col = layout.column()
748             row = col.row()
749             row.label(text="Base Transparency:")
750             row.prop(linestyle, "alpha")
751             col.label(text="Modifiers:")
752             col.operator_menu_enum("scene.freestyle_alpha_modifier_add", "type", text="Add Modifier")
753             for modifier in linestyle.alpha_modifiers:
754                 self.draw_alpha_modifier(context, modifier)
755
756         elif linestyle.panel == 'THICKNESS':
757             col = layout.column()
758             row = col.row()
759             row.label(text="Base Thickness:")
760             row.prop(linestyle, "thickness")
761             subcol = col.column()
762             subcol.active = linestyle.chaining == 'PLAIN' and linestyle.use_same_object
763             row = subcol.row()
764             row.prop(linestyle, "thickness_position", expand=True)
765             row = subcol.row()
766             row.prop(linestyle, "thickness_ratio")
767             row.active = (linestyle.thickness_position == 'RELATIVE')
768             col = layout.column()
769             col.label(text="Modifiers:")
770             col.operator_menu_enum("scene.freestyle_thickness_modifier_add", "type", text="Add Modifier")
771             for modifier in linestyle.thickness_modifiers:
772                 self.draw_thickness_modifier(context, modifier)
773
774         elif linestyle.panel == 'GEOMETRY':
775             col = layout.column()
776             col.label(text="Modifiers:")
777             col.operator_menu_enum("scene.freestyle_geometry_modifier_add", "type", text="Add Modifier")
778             for modifier in linestyle.geometry_modifiers:
779                 self.draw_geometry_modifier(context, modifier)
780
781         elif linestyle.panel == 'TEXTURE':
782             layout.separator()
783
784             row = layout.row()
785             if view_render.use_shading_nodes:
786                 row.prop(linestyle, "use_nodes")
787             else:
788                 row.prop(linestyle, "use_texture")
789             row.prop(linestyle, "texture_spacing", text="Spacing Along Stroke")
790
791             row = layout.row()
792             props = row.operator(
793                     "wm.properties_context_change",
794                     text="Go to Linestyle Textures Properties",
795                     icon='TEXTURE')
796             props.context = 'TEXTURE'
797
798         elif linestyle.panel == 'MISC':
799             pass
800
801
802 # Material properties
803
804 class MaterialFreestyleButtonsPanel:
805     bl_space_type = 'PROPERTIES'
806     bl_region_type = 'WINDOW'
807     bl_context = "material"
808     # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
809
810     @classmethod
811     def poll(cls, context):
812         scene = context.scene
813         material = context.material
814         with_freestyle = bpy.app.build_options.freestyle
815         return with_freestyle and material and scene and scene.render.use_freestyle and \
816             (scene.view_render.engine in cls.COMPAT_ENGINES)
817
818
819 class MATERIAL_PT_freestyle_line(MaterialFreestyleButtonsPanel, Panel):
820     bl_label = "Freestyle Line"
821     bl_options = {'DEFAULT_CLOSED'}
822     COMPAT_ENGINES = {'BLENDER_RENDER'}
823
824     def draw(self, context):
825         layout = self.layout
826
827         mat = context.material
828
829         row = layout.row()
830         row.prop(mat, "line_color", text="")
831         row.prop(mat, "line_priority", text="Priority")
832
833
834 classes = (
835     RENDER_PT_freestyle,
836     RENDERLAYER_UL_linesets,
837     RENDER_MT_lineset_specials,
838     RENDERLAYER_PT_freestyle,
839     RENDERLAYER_PT_freestyle_lineset,
840     RENDERLAYER_PT_freestyle_linestyle,
841     MATERIAL_PT_freestyle_line,
842 )
843
844 if __name__ == "__main__":  # only for live edit.
845     from bpy.utils import register_class
846     for cls in classes:
847         register_class(cls)