kopia lustrzana https://github.com/inkstitch/inkstitch
commit
2ab4c451e8
|
@ -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
|
||||
|
||||
|
@ -92,6 +96,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'),
|
||||
|
@ -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]
|
||||
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()
|
||||
|
||||
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.fatal(message)
|
||||
|
||||
return [Patch(stitches=stitches, color=self.color)]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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]
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
class InkstitchException(Exception):
|
||||
pass
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
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 .stitch_plan import Stitch
|
||||
from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform
|
||||
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)
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
import sys
|
||||
import shapely
|
||||
import networkx
|
||||
from itertools import groupby, izip
|
||||
from collections import deque
|
||||
from itertools import groupby, izip
|
||||
import sys
|
||||
|
||||
from .fill import intersect_region_with_grating, row_num, stitch_row
|
||||
from .running_stitch import running_stitch
|
||||
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
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d26e280020bbe1d2b5af977b9cd74f60db8be71b
|
||||
Subproject commit 47b795a084bdc3281fbf944b940609bf86193fd8
|
|
@ -5,7 +5,7 @@ networkx==2.2
|
|||
shapely
|
||||
lxml
|
||||
appdirs
|
||||
numpy
|
||||
numpy<1.16.0
|
||||
jinja2
|
||||
requests
|
||||
colormath
|
||||
|
|
12
stub.py
12
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)
|
||||
|
|
Ładowanie…
Reference in New Issue