summaryrefslogtreecommitdiff
path: root/lib/elements
diff options
context:
space:
mode:
authorKaalleen <36401965+kaalleen@users.noreply.github.com>2023-02-27 16:01:37 +0100
committerGitHub <noreply@github.com>2023-02-27 16:01:37 +0100
commited4aa55a733986436853e2ee7ad22f757b09fcb1 (patch)
tree5a2fcb1159c855d412bd540763f3a627ee0f1266 /lib/elements
parent1885deff350fc29e80afa292c899a824c90d0c23 (diff)
Circular fill (#2053)
Diffstat (limited to 'lib/elements')
-rw-r--r--lib/elements/fill_stitch.py141
1 files changed, 108 insertions, 33 deletions
diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py
index c6762ad1..1897c8bf 100644
--- a/lib/elements/fill_stitch.py
+++ b/lib/elements/fill_stitch.py
@@ -9,22 +9,25 @@ import re
import sys
import traceback
+from inkex import Transform
from shapely import geometry as shgeo
from shapely.errors import TopologicalError
from shapely.validation import explain_validity, make_valid
+from .. import tiles
from ..i18n import _
from ..marker import get_marker_elements
from ..stitch_plan import StitchGroup
-from ..stitches import auto_fill, contour_fill, guided_fill, legacy_fill
+from ..stitches import (auto_fill, circular_fill, contour_fill, guided_fill,
+ legacy_fill)
from ..stitches.meander_fill import meander_fill
-from ..svg import PIXELS_PER_MM
+from ..svg import PIXELS_PER_MM, get_node_transform
from ..svg.tags import INKSCAPE_LABEL
-from .. import tiles
from ..utils import cache, version
+from ..utils.param import ParamOption
+from ..utils.threading import ExitThread
from .element import EmbroideryElement, param
from .validation import ValidationError, ValidationWarning
-from ..utils.threading import ExitThread
class SmallShapeWarning(ValidationWarning):
@@ -107,15 +110,33 @@ class FillStitch(EmbroideryElement):
def auto_fill(self):
return self.get_boolean_param('auto_fill', True)
+ _fill_methods = [ParamOption('auto_fill', _("Auto Fill"), 0),
+ ParamOption('contour_fill', _("Contour Fill"), 1),
+ ParamOption('guided_fill', _("Guided Fill"), 2),
+ ParamOption('meander_fill', _("Meander Fill")),
+ ParamOption('circular_fill', _("Circular Fill")),
+ ParamOption('legacy_fill', _("Legacy Fill"), 3)]
+
@property
- @param('fill_method', _('Fill method'), type='dropdown', default=0,
- options=[_("Auto Fill"), _("Contour Fill"), _("Guided Fill"), _("Legacy Fill"), _("Meander Fill")], sort_index=2)
+ @param('fill_method',
+ _('Fill method'),
+ type='combo',
+ default=0,
+ options=_fill_methods,
+ sort_index=2)
def fill_method(self):
- return self.get_int_param('fill_method', 0)
+ # convert legacy values
+ legacy_method = self.get_int_param('fill_method', None)
+ if legacy_method in range(0, 4):
+ method = [method.id for method in self._fill_methods if method.legacy == legacy_method][0]
+ self.set_param('fill_method', method)
+ return method
+
+ return self.get_param('fill_method', 'auto_fill')
@property
@param('guided_fill_strategy', _('Guided Fill Strategy'), type='dropdown', default=0,
- options=[_("Copy"), _("Parallel Offset")], select_items=[('fill_method', 2)], sort_index=3,
+ options=[_("Copy"), _("Parallel Offset")], select_items=[('fill_method', 'guided_fill')], sort_index=3,
tooltip=_('Copy (the default) will fill the shape with shifted copies of the line. '
'Parallel offset will ensure that each line is always a consistent distance from its neighbor. '
'Sharp corners may be introduced.'))
@@ -124,18 +145,23 @@ class FillStitch(EmbroideryElement):
@property
@param('contour_strategy', _('Contour Fill Strategy'), type='dropdown', default=0,
- options=[_("Inner to Outer"), _("Single spiral"), _("Double spiral")], select_items=[('fill_method', 1)], sort_index=3)
+ options=[_("Inner to Outer"), _("Single spiral"), _("Double spiral")], select_items=[('fill_method', 'contour_fill')], sort_index=3)
def contour_strategy(self):
return self.get_int_param('contour_strategy', 0)
@property
@param('join_style', _('Join Style'), type='dropdown', default=0,
- options=[_("Round"), _("Mitered"), _("Beveled")], select_items=[('fill_method', 1)], sort_index=4)
+ options=[_("Round"), _("Mitered"), _("Beveled")], select_items=[('fill_method', 'contour_fill')], sort_index=4)
def join_style(self):
return self.get_int_param('join_style', 0)
@property
- @param('avoid_self_crossing', _('Avoid self-crossing'), type='boolean', default=False, select_items=[('fill_method', 1)], sort_index=5)
+ @param('avoid_self_crossing',
+ _('Avoid self-crossing'),
+ type='boolean',
+ default=False,
+ select_items=[('fill_method', 'contour_fill')],
+ sort_index=5)
def avoid_self_crossing(self):
return self.get_boolean_param('avoid_self_crossing', False)
@@ -149,24 +175,29 @@ class FillStitch(EmbroideryElement):
type='integer',
unit='mm',
default=0,
- select_items=[('fill_method', 1), ('fill_method', 4)],
+ select_items=[('fill_method', 'contour_fill'), ('fill_method', 'meander_fill')],
sort_index=5)
def smoothness(self):
return self.get_float_param('smoothness_mm', 0)
@property
- @param('clockwise', _('Clockwise'), type='boolean', default=True, select_items=[('fill_method', 1)], sort_index=5)
+ @param('clockwise', _('Clockwise'), type='boolean', default=True, select_items=[('fill_method', 'contour_fill')], sort_index=5)
def clockwise(self):
return self.get_boolean_param('clockwise', True)
@property
@param('meander_pattern', _('Meander Pattern'), type='combo', default=0,
- options=sorted(tiles.all_tiles()), select_items=[('fill_method', 4)], sort_index=3)
+ options=sorted(tiles.all_tiles()), select_items=[('fill_method', 'meander_fill')], sort_index=3)
def meander_pattern(self):
return self.get_param('meander_pattern', None)
@property
- @param('meander_scale_percent', _('Meander pattern scale'), type='float', unit="%", default=100, select_items=[('fill_method', 4)], sort_index=4)
+ @param('meander_scale_percent',
+ _('Meander pattern scale'),
+ type='float', unit="%",
+ default=100,
+ select_items=[('fill_method', 'meander_fill')],
+ sort_index=4)
def meander_scale(self):
return self.get_split_float_param('meander_scale_percent', (100, 100)) / 100
@@ -177,7 +208,7 @@ class FillStitch(EmbroideryElement):
unit='deg',
type='float',
sort_index=6,
- select_items=[('fill_method', 0), ('fill_method', 3)],
+ select_items=[('fill_method', 'auto_fill'), ('fill_method', 'legacy_fill')],
default=0)
@cache
def angle(self):
@@ -196,8 +227,8 @@ class FillStitch(EmbroideryElement):
'Skipping it decreases stitch count and density.'),
type='boolean',
sort_index=6,
- select_items=[('fill_method', 0), ('fill_method', 2),
- ('fill_method', 3)],
+ select_items=[('fill_method', 'auto_fill'), ('fill_method', 'guided_fill'),
+ ('fill_method', 'legacy_fill')],
default=False)
def skip_last(self):
return self.get_boolean_param("skip_last", False)
@@ -210,7 +241,7 @@ class FillStitch(EmbroideryElement):
'When you enable flip, stitching goes from right-to-left instead of left-to-right.'),
type='boolean',
sort_index=7,
- select_items=[('fill_method', 3)],
+ select_items=[('fill_method', 'legacy_fill')],
default=False)
def flip(self):
return self.get_boolean_param("flip", False)
@@ -222,7 +253,10 @@ class FillStitch(EmbroideryElement):
unit='mm',
sort_index=6,
type='float',
- select_items=[('fill_method', 0), ('fill_method', 1), ('fill_method', 2), ('fill_method', 3)],
+ select_items=[('fill_method', 'auto_fill'),
+ ('fill_method', 'contour_fill'),
+ ('fill_method', 'guided_fill'),
+ ('fill_method', 'legacy_fill')],
default=0.25)
def row_spacing(self):
return max(self.get_float_param("row_spacing_mm", 0.25), 0.1 * PIXELS_PER_MM)
@@ -239,7 +273,10 @@ class FillStitch(EmbroideryElement):
unit='mm',
sort_index=6,
type='float',
- select_items=[('fill_method', 0), ('fill_method', 1), ('fill_method', 2), ('fill_method', 3)],
+ select_items=[('fill_method', 'auto_fill'),
+ ('fill_method', 'contour_fill'),
+ ('fill_method', 'guided_fill'),
+ ('fill_method', 'legacy_fill')],
default=3.0)
def max_stitch_length(self):
return max(self.get_float_param("max_stitch_length_mm", 3.0), 0.1 * PIXELS_PER_MM)
@@ -251,7 +288,7 @@ class FillStitch(EmbroideryElement):
'Fractional values are allowed and can have less visible diagonals than integer values.'),
type='int',
sort_index=6,
- select_items=[('fill_method', 0), ('fill_method', 2), ('fill_method', 3)],
+ select_items=[('fill_method', 'auto_fill'), ('fill_method', 'guided_fill'), ('fill_method', 'legacy_fill')],
default=4)
def staggers(self):
return self.get_float_param("staggers", 4)
@@ -380,7 +417,7 @@ class FillStitch(EmbroideryElement):
yield UnderlayInsetWarning(shape.centroid)
# guided fill warnings
- if self.fill_method == 2:
+ if self.fill_method == 'guided_fill':
guide_lines = self._get_guide_lines(True)
if not guide_lines or guide_lines[0].is_empty:
yield MissingGuideLineWarning(self.shape.centroid)
@@ -406,12 +443,15 @@ class FillStitch(EmbroideryElement):
@property
@param('running_stitch_length_mm',
_('Running stitch length'),
- tooltip=_(
- 'Length of stitches around the outline of the fill region used when moving from section to section. Also used for meander fill.'),
+ tooltip=_('Length of stitches around the outline of the fill region used when moving from section to section. '
+ 'Also used for meander and circular fill.'),
unit='mm',
type='float',
default=1.5,
- select_items=[('fill_method', 0), ('fill_method', 2), ('fill_method', 4)],
+ select_items=[('fill_method', 'auto_fill'),
+ ('fill_method', 'guided_fill'),
+ ('fill_method', 'meander_fill'),
+ ('fill_method', 'circular_fill')],
sort_index=6)
def running_stitch_length(self):
return max(self.get_float_param("running_stitch_length_mm", 1.5), 0.01)
@@ -511,7 +551,10 @@ class FillStitch(EmbroideryElement):
type='float',
default=0,
sort_index=5,
- select_items=[('fill_method', 0), ('fill_method', 2), ('fill_method', 4)])
+ select_items=[('fill_method', 'auto_fill'),
+ ('fill_method', 'guided_fill'),
+ ('fill_method', 'meander_fill'),
+ ('fill_method', 'circular_fill')])
def expand(self):
return self.get_float_param('expand_mm', 0)
@@ -523,7 +566,7 @@ class FillStitch(EmbroideryElement):
'are not visible. This gives them a jagged appearance.'),
type='boolean',
default=True,
- select_items=[('fill_method', 0), ('fill_method', 2)],
+ select_items=[('fill_method', 'auto_fill'), ('fill_method', 'guided_fill'), ('fill_method', 'circular_fill')],
sort_index=6)
def underpath(self):
return self.get_boolean_param('underpath', True)
@@ -586,7 +629,7 @@ class FillStitch(EmbroideryElement):
def to_stitch_groups(self, previous_stitch_group): # noqa: C901
# backwards compatibility: legacy_fill used to be inkstitch:auto_fill == False
- if not self.auto_fill or self.fill_method == 3:
+ if not self.auto_fill or self.fill_method == 'legacy_fill':
return self.do_legacy_fill()
else:
stitch_groups = []
@@ -603,14 +646,16 @@ class FillStitch(EmbroideryElement):
fill_shapes = self.fill_shape(shape)
for i, fill_shape in enumerate(fill_shapes.geoms):
- if self.fill_method == 0:
+ if self.fill_method == 'auto_fill':
stitch_groups.extend(self.do_auto_fill(fill_shape, previous_stitch_group, start, end))
- if self.fill_method == 1:
+ elif self.fill_method == 'contour_fill':
stitch_groups.extend(self.do_contour_fill(fill_shape, previous_stitch_group, start))
- elif self.fill_method == 2:
+ elif self.fill_method == 'guided_fill':
stitch_groups.extend(self.do_guided_fill(fill_shape, previous_stitch_group, start, end))
- elif self.fill_method == 4:
+ elif self.fill_method == 'meander_fill':
stitch_groups.extend(self.do_meander_fill(fill_shape, i, start, end))
+ elif self.fill_method == 'circular_fill':
+ stitch_groups.extend(self.do_circular_fill(fill_shape, previous_stitch_group, start, end))
except ExitThread:
raise
except Exception:
@@ -782,3 +827,33 @@ class FillStitch(EmbroideryElement):
message += traceback.format_exc()
self.fatal(message)
+
+ def do_circular_fill(self, shape, last_patch, starting_point, ending_point):
+ # get target position
+ command = self.get_command('ripple_target')
+ if command:
+ pos = [float(command.use.get("x", 0)), float(command.use.get("y", 0))]
+ transform = get_node_transform(command.use)
+ pos = Transform(transform).apply_to_point(pos)
+ target = shgeo.Point(*pos)
+ else:
+ target = shape.centroid
+ stitches = circular_fill(
+ shape,
+ self.angle,
+ self.row_spacing,
+ self.staggers,
+ self.running_stitch_length,
+ self.running_stitch_tolerance,
+ self.skip_last,
+ starting_point,
+ ending_point,
+ self.underpath,
+ target
+ )
+
+ stitch_group = StitchGroup(
+ color=self.color,
+ tags=("circular_fill", "auto_fill_top"),
+ stitches=stitches)
+ return [stitch_group]