summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKaalleen <36401965+kaalleen@users.noreply.github.com>2023-10-15 07:18:26 +0200
committerGitHub <noreply@github.com>2023-10-15 07:18:26 +0200
commit1ff677722844e311f64d10079ff96b725b00676e (patch)
treef49337585180c7afae0c66d6591702d2fbc7f56b /lib
parent45e430bb60e2a952211a3526fabb5e11badfcce3 (diff)
Add element info extension (#2544)
Diffstat (limited to 'lib')
-rw-r--r--lib/extensions/__init__.py2
-rw-r--r--lib/extensions/element_info.py164
-rw-r--r--lib/gui/element_info.py93
-rw-r--r--lib/stitch_plan/color_block.py6
-rw-r--r--lib/stitch_plan/stitch_plan.py4
5 files changed, 269 insertions, 0 deletions
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 7987a717..79759c6d 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -17,6 +17,7 @@ from .cut_satin import CutSatin
from .cutwork_segmentation import CutworkSegmentation
from .density_map import DensityMap
from .duplicate_params import DuplicateParams
+from .element_info import ElementInfo
from .fill_to_stroke import FillToStroke
from .flip import Flip
from .generate_palette import GeneratePalette
@@ -70,6 +71,7 @@ __all__ = extensions = [ApplyThreadlist,
CutworkSegmentation,
DensityMap,
DuplicateParams,
+ ElementInfo,
FillToStroke,
Flip,
GeneratePalette,
diff --git a/lib/extensions/element_info.py b/lib/extensions/element_info.py
new file mode 100644
index 00000000..a086108b
--- /dev/null
+++ b/lib/extensions/element_info.py
@@ -0,0 +1,164 @@
+# Authors: see git history
+#
+# Copyright (c) 2023 Authors
+# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
+
+from ..elements import FillStitch, SatinColumn, Stroke
+from ..gui.element_info import ElementInfoApp
+from ..i18n import _
+from ..stitch_plan import stitch_groups_to_stitch_plan
+from ..svg import PIXELS_PER_MM
+from .base import InkstitchExtension
+
+
+class ElementInfo(InkstitchExtension):
+
+ def effect(self):
+ if not self.svg.selection or not self.get_elements():
+ return
+
+ self.metadata = self.get_inkstitch_metadata()
+ self.list_items = []
+
+ self._general_info()
+
+ previous_stitch_group = None
+ for element in self.elements:
+ previous_stitch_group = self._element_info(element, previous_stitch_group)
+
+ app = ElementInfoApp(self.list_items)
+ app.MainLoop()
+
+ def _element_info(self, element, previous_stitch_group):
+ stitch_groups = element.to_stitch_groups(previous_stitch_group)
+ stitch_plan = stitch_groups_to_stitch_plan(
+ stitch_groups,
+ collapse_len=self.metadata['collapse_len'],
+ min_stitch_len=self.metadata['min_stitch_len']
+ )
+
+ self.list_items.append(ListItem(
+ name=f"{ element.node.label } ({ element.node.get_id() })",
+ headline=True
+ ))
+ self.list_items.append(ListItem(
+ name=_("Type"),
+ value=element.element_name
+ ))
+ if isinstance(element, FillStitch):
+ fill_method = next((method.name for method in element._fill_methods if method.id == element.fill_method), "")
+ self.list_items.append(ListItem(
+ name=_("Fill Method"),
+ value=fill_method
+ ))
+
+ if isinstance(element, SatinColumn):
+ satin_method = next((method.name for method in element._satin_methods if method.id == element.satin_method), "")
+ self.list_items.append(ListItem(
+ name=_("Satin Method"),
+ value=satin_method
+ ))
+
+ if isinstance(element, Stroke):
+ stroke_method = next((method.name for method in element._stroke_methods if method.id == element.stroke_method), "")
+ self.list_items.append(ListItem(
+ name=_("Satin Method"),
+ value=stroke_method
+ ))
+
+ self.list_items.append(ListItem(
+ name=_("Dimensions (mm)"),
+ value="{:.2f} x {:.2f}".format(stitch_plan.dimensions_mm[0], stitch_plan.dimensions_mm[1])
+ ))
+
+ stitch_lengths = []
+ for group in stitch_groups:
+ stitches = group.stitches
+
+ previous_stitch = stitches[0]
+ for stitch in stitches[1:]:
+ st = stitch - previous_stitch
+ length = st.length() / PIXELS_PER_MM
+ if length <= self.metadata['min_stitch_len_mm']:
+ continue
+ stitch_lengths.append(length)
+ previous_stitch = stitch
+
+ stitches_per_group = ""
+ if len(stitch_groups) > 1:
+ stitches_per_group = f" ({', '.join([str(len(group.stitches)) for group in stitch_groups]) })"
+
+ self.list_items.append(ListItem(
+ name=_("Stitches"),
+ value=str(stitch_plan.num_stitches - stitch_plan.num_jumps) + stitches_per_group
+ ))
+ self.list_items.append(ListItem(
+ name=_("Jumps"),
+ value=str(stitch_plan.num_jumps - 1)
+ ))
+ self.list_items.append(ListItem(
+ name=_("Max stitch length"),
+ value="{:.2f}".format(max(stitch_lengths))
+ ))
+ self.list_items.append(ListItem(
+ name=_("Min stitch length"),
+ value="{:.2f}".format(min(stitch_lengths))
+ ))
+ self.list_items.append(ListItem())
+ return stitch_groups[0]
+
+ def _general_info(self):
+ stitch_groups = self.elements_to_stitch_groups(self.elements)
+ stitch_plan = stitch_groups_to_stitch_plan(
+ stitch_groups,
+ collapse_len=self.metadata['collapse_len'],
+ min_stitch_len=self.metadata['min_stitch_len']
+ )
+
+ self.list_items.append(ListItem(
+ name=_("All Selected Elements"),
+ headline=True
+ ))
+ self.list_items.append(ListItem(
+ name=_("Dimensions (mm)"),
+ value="{:.2f} x {:.2f}".format(stitch_plan.dimensions_mm[0], stitch_plan.dimensions_mm[1])
+ ))
+ self.list_items.append(ListItem(
+ name=_("Colors"),
+ value=str(stitch_plan.num_colors)
+ ))
+ self.list_items.append(ListItem(
+ name=_("Color Changes"),
+ value=str(stitch_plan.num_color_blocks - 1)
+ ))
+ self.list_items.append(ListItem(
+ name=_("Jumps"),
+ value=str(stitch_plan.num_jumps)
+ ))
+ self.list_items.append(ListItem(
+ name=_("Trims"),
+ value=str(stitch_plan.num_trims)
+ ))
+ self.list_items.append(ListItem(
+ name=_("Stops"),
+ value=str(stitch_plan.num_stops)
+ ))
+ self.list_items.append(ListItem(
+ name=_("Stitches"),
+ value=str(stitch_plan.num_stitches - stitch_plan.num_jumps)
+ ))
+ self.list_items.append(ListItem(
+ name=_("Filter stitches smaller than (mm)"),
+ value=str(self.metadata['min_stitch_len_mm'])
+ ))
+ self.list_items.append(ListItem())
+
+
+class ListItem:
+ def __init__(self, name="", value="", headline=False):
+ self.name: str = name
+ self.value: str = value
+ self.headline: bool = headline
+
+ def __repr__(self):
+ return "ListItem(%s, %s, %s)" % (self.name, self.value, self.headline)
diff --git a/lib/gui/element_info.py b/lib/gui/element_info.py
new file mode 100644
index 00000000..dd339081
--- /dev/null
+++ b/lib/gui/element_info.py
@@ -0,0 +1,93 @@
+# 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 _
+
+
+class ElementInfoFrame(wx.Frame):
+
+ def __init__(self, *args, **kwargs):
+ self.list_items = kwargs.pop("list_items")
+ self.index = 0
+ wx.Frame.__init__(self, None, wx.ID_ANY, _("Element Info"), *args, **kwargs)
+
+ self.main_panel = wx.Panel(self, wx.ID_ANY)
+
+ notebook_sizer = wx.BoxSizer(wx.VERTICAL)
+ self.notebook = wx.Notebook(self.main_panel, wx.ID_ANY)
+ notebook_sizer.Add(self.notebook, 1, wx.EXPAND, 0)
+
+ self.info = wx.Panel(self.notebook, wx.ID_ANY)
+ self.notebook.AddPage(self.info, _("Info"))
+
+ info_sizer = wx.BoxSizer(wx.VERTICAL)
+
+ self.info_list = wx.ListCtrl(self.info, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES)
+ self.info_list.AppendColumn(_("Name"), format=wx.LIST_FORMAT_LEFT)
+ self.info_list.AppendColumn(_("Value"), format=wx.LIST_FORMAT_LEFT)
+ self._fill_info_list()
+ self.info_list.SetColumnWidth(0, -1)
+ self.info_list.SetColumnWidth(1, -1)
+ info_sizer.Add(self.info_list, 1, wx.ALL | wx.EXPAND, 10)
+
+ self.help = wx.Panel(self.notebook, wx.ID_ANY)
+ self.notebook.AddPage(self.help, _("Help"))
+
+ help_sizer = wx.BoxSizer(wx.VERTICAL)
+
+ help_text = wx.StaticText(
+ self.help,
+ wx.ID_ANY,
+ _("This extension informs about various parameters of selected stitch elements."),
+ style=wx.ALIGN_LEFT
+ )
+ help_text.Wrap(500)
+ help_sizer.Add(help_text, 0, wx.ALL, 8)
+
+ help_sizer.Add((20, 20), 0, 0, 0)
+
+ website_info = wx.StaticText(self.help, wx.ID_ANY, _("More information on our website:"))
+ help_sizer.Add(website_info, 0, wx.ALL, 8)
+
+ self.website_link = wx.adv.HyperlinkCtrl(
+ self.help,
+ wx.ID_ANY,
+ _("https://inkstitch.org/troubleshoot#element-info"),
+ _("https://inkstitch.org/troubleshoot#element-info")
+ )
+ help_sizer.Add(self.website_link, 0, wx.ALL, 8)
+
+ self.help.SetSizer(help_sizer)
+ self.info.SetSizer(info_sizer)
+ self.main_panel.SetSizer(notebook_sizer)
+
+ self.SetSizeHints(notebook_sizer.CalcMin())
+
+ self.Layout()
+
+ def _fill_info_list(self):
+ for item in self.list_items:
+ self.info_list.InsertItem(self.index, item.name)
+ if item.headline:
+ self.info_list.SetItemBackgroundColour(self.index, "black")
+ self.info_list.SetItemTextColour(self.index, "white")
+ else:
+ self.info_list.SetItem(self.index, 1, item.value)
+ self.index += 1
+
+
+class ElementInfoApp(wx.App):
+ def __init__(self, list_items):
+ self.list_items = list_items
+ super().__init__()
+
+ def OnInit(self):
+ self.frame = ElementInfoFrame(list_items=self.list_items)
+ self.SetTopWindow(self.frame)
+ self.frame.Show()
+ return True
diff --git a/lib/stitch_plan/color_block.py b/lib/stitch_plan/color_block.py
index fdef5eb8..a0f9d43f 100644
--- a/lib/stitch_plan/color_block.py
+++ b/lib/stitch_plan/color_block.py
@@ -80,6 +80,12 @@ class ColorBlock(object):
return sum(1 for stitch in self if stitch.trim)
@property
+ def num_jumps(self):
+ """Number of jumps in this color block."""
+
+ return sum(1 for stitch in self if stitch.jump)
+
+ @property
def stop_after(self):
if self.last_stitch is not None:
return self.last_stitch.stop
diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py
index 68d52699..8067749a 100644
--- a/lib/stitch_plan/stitch_plan.py
+++ b/lib/stitch_plan/stitch_plan.py
@@ -173,6 +173,10 @@ class StitchPlan(object):
return sum(block.num_stitches for block in self)
@property
+ def num_jumps(self):
+ return sum(block.num_jumps for block in self)
+
+ @property
def bounding_box(self):
color_block_bounding_boxes = [cb.bounding_box for cb in self]
minx = min(bb[0] for bb in color_block_bounding_boxes)