kopia lustrzana https://github.com/inkstitch/inkstitch
move StitchGroup into lib.stitch_plan
rodzic
12ef0c84aa
commit
84cb4e2c33
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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])
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue