From e9278c55c34b72bb0beccf0d9b8bfe300aacac70 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 27 Nov 2022 08:37:59 +0100 Subject: This and that (#1727) * dont fail on satin with fill * fill stitch error message * convert to satin mac issue * auto_satin: add rung for two node old style satins * avoid divide by zero in intersect_region_with_grating * fix for incorrect stagger in guided fill * better rail sectioning algorithm * fix #1780 * fix #1816 Co-authored-by: Lex Neva --- lib/elements/satin_column.py | 107 ++++++++++--------------------------------- 1 file changed, 24 insertions(+), 83 deletions(-) (limited to 'lib/elements/satin_column.py') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index e4d69361..886b3f85 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -7,7 +7,6 @@ from copy import deepcopy from itertools import chain import numpy as np -from shapely import affinity as shaffinity from shapely import geometry as shgeo from shapely.ops import nearest_points @@ -16,23 +15,11 @@ from inkex import paths 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 ..utils import Point, cache, cut, cut_multiple from .element import EmbroideryElement, param, PIXELS_PER_MM from .validation import ValidationError, ValidationWarning -class SatinHasFillError(ValidationError): - name = _("Satin column has fill") - description = _("Satin column: Object has a fill (but should not)") - steps_to_solve = [ - _("* Select this object."), - _("* Open the Fill and Stroke panel"), - _("* Open the Fill tab"), - _("* Disable the Fill"), - _("* Alternative: open Params and switch this path to Stroke to disable Satin Column mode") - ] - - class TooFewPathsError(ValidationError): name = _("Too few subpaths") description = _("Satin column: Object has too few subpaths. A satin column should have at least two subpaths (the rails).") @@ -338,24 +325,6 @@ class SatinColumn(EmbroideryElement): @cache def flattened_rungs(self): """The rungs, as LineStrings.""" - rungs = [] - for rung in self._raw_rungs: - # make sure each rung intersects both rails - if not rung.intersects(self.flattened_rails[0]) or not rung.intersects(self.flattened_rails[1]): - # the rung does not intersect both rails - # get nearest points on rungs - start = nearest_points(rung, self.flattened_rails[0])[1] - end = nearest_points(rung, self.flattened_rails[1])[1] - # extend from the nearest points just a little bit to make sure that we get an intersection - rung = shaffinity.scale(shgeo.LineString([start, end]), 1.01, 1.01) - rungs.append(rung) - else: - rungs.append(rung) - return tuple(rungs) - - @property - @cache - def _raw_rungs(self): return tuple(shgeo.LineString(self.flatten_subpath(rung)) for rung in self.rungs) @property @@ -379,21 +348,18 @@ class SatinColumn(EmbroideryElement): for rail in self.rails: points = self.strip_control_points(rail) - # ignore the start and end - points = points[1:-1] + if len(points) > 2: + # Don't bother putting rungs at the start and end. + points = points[1:-1] + else: + # But do include one at the start if we wouldn't add one otherwise. + # This avoids confusing other parts of the code. + points = points[:-1] rung_endpoints.append(points) rungs = [] for start, end in zip(*rung_endpoints): - # Expand the points just a bit to ensure that shapely thinks they - # intersect with the rails even with floating point inaccuracy. - start = Point(*start) - end = Point(*end) - start, end = self.offset_points(start, end, (0.01, 0.01), (0, 0)) - start = list(start) - end = list(end) - rungs.append([[start, start, start], [end, end, end]]) return rungs @@ -446,39 +412,22 @@ class SatinColumn(EmbroideryElement): indices_by_length = sorted(list(range(num_paths)), key=lambda index: paths[index].length, reverse=True) return indices_by_length[:2] - def _cut_rail(self, rail, rung): - for segment_index, rail_segment in enumerate(rail[:]): - if rail_segment is None: - continue - - intersection = rail_segment.intersection(rung) - - # If there are duplicate points in a rung-less satin, then - # intersection will be a GeometryCollection of multiple copies - # of the same point. This reduces it that to a single point. - intersection = collapse_duplicate_point(intersection) - - if not intersection.is_empty: - cut_result = cut(rail_segment, rail_segment.project(intersection)) - rail[segment_index:segment_index + 1] = cut_result - - if cut_result[1] is None: - # if we were exactly at the end of one of the existing rail segments, - # stop here or we'll get a spurious second intersection on the next - # segment - break - @property @cache def flattened_sections(self): """Flatten the rails, cut with the rungs, and return the sections in pairs.""" - rails = [[rail] for rail in self.flattened_rails] + rails = list(self.flattened_rails) rungs = self.flattened_rungs - for rung in rungs: - for rail in rails: - self._cut_rail(rail, rung) + for i, rail in enumerate(rails): + cut_points = [] + + for rung in rungs: + point_on_rung, point_on_rail = nearest_points(rung, rail) + cut_points.append(rail.project(point_on_rail)) + + rails[i] = cut_multiple(rail, cut_points) for rail in rails: for i in range(len(rail)): @@ -502,19 +451,15 @@ class SatinColumn(EmbroideryElement): return sections def validation_warnings(self): - for rung in self._raw_rungs: + for rung in self.flattened_rungs: for rail in self.flattened_rails: intersection = rung.intersection(rail) if intersection.is_empty: yield DanglingRungWarning(rung.interpolate(0.5, normalized=True)) def validation_errors(self): - # The node should have exactly two paths with no fill. Each - # path should have the same number of points, meaning that they - # will both be made up of the same number of bezier curves. - - if self.get_style("fill") is not None: - yield SatinHasFillError(self.shape.centroid) + # The node should have exactly two paths with the same number of points - or it should + # have two rails and at least one rung if len(self.rails) < 2: yield TooFewPathsError(self.shape.centroid) @@ -522,7 +467,7 @@ class SatinColumn(EmbroideryElement): if len(self.rails[0]) != len(self.rails[1]): yield UnequalPointsError(self.flattened_rails[0].interpolate(0.5, normalized=True)) else: - for rung in self._raw_rungs: + for rung in self.flattened_rungs: for rail in self.flattened_rails: intersection = rung.intersection(rail) if not intersection.is_empty and not isinstance(intersection, shgeo.Point): @@ -664,14 +609,10 @@ class SatinColumn(EmbroideryElement): for path_list in path_lists: if len(path_list) in (2, 4): - # Add the rung just after the start of the satin. - rung_start = path_list[0].interpolate(0.1) - rung_end = path_list[1].interpolate(0.1) + # Add the rung at the start of the satin. + rung_start = path_list[0].coords[0] + rung_end = path_list[1].coords[0] rung = shgeo.LineString((rung_start, rung_end)) - - # make it a bit bigger so that it definitely intersects - rung = shaffinity.scale(rung, 1.1, 1.1) - path_list.append(rung) def _path_list_to_satins(self, path_list): -- cgit v1.2.3 From b8161a97c3d279265cb322697f5e2e824b666fbc Mon Sep 17 00:00:00 2001 From: Kaalleen Date: Sun, 27 Nov 2022 10:17:43 +0100 Subject: satin offset_points --- lib/elements/satin_column.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/elements/satin_column.py') diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 886b3f85..26fc411d 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -124,7 +124,7 @@ class SatinColumn(EmbroideryElement): @param( 'pull_compensation_percent', _('Pull compensation percentage'), - tooltip=_('Additional pull compensation which varries as a percentage of stitch width. ' + tooltip=_('Additional pull compensation which varies as a percentage of stitch width. ' 'Two values separated by a space may be used for an aysmmetric effect.'), unit='% (each side)', type='float', @@ -654,7 +654,7 @@ class SatinColumn(EmbroideryElement): # don't contract beyond the midpoint, or we'll start expanding if offset_total < -distance: - scale = distance / offset_total + scale = -distance / offset_total offset_a = offset_a * scale offset_b = offset_b * scale @@ -799,6 +799,7 @@ class SatinColumn(EmbroideryElement): # center line between the bezier curves. inset_prop = -np.array([self.center_walk_underlay_position, 100-self.center_walk_underlay_position]) / 100 + # Do it like contour underlay, but inset all the way to the center. forward, back = self.plot_points_on_rails( self.center_walk_underlay_stitch_length, -- cgit v1.2.3