summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLex Neva <lexelby@users.noreply.github.com>2019-02-16 17:07:34 -0500
committerGitHub <noreply@github.com>2019-02-16 17:07:34 -0500
commit2ab4c451e8042868b2516a6b3fe1f60836f27ffe (patch)
tree0ce616ac57e6a2aa507461486233c71373467c9a
parentbd6e4d9d32fd314b66f3c5d798c7151bf543d07f (diff)
parentfa3236372bcee28f4aaa78da47b68c5d7f32cca4 (diff)
tons of bug fixes (#364)
bug fixes
-rw-r--r--lib/elements/auto_fill.py71
-rw-r--r--lib/elements/element.py29
-rw-r--r--lib/elements/fill.py21
-rw-r--r--lib/elements/satin_column.py25
-rw-r--r--lib/exceptions.py2
-rw-r--r--lib/extensions/convert_to_satin.py27
-rw-r--r--lib/extensions/params.py16
-rw-r--r--lib/gui/simulator.py4
-rw-r--r--lib/output.py11
-rw-r--r--lib/stitches/auto_fill.py63
-rw-r--r--lib/stitches/auto_satin.py2
-rw-r--r--lib/stitches/fill.py15
-rw-r--r--lib/stitches/running_stitch.py8
-rw-r--r--lib/svg/svg.py13
-rw-r--r--lib/utils/geometry.py8
m---------pyembroidery0
-rw-r--r--requirements.txt2
-rw-r--r--stub.py12
18 files changed, 214 insertions, 115 deletions
diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py
index 65b11fb1..b8d8d15f 100644
--- a/lib/elements/auto_fill.py
+++ b/lib/elements/auto_fill.py
@@ -1,8 +1,12 @@
import math
+import traceback
+
from shapely import geometry as shgeo
+
+from ..exceptions import InkstitchException
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
@@ -93,6 +97,18 @@ class AutoFill(Fill):
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'),
tooltip=_('Expand the shape before fill stitching, to compensate for gaps between shapes.'),
@@ -142,25 +158,42 @@ 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,
- 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,
- starting_point,
- ending_point))
+ 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"
+ # 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)
return [Patch(stitches=stitches, color=self.color)]
diff --git a/lib/elements/element.py b/lib/elements/element.py
index d9867351..10b1852a 100644
--- a/lib/elements/element.py
+++ b/lib/elements/element.py
@@ -1,15 +1,15 @@
-import sys
from copy import deepcopy
+import sys
+
+from cspsubdiv import cspsubdiv
+import cubicsuperpath
+import simplestyle
+from ..commands import find_commands
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
+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)
diff --git a/lib/elements/fill.py b/lib/elements/fill.py
index 4156a24b..357adf4b 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):
@@ -42,6 +43,17 @@ class Fill(EmbroideryElement):
@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',
_('Flip fill (start right-to-left)'),
tooltip=_('The flip option can help you with routing your stitch path. '
@@ -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/elements/satin_column.py b/lib/elements/satin_column.py
index 1f9854ed..f891e049 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
+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
@@ -716,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]
diff --git a/lib/exceptions.py b/lib/exceptions.py
new file mode 100644
index 00000000..c1ff36f0
--- /dev/null
+++ b/lib/exceptions.py
@@ -0,0 +1,2 @@
+class InkstitchException(Exception):
+ pass
diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py
index ef4ac557..2b586e36 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 copy import deepcopy
from itertools import chain, groupby
-import numpy
-from numpy import diff, sign, setdiff1d
import math
-from copy import deepcopy
-from .base import InkstitchExtension
-from ..svg.tags import SVG_PATH_TAG
-from ..svg import get_correction_transform, PIXELS_PER_MM
+import inkex
+from numpy import diff, sign, setdiff1d
+import numpy
+from shapely import geometry as shgeo
+
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
diff --git a/lib/extensions/params.py b/lib/extensions/params.py
index d3cb154a..4d04ba23 100644
--- a/lib/extensions/params.py
+++ b/lib/extensions/params.py
@@ -6,6 +6,7 @@ from itertools import groupby
import os
import sys
+
import wx
from wx.lib.scrolledpanel import ScrolledPanel
@@ -460,15 +461,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") is not 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
diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py
index 0eed18c9..e0d78983 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 5fa7791f..21622765 100644
--- a/lib/output.py
+++ b/lib/output.py
@@ -1,11 +1,13 @@
-import sys
import pyembroidery
+import sys
+
import simpletransform
+from .commands import global_command
from .i18n import _
-from .utils import Point
+from .stitch_plan import Stitch
from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform
-from .commands import global_command
+from .utils import Point
def get_command(stitch):
@@ -57,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)
@@ -99,5 +102,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)
diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py
index 28c79eff..0f07b795 100644
--- a/lib/stitches/auto_fill.py
+++ b/lib/stitches/auto_fill.py
@@ -1,16 +1,22 @@
+from collections import deque
+from itertools import groupby, izip
import sys
-import shapely
+
import networkx
-from itertools import groupby, izip
-from collections import deque
+import shapely
-from .fill import intersect_region_with_grating, row_num, stitch_row
-from .running_stitch import running_stitch
+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
@@ -39,16 +45,25 @@ 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)
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))
+ stitches.extend(path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last))
return stitches
@@ -80,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
@@ -163,12 +178,21 @@ 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."))
+ 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."))
+
+
def node_list_to_edge_list(node_list):
return zip(node_list[:-1], node_list[1:])
@@ -317,14 +341,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
@@ -513,7 +539,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 +550,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/auto_satin.py b/lib/stitches/auto_satin.py
index 7bc3e67c..e204a445 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):
diff --git a/lib/stitches/fill.py b/lib/stitches/fill.py
index af0a8403..e00d66de 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
diff --git a/lib/stitches/running_stitch.py b/lib/stitches/running_stitch.py
index 5f8ed21e..fa8c50ba 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
diff --git a/lib/svg/svg.py b/lib/svg/svg.py
index b1cc91d9..3fceebfb 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):
@@ -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
diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py
index ab7f24c1..05cfc4b2 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
diff --git a/pyembroidery b/pyembroidery
-Subproject d26e280020bbe1d2b5af977b9cd74f60db8be71
+Subproject 47b795a084bdc3281fbf944b940609bf86193fd
diff --git a/requirements.txt b/requirements.txt
index e1ab6e08..e81b32e3 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
diff --git a/stub.py b/stub.py
index 3d873196..7c997524 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)