diff options
| -rw-r--r-- | lib/elements/element.py | 19 | ||||
| -rw-r--r-- | lib/stitches/auto_fill.py | 62 |
2 files changed, 73 insertions, 8 deletions
diff --git a/lib/elements/element.py b/lib/elements/element.py index dd6c9063..04f15b6e 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -1,9 +1,9 @@ -from copy import deepcopy import sys +from copy import deepcopy -from cspsubdiv import cspsubdiv import cubicsuperpath import simplestyle +from cspsubdiv import cspsubdiv from ..commands import find_commands from ..i18n import _ @@ -169,6 +169,17 @@ class EmbroideryElement(object): return width * self.stroke_scale @property + @param('ties', + _('Ties'), + tooltip=_('Add ties. Manual stitch will not add ties.'), + type='boolean', + default=True, + sort_index=4) + @cache + def ties(self): + return self.get_boolean_param("ties", True) + + @property def path(self): # A CSP is a "cubic superpath". # @@ -269,6 +280,10 @@ class EmbroideryElement(object): patches = self.to_patches(last_patch) + if not self.ties: + for patch in patches: + patch.stitch_as_is = True + if patches: patches[-1].trim_after = self.has_command("trim") or self.trim_after patches[-1].stop_after = self.has_command("stop") or self.stop_after diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 5833b779..dbbef136 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -1,18 +1,18 @@ # -*- coding: UTF-8 -*- -from itertools import groupby, chain import math +from itertools import chain, groupby import networkx from shapely import geometry as shgeo from shapely.ops import snap from shapely.strtree import STRtree +from .fill import intersect_region_with_grating, stitch_row +from .running_stitch import running_stitch from ..debug import debug from ..svg import PIXELS_PER_MM from ..utils.geometry import Point as InkstitchPoint, line_string_to_point_list -from .fill import intersect_region_with_grating, stitch_row -from .running_stitch import running_stitch class PathEdge(object): @@ -52,8 +52,7 @@ def auto_fill(shape, starting_point, ending_point=None, underpath=True): - - fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing) + fill_stitch_graph = build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point, ending_point) if not graph_is_valid(fill_stitch_graph, shape, max_stitch_length): return fallback(shape, running_stitch_length) @@ -95,7 +94,7 @@ def project(shape, coords, outline_index): @debug.time -def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing): +def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing, starting_point=None, ending_point=None): """build a graph representation of the grating segments This function builds a specialized graph (as in graph theory) that will @@ -146,11 +145,39 @@ def build_fill_stitch_graph(shape, angle, row_spacing, end_row_spacing): tag_nodes_with_outline_and_projection(graph, shape, graph.nodes()) add_edges_between_outline_nodes(graph, duplicate_every_other=True) + if starting_point: + insert_node(graph, shape, starting_point) + + if ending_point: + insert_node(graph, shape, ending_point) + debug.log_graph(graph, "graph") return graph +def insert_node(graph, shape, point): + """Add node to graph, splitting one of the outline edges""" + + point = tuple(point) + outline = which_outline(shape, point) + projection = project(shape, point, outline) + projected_point = list(shape.boundary)[outline].interpolate(projection) + node = (projected_point.x, projected_point.y) + + edges = [] + for start, end, key, data in graph.edges(keys=True, data=True): + if key == "outline": + edges.append(((start, end), data)) + + edge, data = min(edges, key=lambda (edge, data): shgeo.LineString(edge).distance(projected_point)) + + graph.remove_edge(*edge, key="outline") + graph.add_edge(edge[0], node, key="outline", **data) + graph.add_edge(node, edge[1], key="outline", **data) + tag_nodes_with_outline_and_projection(graph, shape, nodes=[node]) + + def tag_nodes_with_outline_and_projection(graph, shape, nodes): for node in nodes: outline_index = which_outline(shape, node) @@ -159,6 +186,27 @@ def tag_nodes_with_outline_and_projection(graph, shape, nodes): graph.add_node(node, outline=outline_index, projection=outline_projection) +def add_boundary_travel_nodes(graph, shape): + for outline_index, outline in enumerate(shape.boundary): + prev = None + for point in outline.coords: + point = shgeo.Point(point) + if prev is not None: + # Subdivide long straight line segments. Otherwise we may not + # have a node near the user's chosen starting or ending point + length = point.distance(prev) + segment = shgeo.LineString((prev, point)) + if length > 1: + # Just plot a point every pixel, that should be plenty of + # resolution. A pixel is around a quarter of a millimeter. + for i in xrange(1, int(length)): + subpoint = segment.interpolate(i) + graph.add_node((subpoint.x, subpoint.y), projection=outline.project(subpoint), outline=outline_index) + + graph.add_node((point.x, point.y), projection=outline.project(point), outline=outline_index) + prev = point + + def add_edges_between_outline_nodes(graph, duplicate_every_other=False): """Add edges around the outlines of the graph, connecting sequential nodes. @@ -240,6 +288,8 @@ def build_travel_graph(fill_stitch_graph, shape, fill_stitch_angle, underpath): # This will ensure that a path traveling inside the shape can reach its # target on the outline, which will be one of the points added above. tag_nodes_with_outline_and_projection(graph, shape, boundary_points) + else: + add_boundary_travel_nodes(graph, shape) add_edges_between_outline_nodes(graph) |
