kopia lustrzana https://github.com/inkstitch/inkstitch
bug fixing + introduction of min_stitch_distance parameter
rodzic
78e0648f99
commit
6916a33716
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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!")
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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',
|
||||
|
|
Ładowanie…
Reference in New Issue