summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLex Neva <github.com@lexneva.name>2021-08-07 11:21:13 -0400
committerLex Neva <github.com@lexneva.name>2021-08-07 11:21:13 -0400
commit84cb4e2c333d331eb863714797a55589f41e51b2 (patch)
treeaaf46187dd21e719600875bf432349ad1ce31530
parent12ef0c84aa732623b210fdce1a7b8301aa435217 (diff)
move StitchGroup into lib.stitch_plan
-rw-r--r--lib/elements/auto_fill.py7
-rw-r--r--lib/elements/element.py28
-rw-r--r--lib/elements/fill.py5
-rw-r--r--lib/elements/polyline.py5
-rw-r--r--lib/elements/satin_column.py5
-rw-r--r--lib/elements/stroke.py3
-rw-r--r--lib/stitch_plan/__init__.py4
-rw-r--r--lib/stitch_plan/color_block.py142
-rw-r--r--lib/stitch_plan/stitch.py3
-rw-r--r--lib/stitch_plan/stitch_group.py34
-rw-r--r--lib/stitch_plan/stitch_plan.py144
11 files changed, 197 insertions, 183 deletions
diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py
index 3c13a081..e72af1ef 100644
--- a/lib/elements/auto_fill.py
+++ b/lib/elements/auto_fill.py
@@ -9,13 +9,14 @@ import traceback
from shapely import geometry as shgeo
+from .element import param
+from .fill import Fill
+from .validation import ValidationWarning
from ..i18n import _
+from ..stitch_plan import StitchGroup
from ..stitches import auto_fill
from ..svg.tags import INKSCAPE_LABEL
from ..utils import cache, version
-from .element import StitchGroup, param
-from .fill import Fill
-from .validation import ValidationWarning
class SmallShapeWarning(ValidationWarning):
diff --git a/lib/elements/element.py b/lib/elements/element.py
index 17ed9167..a3577a5c 100644
--- a/lib/elements/element.py
+++ b/lib/elements/element.py
@@ -18,34 +18,6 @@ from ..svg.tags import INKSCAPE_LABEL, INKSTITCH_ATTRIBS
from ..utils import Point, cache
-class StitchGroup:
- """A raw collection of stitches with attached instructions."""
-
- def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False):
- self.color = color
- self.stitches = stitches or []
- self.trim_after = trim_after
- self.stop_after = stop_after
- self.tie_modus = tie_modus
- self.stitch_as_is = stitch_as_is
-
- def __add__(self, other):
- if isinstance(other, StitchGroup):
- return StitchGroup(self.color, self.stitches + other.stitches)
- else:
- raise TypeError("StitchGroup can only be added to another StitchGroup")
-
- def __len__(self):
- # This method allows `len(patch)` and `if patch:
- return len(self.stitches)
-
- def add_stitch(self, stitch):
- self.stitches.append(stitch)
-
- def reverse(self):
- return StitchGroup(self.color, self.stitches[::-1])
-
-
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):
diff --git a/lib/elements/fill.py b/lib/elements/fill.py
index 75a86ffd..2d2ae2ed 100644
--- a/lib/elements/fill.py
+++ b/lib/elements/fill.py
@@ -10,12 +10,13 @@ import re
from shapely import geometry as shgeo
from shapely.validation import explain_validity
+from .element import EmbroideryElement, param
+from .validation import ValidationError
from ..i18n import _
+from ..stitch_plan import StitchGroup
from ..stitches import legacy_fill
from ..svg import PIXELS_PER_MM
from ..utils import cache
-from .element import EmbroideryElement, StitchGroup, param
-from .validation import ValidationError
class UnconnectedError(ValidationError):
diff --git a/lib/elements/polyline.py b/lib/elements/polyline.py
index f63dfc3b..aeae17d9 100644
--- a/lib/elements/polyline.py
+++ b/lib/elements/polyline.py
@@ -6,11 +6,12 @@
from inkex import Path
from shapely import geometry as shgeo
+from .element import EmbroideryElement, param
+from .validation import ValidationWarning
from ..i18n import _
+from ..stitch_plan import StitchGroup
from ..utils import cache
from ..utils.geometry import Point
-from .element import EmbroideryElement, StitchGroup, param
-from .validation import ValidationWarning
class PolylineWarning(ValidationWarning):
diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py
index 1f28cb45..e066f3fb 100644
--- a/lib/elements/satin_column.py
+++ b/lib/elements/satin_column.py
@@ -11,11 +11,12 @@ from shapely import affinity as shaffinity
from shapely import geometry as shgeo
from shapely.ops import nearest_points
+from .element import EmbroideryElement, param
+from .validation import ValidationError, ValidationWarning
from ..i18n import _
+from ..stitch_plan import StitchGroup
from ..svg import line_strings_to_csp, point_lists_to_csp
from ..utils import Point, cache, collapse_duplicate_point, cut
-from .element import EmbroideryElement, StitchGroup, param
-from .validation import ValidationError, ValidationWarning
class SatinHasFillError(ValidationError):
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 76e80688..9fcb4e21 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -7,11 +7,12 @@ import sys
import shapely.geometry
+from .element import EmbroideryElement, param
from ..i18n import _
+from ..stitch_plan import StitchGroup
from ..stitches import bean_stitch, running_stitch
from ..svg import parse_length_with_units
from ..utils import Point, cache
-from .element import EmbroideryElement, StitchGroup, param
warned_about_legacy_running_stitch = False
diff --git a/lib/stitch_plan/__init__.py b/lib/stitch_plan/__init__.py
index 68301e94..ba4729bb 100644
--- a/lib/stitch_plan/__init__.py
+++ b/lib/stitch_plan/__init__.py
@@ -3,6 +3,8 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from .stitch_plan import patches_to_stitch_plan, StitchPlan, ColorBlock
+from .stitch_plan import patches_to_stitch_plan, StitchPlan
+from .color_block import ColorBlock
+from .stitch_group import StitchGroup
from .stitch import Stitch
from .read_file import stitch_plan_from_file
diff --git a/lib/stitch_plan/color_block.py b/lib/stitch_plan/color_block.py
new file mode 100644
index 00000000..1cff8aa4
--- /dev/null
+++ b/lib/stitch_plan/color_block.py
@@ -0,0 +1,142 @@
+from .stitch import Stitch
+from ..threads import ThreadColor
+from ..utils.geometry import Point
+from ..svg import PIXELS_PER_MM
+
+
+class ColorBlock(object):
+ """Holds a set of stitches, all with the same thread color."""
+
+ def __init__(self, color=None, stitches=None):
+ self.color = color
+ self.stitches = stitches or []
+
+ def __iter__(self):
+ return iter(self.stitches)
+
+ def __len__(self):
+ return len(self.stitches)
+
+ def __repr__(self):
+ return "ColorBlock(%s, %s)" % (self.color, self.stitches)
+
+ def __getitem__(self, item):
+ return self.stitches[item]
+
+ def __delitem__(self, item):
+ del self.stitches[item]
+
+ def __json__(self):
+ return dict(color=self.color, stitches=self.stitches)
+
+ def has_color(self):
+ return self._color is not None
+
+ @property
+ def color(self):
+ return self._color
+
+ @color.setter
+ def color(self, value):
+ if isinstance(value, ThreadColor):
+ self._color = value
+ elif value is None:
+ self._color = None
+ else:
+ self._color = ThreadColor(value)
+
+ @property
+ def last_stitch(self):
+ if self.stitches:
+ return self.stitches[-1]
+ else:
+ return None
+
+ @property
+ def num_stitches(self):
+ """Number of stitches in this color block."""
+ return len(self.stitches)
+
+ @property
+ def num_trims(self):
+ """Number of trims in this color block."""
+
+ return sum(1 for stitch in self if stitch.trim)
+
+ @property
+ def stop_after(self):
+ if self.last_stitch is not None:
+ return self.last_stitch.stop
+ else:
+ return False
+
+ @property
+ def trim_after(self):
+ # If there's a STOP, it will be at the end. We still want to return
+ # True.
+ for stitch in reversed(self.stitches):
+ if stitch.stop or stitch.jump:
+ continue
+ elif stitch.trim:
+ return True
+ else:
+ break
+
+ return False
+
+ def filter_duplicate_stitches(self):
+ if not self.stitches:
+ return
+
+ stitches = [self.stitches[0]]
+
+ for stitch in self.stitches[1:]:
+ if stitches[-1].jump or stitch.stop or stitch.trim or stitch.color_change:
+ # Don't consider jumps, stops, color changes, or trims as candidates for filtering
+ pass
+ else:
+ length = (stitch - stitches[-1]).length()
+ if length <= 0.1 * PIXELS_PER_MM:
+ # duplicate stitch, skip this one
+ continue
+
+ stitches.append(stitch)
+
+ self.stitches = stitches
+
+ def add_stitch(self, *args, **kwargs):
+ if not args:
+ # They're adding a command, e.g. `color_block.add_stitch(stop=True)``.
+ # Use the position from the last stitch.
+ if self.last_stitch:
+ args = (self.last_stitch.x, self.last_stitch.y)
+ else:
+ raise ValueError("internal error: can't add a command to an empty stitch block")
+
+ if isinstance(args[0], Stitch):
+ self.stitches.append(args[0])
+ elif isinstance(args[0], Point):
+ self.stitches.append(Stitch(args[0].x, args[0].y, *args[1:], **kwargs))
+ else:
+ if not args and self.last_stitch:
+ args = (self.last_stitch.x, self.last_stitch.y)
+ self.stitches.append(Stitch(*args, **kwargs))
+
+ def add_stitches(self, stitches, *args, **kwargs):
+ for stitch in stitches:
+ if isinstance(stitch, (Stitch, Point)):
+ self.add_stitch(stitch, *args, **kwargs)
+ else:
+ self.add_stitch(*stitch, *args, **kwargs)
+
+ def replace_stitches(self, stitches):
+ self.stitches = stitches
+
+ @property
+ def bounding_box(self):
+ minx = min(stitch.x for stitch in self)
+ miny = min(stitch.y for stitch in self)
+ maxx = max(stitch.x for stitch in self)
+ maxy = max(stitch.y for stitch in self)
+
+ return minx, miny, maxx, maxy
diff --git a/lib/stitch_plan/stitch.py b/lib/stitch_plan/stitch.py
index ae6fa480..85d71935 100644
--- a/lib/stitch_plan/stitch.py
+++ b/lib/stitch_plan/stitch.py
@@ -8,8 +8,7 @@ from ..utils.geometry import Point
class Stitch(Point):
def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, tie_modus=0, no_ties=False):
- self.x = x
- self.y = y
+ Point.__init__(self, x, y)
self.color = color
self.jump = jump
self.trim = trim
diff --git a/lib/stitch_plan/stitch_group.py b/lib/stitch_plan/stitch_group.py
new file mode 100644
index 00000000..d1e6bae7
--- /dev/null
+++ b/lib/stitch_plan/stitch_group.py
@@ -0,0 +1,34 @@
+class StitchGroup:
+ """A collection of Stitch objects with attached instructions.
+
+ StitchGroups will later be combined to make ColorBlocks, which in turn are
+ combined to make a StitchPlan. Jump stitches are allowed between
+ StitchGroups, but not between stitches inside a StitchGroup. This means
+ that EmbroideryElement classes should produce multiple StitchGroups only if
+ they want to allow for the possibility of jump stitches to be added in
+ between them by the stitch plan generation code.
+ """
+
+ def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False, tie_modus=0, stitch_as_is=False):
+ self.color = color
+ self.stitches = stitches or []
+ self.trim_after = trim_after
+ self.stop_after = stop_after
+ self.tie_modus = tie_modus
+ self.stitch_as_is = stitch_as_is
+
+ def __add__(self, other):
+ if isinstance(other, StitchGroup):
+ return StitchGroup(self.color, self.stitches + other.stitches)
+ else:
+ raise TypeError("StitchGroup can only be added to another StitchGroup")
+
+ def __len__(self):
+ # This method allows `len(patch)` and `if patch:
+ return len(self.stitches)
+
+ def add_stitch(self, stitch):
+ self.stitches.append(stitch)
+
+ def reverse(self):
+ return StitchGroup(self.color, self.stitches[::-1])
diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py
index d1af5365..f5981fb9 100644
--- a/lib/stitch_plan/stitch_plan.py
+++ b/lib/stitch_plan/stitch_plan.py
@@ -3,11 +3,9 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from ..svg import PIXELS_PER_MM
-from ..threads import ThreadColor
-from ..utils.geometry import Point
-from .stitch import Stitch
from .ties import add_ties
+from .color_block import ColorBlock
+from ..svg import PIXELS_PER_MM
def patches_to_stitch_plan(patches, collapse_len=None, disable_ties=False): # noqa: C901
@@ -168,141 +166,3 @@ class StitchPlan(object):
return self.color_blocks[-1]
else:
return None
-
-
-class ColorBlock(object):
- """Holds a set of stitches, all with the same thread color."""
-
- def __init__(self, color=None, stitches=None):
- self.color = color
- self.stitches = stitches or []
-
- def __iter__(self):
- return iter(self.stitches)
-
- def __len__(self):
- return len(self.stitches)
-
- def __repr__(self):
- return "ColorBlock(%s, %s)" % (self.color, self.stitches)
-
- def __getitem__(self, item):
- return self.stitches[item]
-
- def __delitem__(self, item):
- del self.stitches[item]
-
- def __json__(self):
- return dict(color=self.color, stitches=self.stitches)
-
- def has_color(self):
- return self._color is not None
-
- @property
- def color(self):
- return self._color
-
- @color.setter
- def color(self, value):
- if isinstance(value, ThreadColor):
- self._color = value
- elif value is None:
- self._color = None
- else:
- self._color = ThreadColor(value)
-
- @property
- def last_stitch(self):
- if self.stitches:
- return self.stitches[-1]
- else:
- return None
-
- @property
- def num_stitches(self):
- """Number of stitches in this color block."""
- return len(self.stitches)
-
- @property
- def num_trims(self):
- """Number of trims in this color block."""
-
- return sum(1 for stitch in self if stitch.trim)
-
- @property
- def stop_after(self):
- if self.last_stitch is not None:
- return self.last_stitch.stop
- else:
- return False
-
- @property
- def trim_after(self):
- # If there's a STOP, it will be at the end. We still want to return
- # True.
- for stitch in reversed(self.stitches):
- if stitch.stop or stitch.jump:
- continue
- elif stitch.trim:
- return True
- else:
- break
-
- return False
-
- def filter_duplicate_stitches(self):
- if not self.stitches:
- return
-
- stitches = [self.stitches[0]]
-
- for stitch in self.stitches[1:]:
- if stitches[-1].jump or stitch.stop or stitch.trim or stitch.color_change:
- # Don't consider jumps, stops, color changes, or trims as candidates for filtering
- pass
- else:
- length = (stitch - stitches[-1]).length()
- if length <= 0.1 * PIXELS_PER_MM:
- # duplicate stitch, skip this one
- continue
-
- stitches.append(stitch)
-
- self.stitches = stitches
-
- def add_stitch(self, *args, **kwargs):
- if not args:
- # They're adding a command, e.g. `color_block.add_stitch(stop=True)``.
- # Use the position from the last stitch.
- if self.last_stitch:
- args = (self.last_stitch.x, self.last_stitch.y)
- else:
- raise ValueError("internal error: can't add a command to an empty stitch block")
-
- if isinstance(args[0], Stitch):
- self.stitches.append(args[0])
- elif isinstance(args[0], Point):
- self.stitches.append(Stitch(args[0].x, args[0].y, *args[1:], **kwargs))
- else:
- if not args and self.last_stitch:
- args = (self.last_stitch.x, self.last_stitch.y)
- self.stitches.append(Stitch(*args, **kwargs))
-
- def add_stitches(self, stitches, *args, **kwargs):
- for stitch in stitches:
- if isinstance(stitch, (Stitch, Point)):
- self.add_stitch(stitch, *args, **kwargs)
- else:
- self.add_stitch(*(list(stitch) + args), **kwargs)
-
- def replace_stitches(self, stitches):
- self.stitches = stitches
-
- @property
- def bounding_box(self):
- minx = min(stitch.x for stitch in self)
- miny = min(stitch.y for stitch in self)
- maxx = max(stitch.x for stitch in self)
- maxy = max(stitch.y for stitch in self)
-
- return minx, miny, maxx, maxy