summaryrefslogtreecommitdiff
path: root/lib/sew_stack/stitch_layers/running_stitch/running_stitch_layer.py
blob: 0892ed448dd5eca5cf74b08c2e26c707c33fc76e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from copy import copy

from ..mixins.path import PathMixin, PathPropertiesMixin
from ..mixins.randomization import RandomizationPropertiesMixin, RandomizationMixin
from ..stitch_layer import StitchLayer
from ..stitch_layer_editor import Category, Properties, Property
from ..stitch_layer_editor import StitchLayerEditor
from ....i18n import _
from ....stitch_plan import StitchGroup
from ....stitches.running_stitch import running_stitch
from ....svg import PIXELS_PER_MM


class RunningStitchLayerEditor(StitchLayerEditor, RandomizationPropertiesMixin, PathPropertiesMixin):
    @classmethod
    @property
    def properties(cls):
        return Properties(
            Category(_("Running Stitch"), help=_("Stitch along a path using evenly-spaced stitches.")).children(
                Property("stitch_length", _("Stitch length"),
                         help=_('Length of stitches. Stitches can be shorter according to the stitch tolerance setting.'),
                         min=0.1,
                         unit="mm",
                         ),
                Property("tolerance", _("Tolerance"),
                         help=_('Determines how closely the stitch path matches the SVG path.  ' +
                                'A lower tolerance means stitches will be closer together and ' +
                                'fit the SVG path more precisely.  A higher tolerance means ' +
                                'some corners may be rounded and fewer stitches are needed.'),
                         min=0.01,
                         unit="mm",
                         ),
                Category(_("Repeats")).children(
                    Property(
                        "repeats", _("Repeats"),
                        help=_('Defines how many times to run down and back along the path.'),
                        type=int,
                        min=1,
                    ),
                    Property(
                        "repeat_stitches", _("Repeat exact stitches"),
                        type=bool,
                        help=_('Should the exact same stitches be repeated in each pass?  ' +
                               'If not, different randomization settings are applied on each pass.'),
                    ),
                ),
                cls.path_properties(),
            ),
            cls.randomization_properties().children(
                Property(
                    "stitch_length_jitter_percent", _('Stitch length variance'),
                    help=_('Enter a percentage.  Stitch length will vary randomly by up to this percentage.'),
                    prefix="±",
                    unit="%",
                ),
            ),
        )


class RunningStitchLayer(StitchLayer, RandomizationMixin, PathMixin):
    editor_class = RunningStitchLayerEditor

    @classmethod
    @property
    def defaults(cls):
        defaults = dict(
            name=_("Running Stitch"),
            type_name=_("Running Stitch"),
            stitch_length=2,
            tolerance=0.2,
            stitch_length_jitter_percent=0,
            repeats=1,
            repeat_stitches=True,
            reverse_path=False,
        )
        defaults.update(cls.randomization_defaults())

        return defaults

    @property
    def layer_type_name(self):
        return _("Running Stitch")

    def get_num_copies(self):
        if self.config.repeat_stitches:
            # When repeating stitches, we use multiple copies of one pass of running stitch
            return 1
        else:
            # Otherwise, we re-run running stitch every time, so that
            # randomization is different every pass
            return self.config.repeats

    def running_stitch(self, path):
        stitches = []

        for i in range(self.get_num_copies()):
            if i % 2 == 0:
                this_path = path
            else:
                this_path = list(reversed(path))

            stitches.extend(running_stitch(
                this_path,
                self.config.stitch_length * PIXELS_PER_MM,
                self.config.tolerance * PIXELS_PER_MM,
                (self.config.stitch_length_jitter_percent > 0),
                self.config.stitch_length_jitter_percent,
                self.get_random_seed()
            ))

        self.offset_stitches(stitches)

        if self.config.repeats > 0 and self.config.repeat_stitches:
            repeated_stitches = []
            for i in range(self.config.repeats):
                if i % 2 == 0:
                    repeated_stitches.extend(copy(stitches))
                else:
                    # reverse every other pass
                    repeated_stitches.extend(reversed(copy(stitches)))
            stitches = repeated_stitches

        return StitchGroup(stitches=stitches, color=self.stroke_color)

    def to_stitch_groups(self, previous_stitch_group, next_element):
        return [self.running_stitch(path) for path in self.get_paths()]