diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/elements/fill_stitch.py | 15 | ||||
| -rw-r--r-- | lib/stitches/guided_fill.py | 16 | ||||
| -rw-r--r-- | lib/stitches/point_transfer.py | 2 | ||||
| -rw-r--r-- | lib/stitches/sample_linestring.py | 19 | ||||
| -rw-r--r-- | lib/stitches/tangential_fill_stitch_line_creator.py | 10 | ||||
| -rw-r--r-- | lib/stitches/tangential_fill_stitch_pattern_creator.py | 40 | ||||
| -rw-r--r-- | lib/svg/tags.py | 1 |
7 files changed, 69 insertions, 34 deletions
diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py index eaddcfe0..e0de0f22 100644 --- a/lib/elements/fill_stitch.py +++ b/lib/elements/fill_stitch.py @@ -192,6 +192,19 @@ class FillStitch(EmbroideryElement): return max(self.get_float_param("max_stitch_length_mm", 3.0), 0.1 * PIXELS_PER_MM) @property + @param('min_stitch_length_mm', + _('Minimum fill stitch length'), + tooltip=_( + 'The minimum length of a stitch in a row. Larger values might introduce deviations from the desired path. Shorter stitch may be used at the start or end of a row.'), + unit='mm', + sort_index=4, + select_items=[('fill_method', 1), ('fill_method', 2)], + type='float', + default=0.0) + def min_stitch_length(self): + return self.get_float_param("min_stitch_length_mm", 0.0) + + @property @param('staggers', _('Stagger rows this many times before repeating'), tooltip=_('Setting this dictates how many rows apart the stitches will be before they fall in the same column position.'), @@ -557,6 +570,7 @@ class FillStitch(EmbroideryElement): -self.row_spacing, self.join_style+1, self.max_stitch_length, + min(self.min_stitch_length, self.max_stitch_length), self.interlaced, self.tangential_strategy, shgeo.Point(starting_point)) @@ -585,6 +599,7 @@ class FillStitch(EmbroideryElement): self.angle, self.row_spacing, self.max_stitch_length, + min(self.min_stitch_length,self.max_stitch_length), self.running_stitch_length, self.skip_last, starting_point, diff --git a/lib/stitches/guided_fill.py b/lib/stitches/guided_fill.py index 4cc250ef..6948a086 100644 --- a/lib/stitches/guided_fill.py +++ b/lib/stitches/guided_fill.py @@ -24,6 +24,7 @@ def guided_fill(shape, angle, row_spacing, max_stitch_length, + min_stitch_length, running_stitch_length, skip_last, starting_point, @@ -45,7 +46,7 @@ def guided_fill(shape, travel_graph = build_travel_graph(fill_stitch_graph, shape, angle, underpath) path = find_stitch_path(fill_stitch_graph, travel_graph, starting_point, ending_point) result = path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, - max_stitch_length, running_stitch_length, skip_last, offset_by_half) + max_stitch_length, min_stitch_length, running_stitch_length, skip_last, offset_by_half) return result @@ -187,13 +188,13 @@ def process_travel_edges(graph, fill_stitch_graph, shape, travel_edges): del strtree -def stitch_line(stitches, stitching_direction, geometry, projected_points, max_stitch_length, row_spacing, skip_last, offset_by_half): +def stitch_line(stitches, stitching_direction, geometry, projected_points, max_stitch_length, min_stitch_length, row_spacing, skip_last, offset_by_half): if stitching_direction == 1: stitched_line, _ = raster_line_string_with_priority_points( - geometry, 0.0, geometry.length, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) + geometry, 0.0, geometry.length, max_stitch_length, min_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) else: stitched_line, _ = raster_line_string_with_priority_points( - geometry, geometry.length, 0.0, max_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) + geometry, geometry.length, 0.0, max_stitch_length, min_stitch_length, projected_points, abs(row_spacing), offset_by_half, True) stitches.append(Stitch(*stitched_line[0], tags=('fill_row_start',))) for i in range(1, len(stitched_line)-1): @@ -209,7 +210,7 @@ def stitch_line(stitches, stitching_direction, geometry, projected_points, max_s @debug.time -def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, +def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, max_stitch_length, min_stitch_length, running_stitch_length, skip_last, offset_by_half): path = collapse_sequential_outline_edges(path) @@ -230,7 +231,7 @@ def path_to_stitches(path, travel_graph, fill_stitch_graph, angle, row_spacing, abs(edge[0][0]-path_geometry.coords[-1][0])+abs(edge[0][1]-path_geometry.coords[-1][1])): stitching_direction = -1 stitch_line(new_stitches, stitching_direction, path_geometry, projected_points, - max_stitch_length, row_spacing, skip_last, offset_by_half) + max_stitch_length, min_stitch_length, row_spacing, skip_last, offset_by_half) current_edge['already_rastered'] = True transfer_points_to_surrounding_graph( fill_stitch_graph, current_edge, row_spacing, False, new_stitches, overnext_neighbor=True) @@ -264,9 +265,8 @@ def extend_line(line, minx, maxx, miny, maxy): def repair_multiple_parallel_offset_curves(multi_line): - # TODO: linemerge is overritten by the very next line?!? lines = linemerge(multi_line) - lines = list(multi_line.geoms) + lines = list(lines.geoms) max_length = -1 max_length_idx = -1 for idx, subline in enumerate(lines): diff --git a/lib/stitches/point_transfer.py b/lib/stitches/point_transfer.py index cf4597dd..5506324d 100644 --- a/lib/stitches/point_transfer.py +++ b/lib/stitches/point_transfer.py @@ -70,7 +70,7 @@ def transfer_points_to_surrounding(treenode, used_offset, offset_by_half, to_tra assert(not transfer_forbidden_points or transfer_forbidden_points and ( offset_by_half or not offset_by_half and overnext_neighbor)) - if len(to_transfer_points) == 0: + if len(to_transfer_points) < 3: return # Get a list of all possible adjacent nodes which will be considered for transferring the points of treenode: diff --git a/lib/stitches/sample_linestring.py b/lib/stitches/sample_linestring.py index b2298984..838b1792 100644 --- a/lib/stitches/sample_linestring.py +++ b/lib/stitches/sample_linestring.py @@ -63,7 +63,7 @@ def calculate_line_angles(line): return Angles -def raster_line_string_with_priority_points(line, start_distance, end_distance, maxstitch_distance, # noqa: C901 +def raster_line_string_with_priority_points(line, start_distance, end_distance, maxstitch_distance, minstitch_distance, # noqa: C901 must_use_points_deque, abs_offset, offset_by_half, replace_forbidden_points): """ Rasters a line between start_distance and end_distance. @@ -72,6 +72,7 @@ def raster_line_string_with_priority_points(line, start_distance, end_distance, -start_distance: The distance along the line from which the rastering should start -end_distance: The distance along the line until which the rastering should be done -maxstitch_distance: The maximum allowed stitch distance + -minstitch_distance: The minimum allowed stitch distance -Note that start_distance > end_distance for stitching_direction = -1 -must_use_points_deque: deque with projected points on line from its neighbors. An item of the deque is setup as follows: ((projected point on line, LineStringSampling.PointSource), priority=distance along line) @@ -84,7 +85,7 @@ def raster_line_string_with_priority_points(line, start_distance, end_distance, -List which defines the point origin for each point according to the PointSource enum. """ - if (abs(end_distance-start_distance) < constants.line_lengh_seen_as_one_point): + if (abs(end_distance-start_distance) < max(minstitch_distance, constants.line_lengh_seen_as_one_point)): return [line.interpolate(start_distance).coords[0]], [PointSource.HARD_EDGE] deque_points = list(must_use_points_deque) @@ -103,9 +104,9 @@ def raster_line_string_with_priority_points(line, start_distance, end_distance, deque_points = deque_points[::-1] # Remove all points from the deque which do not fall in the segment [start_distance; end_distance] - while (len(deque_points) > 0 and deque_points[0][1] <= start_distance+min(maxstitch_distance/20, constants.point_spacing_to_be_considered_equal)): + while (len(deque_points) > 0 and deque_points[0][1] <= start_distance+min(maxstitch_distance/20, minstitch_distance, constants.point_spacing_to_be_considered_equal)): deque_points.pop(0) - while (len(deque_points) > 0 and deque_points[-1][1] >= end_distance-min(maxstitch_distance/20, constants.point_spacing_to_be_considered_equal)): + while (len(deque_points) > 0 and deque_points[-1][1] >= end_distance-min(maxstitch_distance/20, minstitch_distance, constants.point_spacing_to_be_considered_equal)): deque_points.pop() @@ -185,6 +186,9 @@ def raster_line_string_with_priority_points(line, start_distance, end_distance, while segment_end_index < len(merged_point_list): segment_length = merged_point_list[segment_end_index][1] - \ merged_point_list[segment_start_index][1] + if segment_length < minstitch_distance: + segment_end_index += 1 + continue if segment_length > maxstitch_distance+constants.point_spacing_to_be_considered_equal: new_distance = merged_point_list[segment_start_index][1] + \ maxstitch_distance @@ -214,6 +218,13 @@ def raster_line_string_with_priority_points(line, start_distance, end_distance, iter = segment_start_index+1 while (iter <= segment_end_index): + segment_length = merged_point_list[iter][1] - \ + merged_point_list[segment_start_index][1] + if segment_length < minstitch_distance and merged_point_list[iter][0].point_source != PointSource.HARD_EDGE_INTERNAL: + #We need to create this hard edge exception - otherwise there are some too large deviations posible + iter += 1 + continue + if merged_point_list[iter][0].point_source == PointSource.OVERNEXT: index_overnext = iter elif merged_point_list[iter][0].point_source == PointSource.DIRECT: diff --git a/lib/stitches/tangential_fill_stitch_line_creator.py b/lib/stitches/tangential_fill_stitch_line_creator.py index af14ea0f..4d4377f0 100644 --- a/lib/stitches/tangential_fill_stitch_line_creator.py +++ b/lib/stitches/tangential_fill_stitch_line_creator.py @@ -158,7 +158,7 @@ def check_and_prepare_tree_for_valid_spiral(root): return True -def offset_poly(poly, offset, join_style, stitch_distance, offset_by_half, strategy, starting_point): # noqa: C901 +def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, offset_by_half, strategy, starting_point): # noqa: C901 """ Takes a polygon (which can have holes) as input and creates offsetted versions until the polygon is filled with these smaller offsets. @@ -173,6 +173,8 @@ def offset_poly(poly, offset, join_style, stitch_distance, offset_by_half, strat For examples look at https://shapely.readthedocs.io/en/stable/_images/parallel_offset.png -stitch_distance maximum allowed stitch distance between two points + -min_stitch_distance stitches within a row shall be at least min_stitch_distance apart. Stitches connecting + offsetted paths might be shorter. -offset_by_half: True if the points shall be interlaced -strategy: According to StitchingStrategy enum class you can select between different strategies for the connection between parent and childs. In @@ -315,15 +317,15 @@ def offset_poly(poly, offset, join_style, stitch_distance, offset_by_half, strat if strategy == StitchingStrategy.CLOSEST_POINT: (connected_line, connected_line_origin) = tangential_fill_stitch_pattern_creator.connect_raster_tree_nearest_neighbor( - root, offset, stitch_distance, starting_point, offset_by_half) + root, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half) elif strategy == StitchingStrategy.INNER_TO_OUTER: (connected_line, connected_line_origin) = tangential_fill_stitch_pattern_creator.connect_raster_tree_from_inner_to_outer( - root, offset, stitch_distance, starting_point, offset_by_half) + root, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half) elif strategy == StitchingStrategy.SPIRAL: if not check_and_prepare_tree_for_valid_spiral(root): raise ValueError("Geometry cannot be filled with one spiral!") (connected_line, connected_line_origin) = tangential_fill_stitch_pattern_creator.connect_raster_tree_spiral( - root, offset, stitch_distance, starting_point, offset_by_half) + root, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half) else: raise ValueError("Invalid stitching stratety!") diff --git a/lib/stitches/tangential_fill_stitch_pattern_creator.py b/lib/stitches/tangential_fill_stitch_pattern_creator.py index d7afad0c..95143bce 100644 --- a/lib/stitches/tangential_fill_stitch_pattern_creator.py +++ b/lib/stitches/tangential_fill_stitch_pattern_creator.py @@ -52,7 +52,7 @@ def cut(line, distance): def connect_raster_tree_nearest_neighbor( # noqa: C901 - tree, used_offset, stitch_distance, close_point, offset_by_half): + tree, used_offset, stitch_distance, min_stitch_distance, close_point, offset_by_half): """ Takes the offsetted curves organized as tree, connects and samples them. Strategy: A connection from parent to child is made where both curves @@ -63,6 +63,8 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901 -used_offset: used offset when the offsetted curves were generated -stitch_distance: maximum allowed distance between two points after sampling + -min_stitch_distance stitches within a row shall be at least min_stitch_distance apart. Stitches connecting + offsetted paths might be shorter. -close_point: defines the beginning point for stitching (stitching starts always from the undisplaced curve) -offset_by_half: If true the resulting points are interlaced otherwise not. @@ -136,6 +138,7 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901 # points for start and end) end_distance, stitch_distance, + min_stitch_distance, tree.transferred_point_priority_deque, abs_offset, offset_by_half, @@ -230,6 +233,7 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901 item.child_node, used_offset, stitch_distance, + min_stitch_distance, item.nearest_point_child, offset_by_half, ) @@ -432,7 +436,7 @@ def calculate_replacing_middle_point(line_segment, abs_offset, max_stitch_distan return line_segment.coords[1] -def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, close_point, offset_by_half): # noqa: C901 +def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, min_stitch_distance, close_point, offset_by_half): # noqa: C901 """ Takes the offsetted curves organized as tree, connects and samples them. Strategy: A connection from parent to child is made as fast as possible to @@ -444,6 +448,8 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, -used_offset: used offset when the offsetted curves were generated -stitch_distance: maximum allowed distance between two points after sampling + -min_stitch_distance stitches within a row shall be at least min_stitch_distance apart. Stitches connecting + offsetted paths might be shorter. -close_point: defines the beginning point for stitching (stitching starts always from the undisplaced curve) -offset_by_half: If true the resulting points are interlaced otherwise not. @@ -514,11 +520,12 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, if stitching_direction == 1: (own_coords, own_coords_origin) = sample_linestring.raster_line_string_with_priority_points( current_coords, - start_offset, # We add start_offset to not sample the same - # point again (avoid double points for start + start_offset, # We add start_offset to not sample the initial/end + # point twice (avoid double points for start # and end) end_offset, stitch_distance, + min_stitch_distance, tree.transferred_point_priority_deque, abs_offset, offset_by_half, @@ -529,12 +536,13 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, current_coords, current_coords.length - start_offset, # We subtract # start_offset to not - # sample the same point - # again (avoid double + # sample the initial/end point + # twice (avoid double # points for start # and end) current_coords.length - end_offset, stitch_distance, + min_stitch_distance, tree.transferred_point_priority_deque, abs_offset, offset_by_half, @@ -639,6 +647,7 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, item.child_node, used_offset, stitch_distance, + min_stitch_distance, item.nearest_point_child, offset_by_half, ) @@ -683,19 +692,12 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, if cur_item < len(nearest_points_list) - 1: d = min( d, - abs( - nearest_points_list[cur_item + - 1].proj_distance_parent - - item.proj_distance_parent - ), + abs(nearest_points_list[cur_item + 1].proj_distance_parent - item.proj_distance_parent), ) if d > constants.factor_offset_starting_points * abs_offset: result_coords.append( - current_coords.interpolate( - item.proj_distance_parent - + 2 * constants.factor_offset_starting_points * abs_offset - ).coords[0] + current_coords.interpolate(item.proj_distance_parent + 2 * constants.factor_offset_starting_points * abs_offset).coords[0] ) result_coords_origin.append( sample_linestring.PointSource.ENTER_LEAVING_POINT @@ -792,7 +794,7 @@ def interpolate_LinearRings(a, b, start=None, step=.005): def connect_raster_tree_spiral( - tree, used_offset, stitch_distance, close_point, offset_by_half): + tree, used_offset, stitch_distance, min_stitch_distance, close_point, offset_by_half): """ Takes the offsetted curves organized as tree, connects and samples them as a spiral. It expects that each node in the tree has max. one child @@ -802,6 +804,8 @@ def connect_raster_tree_spiral( -used_offset: used offset when the offsetted curves were generated -stitch_distance: maximum allowed distance between two points after sampling + -min_stitch_distance stitches within a row shall be at least min_stitch_distance apart. Stitches connecting + offsetted paths might be shorter. -close_point: defines the beginning point for stitching (stitching starts always from the undisplaced curve) -offset_by_half: If true the resulting points are interlaced otherwise not. @@ -819,6 +823,7 @@ def connect_raster_tree_spiral( 0, tree.val.length, stitch_distance, + min_stitch_distance, tree.transferred_point_priority_deque, abs_offset, offset_by_half, @@ -842,6 +847,7 @@ def connect_raster_tree_spiral( 0, node.val.length, stitch_distance, + min_stitch_distance, node.transferred_point_priority_deque, abs_offset, offset_by_half, @@ -883,7 +889,7 @@ def connect_raster_tree_spiral( lineseg = LineString([result_coords[-2], result_coords[-1], own_coords[0], own_coords[1]]) else: lineseg = LineString([result_coords[-2], result_coords[-1], own_coords[1]]) - (temp_coords, _) = sample_linestring.raster_line_string_with_priority_points(lineseg, 0, lineseg.length, stitch_distance, + (temp_coords, _) = sample_linestring.raster_line_string_with_priority_points(lineseg, 0, lineseg.length, stitch_distance, min_stitch_distance, DEPQ(), abs_offset, offset_by_half, False) if len(temp_coords) == 2: # only start and end point of lineseg was needed result_coords.pop() diff --git a/lib/svg/tags.py b/lib/svg/tags.py index f3118661..37eb5752 100644 --- a/lib/svg/tags.py +++ b/lib/svg/tags.py @@ -64,6 +64,7 @@ inkstitch_attribs = [ 'fill_underlay_row_spacing_mm', 'fill_underlay_skip_last', 'max_stitch_length_mm', + 'min_stitch_length_mm', 'row_spacing_mm', 'end_row_spacing_mm', 'skip_last', |
