From ef83ff75a0c6f83f63ee38e1617c097a887affe8 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 11 Dec 2018 19:58:13 -0500 Subject: [PATCH 01/26] use proper defaults for fill and stroke in Params (#362) --- lib/extensions/params.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 1f3032cad..386ce5dd7 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -680,15 +680,16 @@ class Params(InkstitchExtension): element = EmbroideryElement(node) classes = [] - if element.get_style("fill"): - classes.append(AutoFill) - classes.append(Fill) + if not is_command(node): + if element.get_style("fill", "black") != "none": + classes.append(AutoFill) + classes.append(Fill) - if element.get_style("stroke") and not is_command(node): - classes.append(Stroke) + if element.get_style("stroke") is not None: + classes.append(Stroke) - if element.get_style("stroke-dasharray") is None: - classes.append(SatinColumn) + if element.get_style("stroke-dasharray") is None: + classes.append(SatinColumn) return classes From e47fcaa8da14a4a40f151e90915f3d0d208ec7b8 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 11 Dec 2018 19:58:20 -0500 Subject: [PATCH 02/26] tidy up imports --- lib/extensions/params.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 386ce5dd7..6525cae28 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -1,23 +1,24 @@ # -*- coding: UTF-8 -*- +from collections import defaultdict +from copy import copy +from itertools import groupby +import json import os import sys -import json -import traceback from threading import Thread, Event -from copy import copy +import traceback + import wx from wx.lib.scrolledpanel import ScrolledPanel -from collections import defaultdict -from itertools import groupby -from .base import InkstitchExtension -from ..i18n import _ -from ..stitch_plan import patches_to_stitch_plan -from ..elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn -from ..utils import get_resource_dir -from ..simulator import EmbroiderySimulator from ..commands import is_command +from ..elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn +from ..i18n import _ +from ..simulator import EmbroiderySimulator +from ..stitch_plan import patches_to_stitch_plan +from ..utils import get_resource_dir +from .base import InkstitchExtension def presets_path(): From 74e6dac0115fc508da4cae3f365739b07e241e59 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 11 Dec 2018 20:09:43 -0500 Subject: [PATCH 03/26] avoid creating paths with empty 'd' in stitch plan (#363) --- lib/svg/svg.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/svg/svg.py b/lib/svg/svg.py index b1cc91d91..e753243ce 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -21,6 +21,9 @@ def color_block_to_point_lists(color_block): if not stitch.jump and not stitch.color_change: point_lists[-1].append(stitch.as_tuple()) + # filter out empty point lists + point_lists = [p for p in point_lists if p] + return point_lists From 7a73b201159bec1c45ad483251264baf872f6952 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 11 Dec 2018 20:09:50 -0500 Subject: [PATCH 04/26] tidy imports --- lib/svg/svg.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/svg/svg.py b/lib/svg/svg.py index e753243ce..3fceebfba 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -1,12 +1,12 @@ -import simpletransform -import simplestyle import inkex +import simplestyle +import simpletransform -from .units import get_viewbox_transform -from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG, SVG_DEFS_TAG -from .realistic_rendering import realistic_stitch, realistic_filter from ..i18n import _ from ..utils import cache +from .realistic_rendering import realistic_stitch, realistic_filter +from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG, SVG_DEFS_TAG +from .units import get_viewbox_transform def color_block_to_point_lists(color_block): From 9ae97154d689b188c796e5d11820e026ed1f9326 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 12 Dec 2018 20:26:22 -0500 Subject: [PATCH 05/26] add option to skip last stitch in fill rows --- lib/elements/auto_fill.py | 18 +++++++++++++++++- lib/elements/fill.py | 21 +++++++++++++++++---- lib/stitches/auto_fill.py | 26 +++++++++++++++----------- lib/stitches/fill.py | 15 ++++++++------- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 65b11fb16..486243ea6 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -1,8 +1,10 @@ import math + from shapely import geometry as shgeo + from ..i18n import _ -from ..utils import cache from ..stitches import auto_fill +from ..utils import cache from .element import param, Patch from .fill import Fill @@ -92,6 +94,18 @@ class AutoFill(Fill): def fill_underlay_inset(self): return self.get_float_param('fill_underlay_inset_mm', 0) + @property + @param( + 'fill_underlay_skip_last', + _('Skip last stitch in each row'), + tooltip=_('The last stitch in each row is quite close to the first stitch in the next row. ' + 'Skipping it decreases stitch count and density.'), + group=_('AutoFill Underlay'), + type='boolean', + default=False) + def fill_underlay_skip_last(self): + return self.get_boolean_param("fill_underlay_skip_last", False) + @property @param('expand_mm', _('Expand'), @@ -150,6 +164,7 @@ class AutoFill(Fill): self.fill_underlay_max_stitch_length, self.running_stitch_length, self.staggers, + self.fill_underlay_skip_last, starting_point)) starting_point = stitches[-1] @@ -160,6 +175,7 @@ class AutoFill(Fill): self.max_stitch_length, self.running_stitch_length, self.staggers, + self.skip_last, starting_point, ending_point)) diff --git a/lib/elements/fill.py b/lib/elements/fill.py index 4156a24b0..357adf4b7 100644 --- a/lib/elements/fill.py +++ b/lib/elements/fill.py @@ -1,11 +1,12 @@ -from shapely import geometry as shgeo import math -from .element import param, EmbroideryElement, Patch +from shapely import geometry as shgeo + from ..i18n import _ +from ..stitches import legacy_fill from ..svg import PIXELS_PER_MM from ..utils import cache -from ..stitches import legacy_fill +from .element import param, EmbroideryElement, Patch class Fill(EmbroideryElement): @@ -40,6 +41,17 @@ class Fill(EmbroideryElement): # SVG spec says the default fill is black return self.get_style("fill", "#000000") + @property + @param( + 'skip_last', + _('Skip last stitch in each row'), + tooltip=_('The last stitch in each row is quite close to the first stitch in the next row. ' + 'Skipping it decreases stitch count and density.'), + type='boolean', + default=False) + def skip_last(self): + return self.get_boolean_param("skip_last", False) + @property @param( 'flip', @@ -133,5 +145,6 @@ class Fill(EmbroideryElement): self.end_row_spacing, self.max_stitch_length, self.flip, - self.staggers) + self.staggers, + self.skip_last) return [Patch(stitches=stitch_list, color=self.color) for stitch_list in stitch_lists] diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 28c79effa..0b5782e58 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -1,13 +1,14 @@ -import sys -import shapely -import networkx -from itertools import groupby, izip from collections import deque +from itertools import groupby, izip +import sys + +import networkx +import shapely -from .fill import intersect_region_with_grating, row_num, stitch_row -from .running_stitch import running_stitch from ..i18n import _ from ..utils.geometry import Point as InkstitchPoint, cut +from .fill import intersect_region_with_grating, row_num, stitch_row +from .running_stitch import running_stitch class MaxQueueLengthExceeded(Exception): @@ -39,7 +40,7 @@ class PathEdge(object): return self.key == self.SEGMENT_KEY -def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, running_stitch_length, staggers, starting_point, ending_point=None): +def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last, starting_point, ending_point=None): stitches = [] rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) @@ -48,7 +49,7 @@ def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, run graph = build_graph(shape, segments, angle, row_spacing) path = find_stitch_path(graph, segments, starting_point, ending_point) - stitches.extend(path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers)) + stitches.extend(path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last)) return stitches @@ -513,7 +514,10 @@ def connect_points(shape, start, end, running_stitch_length, row_spacing): # Now do running stitch along the path we've found. running_stitch() will # avoid cutting sharp corners. path = [InkstitchPoint(*p) for p in points] - return running_stitch(path, running_stitch_length) + stitches = running_stitch(path, running_stitch_length) + + # The row of stitches already stitched the first point, so skip it. + return stitches[1:] def trim_end(path): @@ -521,14 +525,14 @@ def trim_end(path): path.pop() -def path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers): +def path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last): path = collapse_sequential_outline_edges(graph, path) stitches = [] for edge in path: if edge.is_segment(): - stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers) + stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers, skip_last) else: stitches.extend(connect_points(shape, edge[0], edge[1], running_stitch_length, row_spacing)) diff --git a/lib/stitches/fill.py b/lib/stitches/fill.py index af0a84036..e00d66dec 100644 --- a/lib/stitches/fill.py +++ b/lib/stitches/fill.py @@ -1,15 +1,16 @@ -import shapely import math +import shapely + from ..svg import PIXELS_PER_MM from ..utils import cache, Point as InkstitchPoint -def legacy_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, flip, staggers): +def legacy_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, flip, staggers, skip_last): rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing, flip) groups_of_segments = pull_runs(rows_of_segments, shape, row_spacing) - return [section_to_stitches(group, angle, row_spacing, max_stitch_length, staggers) + return [section_to_stitches(group, angle, row_spacing, max_stitch_length, staggers, skip_last) for group in groups_of_segments] @@ -37,7 +38,7 @@ def adjust_stagger(stitch, angle, row_spacing, max_stitch_length, staggers): return stitch - offset * east(angle) -def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers): +def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers, skip_last=False): # We want our stitches to look like this: # # ---*-----------*----------- @@ -81,7 +82,7 @@ def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, stagge stitches.append(beg + offset * row_direction) offset += max_stitch_length - if (end - stitches[-1]).length() > 0.1 * PIXELS_PER_MM: + if (end - stitches[-1]).length() > 0.1 * PIXELS_PER_MM and not skip_last: stitches.append(end) @@ -164,7 +165,7 @@ def intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing=Non return rows -def section_to_stitches(group_of_segments, angle, row_spacing, max_stitch_length, staggers): +def section_to_stitches(group_of_segments, angle, row_spacing, max_stitch_length, staggers, skip_last): stitches = [] swap = False @@ -174,7 +175,7 @@ def section_to_stitches(group_of_segments, angle, row_spacing, max_stitch_length if (swap): (beg, end) = (end, beg) - stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers) + stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers, skip_last) swap = not swap From 8f3c922011a5c39a6154863160ea8354a502ed42 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 13 Dec 2018 20:10:50 -0500 Subject: [PATCH 06/26] fix style --- lib/stitches/auto_fill.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 0b5782e58..4a0fbed70 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -40,7 +40,16 @@ class PathEdge(object): return self.key == self.SEGMENT_KEY -def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last, starting_point, ending_point=None): +def auto_fill(shape, + angle, + row_spacing, + end_row_spacing, + max_stitch_length, + running_stitch_length, + staggers, + skip_last, + starting_point, + ending_point=None): stitches = [] rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) From dc77f5cebcdfa4e1fdeff9c8b231061f5b07de42 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 18 Dec 2018 20:25:08 -0500 Subject: [PATCH 07/26] fix style --- lib/extensions/params.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 8cd0debe8..074a4288d 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -3,7 +3,6 @@ from collections import defaultdict from copy import copy from itertools import groupby -import json import os import sys @@ -13,7 +12,6 @@ from wx.lib.scrolledpanel import ScrolledPanel from ..commands import is_command from ..elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn -from ..simulator import EmbroiderySimulator from ..gui import PresetsPanel, SimulatorPreview from ..i18n import _ from ..utils import get_resource_dir From 3cf7f1547c9162f81afcbfb53df65b0897b43059 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 18 Dec 2018 20:31:53 -0500 Subject: [PATCH 08/26] fix error message --- lib/elements/satin_column.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 1f9854edb..a1ecfb472 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -1,12 +1,13 @@ -from itertools import chain, izip from copy import deepcopy -from shapely import geometry as shgeo, affinity as shaffinity -import cubicsuperpath +from itertools import chain, izip + +import cubicsuperpath +from shapely import geometry as shgeo, affinity as shaffinity -from .element import param, EmbroideryElement, Patch from ..i18n import _ -from ..utils import cache, Point, cut, collapse_duplicate_point from ..svg import line_strings_to_csp, point_lists_to_csp +from ..utils import cache, Point, cut, collapse_duplicate_point +from .element import param, EmbroideryElement, Patch class SatinColumn(EmbroideryElement): @@ -255,7 +256,7 @@ class SatinColumn(EmbroideryElement): intersections += len(intersection) break elif not isinstance(intersection, shgeo.Point): - self.fatal("intersection is a: %s %s" % (intersection, intersection.geoms)) + self.fatal("INTERNAL ERROR: intersection is: %s %s" % (intersection, getattr(intersection, 'geoms', None))) else: intersections += 1 From 9f4f6d85a03029af12c04bdfc61a22b639ff738f Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 19 Dec 2018 12:46:31 -0500 Subject: [PATCH 09/26] fix duplicate point handling --- lib/utils/geometry.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py index ab7f24c11..05cfc4b28 100644 --- a/lib/utils/geometry.py +++ b/lib/utils/geometry.py @@ -1,6 +1,7 @@ -from shapely.geometry import LineString, Point as ShapelyPoint import math +from shapely.geometry import LineString, Point as ShapelyPoint + def cut(line, distance, normalized=False): """ Cuts a LineString in two at a distance from its starting point. @@ -51,9 +52,8 @@ def cut_path(points, length): def collapse_duplicate_point(geometry): - if hasattr(geometry, 'geoms'): - if geometry.area < 0.01: - return geometry.representative_point() + if geometry.area < 0.01: + return geometry.representative_point() return geometry From 32458c619a38928f403e5924c8ba864fe7ae346c Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 19 Dec 2018 14:39:47 -0500 Subject: [PATCH 10/26] fix fill == none detection --- lib/extensions/params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 074a4288d..4d04ba230 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -462,7 +462,7 @@ class Params(InkstitchExtension): classes = [] if not is_command(node): - if element.get_style("fill", "black") != "none": + if element.get_style("fill", "black") is not None: classes.append(AutoFill) classes.append(Fill) From c8d354a2fc663d49478b1b87329b0e6f11616704 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 22 Dec 2018 22:12:23 -0500 Subject: [PATCH 11/26] print error message correctly --- lib/output.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/output.py b/lib/output.py index 5fa7791fd..2745de1d1 100644 --- a/lib/output.py +++ b/lib/output.py @@ -1,11 +1,12 @@ -import sys import pyembroidery +import sys + import simpletransform -from .i18n import _ -from .utils import Point -from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform from .commands import global_command +from .i18n import _ +from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform +from .utils import Point def get_command(stitch): @@ -99,5 +100,5 @@ def write_embroidery_file(file_path, stitch_plan, svg, settings={}): except IOError as e: # L10N low-level file error. %(error)s is (hopefully?) translated by # the user's system automatically. - print >> sys.stderr, _("Error writing to %(path)s: %(error)s") % dict(path=file_path, error=e.message) + print >> sys.stderr, _("Error writing to %(path)s: %(error)s") % dict(path=file_path, error=e.strerror) sys.exit(1) From 004df12e88d1531740238ab9831577123e539196 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 30 Dec 2018 19:59:07 -0500 Subject: [PATCH 12/26] don't crash on a design with no stitches --- lib/gui/simulator.py | 4 ++-- lib/output.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py index 0eed18c92..e0d78983c 100644 --- a/lib/gui/simulator.py +++ b/lib/gui/simulator.py @@ -385,8 +385,8 @@ class DrawingPanel(wx.Panel): self.go() def choose_zoom_and_pan(self, event=None): - # ignore if called before we load the stitch plan - if not self.width and not self.height: + # ignore if EVT_SIZE fired before we load the stitch plan + if not self.width and not self.height and event is not None: return panel_width, panel_height = self.GetClientSize() diff --git a/lib/output.py b/lib/output.py index 2745de1d1..216227657 100644 --- a/lib/output.py +++ b/lib/output.py @@ -5,6 +5,7 @@ import simpletransform from .commands import global_command from .i18n import _ +from .stitch_plan import Stitch from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform from .utils import Point @@ -58,6 +59,7 @@ def write_embroidery_file(file_path, stitch_plan, svg, settings={}): origin = get_origin(svg) pattern = pyembroidery.EmbPattern() + stitch = Stitch(0, 0) for color_block in stitch_plan: pattern.add_thread(color_block.color.pyembroidery_thread) From 04ed93cb25198d216ea7adef66a1f4a1be854b58 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 4 Jan 2019 20:08:11 -0500 Subject: [PATCH 13/26] ability to stitch a single-point running stitch path --- lib/stitches/running_stitch.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/stitches/running_stitch.py b/lib/stitches/running_stitch.py index 5f8ed21e6..fa8c50ba2 100644 --- a/lib/stitches/running_stitch.py +++ b/lib/stitches/running_stitch.py @@ -23,7 +23,7 @@ def running_stitch(points, stitch_length): segment_start = points[0] last_segment_direction = None - # This tracks the distance we've travelled along the current segment so + # This tracks the distance we've traveled along the current segment so # far. Each time we make a stitch, we add the stitch_length to this # value. If we fall off the end of the current segment, we carry over # the remainder to the next segment. @@ -62,8 +62,12 @@ def running_stitch(points, stitch_length): last_segment_direction = segment_direction distance -= segment_length + # stitch a single point if the path has a length of zero + if not output: + output.append(segment_start) + # stitch the last point unless we're already almost there - if (segment_start - output[-1]).length() > 0.1: + if (segment_start - output[-1]).length() > 0.1 or len(output) == 0: output.append(segment_start) return output From 6a8edfc1cdc19e90b85c4023ef8611f506317e2d Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 4 Jan 2019 20:22:10 -0500 Subject: [PATCH 14/26] fix crash if fill start and end points are very close --- lib/stitches/auto_fill.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 4a0fbed70..c7e654735 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -327,14 +327,16 @@ def get_outline_nodes(graph, outline_index=0): def find_initial_path(graph, starting_point, ending_point=None): starting_node = nearest_node_on_outline(graph, starting_point) - if ending_point is None: + if ending_point is not None: + ending_node = nearest_node_on_outline(graph, ending_point) + + if ending_point is None or starting_node is ending_node: # If they didn't give an ending point, pick either neighboring node # along the outline -- doesn't matter which. We do this because # the algorithm requires we start with _some_ path. neighbors = [n for n, keys in graph.adj[starting_node].iteritems() if 'outline' in keys] return [PathEdge((starting_node, neighbors[0]), "initial")] else: - ending_node = nearest_node_on_outline(graph, ending_point) outline_nodes = get_outline_nodes(graph) # Multiply the outline_nodes list by 2 (duplicate it) because From dd4e5c8e2344518d08ec76559e0ee8a1f8bfb327 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 17 Jan 2019 19:38:15 -0500 Subject: [PATCH 15/26] use pyembroidery 1.2.30 for JEF fix --- pyembroidery | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyembroidery b/pyembroidery index d26e28002..73ca3eb8c 160000 --- a/pyembroidery +++ b/pyembroidery @@ -1 +1 @@ -Subproject commit d26e280020bbe1d2b5af977b9cd74f60db8be71b +Subproject commit 73ca3eb8c32da0636a4cad7a6915440705a5c71f From 3d993778524c523cfa96d80d9f1df88a671bbe24 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 17 Jan 2019 19:55:51 -0500 Subject: [PATCH 16/26] don't add jump stitches between satin column fill and top stitching --- lib/elements/satin_column.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index a1ecfb472..f891e049b 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -717,22 +717,22 @@ class SatinColumn(EmbroideryElement): # First, verify that we have valid paths. self.validate_satin_column() - patches = [] + patch = Patch(color=self.color) if self.center_walk_underlay: - patches.append(self.do_center_walk()) + patch += self.do_center_walk() if self.contour_underlay: - patches.append(self.do_contour_underlay()) + patch += self.do_contour_underlay() 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. - patches.append(self.do_zigzag_underlay()) + patch += self.do_zigzag_underlay() if self.e_stitch: - patches.append(self.do_e_stitch()) + patch += self.do_e_stitch() else: - patches.append(self.do_satin()) + patch += self.do_satin() - return patches + return [patch] From 89def2161123a12b38bc0cedea9853ee02c10c87 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 19 Jan 2019 18:35:59 -0500 Subject: [PATCH 17/26] pin to numpy 1.16 to fix pyinstaller on windows --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e1ab6e084..e81b32e3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ networkx==2.2 shapely lxml appdirs -numpy +numpy<1.16.0 jinja2 requests colormath From 7b04eb123423d52aa088c9f5976079a0297ed5b6 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Tue, 1 Jan 2019 19:47:28 +0100 Subject: [PATCH 18/26] Fix print metadata (#372) --- lib/extensions/print_pdf.py | 1 - print/templates/operator_detailedview.html | 4 ++-- print/templates/operator_overview.html | 2 +- print/templates/print_detail.html | 2 +- print/templates/print_overview.html | 2 +- print/templates/ui.html | 4 ++-- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/extensions/print_pdf.py b/lib/extensions/print_pdf.py index d2e2365d7..c718fa092 100644 --- a/lib/extensions/print_pdf.py +++ b/lib/extensions/print_pdf.py @@ -365,7 +365,6 @@ class Print(InkstitchExtension): 'num_trims': stitch_plan.num_trims, 'dimensions': stitch_plan.dimensions_mm, 'num_stitches': stitch_plan.num_stitches, - 'estimated_time': '', # TODO 'estimated_thread': '', # TODO }, svg_overview=overview_svg, diff --git a/print/templates/operator_detailedview.html b/print/templates/operator_detailedview.html index 608b36619..c74a7a728 100644 --- a/print/templates/operator_detailedview.html +++ b/print/templates/operator_detailedview.html @@ -18,7 +18,7 @@ ##

- {{ svg_overview|safe }} + {{ svg_overview|replace("

  • ", "")|replace("
  • ", "")|safe }}

    {{ _('Unique Colors') }}: {{ job.num_colors }} @@ -51,7 +51,7 @@

    - {{ color_block.svg_preview|safe }} + {{ color_block.svg_preview|replace("

  • ", "")|replace("
  • ", "")|safe }}

    {{ color_block.color.name }} diff --git a/print/templates/operator_overview.html b/print/templates/operator_overview.html index 359d4f501..8f70b4f01 100644 --- a/print/templates/operator_overview.html +++ b/print/templates/operator_overview.html @@ -26,7 +26,7 @@

    - {{ svg_overview|safe }} + {{ svg_overview|replace("
  • ", "")|replace("
  • ", "")|safe }} {% include 'ui_svg_action_buttons.html' %}
    diff --git a/print/templates/print_detail.html b/print/templates/print_detail.html index 0c6bbefeb..0dca49785 100644 --- a/print/templates/print_detail.html +++ b/print/templates/print_detail.html @@ -16,7 +16,7 @@
    - {{color_block.svg_preview|safe}} + {{color_block.svg_preview|replace("
  • ", "")|replace("
  • ", "")|safe}} {% include 'ui_svg_action_buttons.html' %}
    diff --git a/print/templates/print_overview.html b/print/templates/print_overview.html index a2548070d..d5111562e 100644 --- a/print/templates/print_overview.html +++ b/print/templates/print_overview.html @@ -26,7 +26,7 @@
    - {{ svg_overview|safe }} + {{ svg_overview|replace("
  • ", "")|replace("
  • ", "")|safe }} {% include 'ui_svg_action_buttons.html' %}
    diff --git a/print/templates/ui.html b/print/templates/ui.html index 2db3a81d8..71908b52f 100644 --- a/print/templates/ui.html +++ b/print/templates/ui.html @@ -112,12 +112,12 @@
    {{ _('Display Time On') }} -

    +

    -

    +
    From f3cdcfbdfa693c569981c5ec6b3c87e6f352916e Mon Sep 17 00:00:00 2001 From: redhat421 Date: Tue, 15 Jan 2019 16:45:31 -0800 Subject: [PATCH 19/26] Remove extra self param from store_preset call. (#381) It looks like an extra "self" parameter was added to the store_preset call in the add_preset function. This prevents add_preset from working. --- lib/gui/presets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui/presets.py b/lib/gui/presets.py index e6000718d..5337d8792 100644 --- a/lib/gui/presets.py +++ b/lib/gui/presets.py @@ -141,7 +141,7 @@ class PresetsPanel(wx.Panel): if not overwrite and preset_name in self._load_presets(): info_dialog(self, _('Preset "%s" already exists. Please use another name or press "Overwrite"') % preset_name, caption=_('Preset')) - self.store_preset(self, preset_name, self.parent.get_preset_data()) + self.store_preset(preset_name, self.parent.get_preset_data()) event.Skip() From fdd9a74fd6fc6e0aacc6766e186c4ffb2fec8e7a Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 26 Jan 2019 19:48:25 -0500 Subject: [PATCH 20/26] fix argument bug in auto-satin --- lib/stitches/auto_satin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stitches/auto_satin.py b/lib/stitches/auto_satin.py index 7bc3e67ce..e204a4456 100644 --- a/lib/stitches/auto_satin.py +++ b/lib/stitches/auto_satin.py @@ -242,7 +242,7 @@ class RunningStitch(object): @cache def reversed(self): - return RunningStitch(shgeo.LineString(reversed(self.path.coords)), self.style) + return RunningStitch(shgeo.LineString(reversed(self.path.coords)), self.original_element) def is_sequential(self, other): if not isinstance(other, RunningStitch): From 4fe95be93ac3cbf29b721ca861828f580efbc484 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 7 Feb 2019 20:37:50 -0500 Subject: [PATCH 21/26] another try at a .JEF trim fix --- pyembroidery | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyembroidery b/pyembroidery index 73ca3eb8c..47b795a08 160000 --- a/pyembroidery +++ b/pyembroidery @@ -1 +1 @@ -Subproject commit 73ca3eb8c32da0636a4cad7a6915440705a5c71f +Subproject commit 47b795a084bdc3281fbf944b940609bf86193fd8 From 52bf2c80b66889d471745260bf1679289d1c59e3 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 9 Feb 2019 20:37:14 -0500 Subject: [PATCH 22/26] fix style --- lib/extensions/convert_to_satin.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py index ef4ac557a..2b586e360 100644 --- a/lib/extensions/convert_to_satin.py +++ b/lib/extensions/convert_to_satin.py @@ -1,17 +1,18 @@ -import inkex -from shapely import geometry as shgeo -from itertools import chain, groupby -import numpy -from numpy import diff, sign, setdiff1d -import math from copy import deepcopy +from itertools import chain, groupby +import math + +import inkex +from numpy import diff, sign, setdiff1d +import numpy +from shapely import geometry as shgeo -from .base import InkstitchExtension -from ..svg.tags import SVG_PATH_TAG -from ..svg import get_correction_transform, PIXELS_PER_MM from ..elements import Stroke -from ..utils import Point from ..i18n import _ +from ..svg import get_correction_transform, PIXELS_PER_MM +from ..svg.tags import SVG_PATH_TAG +from ..utils import Point +from .base import InkstitchExtension class SelfIntersectionError(Exception): @@ -112,10 +113,10 @@ class ConvertToSatin(InkstitchExtension): 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 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 raise SelfIntersectionError() # for whatever reason, shapely returns a right-side offset's coordinates in reverse From 754c2aa63af84a25efc401e8f364deb660361e98 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Thu, 14 Feb 2019 20:25:36 -0500 Subject: [PATCH 23/26] fix stub output handling --- stub.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stub.py b/stub.py index 3d8731962..7c9975244 100644 --- a/stub.py +++ b/stub.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -import sys import os import subprocess +import sys import traceback + # ink/stitch # # stub.py: pyinstaller execution stub @@ -15,7 +16,6 @@ import traceback # This Python script exists only to execute the actual extension binary. It # can be copied to, e.g., "embroider_params.py", in which case it will look # for a binary at inkstitch/bin/embroider_params. - script_name = os.path.basename(__file__) if script_name.endswith('.py'): @@ -48,12 +48,12 @@ if sys.platform == "win32": import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) -stdout = stdout.strip() -if stdout: - print stdout.strip(), +sys.stdout.write(stdout) +sys.stdout.flush() stderr = stderr.strip() if stderr: - print >> sys.stderr, stderr.strip(), + sys.stderr.write(stderr.strip()) + sys.stderr.flush() sys.exit(extension.returncode) From 4c7f8f32fd13cb41680efe9b9d478fab23dacfa0 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 15 Feb 2019 20:45:39 -0500 Subject: [PATCH 24/26] show inkscape label in error messages --- lib/elements/element.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/elements/element.py b/lib/elements/element.py index d98673513..10b1852ad 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -1,15 +1,15 @@ -import sys from copy import deepcopy +import sys -from ..i18n import _ -from ..utils import cache -from ..svg import PIXELS_PER_MM, convert_length, get_doc_size, apply_transforms -from ..commands import find_commands - -# inkscape-provided utilities -import simplestyle -import cubicsuperpath from cspsubdiv import cspsubdiv +import cubicsuperpath +import simplestyle + +from ..commands import find_commands +from ..i18n import _ +from ..svg import PIXELS_PER_MM, convert_length, get_doc_size, apply_transforms +from ..svg.tags import INKSCAPE_LABEL +from ..utils import cache class Patch: @@ -274,7 +274,14 @@ class EmbroideryElement(object): return patches def fatal(self, message): - # L10N used when showing an error message to the user such as "satin column: One or more of the rungs doesn't - # intersect both rails." - print >> sys.stderr, self.node.get("id") + ":", _("error:"), message.encode("UTF-8") + label = self.node.get(INKSCAPE_LABEL) + id = self.node.get("id") + if label: + name = "%s (%s)" % (label, id) + else: + name = id + + # L10N used when showing an error message to the user such as + # "Some Path (path1234): error: satin column: One or more of the rungs doesn't intersect both rails." + print >> sys.stderr, "%s: %s %s" % (name, _("error:"), message.encode("UTF-8")) sys.exit(1) From be7d0af82da95d64261351b6281153db1944c0b4 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 15 Feb 2019 20:51:10 -0500 Subject: [PATCH 25/26] improve error message when trying to autofill tiny shapes --- lib/elements/auto_fill.py | 54 +++++++++++++++++++++++++-------------- lib/exceptions.py | 2 ++ lib/stitches/auto_fill.py | 19 ++++++++++---- 3 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 lib/exceptions.py diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 486243ea6..552922126 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -1,7 +1,9 @@ import math +import traceback from shapely import geometry as shgeo +from ..exceptions import InkstitchException from ..i18n import _ from ..stitches import auto_fill from ..utils import cache @@ -156,27 +158,41 @@ class AutoFill(Fill): starting_point = self.get_starting_point(last_patch) ending_point = self.get_ending_point() - if self.fill_underlay: - stitches.extend(auto_fill(self.underlay_shape, - self.fill_underlay_angle, - self.fill_underlay_row_spacing, - self.fill_underlay_row_spacing, - self.fill_underlay_max_stitch_length, + try: + if self.fill_underlay: + stitches.extend(auto_fill(self.underlay_shape, + self.fill_underlay_angle, + self.fill_underlay_row_spacing, + self.fill_underlay_row_spacing, + self.fill_underlay_max_stitch_length, + self.running_stitch_length, + self.staggers, + self.fill_underlay_skip_last, + starting_point)) + starting_point = stitches[-1] + + stitches.extend(auto_fill(self.fill_shape, + self.angle, + self.row_spacing, + self.end_row_spacing, + self.max_stitch_length, self.running_stitch_length, self.staggers, - self.fill_underlay_skip_last, - starting_point)) - starting_point = stitches[-1] + self.skip_last, + starting_point, + ending_point)) + except InkstitchException, exc: + # for one of our exceptions, just print the message + self.fatal(_("Unable to autofill: ") + str(exc)) + except Exception, exc: + # for an uncaught exception, give a little more info so that they can create a bug report + message = "" + message += _("Error during autofill! This means that there is a problem with Ink/Stitch.") + message += "\n\n" + message += _("If you'd like to help us make Ink/Stitch better, please paste this whole message into a new issue at: https://github.com/inkstitch/inkstitch/issues/new") + message += "\n\n" + message += traceback.format_exc() - stitches.extend(auto_fill(self.fill_shape, - self.angle, - self.row_spacing, - self.end_row_spacing, - self.max_stitch_length, - self.running_stitch_length, - self.staggers, - self.skip_last, - starting_point, - ending_point)) + self.fatal(message) return [Patch(stitches=stitches, color=self.color)] diff --git a/lib/exceptions.py b/lib/exceptions.py new file mode 100644 index 000000000..30e595ea4 --- /dev/null +++ b/lib/exceptions.py @@ -0,0 +1,2 @@ +class InkstitchException(Exception): + pass \ No newline at end of file diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index c7e654735..1c0be0a00 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -5,13 +5,18 @@ import sys import networkx import shapely +from ..exceptions import InkstitchException from ..i18n import _ from ..utils.geometry import Point as InkstitchPoint, cut from .fill import intersect_region_with_grating, row_num, stitch_row from .running_stitch import running_stitch -class MaxQueueLengthExceeded(Exception): +class MaxQueueLengthExceeded(InkstitchException): + pass + + +class InvalidPath(InkstitchException): pass @@ -55,7 +60,7 @@ def auto_fill(shape, rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) segments = [segment for row in rows_of_segments for segment in row] - graph = build_graph(shape, segments, angle, row_spacing) + graph = build_graph(shape, segments, angle, row_spacing, max_stitch_length) path = find_stitch_path(graph, segments, starting_point, ending_point) stitches.extend(path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last)) @@ -90,7 +95,7 @@ def project(shape, coords, outline_index): return outline.project(shapely.geometry.Point(*coords)) -def build_graph(shape, segments, angle, row_spacing): +def build_graph(shape, segments, angle, row_spacing, max_stitch_length): """build a graph representation of the grating segments This function builds a specialized graph (as in graph theory) that will @@ -173,8 +178,12 @@ def build_graph(shape, segments, angle, row_spacing): if i % 2 == edge_set: graph.add_edge(node1, node2, key="extra") - if not networkx.is_eulerian(graph): - raise Exception(_("Unable to autofill. This most often happens because your shape is made up of multiple sections that aren't connected.")) + if networkx.is_empty(graph) or not networkx.is_eulerian(graph): + if shape.area < max_stitch_length ** 2: + raise InvalidPath(_("This shape is so small that it cannot be filled with rows of stitches. " + "It would probably look best as a satin column or running stitch.")) + else: + raise InvalidPath(_("Cannot parse shape. This most often happens because your shape is made up of multiple sections that aren't connected.")) return graph From fa3236372bcee28f4aaa78da47b68c5d7f32cca4 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 16 Feb 2019 16:46:16 -0500 Subject: [PATCH 26/26] fix style --- lib/elements/auto_fill.py | 5 +++-- lib/exceptions.py | 2 +- lib/stitches/auto_fill.py | 11 ++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 552922126..b8d8d15fb 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -189,8 +189,9 @@ class AutoFill(Fill): message = "" message += _("Error during autofill! This means that there is a problem with Ink/Stitch.") message += "\n\n" - message += _("If you'd like to help us make Ink/Stitch better, please paste this whole message into a new issue at: https://github.com/inkstitch/inkstitch/issues/new") - message += "\n\n" + # L10N this message is followed by a URL: https://github.com/inkstitch/inkstitch/issues/new + message += _("If you'd like to help us make Ink/Stitch better, please paste this whole message into a new issue at: ") + message += "https://github.com/inkstitch/inkstitch/issues/new\n\n" message += traceback.format_exc() self.fatal(message) diff --git a/lib/exceptions.py b/lib/exceptions.py index 30e595ea4..c1ff36f0b 100644 --- a/lib/exceptions.py +++ b/lib/exceptions.py @@ -1,2 +1,2 @@ class InkstitchException(Exception): - pass \ No newline at end of file + pass diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 1c0be0a00..0f07b7952 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -178,14 +178,19 @@ def build_graph(shape, segments, angle, row_spacing, max_stitch_length): if i % 2 == edge_set: graph.add_edge(node1, node2, key="extra") + check_graph(graph, shape, max_stitch_length) + + return graph + + +def check_graph(graph, shape, max_stitch_length): if networkx.is_empty(graph) or not networkx.is_eulerian(graph): if shape.area < max_stitch_length ** 2: raise InvalidPath(_("This shape is so small that it cannot be filled with rows of stitches. " "It would probably look best as a satin column or running stitch.")) else: - raise InvalidPath(_("Cannot parse shape. This most often happens because your shape is made up of multiple sections that aren't connected.")) - - return graph + raise InvalidPath(_("Cannot parse shape. " + "This most often happens because your shape is made up of multiple sections that aren't connected.")) def node_list_to_edge_list(node_list):