diff options
| author | Kaalleen <36401965+kaalleen@users.noreply.github.com> | 2024-12-14 16:49:10 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-14 16:49:10 +0100 |
| commit | 1abd3051325273f139508429821c5aad7ca39266 (patch) | |
| tree | 14a672da5b78adea89d0be8c0c37be7ce4fd5109 | |
| parent | da5ab4fa78e0839583d088508c1f3438078f7227 (diff) | |
Split satins at stitch level (#3336)
| -rw-r--r-- | lib/elements/satin_column.py | 312 |
1 files changed, 157 insertions, 155 deletions
diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 49f09e22..c678c317 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -877,7 +877,7 @@ class SatinColumn(EmbroideryElement): ends. Finds corresponding point on the other rail (taking into account the rungs) and breaks the rails at these points. - split_point can also be a noramlized projection of a distance along the + split_point can also be a normalized projection of a distance along the satin, in the range 0.0 to 1.0. Returns two new SatinColumn instances: the part before and the part @@ -1099,12 +1099,12 @@ class SatinColumn(EmbroideryElement): @property @cache def offset_center_line(self): - stitches = self._get_center_line_stitches() + stitches = self._get_center_line_stitches(self.running_stitch_position) linestring = shgeo.LineString(stitches) return linestring - def _get_center_line_stitches(self): - inset_prop = -np.array([self.running_stitch_position, 100-self.running_stitch_position]) / 100 + def _get_center_line_stitches(self, position): + inset_prop = -np.array([position, 100-position]) / 100 # Do it like contour underlay, but inset all the way to the center. pairs = self.plot_points_on_rails(self.running_stitch_tolerance, (0, 0), inset_prop) @@ -1247,74 +1247,58 @@ class SatinColumn(EmbroideryElement): return pairs - def do_start_path(self, satins, start_point): + def _connect_stitch_group_with_point(self, first_stitch_group, start_point, connect_to_satin_end=False): start_stitch_group = StitchGroup( color=self.color, - tags=("satin_column", "satin_column_underlay"), stitches=[Stitch(*start_point)] ) connector = self.offset_center_line split_line = shgeo.LineString(self.find_cut_points(start_point)) start = connector.project(nearest_points(split_line, connector)[1]) - - if self.end_point is None: - end = 0 - elif satins[0] is None: - if self._center_walk_is_odd(): - end = 0 - else: - end = connector.length - elif satins[1] is None: - if self._center_walk_is_odd(): - end = connector.length - else: - end = 0 + if connect_to_satin_end and not self._center_walk_is_odd(): + end = connector.length else: - if not self._center_walk_is_odd(): - end = 0 - else: - split_line = shgeo.LineString(self.find_cut_points(self.end_point)) - end = connector.project(nearest_points(split_line, connector)[1]) - + split_line = shgeo.LineString(self.find_cut_points(first_stitch_group.stitches[0])) + end = connector.project(nearest_points(split_line, connector)[1]) start_path = substring(connector, start, end) + stitches = [Stitch(*coord) for coord in start_path.coords] stitch_group = StitchGroup( color=self.color, - tags=("satin_column", "satin_column_underlay"), stitches=stitches ) stitch_group = self.connect_and_add(start_stitch_group, stitch_group) + stitch_group.add_tags(("satin_column", "satin_column_underlay")) return stitch_group - def do_end_point_connection(self): - if self._center_walk_is_odd(): - return StitchGroup() - center_line = self.offset_center_line.reverse() - stitches = [Stitch(*coord) for coord in center_line.coords] - stitch_group = StitchGroup( + def do_end_path(self): + return StitchGroup( color=self.color, - tags=("satin_column", "satin_column_underlay"), - stitches=stitches + tags=("satin_column",), + stitches=[Point(*self.end_point)] ) - return stitch_group - def _do_underlay_stitch_groups(self, i, satin, stitch_group): + def _do_underlay_stitch_groups(self): + stitch_groups = [] if self.center_walk_underlay: - center_walk = satin.do_center_walk() - stitch_group = self.connect_and_add(stitch_group, center_walk) - - # if they just went one stitch back, it's not really necessary to add all the underlays - if i == 0 or satin.center_line.length > self.zigzag_spacing: - if self.contour_underlay: - contour = satin.do_contour_underlay() - stitch_group = self.connect_and_add(stitch_group, contour) - - if self.zigzag_underlay: - # zigzag underlay comes after contour walk underlay, so that the - # zigzags sit on the contour walk underlay like rail ties on rails. - zigzag = satin.do_zigzag_underlay() - stitch_group = self.connect_and_add(stitch_group, zigzag) - return stitch_group + stitch_groups.extend(self.do_center_walk()) + + if self.contour_underlay: + stitch_groups.extend(self.do_contour_underlay()) + + if self.zigzag_underlay: + stitch_groups.extend(self.do_zigzag_underlay()) + + return stitch_groups + + def _to_stitch_group(self, linestring, tags, reverse=False): + if reverse: + linestring = linestring.reverse() + return StitchGroup( + color=self.color, + tags=tags, + stitches=[Stitch(*coord) for coord in linestring.coords] + ) def do_contour_underlay(self): # "contour walk" underlay: do stitches up one side and down the @@ -1341,6 +1325,19 @@ class SatinColumn(EmbroideryElement): else: second_side.reverse() + if self.end_point: + stitch_groups = [] + tags = ("satin_column", "satin_column_underlay", "satin_contour_underlay") + first_linestring = shgeo.LineString(first_side) + first_start, first_end = self._split_linestring_at_end_point(first_linestring) + second_linestring = shgeo.LineString(second_side) + second_end, second_start = self._split_linestring_at_end_point(second_linestring) + stitch_groups.append(self._to_stitch_group(first_start, tags)) + stitch_groups.append(self._to_stitch_group(second_end, tags)) + stitch_groups.append(self._to_stitch_group(second_start, tags)) + stitch_groups.append(self._to_stitch_group(first_end, tags)) + return stitch_groups + stitch_group = StitchGroup( color=self.color, tags=("satin_column", "satin_column_underlay", "satin_contour_underlay"), @@ -1349,28 +1346,39 @@ class SatinColumn(EmbroideryElement): self.add_running_stitches(first_side[-1], second_side[0], stitch_group) stitch_group.stitches += second_side - return stitch_group + return [stitch_group] def do_center_walk(self): # Center walk underlay is just a running stitch down and back on the # center line between the bezier curves. repeats = self.center_walk_underlay_repeats - stitches = self._get_center_line_stitches() - - repeated_stitches = [] - for i in range(repeats - 1): - if i % 2 == 0: - repeated_stitches.extend(reversed(stitches)) - else: - repeated_stitches.extend(stitches) - stitches.extend(repeated_stitches) + stitch_groups = [] + stitches = self._get_center_line_stitches(self.center_walk_underlay_position) + if self.end_point: + tags = ("satin_column", "satin_column_underlay", "satin_center_walk") + stitches = shgeo.LineString(stitches) + start, end = self._split_linestring_at_end_point(stitches) + if self._center_walk_is_odd(): + end, start = start, end + stitch_groups.append(self._to_stitch_group(start, tags)) + stitch_groups.append(self._to_stitch_group(end, tags, True)) + else: + stitch_group = StitchGroup( + color=self.color, + tags=("satin_column", "satin_column_underlay", "satin_center_walk"), + stitches=stitches + ) + stitch_groups.append(stitch_group) - return StitchGroup( - color=self.color, - tags=("satin_column", "satin_column_underlay", "satin_center_walk"), - stitches=stitches - ) + for stitch_group in stitch_groups: + stitch_count = len(stitch_group.stitches) + for i in range(repeats - 1): + if i % 2 == 0: + stitch_group.stitches += reversed(stitch_group.stitches[:stitch_count]) + else: + stitch_group.stitches += stitch_group.stitches[:stitch_count] + return stitch_groups def do_zigzag_underlay(self): # zigzag underlay, usually done at a much lower density than the @@ -1383,7 +1391,7 @@ class SatinColumn(EmbroideryElement): # "German underlay" described here: # http://www.mrxstitch.com/underlay-what-lies-beneath-machine-embroidery/ - stitch_group = StitchGroup(color=self.color) + stitch_groups = [] pairs = self.plot_points_on_rails(self.zigzag_underlay_spacing / 2.0, -self.zigzag_underlay_inset_px, @@ -1392,35 +1400,72 @@ class SatinColumn(EmbroideryElement): if self._center_walk_is_odd(): pairs = list(reversed(pairs)) - # This organizes the points in each side in the order that they'll be - # visited. - # take a points, from each side in turn, then go backed over the other points - points = [p[i % 2] for i, p in enumerate(pairs)] + list(reversed([p[i % 2] for i, p in enumerate(pairs, 1)])) + # This organizes the points in each side in the order that they'll be visited. + # take a point, from each side in turn, then go backed over the other points + point_groups = [p[i % 2] for i, p in enumerate(pairs)], list(reversed([p[i % 2] for i, p in enumerate(pairs, 1)])) + + start_groups = [] + end_groups = [] + for points in point_groups: + if not self.end_point: + stitch_groups.append(self._generate_zigzag_stitch_group(points)) + continue + zigzag_line = shgeo.LineString(points) + start, end = self._split_linestring_at_end_point(zigzag_line) + start_groups.append(self._generate_zigzag_stitch_group([Stitch(*point) for point in start.coords])) + end_groups.append(self._generate_zigzag_stitch_group([Stitch(*point) for point in end.coords])) + if start_groups: + stitch_groups.append(self.connect_and_add(start_groups[0], end_groups[-1])) + stitch_groups.append(self.connect_and_add(start_groups[-1], end_groups[0])) + return stitch_groups + + def _generate_zigzag_stitch_group(self, points): max_len = self.zigzag_underlay_max_stitch_length last_point = None + stitch_group = StitchGroup(color=self.color) for point in points: if last_point and max_len: if last_point.distance(point) > max_len: split_points = running_stitch.split_segment_even_dist(last_point, point, max_len) for p in split_points: - stitch_group.add_stitch(p) + stitch_group.add_stitch(p, ("split_stitch",)) last_point = point - stitch_group.add_stitch(point) - + stitch_group.add_stitch(point, ("edge",)) stitch_group.add_tags(("satin_column", "satin_column_underlay", "satin_zigzag_underlay")) return stitch_group - def _do_top_layer_stitch_group(self, satin): + def _do_top_layer_stitch_group(self): if self.satin_method == 'e_stitch': - stitch_group = satin.do_e_stitch() + stitch_group = self.do_e_stitch() elif self.satin_method == 's_stitch': - stitch_group = satin.do_s_stitch() + stitch_group = self.do_s_stitch() elif self.satin_method == 'zigzag': - stitch_group = satin.do_zigzag() + stitch_group = self.do_zigzag() else: - stitch_group = satin.do_satin() - return stitch_group + stitch_group = self.do_satin() + + if self.end_point: + return self._split_top_layer(stitch_group) + return [stitch_group] + + def _split_linestring_at_end_point(self, linestring): + split_line = shgeo.LineString(self.find_cut_points(self.end_point)) + split_point = nearest_points(linestring, split_line)[0] + project = linestring.project(split_point) + start = substring(linestring, 0, project) + end = substring(linestring, project, linestring.length) + return start, end + + def _split_top_layer(self, stitch_group): + top_layer = shgeo.LineString(stitch_group.stitches) + start, end = self._split_linestring_at_end_point(top_layer) + stitch_group2 = deepcopy(stitch_group) + stitch_group2.stitches = [Stitch(*point) for point in end.reverse().coords] + stitch_group1 = stitch_group + stitch_group1.stitches = [Stitch(*point) for point in start.coords] + top_layer_stitch_groups = [stitch_group1, stitch_group2] + return top_layer_stitch_groups def do_satin(self): # satin: do a zigzag pattern, alternating between the paths. The @@ -1750,54 +1795,14 @@ class SatinColumn(EmbroideryElement): point = point.target_point return point - def _split_satin(self): - if self.end_point is not None: - satins = self.split(self.end_point) - if self.reverse_rails == 'automatic': - self._adapt_automatic_rail_swapping(satins) - if satins[0] is None: - if not self._center_walk_is_odd(): - satins[1] = satins[1].reverse() - if self.swap_rails: - satins[1] = satins[1].flip() - satins = [None, satins[1]] - elif satins[1] is not None: - if self._center_walk_is_odd(): - satins[0] = satins[0].reverse() - else: - satins[1] = satins[1].reverse() - if self.swap_rails: - satins[0] = satins[0].flip() - else: - if self._center_walk_is_odd(): - satins[0] = satins[0].reverse() - satins = [satins[0], None] - else: - satins = [self, None] - return satins - - def _adapt_automatic_rail_swapping(self, satins): # noqa: C901 - reverse_rails = self._get_rails_to_reverse() - if reverse_rails == (False, False): - if satins[0] is not None: - satins[0].set_param('reverse_rails', 'none') - if satins[1] is not None: - satins[1].set_param('reverse_rails', 'none') - elif reverse_rails == (True, False): - if satins[0] is not None: - satins[0].set_param('reverse_rails', 'first') - if satins[1] is not None: - satins[1].set_param('reverse_rails', 'first') - elif reverse_rails == (False, True): - if satins[0] is not None: - satins[0].set_param('reverse_rails', 'second') - if satins[1] is not None: - satins[1].set_param('reverse_rails', 'second') - elif reverse_rails == (True, True): - if satins[0] is not None: - satins[0].set_param('reverse_rails', 'both') - if satins[1] is not None: - satins[1].set_param('reverse_rails', 'both') + def _sort_stitch_groups(self, stitch_groups): + if self.end_point: + ordered_stitch_groups = [] + ordered_stitch_groups.extend(stitch_groups[::2]) + ordered_stitch_groups.append(self._connect_stitch_group_with_point(stitch_groups[1], ordered_stitch_groups[-1].stitches[-1], True)) + ordered_stitch_groups.extend(stitch_groups[1::2]) + return ordered_stitch_groups + return stitch_groups def to_stitch_groups(self, last_stitch_group=None): # Stitch a variable-width satin column, zig-zagging between two paths. @@ -1805,45 +1810,42 @@ class SatinColumn(EmbroideryElement): # beziers. The boundary points between beziers serve as "checkpoints", # allowing the user to control how the zigzags flow around corners. - satins = self._split_satin() - - stitch_group = StitchGroup( - color=self.color, - force_lock_stitches=self.force_lock_stitches, - lock_stitches=self.lock_stitches - ) + stitch_groups = [] start_point = self.start_point if start_point is None and last_stitch_group is not None and self.start_at_nearest_point: start_point = nearest_points(shgeo.Point(*last_stitch_group.stitches[-1]), self.center_line)[1] start_point = Point(*list(start_point.coords[0])) - if start_point is not None: - start_path = self.do_start_path(satins, start_point) - stitch_group = self.connect_and_add(stitch_group, start_path) - for i, satin in enumerate(satins): - if satin is None: - continue + # underlays + stitch_groups.extend(self._do_underlay_stitch_groups()) + + # top layer + stitch_groups.extend(self._do_top_layer_stitch_group()) + + # order stitch groups + stitch_groups = self._sort_stitch_groups(stitch_groups) + + # start and end + if start_point is not None: + stitch_groups = [self._connect_stitch_group_with_point(stitch_groups[0], start_point)] + stitch_groups + if self.end_point: + stitch_groups.append(self.do_end_path()) - if i > 0 and None not in satins: - end_point_connection = satin.do_end_point_connection() - stitch_group = self.connect_and_add(stitch_group, end_point_connection) + # assemble stitch groups + stitch_group = StitchGroup( + color=self.color, + force_lock_stitches=self.force_lock_stitches, + lock_stitches=self.lock_stitches + ) - stitch_group = self._do_underlay_stitch_groups(i, satin, stitch_group) - final_stitch_group = self._do_top_layer_stitch_group(satin) - stitch_group = self.connect_and_add(stitch_group, final_stitch_group) + for satin_layer in stitch_groups: + if satin_layer and satin_layer.stitches: + stitch_group = self.connect_and_add(stitch_group, satin_layer) if not stitch_group.stitches: return [] - if self.end_point: - ending_point_stitch_group = StitchGroup( - color=self.color, - tags=("satin_column"), - stitches=[Point(*self.end_point)] - ) - stitch_group = self.connect_and_add(stitch_group, ending_point_stitch_group) - return [stitch_group] |
