From 1112959da18dad9a1a7790905cd57e4ed5903a8b Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sat, 23 Mar 2024 09:00:40 +0100 Subject: [PATCH] use offset curve for convert satin extension (#2775) filter invalid rungs when merging satin sections --- lib/elements/satin_column.py | 12 ++++++++++++ lib/extensions/convert_to_satin.py | 23 ++++++++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 75d7e2599..10de9f825 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -941,8 +941,20 @@ class SatinColumn(EmbroideryElement): # add on the other satin's rungs rungs.extend(other_rungs) + rungs = self._get_filtered_rungs(rails, rungs) + return self._csp_to_satin(point_lists_to_csp(rails + rungs)) + def _get_filtered_rungs(self, rails, rungs): + # returns a filtered list of rungs which do intersect the rails exactly twice + rails = shgeo.MultiLineString(rails) + filtered_rungs = [] + for rung in shgeo.MultiLineString(rungs).geoms: + intersection = rung.intersection(rails) + if intersection.geom_type == "MultiPoint" and len(intersection.geoms) == 2: + filtered_rungs.append(list(rung.coords)) + return filtered_rungs + @property @cache def center_line(self): diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py index 4bb3588e6..41ed9af8b 100644 --- a/lib/extensions/convert_to_satin.py +++ b/lib/extensions/convert_to_satin.py @@ -12,12 +12,12 @@ import numpy from numpy import diff, setdiff1d, sign from shapely import geometry as shgeo -from .base import InkstitchExtension from ..elements import SatinColumn, Stroke from ..i18n import _ from ..svg import PIXELS_PER_MM, get_correction_transform from ..svg.tags import INKSTITCH_ATTRIBS from ..utils import Point +from .base import InkstitchExtension class SelfIntersectionError(Exception): @@ -119,7 +119,7 @@ class ConvertToSatin(InkstitchExtension): return [point for point, repeats in groupby(path)] def join_style_args(self, element): - """Convert svg line join style to shapely parallel offset arguments.""" + """Convert svg line join style to shapely offset_curve arguments.""" args = { # mitre is the default per SVG spec @@ -146,16 +146,12 @@ class ConvertToSatin(InkstitchExtension): if Point(*path[0]).distance(Point(*path[-1])) < 1: raise SelfIntersectionError() - # Shapely is supposed to return right sided offsets in reversed direction, which it does, except for macOS. - # To avoid direction checking, we are going to rely on left side offsets only. - # Therefore we need to reverse the original path. - reversed_path = shgeo.LineString(reversed(path)) path = shgeo.LineString(path) distance = stroke_width / 2.0 try: - left_rail = path.parallel_offset(distance, 'left', **style_args) - right_rail = reversed_path.parallel_offset(distance, 'left', **style_args) + left_rail = path.offset_curve(-distance, **style_args) + right_rail = path.offset_curve(distance, **style_args) except ValueError: # TODO: fix this error automatically # Error reference: https://github.com/inkstitch/inkstitch/issues/964 @@ -163,16 +159,13 @@ class ConvertToSatin(InkstitchExtension): "Please break up your path and try again.") + '\n') sys.exit(1) - if not isinstance(left_rail, shgeo.LineString) or \ - not isinstance(right_rail, shgeo.LineString): - # If the parallel offsets come out as anything but a LineString, that means the - # path intersects itself, when taking its stroke width into consideration. See - # the last example for parallel_offset() in the Shapely documentation: - # https://shapely.readthedocs.io/en/latest/manual.html#object.parallel_offset + if left_rail.geom_type != 'LineString' or right_rail.geom_type != 'LineString': + # If the offset curve come out as anything but a LineString, that means the + # path intersects itself, when taking its stroke width into consideration. raise SelfIntersectionError() left_rail = list(left_rail.coords) - right_rail = list(reversed(right_rail.coords)) + right_rail = list(right_rail.coords) rungs = self.generate_rungs(path, stroke_width)