From dd633f1affc8951a0a6ba19cdffbb58ac7f54acd Mon Sep 17 00:00:00 2001 From: Tamito Kajiyama Date: Sun, 4 Nov 2012 23:52:26 +0000 Subject: [PATCH] Fix for dashed line options not working as expected in some applications when combined with geometry modifiers. The problem is that users were not able to choose the time when the dashed line options are applied. Instead, the dashed line options were applied only before geometry modifiers were employed. Since dashes were separate strokes, the geometry modifiers were processed dash by dash. Depending on users' artistic intention, this may or may not lead to expected stylization results, as reported by octane98 in the BlenderArtists Freestyle thread on January 3, 2012. http://blenderartists.org/forum/showthread.php?89986-Freestyle-for-Blender&p=2018592&viewfull=1#post2018592 Now the Strokes tab of the Freestyle Line Style panel has two sets of dashed line options. One is in the Splitting section of the Strokes tab and used for splitting strokes by dashed line patterns. The other set is called "Dashed Line" and used to generate dashed lines based on the strokes after the geometry modifiers are applied. The two sets of dashed line options are independent of each other, so that users can enable one of them as well as both at the same time. --- .../style_modules/parameter_editor.py | 89 ++++++++++++++----- .../startup/bl_ui/properties_render_layer.py | 10 +++ source/blender/makesdna/DNA_linestyle_types.h | 5 ++ .../blender/makesrna/intern/rna_linestyle.c | 53 +++++++++-- 4 files changed, 129 insertions(+), 28 deletions(-) diff --git a/release/scripts/freestyle/style_modules/parameter_editor.py b/release/scripts/freestyle/style_modules/parameter_editor.py index e817771732a..71d55e9a541 100644 --- a/release/scripts/freestyle/style_modules/parameter_editor.py +++ b/release/scripts/freestyle/style_modules/parameter_editor.py @@ -762,6 +762,8 @@ class RoundCapShader(StrokeShader): r = self.round_cap_thickness((nverts_end - i + 1) * n) stroke[-i-1].setAttribute(attr) stroke[-i-1].attribute().setThickness(R * r, L * r) + # update the curvilinear 2D length of each vertex + stroke.UpdateLength() class SquareCapShader(StrokeShader): def shade(self, stroke): @@ -798,24 +800,26 @@ class SquareCapShader(StrokeShader): d = p - q stroke[-1].setPoint(p + d / d.length * caplen_beg) stroke[-1].setAttribute(attr) + # update the curvilinear 2D length of each vertex + stroke.UpdateLength() -# dashed line +# Split by dashed line pattern -class DashedLineStartingUP0D(UnaryPredicate0D): +class SplitPatternStartingUP0D(UnaryPredicate0D): def __init__(self, controller): UnaryPredicate0D.__init__(self) self._controller = controller def __call__(self, inter): return self._controller.start() -class DashedLineStoppingUP0D(UnaryPredicate0D): +class SplitPatternStoppingUP0D(UnaryPredicate0D): def __init__(self, controller): UnaryPredicate0D.__init__(self) self._controller = controller def __call__(self, inter): return self._controller.stop() -class DashedLineController: +class SplitPatternController: def __init__(self, pattern, sampling): self.sampling = float(sampling) k = len(pattern) // 2 @@ -845,6 +849,35 @@ class DashedLineController: return True return False +# Dashed line + +class DashedLineShader(StrokeShader): + def __init__(self, pattern): + StrokeShader.__init__(self) + self._pattern = pattern + def getName(self): + return "DashedLineShader" + def shade(self, stroke): + index = 0 # pattern index + start = 0.0 # 2D curvilinear length + visible = True + sampling = 1.0 + it = stroke.strokeVerticesBegin(sampling) + while not it.isEnd(): + pos = it.t() + # The extra 'sampling' term is added below, because the + # visibility attribute of the i-th vertex refers to the + # visibility of the stroke segment between the i-th and + # (i+1)-th vertices. + if pos - start + sampling > self._pattern[index]: + start = pos + index += 1 + if index == len(self._pattern): + index = 0 + visible = not visible + it.getObject().attribute().setVisible(visible) + it.increment() + # predicates for chaining class AngleLargerThanBP1D(BinaryPredicate1D): @@ -1117,29 +1150,28 @@ def process(layer_name, lineset_name): Operators.sequentialSplit(Curvature2DAngleThresholdUP0D(min_angle, max_angle)) if linestyle.use_split_length: Operators.sequentialSplit(Length2DThresholdUP0D(linestyle.split_length), 1.0) + if linestyle.use_split_pattern: + pattern = [] + if linestyle.split_dash1 > 0 and linestyle.split_gap1 > 0: + pattern.append(linestyle.split_dash1) + pattern.append(linestyle.split_gap1) + if linestyle.split_dash2 > 0 and linestyle.split_gap2 > 0: + pattern.append(linestyle.split_dash2) + pattern.append(linestyle.split_gap2) + if linestyle.split_dash3 > 0 and linestyle.split_gap3 > 0: + pattern.append(linestyle.split_dash3) + pattern.append(linestyle.split_gap3) + if len(pattern) > 0: + sampling = 1.0 + controller = SplitPatternController(pattern, sampling) + Operators.sequentialSplit(SplitPatternStartingUP0D(controller), + SplitPatternStoppingUP0D(controller), + sampling) # select chains if linestyle.use_min_length or linestyle.use_max_length: min_length = linestyle.min_length if linestyle.use_min_length else None max_length = linestyle.max_length if linestyle.use_max_length else None Operators.select(LengthThresholdUP1D(min_length, max_length)) - # dashed line - if linestyle.use_dashed_line: - pattern = [] - if linestyle.dash1 > 0 and linestyle.gap1 > 0: - pattern.append(linestyle.dash1) - pattern.append(linestyle.gap1) - if linestyle.dash2 > 0 and linestyle.gap2 > 0: - pattern.append(linestyle.dash2) - pattern.append(linestyle.gap2) - if linestyle.dash3 > 0 and linestyle.gap3 > 0: - pattern.append(linestyle.dash3) - pattern.append(linestyle.gap3) - if len(pattern) > 0: - sampling = 1.0 - controller = DashedLineController(pattern, sampling) - Operators.sequentialSplit(DashedLineStartingUP0D(controller), - DashedLineStoppingUP0D(controller), - sampling) # prepare a list of stroke shaders shaders_list = [] for m in linestyle.geometry_modifiers: @@ -1269,5 +1301,18 @@ def process(layer_name, lineset_name): shaders_list.append(RoundCapShader()) elif linestyle.caps == "SQUARE": shaders_list.append(SquareCapShader()) + if linestyle.use_dashed_line: + pattern = [] + if linestyle.dash1 > 0 and linestyle.gap1 > 0: + pattern.append(linestyle.dash1) + pattern.append(linestyle.gap1) + if linestyle.dash2 > 0 and linestyle.gap2 > 0: + pattern.append(linestyle.dash2) + pattern.append(linestyle.gap2) + if linestyle.dash3 > 0 and linestyle.gap3 > 0: + pattern.append(linestyle.dash3) + pattern.append(linestyle.gap3) + if len(pattern) > 0: + shaders_list.append(DashedLineShader(pattern)) # create strokes using the shaders list Operators.create(TrueUP1D(), shaders_list) diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py index 940d5f0d631..631c636a783 100644 --- a/release/scripts/startup/bl_ui/properties_render_layer.py +++ b/release/scripts/startup/bl_ui/properties_render_layer.py @@ -620,6 +620,7 @@ class RENDERLAYER_PT_freestyle_linestyle(RenderLayerButtonsPanel, Panel): subsub = sub.row() subsub.enabled = linestyle.use_max_angle subsub.prop(linestyle, "max_angle") + col.prop(linestyle, "use_split_pattern", text="Split Pattern") col = row.column() sub = col.row(align=True) sub.prop(linestyle, "use_split_length", text="") @@ -627,6 +628,15 @@ class RENDERLAYER_PT_freestyle_linestyle(RenderLayerButtonsPanel, Panel): subsub.enabled = linestyle.use_split_length subsub.prop(linestyle, "split_length", text="2D Length") col.prop(linestyle, "material_boundary") + row = layout.row(align=True) + row.enabled = linestyle.use_split_pattern + row.prop(linestyle, "split_dash1") + row.prop(linestyle, "split_gap1") + row.prop(linestyle, "split_dash2") + row.prop(linestyle, "split_gap2") + row.prop(linestyle, "split_dash3") + row.prop(linestyle, "split_gap3") + # Selection layout.label(text="Selection:") row = layout.row() diff --git a/source/blender/makesdna/DNA_linestyle_types.h b/source/blender/makesdna/DNA_linestyle_types.h index 9ffb53e83b6..2507b072a2f 100644 --- a/source/blender/makesdna/DNA_linestyle_types.h +++ b/source/blender/makesdna/DNA_linestyle_types.h @@ -390,6 +390,7 @@ typedef struct LineStyleThicknessModifier_Calligraphy { #define LS_MIN_2D_ANGLE 128 #define LS_MAX_2D_ANGLE 256 #define LS_SPLIT_LENGTH 512 +#define LS_SPLIT_PATTERN 1024 /* FreestyleLineStyle::chaining */ #define LS_CHAINING_PLAIN 1 @@ -420,6 +421,10 @@ typedef struct FreestyleLineStyle { float split_length; float min_angle, max_angle; /* for splitting */ float min_length, max_length; + unsigned short split_dash1, split_gap1; + unsigned short split_dash2, split_gap2; + unsigned short split_dash3, split_gap3; + int pad; unsigned short dash1, gap1, dash2, gap2, dash3, gap3; int panel; /* for UI */ diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c index 62d69ee8732..fbe8eae31b3 100644 --- a/source/blender/makesrna/intern/rna_linestyle.c +++ b/source/blender/makesrna/intern/rna_linestyle.c @@ -1019,6 +1019,47 @@ static void rna_def_linestyle(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Max 2D Length", "Maximum curvilinear 2D length for the selection of chains"); RNA_def_property_update(prop, NC_LINESTYLE, NULL); + prop= RNA_def_property(srna, "use_split_pattern", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", LS_SPLIT_PATTERN); + RNA_def_property_ui_text(prop, "Use Split Pattern", "Enable chain splitting by dashed line patterns"); + RNA_def_property_update(prop, NC_LINESTYLE, NULL); + + prop= RNA_def_property(srna, "split_dash1", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "split_dash1"); + RNA_def_property_range(prop, 0, USHRT_MAX); + RNA_def_property_ui_text(prop, "Split Dash 1", "Length of the 1st dash for splitting"); + RNA_def_property_update(prop, NC_LINESTYLE, NULL); + + prop= RNA_def_property(srna, "split_gap1", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "split_gap1"); + RNA_def_property_range(prop, 0, USHRT_MAX); + RNA_def_property_ui_text(prop, "Split Gap 1", "Length of the 1st gap for splitting"); + RNA_def_property_update(prop, NC_LINESTYLE, NULL); + + prop= RNA_def_property(srna, "split_dash2", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "split_dash2"); + RNA_def_property_range(prop, 0, USHRT_MAX); + RNA_def_property_ui_text(prop, "Split Dash 2", "Length of the 2nd dash for splitting"); + RNA_def_property_update(prop, NC_LINESTYLE, NULL); + + prop= RNA_def_property(srna, "split_gap2", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "split_gap2"); + RNA_def_property_range(prop, 0, USHRT_MAX); + RNA_def_property_ui_text(prop, "Split Gap 2", "Length of the 2nd gap for splitting"); + RNA_def_property_update(prop, NC_LINESTYLE, NULL); + + prop= RNA_def_property(srna, "split_dash3", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "split_dash3"); + RNA_def_property_range(prop, 0, USHRT_MAX); + RNA_def_property_ui_text(prop, "Split Dash 3", "Length of the 3rd dash for splitting"); + RNA_def_property_update(prop, NC_LINESTYLE, NULL); + + prop= RNA_def_property(srna, "split_gap3", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "split_gap3"); + RNA_def_property_range(prop, 0, USHRT_MAX); + RNA_def_property_ui_text(prop, "Split Gap 3", "Length of the 3rd gap for splitting"); + RNA_def_property_update(prop, NC_LINESTYLE, NULL); + prop= RNA_def_property(srna, "material_boundary", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", LS_MATERIAL_BOUNDARY); RNA_def_property_ui_text(prop, "Material Boundary", "If true, chains of feature edges are split at material boundaries"); @@ -1038,37 +1079,37 @@ static void rna_def_linestyle(BlenderRNA *brna) prop= RNA_def_property(srna, "dash1", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "dash1"); RNA_def_property_range(prop, 0, USHRT_MAX); - RNA_def_property_ui_text(prop, "Dash 1", "Length of the 1st dash"); + RNA_def_property_ui_text(prop, "Dash 1", "Length of the 1st dash for dashed lines"); RNA_def_property_update(prop, NC_LINESTYLE, NULL); prop= RNA_def_property(srna, "gap1", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "gap1"); RNA_def_property_range(prop, 0, USHRT_MAX); - RNA_def_property_ui_text(prop, "Gap 1", "Length of the 1st gap"); + RNA_def_property_ui_text(prop, "Gap 1", "Length of the 1st gap for dashed lines"); RNA_def_property_update(prop, NC_LINESTYLE, NULL); prop= RNA_def_property(srna, "dash2", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "dash2"); RNA_def_property_range(prop, 0, USHRT_MAX); - RNA_def_property_ui_text(prop, "Dash 2", "Length of the 2nd dash"); + RNA_def_property_ui_text(prop, "Dash 2", "Length of the 2nd dash for dashed lines"); RNA_def_property_update(prop, NC_LINESTYLE, NULL); prop= RNA_def_property(srna, "gap2", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "gap2"); RNA_def_property_range(prop, 0, USHRT_MAX); - RNA_def_property_ui_text(prop, "Gap 2", "Length of the 2nd gap"); + RNA_def_property_ui_text(prop, "Gap 2", "Length of the 2nd gap for dashed lines"); RNA_def_property_update(prop, NC_LINESTYLE, NULL); prop= RNA_def_property(srna, "dash3", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "dash3"); RNA_def_property_range(prop, 0, USHRT_MAX); - RNA_def_property_ui_text(prop, "Dash 3", "Length of the 3rd dash"); + RNA_def_property_ui_text(prop, "Dash 3", "Length of the 3rd dash for dashed lines"); RNA_def_property_update(prop, NC_LINESTYLE, NULL); prop= RNA_def_property(srna, "gap3", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "gap3"); RNA_def_property_range(prop, 0, USHRT_MAX); - RNA_def_property_ui_text(prop, "Gap 3", "Length of the 3rd gap"); + RNA_def_property_ui_text(prop, "Gap 3", "Length of the 3rd gap for dashed lines"); RNA_def_property_update(prop, NC_LINESTYLE, NULL); }