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