diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/stitches/ConnectAndSamplePattern.py | 9 | ||||
| -rw-r--r-- | lib/stitches/LineStringSampling.py | 273 | ||||
| -rw-r--r-- | lib/stitches/StitchPattern.py | 16 | ||||
| -rw-r--r-- | lib/stitches/auto_fill.py | 10 |
4 files changed, 62 insertions, 246 deletions
diff --git a/lib/stitches/ConnectAndSamplePattern.py b/lib/stitches/ConnectAndSamplePattern.py index 9b3572d9..e8f1def5 100644 --- a/lib/stitches/ConnectAndSamplePattern.py +++ b/lib/stitches/ConnectAndSamplePattern.py @@ -139,9 +139,10 @@ def connect_raster_tree_nearest_neighbor( # points for start and end) end_distance, stitch_distance, - stitching_direction, tree.transferred_point_priority_deque, abs_offset, + offset_by_half, + False ) assert len(own_coords) == len(own_coords_origin) own_coords_origin[0] = LineStringSampling.PointSource.ENTER_LEAVING_POINT @@ -548,9 +549,10 @@ def connect_raster_tree_from_inner_to_outer( # and end) end_offset, stitch_distance, - stitching_direction, tree.transferred_point_priority_deque, abs_offset, + offset_by_half, + False ) else: ( @@ -566,9 +568,10 @@ def connect_raster_tree_from_inner_to_outer( # and end) current_coords.length - end_offset, stitch_distance, - stitching_direction, tree.transferred_point_priority_deque, abs_offset, + offset_by_half, + False ) current_coords.coords = current_coords.coords[::-1] diff --git a/lib/stitches/LineStringSampling.py b/lib/stitches/LineStringSampling.py index 07106515..bd20f55c 100644 --- a/lib/stitches/LineStringSampling.py +++ b/lib/stitches/LineStringSampling.py @@ -7,10 +7,11 @@ from enum import IntEnum from ..stitches import constants from ..stitches import PointTransfer -# Used to tag the origin of a rastered point - class PointSource(IntEnum): + """ + Used to tag the origin of a rastered point + """ # MUST_USE = 0 # Legacy REGULAR_SPACING = 1 # introduced to not exceed maximal stichting distance # INITIAL_RASTERING = 2 #Legacy @@ -40,9 +41,12 @@ class PointSource(IntEnum): OVERNEXT = 20 # Calculated by overnext neighbor projection -# Calculates the angles between adjacent edges at each interior point -# Note that the first and last values in the return array are zero since for the boundary points no angle calculations were possible def calculate_line_angles(line): + """ + Calculates the angles between adjacent edges at each interior point + Note that the first and last values in the return array are zero since for the boundary points no + angle calculations were possible + """ Angles = np.zeros(len(line.coords)) for i in range(1, len(line.coords)-1): vec1 = np.array(line.coords[i])-np.array(line.coords[i-1]) @@ -65,31 +69,31 @@ def calculate_line_angles(line): Angles[i] = math.acos(scalar_prod) return Angles -# Rasters a line between start_distance and end_distance. -# Input: -# -line: The line to be rastered -# -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 -# -stitching_direction: =1 is stitched along line direction, =-1 if stitched in reversed order. 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) -# index of point_origin is the index of the point in the neighboring line -# -abs_offset: used offset between to offsetted curves -# Output: -# -List of tuples with the rastered point coordinates -# -List which defines the point origin for each point according to the PointSource enum. - def raster_line_string_with_priority_points(line, start_distance, end_distance, maxstitch_distance, - stitching_direction, must_use_points_deque, abs_offset): + must_use_points_deque, abs_offset, offset_by_half, replace_forbidden_points): + """ + Rasters a line between start_distance and end_distance. + Input: + -line: The line to be rastered + -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 + -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) + index of point_origin is the index of the point in the neighboring line + -abs_offset: used offset between to offsetted curves + -offset_by_half: Whether the points of neighboring lines shall be interlaced or not + -replace_forbidden_points: Whether points marked as forbidden in must_use_points_deque shall be replaced by adjacend points + Output: + -List of tuples with the rastered point coordinates + -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): return [line.interpolate(start_distance).coords[0]], [PointSource.HARD_EDGE] - assert (stitching_direction == -1 and start_distance >= end_distance) or ( - stitching_direction == 1 and start_distance <= end_distance) - deque_points = list(must_use_points_deque) linecoords = line.coords @@ -114,6 +118,7 @@ def raster_line_string_with_priority_points(line, start_distance, end_distance, # Ordering in priority queue: # (point, LineStringSampling.PointSource), priority) + # might be different from line for stitching_direction=-1 aligned_line = LineString(linecoords) path_coords = substring(aligned_line, start_distance, end_distance) @@ -130,212 +135,11 @@ def raster_line_string_with_priority_points(line, start_distance, end_distance, path_coords.coords = path_coords.coords[:-1] angles = calculate_line_angles(path_coords) - - current_distance = start_distance - - # Next we merge the line points and the projected (deque) points into one list - merged_point_list = [] - dq_iter = 0 - for point, angle in zip(path_coords.coords, angles): - # if abs(point[0]-40.4) < 0.2 and abs(point[1]-2.3)< 0.2: - # print("GEFUNDEN") - current_distance = start_distance+path_coords.project(Point(point)) - while dq_iter < len(deque_points) and deque_points[dq_iter][1] < current_distance: - # We want to avoid setting points at soft edges close to forbidden points - if deque_points[dq_iter][0].point_source == PointSource.FORBIDDEN_POINT: - # Check whether a previous added point is a soft edge close to the forbidden point - if (merged_point_list[-1][0].point_source == PointSource.SOFT_EDGE_INTERNAL and - abs(merged_point_list[-1][1]-deque_points[dq_iter][1] < abs_offset*constants.factor_offset_forbidden_point)): - item = merged_point_list.pop() - merged_point_list.append((PointTransfer.projected_point_tuple( - point=item[0].point, point_source=PointSource.FORBIDDEN_POINT), item[1])) - else: - merged_point_list.append(deque_points[dq_iter]) - dq_iter += 1 - # Check whether the current point is close to a forbidden point - if (dq_iter < len(deque_points) and - deque_points[dq_iter-1][0].point_source == PointSource.FORBIDDEN_POINT and - angle < constants.limiting_angle and - abs(deque_points[dq_iter-1][1]-current_distance) < abs_offset*constants.factor_offset_forbidden_point): - point_source = PointSource.FORBIDDEN_POINT - else: - if angle < constants.limiting_angle: - point_source = PointSource.SOFT_EDGE_INTERNAL - else: - point_source = PointSource.HARD_EDGE_INTERNAL - merged_point_list.append((PointTransfer.projected_point_tuple( - point=Point(point), point_source=point_source), current_distance)) - - result_list = [merged_point_list[0]] - - # General idea: Take one point of merged_point_list after another into the current segment until this segment is not simplified - # to a straight line by shapelys simplify method. - # Then, look at the points within this segment and choose the best fitting one - # (HARD_EDGE > OVERNEXT projected point > DIRECT projected point) as termination of this segment - # and start point for the next segment (so we do not always take the maximum possible length for a segment) - segment_start_index = 0 - segment_end_index = 1 - forbidden_point_list = [] - while segment_end_index < len(merged_point_list): - # if abs(merged_point_list[segment_end_index-1][0].point.coords[0][0]-67.9) < 0.2 and - # abs(merged_point_list[segment_end_index-1][0].point.coords[0][1]-161.0)< 0.2: - # print("GEFUNDEN") - - # Collection of points for the current segment - current_point_list = [merged_point_list[segment_start_index][0].point] - - 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 > maxstitch_distance+constants.point_spacing_to_be_considered_equal: - new_distance = merged_point_list[segment_start_index][1] + \ - maxstitch_distance - merged_point_list.insert(segment_end_index, (PointTransfer.projected_point_tuple( - point=aligned_line.interpolate(new_distance), point_source=PointSource.REGULAR_SPACING_INTERNAL), new_distance)) - # if (abs(merged_point_list[segment_end_index][0].point.coords[0][0]-12.2) < 0.2 and - # abs(merged_point_list[segment_end_index][0].point.coords[0][1]-0.9) < 0.2): - # print("GEFUNDEN") - segment_end_index += 1 - break - # if abs(merged_point_list[segment_end_index][0].point.coords[0][0]-93.6) < 0.2 and - # abs(merged_point_list[segment_end_index][0].point.coords[0][1]-122.7)< 0.2: - # print("GEFUNDEN") - - current_point_list.append( - merged_point_list[segment_end_index][0].point) - simplified_len = len(LineString(current_point_list).simplify( - constants.factor_offset_remove_dense_points*abs_offset, preserve_topology=False).coords) - if simplified_len > 2: # not all points have been simplified - so we need to add it - break - - if merged_point_list[segment_end_index][0].point_source == PointSource.HARD_EDGE_INTERNAL: - segment_end_index += 1 - break - segment_end_index += 1 - - segment_end_index -= 1 - - # Now we choose the best fitting point within this segment - index_overnext = -1 - index_direct = -1 - index_hard_edge = -1 - - iter = segment_start_index+1 - while (iter <= segment_end_index): - if merged_point_list[iter][0].point_source == PointSource.OVERNEXT: - index_overnext = iter - elif merged_point_list[iter][0].point_source == PointSource.DIRECT: - index_direct = iter - elif merged_point_list[iter][0].point_source == PointSource.HARD_EDGE_INTERNAL: - index_hard_edge = iter - iter += 1 - if index_hard_edge != -1: - segment_end_index = index_hard_edge - else: - if index_overnext != -1: - if (index_direct != -1 and index_direct > index_overnext and - (merged_point_list[index_direct][1]-merged_point_list[index_overnext][1]) >= - constants.factor_segment_length_direct_preferred_over_overnext * - (merged_point_list[index_overnext][1]-merged_point_list[segment_start_index][1])): - # We allow to take the direct projected point instead of the overnext projected point if it would result in a - # significant longer segment length - segment_end_index = index_direct - else: - segment_end_index = index_overnext - elif index_direct != -1: - segment_end_index = index_direct - - # Usually OVERNEXT and DIRECT points are close to each other and in some cases both were selected as segment edges - # If they are too close (<abs_offset) we remove one of it - if (((merged_point_list[segment_start_index][0].point_source == PointSource.OVERNEXT and - merged_point_list[segment_end_index][0].point_source == PointSource.DIRECT) or - (merged_point_list[segment_start_index][0].point_source == PointSource.DIRECT and - merged_point_list[segment_end_index][0].point_source == PointSource.OVERNEXT)) and - abs(merged_point_list[segment_end_index][1] - merged_point_list[segment_start_index][1]) < abs_offset): - result_list.pop() - - result_list.append(merged_point_list[segment_end_index]) - # To have a chance to replace all forbidden points afterwards - if merged_point_list[segment_end_index][0].point_source == PointSource.FORBIDDEN_POINT: - forbidden_point_list.append(len(result_list)-1) - - segment_start_index = segment_end_index - segment_end_index += 1 - - return_point_list = [] # [result_list[0][0].point.coords[0]] - return_point_source_list = [] # [result_list[0][0].point_source] - - # Currently replacement of forbidden points not satisfying - # result_list = replace_forbidden_points(aligned_line, result_list, forbidden_point_list,abs_offset) - - # Finally we create the final return_point_list and return_point_source_list - for i in range(len(result_list)): - return_point_list.append(result_list[i][0].point.coords[0]) - # if abs(result_list[i][0].point.coords[0][0]-91.7) < 0.2 and abs(result_list[i][0].point.coords[0][1]-106.15)< 0.2: - # print("GEFUNDEN") - if result_list[i][0].point_source == PointSource.HARD_EDGE_INTERNAL: - point_source = PointSource.HARD_EDGE - elif result_list[i][0].point_source == PointSource.SOFT_EDGE_INTERNAL: - point_source = PointSource.SOFT_EDGE - elif result_list[i][0].point_source == PointSource.REGULAR_SPACING_INTERNAL: - point_source = PointSource.REGULAR_SPACING - elif result_list[i][0].point_source == PointSource.FORBIDDEN_POINT: - point_source = PointSource.FORBIDDEN_POINT - else: - point_source = PointSource.PROJECTED_POINT - - return_point_source_list.append(point_source) - - assert(len(return_point_list) == len(return_point_source_list)) - - # return remove_dense_points(returnpointlist, returnpointsourcelist, maxstitch_distance,abs_offset) - return return_point_list, return_point_source_list - -# Rasters a line between start_distance and end_distance. -# Input: -# -line: The line to be rastered -# -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 -# -stitching_direction: =1 is stitched along line direction, =-1 if stitched in reversed order. 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) -# index of point_origin is the index of the point in the neighboring line -# -abs_offset: used offset between to offsetted curves -# Output: -# -List of tuples with the rastered point coordinates -# -List which defines the point origin for each point according to the PointSource enum. - - -def raster_line_string_with_priority_points_graph(line, maxstitch_distance, stitching_direction, must_use_points_deque, abs_offset, offset_by_half): - if (line.length < constants.line_lengh_seen_as_one_point): - return [line.coords[0]], [PointSource.HARD_EDGE] - - deque_points = list(must_use_points_deque) - - linecoords = line.coords - - if stitching_direction == -1: - linecoords = linecoords[::-1] - for i in range(len(deque_points)): - deque_points[i] = (deque_points[i][0], - line.length-deque_points[i][1]) - else: - # Since points with highest priority (=distance along line) are first (descending sorted) - deque_points = deque_points[::-1] - -# Ordering in priority queue: -# (point, LineStringSampling.PointSource), priority) - # might be different from line for stitching_direction=-1 - aligned_line = LineString(linecoords) - - angles = calculate_line_angles(aligned_line) # For the first and last point we cannot calculate an angle. Set it to above the limit to make it a hard edge angles[0] = 1.1*constants.limiting_angle angles[-1] = 1.1*constants.limiting_angle - current_distance = 0.0 + current_distance = start_distance # Next we merge the line points and the projected (deque) points into one list merged_point_list = [] @@ -396,13 +200,13 @@ def raster_line_string_with_priority_points_graph(line, maxstitch_distance, stit maxstitch_distance merged_point_list.insert(segment_end_index, (PointTransfer.projected_point_tuple( point=aligned_line.interpolate(new_distance), point_source=PointSource.REGULAR_SPACING_INTERNAL), new_distance)) - # if abs(merged_point_list[segment_end_index][0].point.coords[0][0]-12.2) < 0.2 and 7 - # abs(merged_point_list[segment_end_index][0].point.coords[0][1]-0.9)< 0.2: + # if (abs(merged_point_list[segment_end_index][0].point.coords[0][0]-12.2) < 0.2 and + # abs(merged_point_list[segment_end_index][0].point.coords[0][1]-0.9) < 0.2): # print("GEFUNDEN") segment_end_index += 1 break - # if abs(merged_point_list[segment_end_index][0].point.coords[0][0]-34.4) < 0.2 and - # abs(merged_point_list[segment_end_index][0].point.coords[0][1]-6.2)< 0.2: + # if abs(merged_point_list[segment_end_index][0].point.coords[0][0]-93.6) < 0.2 and + # abs(merged_point_list[segment_end_index][0].point.coords[0][1]-122.7)< 0.2: # print("GEFUNDEN") current_point_list.append( @@ -476,9 +280,10 @@ def raster_line_string_with_priority_points_graph(line, maxstitch_distance, stit return_point_list = [] # [result_list[0][0].point.coords[0]] return_point_source_list = [] # [result_list[0][0].point_source] - # Currently replacement of forbidden points not satisfying - result_list = replace_forbidden_points( - aligned_line, result_list, forbidden_point_list, abs_offset) + # Note: replacement of forbidden points sometimes not satisfying + if replace_forbidden_points: + result_list = _replace_forbidden_points( + aligned_line, result_list, forbidden_point_list, abs_offset) # Finally we create the final return_point_list and return_point_source_list for i in range(len(result_list)): @@ -504,7 +309,7 @@ def raster_line_string_with_priority_points_graph(line, maxstitch_distance, stit return return_point_list, return_point_source_list -def replace_forbidden_points(line, result_list, forbidden_point_list_indices, abs_offset): +def _replace_forbidden_points(line, result_list, forbidden_point_list_indices, abs_offset): # since we add and remove points in the result_list, we need to adjust the indices stored in forbidden_point_list_indices current_index_shift = 0 for index in forbidden_point_list_indices: diff --git a/lib/stitches/StitchPattern.py b/lib/stitches/StitchPattern.py index ba3e3031..1fc5e389 100644 --- a/lib/stitches/StitchPattern.py +++ b/lib/stitches/StitchPattern.py @@ -152,7 +152,8 @@ def offset_poly( at this position """ ordered_poly = orient(poly, -1) - ordered_poly = ordered_poly.simplify(constants.simplification_threshold, False) + ordered_poly = ordered_poly.simplify( + constants.simplification_threshold, False) root = AnyNode( id="node", val=ordered_poly.exterior, @@ -168,7 +169,8 @@ def offset_poly( id="hole", val=holes, already_rastered=False, - transferred_point_priority_deque=DEPQ(iterable=None, maxlen=None), + transferred_point_priority_deque=DEPQ( + iterable=None, maxlen=None), ) ) @@ -182,7 +184,7 @@ def offset_poly( offset, "left", resolution=5, - joint_style=join_style, + join_style=join_style, mitre_limit=10, ) outer = outer.simplify(constants.simplification_threshold, False) @@ -194,7 +196,7 @@ def offset_poly( offset, "left", resolution=5, - joint_style=join_style, + join_style=join_style, mitre_limit=10, ) inner = inner.simplify(constants.simplification_threshold, False) @@ -209,9 +211,11 @@ def offset_poly( result = MultiPolygon(polygonize(outer)) else: if outer.geom_type == "LineString": - result = Polygon(outer).difference(MultiPolygon(poly_inners)) + result = Polygon(outer).difference( + MultiPolygon(poly_inners)) else: - result = MultiPolygon(outer).difference(MultiPolygon(poly_inners)) + result = MultiPolygon(outer).difference( + MultiPolygon(poly_inners)) if not result.is_empty and result.area > offset * offset / 10: result_list = [] diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 1331ecb2..a0d9637a 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -21,7 +21,7 @@ from ..utils.geometry import line_string_to_point_list from .fill import intersect_region_with_grating, intersect_region_with_grating_line, stitch_row from .running_stitch import running_stitch from .PointTransfer import transfer_points_to_surrounding_graph -from .LineStringSampling import raster_line_string_with_priority_points_graph +from .LineStringSampling import raster_line_string_with_priority_points class PathEdge(object): @@ -666,8 +666,12 @@ def stitch_line(stitches, stitching_direction, geometry, projected_points, max_s # print(geometry[0]) # if stitching_direction == -1: # geometry.coords = geometry.coords[::-1] - stitched_line, stitched_line_origin = raster_line_string_with_priority_points_graph( - geometry, max_stitch_length, stitching_direction, projected_points, abs(row_spacing), 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) + 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) stitches.append(Stitch(*stitched_line[0], tags=('fill_row_start',))) for i in range(1, len(stitched_line)): |
