summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/extensions/test_swatches.py122
-rw-r--r--lib/gui/test_swatches.py189
2 files changed, 276 insertions, 35 deletions
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