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