diff --git a/lib/elements/element.py b/lib/elements/element.py index 5c463c539..4ce47c794 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -27,7 +27,7 @@ from ..utils.cache import get_stitch_plan_cache, is_cache_disabled, CacheKeyGene class Param(object): def __init__(self, name, description, unit=None, values=[], type=None, group=None, inverse=False, - options=[], default=None, tooltip=None, sort_index=0, select_items=None): + options=[], default=None, tooltip=None, sort_index=0, select_items=None, enables=None): self.name = name self.description = description self.unit = unit @@ -40,6 +40,7 @@ class Param(object): self.tooltip = tooltip self.sort_index = sort_index self.select_items = select_items + self.enables = enables def __repr__(self): return "Param(%s)" % vars(self) diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py index f65c9c2bb..188c2ad7b 100644 --- a/lib/elements/fill_stitch.py +++ b/lib/elements/fill_stitch.py @@ -487,19 +487,20 @@ class FillStitch(EmbroideryElement): return max(self.get_float_param("running_stitch_tolerance_mm", 0.2), 0.01) @property - @param('enable_random_stitches', - _('Randomize stitches'), + @param('enable_random_stitch_length', + _('Randomize stitch length'), tooltip=_('Randomize stitch length and phase instead of dividing evenly or staggering. ' 'This is recommended for closely-spaced curved fills to avoid Moiré artefacts.'), type='boolean', + enables=['random_stitch_length_jitter_percent'], select_items=[('fill_method', 'auto_fill'), ('fill_method', 'contour_fill'), ('fill_method', 'guided_fill'), ('fill_method', 'circular_fill')], default=False, sort_index=44) - def enable_random_stitches(self): - return self.get_boolean_param('enable_random_stitches', False) + def enable_random_stitch_length(self): + return self.get_boolean_param('enable_random_stitch_length', False) @property @param('random_stitch_length_jitter_percent', @@ -1023,7 +1024,7 @@ class FillStitch(EmbroideryElement): ending_point, self.underpath, self.gap_fill_rows, - self.enable_random_stitches, + self.enable_random_stitch_length, self.random_stitch_length_jitter, self.random_seed, ) @@ -1049,7 +1050,7 @@ class FillStitch(EmbroideryElement): self.smoothness, starting_point, self.avoid_self_crossing, - self.enable_random_stitches, + self.enable_random_stitch_length, self.random_stitch_length_jitter, self.random_seed ) @@ -1059,7 +1060,7 @@ class FillStitch(EmbroideryElement): self.max_stitch_length, self.running_stitch_tolerance, starting_point, - self.enable_random_stitches, + self.enable_random_stitch_length, self.random_stitch_length_jitter, self.random_seed ) @@ -1069,7 +1070,7 @@ class FillStitch(EmbroideryElement): self.max_stitch_length, self.running_stitch_tolerance, starting_point, - self.enable_random_stitches, + self.enable_random_stitch_length, self.random_stitch_length_jitter, self.random_seed ) @@ -1110,7 +1111,7 @@ class FillStitch(EmbroideryElement): ending_point, self.underpath, self.guided_fill_strategy, - self.enable_random_stitches, + self.enable_random_stitch_length, self.random_stitch_length_jitter, self.random_seed, ) @@ -1164,7 +1165,7 @@ class FillStitch(EmbroideryElement): ending_point, self.underpath, target, - self.enable_random_stitches, + self.enable_random_stitch_length, self.random_stitch_length_jitter, self.random_seed, ) diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py index 0bb18ce8c..1be3953db 100644 --- a/lib/elements/stroke.py +++ b/lib/elements/stroke.py @@ -130,16 +130,17 @@ class Stroke(EmbroideryElement): return max(self.get_float_param("running_stitch_tolerance_mm", 0.2), 0.01) @property - @param('enable_random_stitches', - _('Randomize stitches'), + @param('enable_random_stitch_length', + _('Randomized stitch length'), tooltip=_('Randomize stitch length and phase instead of dividing evenly or staggering. ' 'This is recommended for closely-spaced curved fills to avoid Moiré artefacts.'), type='boolean', select_items=[('stroke_method', 'running_stitch'), ('stroke_method', 'ripple_stitch')], + enables=['random_stitch_length_jitter_percent'], default=False, sort_index=5) - def enable_random_stitches(self): - return self.get_boolean_param('enable_random_stitches', False) + def enable_random_stitch_length(self): + return self.get_boolean_param('enable_random_stitch_length', False) @property @param('random_stitch_length_jitter_percent', @@ -490,9 +491,9 @@ class Stroke(EmbroideryElement): return stitch_group - def running_stitch(self, path, stitch_length, tolerance, enable_random, random_sigma, random_seed): + def running_stitch(self, path, stitch_length, tolerance, enable_random_stitch_length, random_sigma, random_seed): # running stitch with repeats - stitches = running_stitch(path, stitch_length, tolerance, enable_random, random_sigma, random_seed) + stitches = running_stitch(path, stitch_length, tolerance, enable_random_stitch_length, random_sigma, random_seed) repeated_stitches = [] # go back and forth along the path as specified by self.repeats @@ -573,7 +574,7 @@ class Stroke(EmbroideryElement): # running stitch else: stitch_group = self.running_stitch(path, self.running_stitch_length, self.running_stitch_tolerance, - self.enable_random_stitches, self.random_stitch_length_jitter, self.random_seed) + self.enable_random_stitch_length, self.random_stitch_length_jitter, self.random_seed) # bean stitch if any(self.bean_stitch_repeats): stitch_group.stitches = self.do_bean_repeats(stitch_group.stitches) diff --git a/lib/extensions/params.py b/lib/extensions/params.py index ef4dc6168..b4efdc1bd 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -97,7 +97,8 @@ class ParamsTab(ScrolledPanel): if self.toggle: self.update_toggle_state() - # end wxGlade + + self.update_enable_widgets() def pair(self, tab): self.paired_tab = tab @@ -349,6 +350,28 @@ class ParamsTab(ScrolledPanel): widgets[3].Show(True) choice["last_initialized_choice"] = current_selection + def update_enable_widgets(self, event=None): + if event is None: + for param in self.params: + if param.enables is not None: + value = self.param_inputs[param.name].GetValue() + for item in param.enables: + enable_input = self.param_inputs[item] + enable_input.Enable(value) + enable_input.GetPrevSibling().Enable(value) + else: + input = event.GetEventObject() + param = [param for param in self.params if param.name == input.Name] + if not param: + return + param = param[0] + if param.enables is not None: + value = input.GetValue() + for item in param.enables: + enable_input = self.param_inputs[item] + enable_input.Enable(value) + enable_input.GetPrevSibling().Enable(value) + def __do_layout(self, only_settings_grid=False): # noqa: C901 # just to add space around the settings @@ -393,7 +416,8 @@ class ParamsTab(ScrolledPanel): input = wx.CheckBox(self) if param.values: input.SetValue(param.values[0]) - + input.SetName(param.name) + input.Bind(wx.EVT_CHECKBOX, self.update_enable_widgets) input.Bind(wx.EVT_CHECKBOX, self.changed) elif param.type == 'dropdown': input = wx.Choice(self, wx.ID_ANY, choices=param.options) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 5a6b7d91a..d3db978a2 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -80,7 +80,7 @@ def auto_fill(shape, ending_point=None, underpath=True, gap_fill_rows=0, - enable_random=False, + enable_random_stitch_length=False, random_sigma=0.0, random_seed=""): rows = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) @@ -108,7 +108,7 @@ def auto_fill(shape, path = fill_gaps(path, round_to_multiple_of_2(gap_fill_rows)) result = path_to_stitches(shape, path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, running_stitch_length, running_stitch_tolerance, - staggers, skip_last, underpath, enable_random, random_sigma, random_seed) + staggers, skip_last, underpath, enable_random_stitch_length, random_sigma, random_seed) return result @@ -852,7 +852,7 @@ def travel(shape, travel_graph, edge, running_stitch_length, running_stitch_tole @debug.time def path_to_stitches(shape, path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, running_stitch_length, - running_stitch_tolerance, staggers, skip_last, underpath, enable_random, random_sigma, random_seed): + running_stitch_tolerance, staggers, skip_last, underpath, enable_random_stitch_length, random_sigma, random_seed): path = collapse_sequential_outline_edges(path, fill_stitch_graph) stitches = [] @@ -864,7 +864,7 @@ def path_to_stitches(shape, path, travel_graph, fill_stitch_graph, angle, row_sp for i, edge in enumerate(path): if edge.is_segment(): stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers, skip_last, - enable_random, random_sigma, join_args(random_seed, i)) + enable_random_stitch_length, random_sigma, join_args(random_seed, i)) # note: gap fill segments won't be in the graph if fill_stitch_graph.has_edge(edge[0], edge[1], key='segment'): diff --git a/lib/stitches/circular_fill.py b/lib/stitches/circular_fill.py index 11fd04325..7be9469a5 100644 --- a/lib/stitches/circular_fill.py +++ b/lib/stitches/circular_fill.py @@ -27,7 +27,7 @@ def circular_fill(shape, ending_point, underpath, target, - use_random, + enable_random_stitch_length, running_stitch_length_jitter, random_seed, ): @@ -69,7 +69,7 @@ def circular_fill(shape, stitches = running_stitch([Stitch(*point) for point in path], running_stitch_length, running_stitch_tolerance, - use_random, + enable_random_stitch_length, running_stitch_length_jitter, random_seed) return _apply_bean_stitch_and_repeats(stitches, repeats, bean_stitch_repeats) @@ -81,7 +81,7 @@ def circular_fill(shape, coords = running_stitch([Point(*point) for point in line.coords], running_stitch_length, running_stitch_tolerance, - use_random, + enable_random_stitch_length, running_stitch_length_jitter, prng.join_args(random_seed, n)) segments.append([(point.x, point.y) for point in coords]) diff --git a/lib/stitches/contour_fill.py b/lib/stitches/contour_fill.py index 9eea90ab0..0665aacb5 100644 --- a/lib/stitches/contour_fill.py +++ b/lib/stitches/contour_fill.py @@ -412,7 +412,7 @@ def _find_path_inner_to_outer(tree, node, offset, starting_point, avoid_self_cro def inner_to_outer(tree, polygon, offset, stitch_length, tolerance, smoothness, starting_point, avoid_self_crossing, - enable_random, random_sigma, random_seed): + enable_random_stitch_length, random_sigma, random_seed): """Fill a shape with spirals, from innermost to outermost.""" stitch_path = _find_path_inner_to_outer(tree, 'root', offset, starting_point, avoid_self_crossing) @@ -422,7 +422,7 @@ def inner_to_outer(tree, polygon, offset, smoothed = smooth_path(points, smoothness) points = clamp_path_to_polygon(smoothed, polygon) - stitches = running_stitch(points, stitch_length, tolerance, enable_random, random_sigma, random_seed) + stitches = running_stitch(points, stitch_length, tolerance, enable_random_stitch_length, random_sigma, random_seed) return stitches @@ -518,24 +518,24 @@ def _check_and_prepare_tree_for_valid_spiral(tree): return process_node('root') -def single_spiral(tree, stitch_length, tolerance, starting_point, enable_random, random_sigma, random_seed): +def single_spiral(tree, stitch_length, tolerance, starting_point, enable_random_stitch_length, random_sigma, random_seed): """Fill a shape with a single spiral going from outside to center.""" - return _spiral_fill(tree, stitch_length, tolerance, starting_point, enable_random, random_sigma, random_seed, _make_spiral) + return _spiral_fill(tree, stitch_length, tolerance, starting_point, enable_random_stitch_length, random_sigma, random_seed, _make_spiral) -def double_spiral(tree, stitch_length, tolerance, starting_point, enable_random, random_sigma, random_seed): +def double_spiral(tree, stitch_length, tolerance, starting_point, enable_random_stitch_length, random_sigma, random_seed): """Fill a shape with a double spiral going from outside to center and back to outside. """ - return _spiral_fill(tree, stitch_length, tolerance, starting_point, enable_random, random_sigma, random_seed, _make_fermat_spiral) + return _spiral_fill(tree, stitch_length, tolerance, starting_point, enable_random_stitch_length, random_sigma, random_seed, _make_fermat_spiral) -def _spiral_fill(tree, stitch_length, tolerance, close_point, enable_random, random_sigma, random_seed, spiral_maker): +def _spiral_fill(tree, stitch_length, tolerance, close_point, enable_random_stitch_length, random_sigma, random_seed, spiral_maker): starting_point = close_point.coords[0] rings = _get_spiral_rings(tree) path = spiral_maker(rings, stitch_length, starting_point) path = [Stitch(*stitch) for stitch in path] - return running_stitch(path, stitch_length, tolerance, enable_random, random_sigma, random_seed) + return running_stitch(path, stitch_length, tolerance, enable_random_stitch_length, random_sigma, random_seed) def _get_spiral_rings(tree): diff --git a/lib/stitches/fill.py b/lib/stitches/fill.py index 59501a9e2..4f3e2208c 100644 --- a/lib/stitches/fill.py +++ b/lib/stitches/fill.py @@ -52,12 +52,13 @@ def adjust_stagger(stitch, angle, row_spacing, max_stitch_length, staggers): return stitch - offset * east(angle) -def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers, skip_last, enable_random, random_sigma, random_seed): +def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers, skip_last, + enable_random_stitch_length, random_sigma, random_seed): beg = Stitch(*beg, tags=('fill_row_start',)) end = Stitch(*end, tags=('fill_row_start',)) stitches.append(beg) - if enable_random: + if enable_random_stitch_length: stitches += split_segment_random_phase(beg, end, max_stitch_length, random_sigma, random_seed) else: # We want our stitches to look like this: diff --git a/lib/stitches/guided_fill.py b/lib/stitches/guided_fill.py index f170f0247..dcf9971a0 100644 --- a/lib/stitches/guided_fill.py +++ b/lib/stitches/guided_fill.py @@ -35,12 +35,12 @@ def guided_fill(shape, ending_point, underpath, strategy, - enable_random, + enable_random_stitch_length, random_sigma, random_seed, ): segments = intersect_region_with_grating_guideline(shape, guideline, row_spacing, num_staggers, max_stitch_length, strategy, - enable_random, running_stitch_tolerance, random_sigma, random_seed,) + enable_random_stitch_length, running_stitch_tolerance, random_sigma, random_seed,) if not segments: return fallback(shape, guideline, row_spacing, max_stitch_length, running_stitch_length, running_stitch_tolerance, num_staggers, skip_last, starting_point, ending_point, underpath) @@ -239,7 +239,7 @@ def _get_start_row(line, shape, row_spacing, line_direction): def intersect_region_with_grating_guideline(shape, line, row_spacing, num_staggers, max_stitch_length, strategy, - enable_random, tolerance, random_sigma, random_seed): + enable_random_stitch_length, tolerance, random_sigma, random_seed): line = prepare_guide_line(line, shape) debug.log_line_string(shape.exterior, "guided fill shape") @@ -271,7 +271,7 @@ def intersect_region_with_grating_guideline(shape, line, row_spacing, num_stagge debug.log_line_string(offset_line, f"offset {row}") - if enable_random: + if enable_random_stitch_length: points = [InkstitchPoint(*x) for x in offset_line.coords] stitched_line = shgeo.LineString(random_running_stitch( points, max_stitch_length, tolerance, random_sigma, prng.join_args(random_seed, row))) diff --git a/lib/stitches/ripple_stitch.py b/lib/stitches/ripple_stitch.py index 02dbdeb29..a354166c9 100644 --- a/lib/stitches/ripple_stitch.py +++ b/lib/stitches/ripple_stitch.py @@ -50,7 +50,7 @@ def _get_stitches(stroke, is_linear, lines, skip_start): return running_stitch(points, stroke.running_stitch_length, stroke.running_stitch_tolerance, - stroke.enable_random_stitches, + stroke.enable_random_stitch_length, stroke.random_stitch_length_jitter, stroke.random_seed) @@ -59,7 +59,7 @@ def _get_staggered_stitches(stroke, lines, skip_start): stitches = [] stitch_length = stroke.running_stitch_length tolerance = stroke.running_stitch_tolerance - enable_random = stroke.enable_random_stitches + enable_random_stitch_length = stroke.enable_random_stitch_length length_sigma = stroke.random_stitch_length_jitter random_seed = stroke.random_seed last_point = None @@ -77,10 +77,10 @@ def _get_staggered_stitches(stroke, lines, skip_start): elif stroke.join_style == 1: should_reverse = (i + skip_start) % 2 == 1 - if enable_random or stroke.staggers == 0: + if enable_random_stitch_length or stroke.staggers == 0: if should_reverse: line.reverse() - points = running_stitch(line, stitch_length, tolerance, enable_random, length_sigma, prng.join_args(random_seed, i)) + points = running_stitch(line, stitch_length, tolerance, enable_random_stitch_length, length_sigma, prng.join_args(random_seed, i)) stitched_line = connector + points else: # uses the guided fill alforithm to stagger rows of stitches diff --git a/lib/svg/rendering.py b/lib/svg/rendering.py index a37073fb6..ee0b5d0c8 100644 --- a/lib/svg/rendering.py +++ b/lib/svg/rendering.py @@ -56,11 +56,12 @@ stitch_path = ( "l-0.55,-0.1,0.55,0.1" # Bottom-left whisker "z") # return to start + def generate_realistic_filter() -> inkex.BaseElement: """ Return a copy of the realistic stitch filter, ready to add to svg defs. """ - filter = inkex.Filter(attrib = { + filter = inkex.Filter(attrib={ "style": "color-interpolation-filters:sRGB", "id": "realistic-stitch-filter", "x": "0", @@ -71,27 +72,27 @@ def generate_realistic_filter() -> inkex.BaseElement: }) filter.add( - inkex.Filter.GaussianBlur(attrib = { + inkex.Filter.GaussianBlur(attrib={ "edgeMode": "none", "stdDeviation": "0.9", "in": "SourceAlpha", }), inkex.Filter.SpecularLighting( - inkex.Filter.DistantLight(attrib = { + inkex.Filter.DistantLight(attrib={ "azimuth": "-125", "elevation": "20", - }), attrib = { + }), attrib={ "result": "result2", "surfaceScale": "1.5", "specularConstant": "0.78", "specularExponent": "2.5", } ), - inkex.Filter.Composite(attrib = { + inkex.Filter.Composite(attrib={ "in2": "SourceAlpha", "operator": "atop", }), - inkex.Filter.Composite(attrib = { + inkex.Filter.Composite(attrib={ "in2": "SourceGraphic", "operator": "arithmetic", "result": "result3", diff --git a/lib/svg/tags.py b/lib/svg/tags.py index 089fee1ac..470288a2e 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -106,7 +106,7 @@ inkstitch_attribs = [ 'rows_per_thread', 'herringbone_width_mm', 'tartan_angle', - 'enable_random_stitches', + 'enable_random_stitch_length', 'random_stitch_length_jitter_percent', 'gap_fill_rows', # stroke