bug fixing + introduction of min_stitch_distance parameter

pull/1548/head
Andreas 2022-03-20 17:15:39 +01:00 zatwierdzone przez Kaalleen
rodzic 78e0648f99
commit 6916a33716
7 zmienionych plików z 69 dodań i 34 usunięć

Wyświetl plik

@ -191,6 +191,19 @@ class FillStitch(EmbroideryElement):
def max_stitch_length(self):
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'),
@ -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,

Wyświetl plik

@ -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):

Wyświetl plik

@ -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:

Wyświetl plik

@ -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:

Wyświetl plik

@ -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!")

Wyświetl plik

@ -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()

Wyświetl plik

@ -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',