From bbd7391b181673eb86e9f3dd88066b9da34ba7ba Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 15 Oct 2023 07:06:43 +0200 Subject: Test Swatches: Reduce length of param list by checking for element type (#2545) --- lib/extensions/test_swatches.py | 122 ++++++++++++++++++-------- lib/gui/test_swatches.py | 189 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 276 insertions(+), 35 deletions(-) create mode 100644 lib/gui/test_swatches.py (limited to 'lib') diff --git a/lib/extensions/test_swatches.py b/lib/extensions/test_swatches.py index a5b12d2f..b195f30b 100644 --- a/lib/extensions/test_swatches.py +++ b/lib/extensions/test_swatches.py @@ -5,8 +5,10 @@ from inkex import errormsg +from ..elements import FillStitch, SatinColumn, Stroke +from ..gui.test_swatches import GenerateSwatchesApp from ..i18n import _ -from ..svg.tags import EMBROIDERABLE_TAGS +from ..utils.param import ParamOption from .base import InkstitchExtension @@ -14,42 +16,92 @@ class TestSwatches(InkstitchExtension): ''' This generates swatches from selection by altering one param each time. ''' - def __init__(self, *args, **kwargs): - InkstitchExtension.__init__(self, *args, **kwargs) - self.arg_parser.add_argument("--options") - self.arg_parser.add_argument("--info") - - self.arg_parser.add_argument("-p", "--param", type=str, default="max_stitch_length_mm", dest="param") - self.arg_parser.add_argument("-s", "--start-value", type=float, default="max_stitch_length_mm", dest="start_value") - self.arg_parser.add_argument("-i", "--step", type=float, default="0.5", dest="step") - self.arg_parser.add_argument("-r", "--num-rows", type=int, default="5", dest="num_rows") - self.arg_parser.add_argument("-c", "--num-cols", type=int, default="5", dest="num_cols") - self.arg_parser.add_argument("-d", "--spacing", type=float, default="1", dest="spacing") def effect(self): - if not self.svg.selection: + if not self.svg.selection or not self.get_elements(): errormsg(_("Please select one or more elements.")) return - for element in self.svg.selection: - dimensions = element.bounding_box() - param_value = self.options.start_value - for rows in range(0, self.options.num_rows): - for cols in range(0, self.options.num_cols): - new_element = element.duplicate() - translate_x = cols * dimensions.width + cols * self.options.spacing - translate_y = rows * dimensions.height + rows * self.options.spacing - new_element.transform.add_translate((translate_x, translate_y)) - if new_element.TAG == "g": - for embroidery_element in new_element.iterdescendants(EMBROIDERABLE_TAGS): - # Since this won't effect functionality, we can simply ignore the fact - # that this will also set the value to guide lines etc. - self._set_param(embroidery_element, param_value) - else: - self._set_param(new_element, param_value) - param_value += self.options.step - # remove old element - element.getparent().remove(element) - - def _set_param(self, element, value): - element.set(f'inkstitch:{ self.options.param }', value) + choices = [] + + fill_added = False + stroke_added = False + satin_added = False + + for element in self.elements: + if not fill_added and isinstance(element, FillStitch): + choices.extend(fill_choices) + fill_added = True + elif not satin_added and isinstance(element, SatinColumn): + choices.extend(satin_choices) + satin_added = True + elif not stroke_added and isinstance(element, Stroke): + choices.extend(stroke_choices) + stroke_added = True + + app = GenerateSwatchesApp(self, choices) + app.MainLoop() + + +stroke_choices = [ + ParamOption("running_stitch_tolerance_mm", "Running Stitch Tolerance mm"), + ParamOption("repeats", "Repeats"), + ParamOption("running_stitch_length_mm", "Running Stitch Length mm"), + ParamOption("bean_stitch_repeats", "Bean Stitch Repeats (Bean Stitch)"), + ParamOption("exponent", "Exponent (Ripple)"), + ParamOption("grid_size_mm", "Grid Size mm (Ripple)"), + ParamOption("line_count", "Line Count (Ripple)"), + ParamOption("min_line_dist_mm", "Minimum Line Distanse mm (Ripple)"), + ParamOption("scale_end", "Scale End (Ripple)"), + ParamOption("scale_start", "Scale Start (Ripple)"), + ParamOption("skip_end", "Skip End (Ripple)"), + ParamOption("skip_start", "Skip Start (Ripple)"), + ParamOption("max_stitch_length_mm", "Maximum Stitch Length mm (Manual Stitch)"), + ParamOption("pull_compensation_mm", "Pull Compensation mm (ZigZag)"), + ParamOption("zigzag_spacing_mm", "Zigzag Spacing mm (Satin, Zigzag)") +] + +fill_choices = [ + ParamOption("running_stitch_length_mm", "Running Stitch Length mm"), + ParamOption("running_stitch_tolerance_mm", "Running Stitch Tolerance mm"), + ParamOption("max_stitch_length_mm", "Maximum Stitch Length mm (Auto Fill, Contour Fill, Guided Fill)"), + ParamOption("angle", "Angle (Auto Fill)"), + ParamOption("row_spacing_mm", "Row Spacing mm"), + ParamOption("end_row_spacing_mm", "End Row Spacing mm"), + ParamOption("expand_mm", "Expand mm"), + ParamOption("repeats", "Repeats (Circular Fill, Meander Fill)"), + ParamOption("bean_stitch_repeats", "Bean Stitch Repeats (Circular Fill, Meander Fill)"), + ParamOption("meander_angle", "Meander Pattern: Angle (Meander Fill)"), + ParamOption("meander_scale_percent", "Meander Pattern: Scale Percent (Meander Fill)"), + ParamOption("smoothness_mm", "Smoothness mm (Contour Fill, Meander Fill)"), + ParamOption("staggers", "Staggers (Auto Fill, Fuided Fill"), + ParamOption("fill_underlay_angle", "Fill Underlay: Angle"), + ParamOption("fill_underlay_inset_mm", "Fill Underlay: Inset mm"), + ParamOption("fill_underlay_max_stitch_length_mm", "Fill Underlay: Maximum Stitch Length mm"), + ParamOption("fill_underlay_row_spacing_mm", "Fill Underlay: Row Spacing mm") +] + +satin_choices = [ + ParamOption("zigzag_spacing_mm", "Zigzag Spacing mm"), + ParamOption("random_zigzag_spacing_percent", "Random Zigzag Spacing percent"), + ParamOption("pull_compensation_mm", "Pull Compensation mm"), + ParamOption("pull_compensation_percent", "Pull Compensation percent"), + ParamOption("random_width_decrease_percent", "Random Width Decrease percent"), + ParamOption("random_width_increase_percent", "Random width Increase percent"), + ParamOption("short_stitch_inset", "Short Stitch: Inset"), + ParamOption("max_stitch_length_mm", "Maximum Stitch Length mm"), + ParamOption("split_staggers", "Split Staggers"), + ParamOption("short_stitch_distance_mm", "Short Stitch: Distance mm"), + ParamOption("min_random_split_length_mm", "Minimum Random Split Length mm"), + ParamOption("random_split_jitter_percent", "Random Split Jitter percent"), + ParamOption("center_walk_underlay_position", "Center Walk Underlay: Position"), + ParamOption("center_walk_underlay_repeats", "Center Walk Underlay: Repeats"), + ParamOption("center_walk_underlay_stitch_length_mm", "center Walk Underlay: Stitch Length mm"), + ParamOption("contour_underlay_inset_mm", "Contour Underlay: Inset mm"), + ParamOption("contour_underlay_inset_percent", "Contour Underlay: Inset percent"), + ParamOption("contour_underlay_stitch_length_mm", "Contour Underlay: Stitch Length mm"), + ParamOption("zigzag_underlay_inset_mm", "Zigzag Underlay: Inset_mm"), + ParamOption("zigzag_underlay_inset_percent", "Zigzag Underlay: Inset percent"), + ParamOption("zigzag_underlay_max_stitch_length_mm", "ZigZag Underlay: Maximum Stitch Length mm"), + ParamOption("zigzag_underlay_spacing_mm", "Zigzag Underlay: Spacing mm") +] diff --git a/lib/gui/test_swatches.py b/lib/gui/test_swatches.py new file mode 100644 index 00000000..b315a8f4 --- /dev/null +++ b/lib/gui/test_swatches.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# +# Authors: see git history +# +# Copyright (c) 2023 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +import wx +import wx.adv + +from ..i18n import _ +from ..svg.tags import EMBROIDERABLE_TAGS + + +class GenerateSwatchesFrame(wx.Frame): + def __init__(self, *args, **kwargs): + self.extension = kwargs.pop("extension") + self.choices = kwargs.pop("choices") + wx.Frame.__init__(self, *args, **kwargs) + wx.Frame.__init__(self, None, wx.ID_ANY, _("Generate Swatches"), *args, **kwargs) + + self.panel = wx.Panel(self, wx.ID_ANY) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + + self.notebook = wx.Notebook(self.panel, wx.ID_ANY) + main_sizer.Add(self.notebook, 0, wx.ALL | wx.EXPAND, 10) + + self.options = wx.Panel(self.notebook, wx.ID_ANY) + self.notebook.AddPage(self.options, _("Options")) + + options_sizer = wx.BoxSizer(wx.VERTICAL) + + grid_main_sizer = wx.FlexGridSizer(6, 2, 10, 10) + options_sizer.Add(grid_main_sizer, 0, wx.ALL, 10) + + param_label = wx.StaticText(self.options, wx.ID_ANY, _("Param")) + grid_main_sizer.Add(param_label, 0, 0, 0) + + self.param = wx.ComboBox(self.options, wx.ID_ANY, choices=[]) + for choice in self.choices: + self.param.Append(choice.name, choice) + self.param.SetSelection(0) + grid_main_sizer.Add(self.param, 0, 0, 0) + + start_value_label = wx.StaticText(self.options, wx.ID_ANY, _("Start Value")) + grid_main_sizer.Add(start_value_label, 0, 0, 0) + + self.start_value = wx.SpinCtrlDouble(self.options, wx.ID_ANY, initial=2.5, min=0.0, max=500.0) + self.start_value.SetDigits(2) + grid_main_sizer.Add(self.start_value, 0, 0, 0) + + step_label = wx.StaticText(self.options, wx.ID_ANY, _("Increase by")) + grid_main_sizer.Add(step_label, 0, 0, 0) + + self.step = wx.SpinCtrlDouble(self.options, wx.ID_ANY, initial=0.5, min=0.01, max=500.0) + self.step.SetDigits(2) + grid_main_sizer.Add(self.step, 0, 0, 0) + + cols_label = wx.StaticText(self.options, wx.ID_ANY, _("Columns")) + grid_main_sizer.Add(cols_label, 0, 0, 0) + + self.columns = wx.SpinCtrl(self.options, wx.ID_ANY, "0", min=1, max=100) + grid_main_sizer.Add(self.columns, 0, 0, 0) + + rows_label = wx.StaticText(self.options, wx.ID_ANY, _("Rows")) + grid_main_sizer.Add(rows_label, 0, 0, 0) + + self.rows = wx.SpinCtrl(self.options, wx.ID_ANY, "0", min=1, max=100) + grid_main_sizer.Add(self.rows, 0, 0, 0) + + spacing_label = wx.StaticText(self.options, wx.ID_ANY, _("Spacing")) + grid_main_sizer.Add(spacing_label, 0, 0, 0) + + self.spacing = wx.SpinCtrlDouble(self.options, wx.ID_ANY, initial=5, min=0.0, max=100.0) + self.spacing.SetDigits(2) + grid_main_sizer.Add(self.spacing, 0, 0, 0) + + self.info = wx.Panel(self.notebook, wx.ID_ANY) + self.notebook.AddPage(self.info, _("Help")) + + info_sizer = wx.BoxSizer(wx.VERTICAL) + + info_text_label = wx.StaticText( + self.info, + wx.ID_ANY, + _("This extension generates test swatches from a selection.\n" + "Test swatches help to find best stitch settings for your design.\n" + "Sew them out with the same thread and fabric as the final designs."), + style=wx.ALIGN_LEFT + ) + info_text_label.Wrap(500) + info_sizer.Add(info_text_label, 0, wx.ALL, 8) + + info_sizer.Add((20, 20), 0, 0, 0) + + more_info_label = wx.StaticText(self.info, wx.ID_ANY, _("Get more information on our website")) + info_sizer.Add(more_info_label, 0, wx.ALL, 8) + + self.help_link = wx.adv.HyperlinkCtrl( + self.info, + wx.ID_ANY, + _("https://inkstitch.org/docs/edit/#generate-test-swatches-from-selection"), + _("https://inkstitch.org/docs/edit/#generate-test-swatches-from-selection"), + style=wx.adv.HL_ALIGN_CENTRE + ) + info_sizer.Add(self.help_link, 0, 0, 0) + + button_sizer = wx.StdDialogButtonSizer() + main_sizer.Add(button_sizer, 1, wx.BOTTOM | wx.EXPAND, 10) + + button_sizer.Add((0, 0), 1, 0, 0) + + self.apply_button = wx.Button(self.panel, wx.ID_ANY, _("Apply")) + button_sizer.Add(self.apply_button, 0, wx.RIGHT, 10) + + self.options.SetSizer(options_sizer) + self.info.SetSizer(info_sizer) + self.panel.SetSizer(main_sizer) + + main_sizer.Fit(self) + self.Layout() + self.SetSizeHints(main_sizer.CalcMin()) + + self.param.Bind(wx.EVT_TEXT, self.on_text_input) + self.Bind(wx.EVT_BUTTON, self.apply_button_clicked, self.apply_button) + + def on_text_input(self, event): + # this will allow us to catch manual text input, but + # we only want to catch it when we actually create the swatches + pass + + def apply_button_clicked(self, event): + self.apply() + self.Destroy() + + def apply(self): + num_cols = self.columns.GetValue() + num_rows = self.rows.GetValue() + spacing = self.spacing.GetValue() + + start_value = self.start_value.GetValue() + step = self.step.GetValue() + + choice_names = [choice.name for choice in self.choices] + if self.param.GetValue() not in choice_names: + # catch manual text input + param = self.param.GetValue() + if param.startswith("inkstitch:"): + param = param[10:] + else: + param = self.choices[self.param.GetSelection()].id + + for element in self.extension.svg.selection: + dimensions = element.bounding_box() + param_value = start_value + for rows in range(0, num_rows): + for cols in range(0, num_cols): + new_element = element.duplicate() + translate_x = cols * dimensions.width + cols * spacing + translate_y = rows * dimensions.height + rows * spacing + new_element.transform.add_translate((translate_x, translate_y)) + if new_element.TAG == "g": + for embroidery_element in new_element.iterdescendants(EMBROIDERABLE_TAGS): + # Since this won't effect functionality, we can simply ignore the fact + # that this will also set the value to patterns, guide lines etc. + self._set_param(embroidery_element, param, param_value) + else: + self._set_param(new_element, param, param_value) + param_value += step + # remove old element + element.getparent().remove(element) + + def _set_param(self, element, param, value): + element.set(f'inkstitch:{ param }', value) + + +class GenerateSwatchesApp(wx.App): + def __init__(self, extension, choices): + self.extension = extension + self.choices = choices + super().__init__() + + def OnInit(self): + self.frame = GenerateSwatchesFrame(extension=self.extension, choices=self.choices) + self.SetTopWindow(self.frame) + self.frame.Show() + return True -- cgit v1.2.3