From 8a1f70a6cdcbf7c599d9f00837996a0f6734f780 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 2 May 2022 14:41:34 -0400 Subject: [PATCH] remove sample_linestring and point_transfer --- lib/stitches/point_transfer.py | 503 ------------------ lib/stitches/sample_linestring.py | 342 ------------ .../tangential_fill_stitch_pattern_creator.py | 18 - 3 files changed, 863 deletions(-) delete mode 100644 lib/stitches/point_transfer.py delete mode 100644 lib/stitches/sample_linestring.py diff --git a/lib/stitches/point_transfer.py b/lib/stitches/point_transfer.py deleted file mode 100644 index c0d519efb..000000000 --- a/lib/stitches/point_transfer.py +++ /dev/null @@ -1,503 +0,0 @@ -import math -from collections import namedtuple - -from shapely.geometry import LineString, LinearRing, MultiPoint, Point -from shapely.ops import nearest_points - -from ..stitches import constants, sample_linestring - -"""This file contains routines which shall project already selected points for stitching to remaining -unstitched lines in the neighborhood to create a regular pattern of points.""" - -projected_point_tuple = namedtuple( - 'projected_point_tuple', ['point', 'point_source']) - - -def calc_transferred_point(bisectorline, child): - """ - Calculates the nearest intersection point of "bisectorline" with the coordinates of child (child.val). - It returns the intersection point and its distance along the coordinates of the child or "None, None" if no - intersection was found. - """ - result = bisectorline.intersection(child.val) - if result.is_empty: - return None, None - desired_point = Point() - if result.geom_type == 'Point': - desired_point = result - elif result.geom_type == 'LineString': - desired_point = Point(result.coords[0]) - else: - resultlist = list(result) - desired_point = resultlist[0] - if len(resultlist) > 1: - desired_point = nearest_points( - result, Point(bisectorline.coords[0]))[0] - - priority = child.val.project(desired_point) - point = desired_point - return point, priority - - -def transfer_points_to_surrounding(tree, node, used_offset, offset_by_half, to_transfer_points, to_transfer_points_origin=[], # noqa: C901 - overnext_neighbor=False, transfer_forbidden_points=False, - transfer_to_parent=True, transfer_to_sibling=True, transfer_to_child=True): - """ - Takes the current tree item and its rastered points (to_transfer_points) and transfers these points to its parent, siblings and childs - To do so it calculates the current normal and determines its intersection with the neighbors which gives the transferred points. - Input: - -treenode: Tree node whose points stored in "to_transfer_points" shall be transferred to its neighbors. - -used_offset: The used offset when the curves where offsetted - -offset_by_half: True if the transferred points shall be interlaced with respect to the points in "to_transfer_points" - -to_transfer_points: List of points belonging to treenode which shall be transferred - it is assumed that to_transfer_points - can be handled as closed ring - -to_transfer_points_origin: The origin tag of each point in to_transfer_points - -overnext_neighbor: Transfer the points to the overnext neighbor (gives a more stable interlacing) - -transfer_forbidden_points: Only allowed for interlacing (offset_by_half): Might be used to transfer points unshifted as - forbidden points to the neighbor to avoid a point placing there - -transfer_to_parent: If True, points will be transferred to the parent - -transfer_to_sibling: If True, points will be transferred to the siblings - -transfer_to_child: If True, points will be transferred to the childs - Output: - -Fills the attribute "transferred_point_priority_deque" of the siblings and parent in the tree datastructure. 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 - """ - - assert (len(to_transfer_points) == len(to_transfer_points_origin) - or len(to_transfer_points_origin) == 0) - assert ((overnext_neighbor and not offset_by_half) or not overnext_neighbor) - 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) < 3: - return - - current_node = tree.nodes[node] - - # Get a list of all possible adjacent nodes which will be considered for transferring the points of treenode: - childs_tuple = tuple(tree.successors(node)) - if current_node.parent: - siblings_tuple = tuple(child for child in tree[current_node.parent] if child != node) - else: - siblings_tuple = () - - # Take only neighbors which have not rastered before - # We need to distinguish between childs (project towards inner) and parent/siblings (project towards outer) - child_list = [] - child_list_forbidden = [] - neighbor_list = [] - neighbor_list_forbidden = [] - - if transfer_to_child: - for child in childs_tuple: - if not tree.nodes[child].already_rastered: - if not overnext_neighbor: - child_list.append(child) - if transfer_forbidden_points: - child_list_forbidden.append(child) - if overnext_neighbor: - for grandchild in tree[child]: - if not tree.nodes[grandchild].already_rastered: - child_list.append(grandchild) - - if transfer_to_sibling: - for sibling in siblings_tuple: - if not tree.nodes[sibling].already_rastered: - if not overnext_neighbor: - neighbor_list.append(sibling) - if transfer_forbidden_points: - neighbor_list_forbidden.append(sibling) - if overnext_neighbor: - for nibling in tree[sibling]: - if not tree.nodes[nibling].already_rastered: - neighbor_list.append(nibling) - - if transfer_to_parent and current_node.parent is not None: - if not tree.nodes[current_node.parent].already_rastered: - if not overnext_neighbor: - neighbor_list.append(current_node.parent) - if transfer_forbidden_points: - neighbor_list_forbidden.append(current_node.parent) - if overnext_neighbor: - grandparent = tree.nodes[current_node].parent - if grandparent is not None: - if not tree.nodes[grandparent].already_rastered: - neighbor_list.append(grandparent) - - if not neighbor_list and not child_list: - return - - # Go through all rastered points of treenode and check where they should be transferred to its neighbar - point_list = list(MultiPoint(to_transfer_points)) - point_source_list = to_transfer_points_origin.copy() - - # For a linear ring the last point is the same as the starting point which we delete - # since we do not want to transfer the starting and end point twice - closed_line = LineString(to_transfer_points) - if point_list[0].distance(point_list[-1]) < constants.point_spacing_to_be_considered_equal: - point_list.pop() - if point_source_list: - point_source_list.pop() - if len(point_list) == 0: - return - else: - # closed line is needed if we offset by half since we need to determine the line - # length including the closing segment - closed_line = LinearRing(to_transfer_points) - - bisectorline_length = abs(used_offset) * constants.transfer_point_distance_factor * (2.0 if overnext_neighbor else 1.0) - - bisectorline_length_forbidden_points = abs(used_offset) * constants.transfer_point_distance_factor - - linesign_child = math.copysign(1, used_offset) - - i = 0 - currentDistance = 0 - while i < len(point_list): - assert(point_source_list[i] != - sample_linestring.PointSource.ENTER_LEAVING_POINT) - - # We create a bisecting line through the current point - normalized_vector_prev_x = ( - point_list[i].coords[0][0]-point_list[i-1].coords[0][0]) # makes use of closed shape - normalized_vector_prev_y = ( - point_list[i].coords[0][1]-point_list[i-1].coords[0][1]) - prev_spacing = math.sqrt(normalized_vector_prev_x*normalized_vector_prev_x + - normalized_vector_prev_y*normalized_vector_prev_y) - - normalized_vector_prev_x /= prev_spacing - normalized_vector_prev_y /= prev_spacing - - normalized_vector_next_x = normalized_vector_next_y = 0 - next_spacing = 0 - while True: - normalized_vector_next_x = ( - point_list[i].coords[0][0]-point_list[(i+1) % len(point_list)].coords[0][0]) - normalized_vector_next_y = ( - point_list[i].coords[0][1]-point_list[(i+1) % len(point_list)].coords[0][1]) - next_spacing = math.sqrt(normalized_vector_next_x*normalized_vector_next_x + - normalized_vector_next_y*normalized_vector_next_y) - if next_spacing < constants.line_lengh_seen_as_one_point: - point_list.pop(i) - if(point_source_list): - point_source_list.pop(i) - currentDistance += next_spacing - continue - - normalized_vector_next_x /= next_spacing - normalized_vector_next_y /= next_spacing - break - - vecx = (normalized_vector_next_x+normalized_vector_prev_x) - vecy = (normalized_vector_next_y+normalized_vector_prev_y) - vec_length = math.sqrt(vecx*vecx+vecy*vecy) - - vecx_forbidden_point = vecx - vecy_forbidden_point = vecy - - # The two sides are (anti)parallel - construct normal vector (bisector) manually: - # If we offset by half we are offseting normal to the next segment - if(vec_length < constants.line_lengh_seen_as_one_point or offset_by_half): - vecx = linesign_child*bisectorline_length*normalized_vector_next_y - vecy = -linesign_child*bisectorline_length*normalized_vector_next_x - - if transfer_forbidden_points: - vecx_forbidden_point = linesign_child * \ - bisectorline_length_forbidden_points*normalized_vector_next_y - vecy_forbidden_point = -linesign_child * \ - bisectorline_length_forbidden_points*normalized_vector_next_x - - else: - vecx *= bisectorline_length/vec_length - vecy *= bisectorline_length/vec_length - - if (vecx*normalized_vector_next_y-vecy * normalized_vector_next_x)*linesign_child < 0: - vecx = -vecx - vecy = -vecy - vecx_forbidden_point = vecx - vecy_forbidden_point = vecy - - assert((vecx*normalized_vector_next_y-vecy * - normalized_vector_next_x)*linesign_child >= 0) - - originPoint = point_list[i] - originPoint_forbidden_point = point_list[i] - if(offset_by_half): - off = currentDistance+next_spacing/2 - if off > closed_line.length: - off -= closed_line.length - originPoint = closed_line.interpolate(off) - - bisectorline_child = LineString([(originPoint.coords[0][0], - originPoint.coords[0][1]), - (originPoint.coords[0][0]+vecx, - originPoint.coords[0][1]+vecy)]) - - bisectorline_neighbor = LineString([(originPoint.coords[0][0], - originPoint.coords[0][1]), - (originPoint.coords[0][0]-vecx, - originPoint.coords[0][1]-vecy)]) - - bisectorline_forbidden_point_child = LineString([(originPoint_forbidden_point.coords[0][0], - originPoint_forbidden_point.coords[0][1]), - (originPoint_forbidden_point.coords[0][0]+vecx_forbidden_point, - originPoint_forbidden_point.coords[0][1]+vecy_forbidden_point)]) - - bisectorline_forbidden_point_neighbor = LineString([(originPoint_forbidden_point.coords[0][0], - originPoint_forbidden_point.coords[0][1]), - (originPoint_forbidden_point.coords[0][0]-vecx_forbidden_point, - originPoint_forbidden_point.coords[0][1]-vecy_forbidden_point)]) - - for child in child_list: - current_child = tree.nodes[child] - point, priority = calc_transferred_point(bisectorline_child, current_child) - if point is None: - continue - current_child.transferred_point_priority_deque.insert(projected_point_tuple( - point=point, point_source=sample_linestring.PointSource.OVERNEXT if overnext_neighbor - else sample_linestring.PointSource.DIRECT), priority) - for child in child_list_forbidden: - current_child = tree.nodes[child] - point, priority = calc_transferred_point(bisectorline_forbidden_point_child, current_child) - if point is None: - continue - current_child.transferred_point_priority_deque.insert(projected_point_tuple( - point=point, point_source=sample_linestring.PointSource.FORBIDDEN_POINT), priority) - - for neighbor in neighbor_list: - current_neighbor = tree.nodes[neighbor] - point, priority = calc_transferred_point(bisectorline_neighbor, current_neighbor) - if point is None: - continue - current_neighbor.transferred_point_priority_deque.insert(projected_point_tuple( - point=point, point_source=sample_linestring.PointSource.OVERNEXT if overnext_neighbor - else sample_linestring.PointSource.DIRECT), priority) - for neighbor in neighbor_list_forbidden: - current_neighbor = tree.nodes[neighbor] - point, priority = calc_transferred_point(bisectorline_forbidden_point_neighbor, current_neighbor) - if point is None: - continue - current_neighbor.transferred_point_priority_deque.insert(projected_point_tuple( - point=point, point_source=sample_linestring.PointSource.FORBIDDEN_POINT), priority) - - i += 1 - currentDistance += next_spacing - - assert(len(point_list) == len(point_source_list)) - - -# Calculates the nearest interserction point of "bisectorline" with the coordinates of child. -# It returns the intersection point and its distance along the coordinates of the child or "None, None" if no -# intersection was found. -def calc_transferred_point_graph(bisectorline, edge_geometry): - result = bisectorline.intersection(edge_geometry) - if result.is_empty: - return None, None - desired_point = Point() - if result.geom_type == 'Point': - desired_point = result - elif result.geom_type == 'LineString': - desired_point = Point(result.coords[0]) - else: - resultlist = list(result) - desired_point = resultlist[0] - if len(resultlist) > 1: - desired_point = nearest_points( - result, Point(bisectorline.coords[0]))[0] - - priority = edge_geometry.project(desired_point) - point = desired_point - return point, priority - - -def transfer_points_to_surrounding_graph(fill_stitch_graph, current_edge, used_offset, offset_by_half, to_transfer_points, # noqa: C901 - overnext_neighbor=False, transfer_forbidden_points=False, transfer_to_previous=True, transfer_to_next=True): - """ - Takes the current graph edge and its rastered points (to_transfer_points) and transfers these points to its previous and next edges (if selected) - To do so it calculates the current normal and determines its intersection with the neighbors which gives the transferred points. - Input: - -fill_stitch_graph: Graph data structure of the stitching lines - -current_edge: Current graph edge whose neighbors in fill_stitch_graph shall be considered - -used_offset: The used offset when the curves where offsetted - -offset_by_half: True if the transferred points shall be interlaced with respect to the points in "to_transfer_points" - -to_transfer_points: List of points belonging to treenode which shall be transferred - it is assumed that to_transfer_points - can be handled as closed ring - -overnext_neighbor: Transfer the points to the overnext neighbor (gives a more stable interlacing) - -transfer_forbidden_points: Only allowed for interlacing (offset_by_half): Might be used to transfer points unshifted as - forbidden points to the neighbor to avoid a point placing there - -transfer_to_previous: If True, points will be transferred to the previous edge in the graph - -transfer_to_next: If True, points will be transferred to the next edge in the graph - Output: - -Fills the attribute "transferred_point_priority_deque" of the next/previous edges. 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 - """ - - assert((overnext_neighbor and not offset_by_half) or not overnext_neighbor) - 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: - return - - # Take only neighbors which have not rastered before - # We need to distinguish between childs (project towards inner) and parent/siblings (project towards outer) - previous_edge_list = [] - previous_edge_list_forbidden = [] - next_edge_list = [] - next_edge_list_forbidden = [] - - if transfer_to_previous: - previous_neighbors_tuples = current_edge['previous_neighbors'] - for neighbor in previous_neighbors_tuples: - neighbor_edge = fill_stitch_graph[neighbor[0] - ][neighbor[-1]]['segment'] - if not neighbor_edge['already_rastered']: - if not overnext_neighbor: - previous_edge_list.append(neighbor_edge) - if transfer_forbidden_points: - previous_edge_list_forbidden.append(neighbor_edge) - if overnext_neighbor: - overnext_previous_neighbors_tuples = neighbor_edge['previous_neighbors'] - for overnext_neighbor in overnext_previous_neighbors_tuples: - overnext_neighbor_edge = fill_stitch_graph[overnext_neighbor[0] - ][overnext_neighbor[-1]]['segment'] - if not overnext_neighbor_edge['already_rastered']: - previous_edge_list.append(overnext_neighbor_edge) - - if transfer_to_next: - next_neighbors_tuples = current_edge['next_neighbors'] - for neighbor in next_neighbors_tuples: - neighbor_edge = fill_stitch_graph[neighbor[0] - ][neighbor[-1]]['segment'] - if not neighbor_edge['already_rastered']: - if not overnext_neighbor: - next_edge_list.append(neighbor_edge) - if transfer_forbidden_points: - next_edge_list_forbidden.append(neighbor_edge) - if overnext_neighbor: - overnext_next_neighbors_tuples = neighbor_edge['next_neighbors'] - for overnext_neighbor in overnext_next_neighbors_tuples: - overnext_neighbor_edge = fill_stitch_graph[overnext_neighbor[0] - ][overnext_neighbor[-1]]['segment'] - if not overnext_neighbor_edge['already_rastered']: - next_edge_list.append(overnext_neighbor_edge) - - if not previous_edge_list and not next_edge_list: - return - - # Go through all rastered points of treenode and check where they should be transferred to its neighbar - point_list = list(MultiPoint(to_transfer_points)) - line = LineString(to_transfer_points) - - bisectorline_length = abs(used_offset) * \ - constants.transfer_point_distance_factor * \ - (2.0 if overnext_neighbor else 1.0) - - bisectorline_length_forbidden_points = abs(used_offset) * \ - constants.transfer_point_distance_factor - - linesign_child = math.copysign(1, used_offset) - - i = 0 - currentDistance = 0 - while i < len(point_list): - - # We create a bisecting line through the current point - normalized_vector_prev_x = ( - point_list[i].coords[0][0]-point_list[i-1].coords[0][0]) # makes use of closed shape - normalized_vector_prev_y = ( - point_list[i].coords[0][1]-point_list[i-1].coords[0][1]) - prev_spacing = math.sqrt(normalized_vector_prev_x*normalized_vector_prev_x + - normalized_vector_prev_y*normalized_vector_prev_y) - - normalized_vector_prev_x /= prev_spacing - normalized_vector_prev_y /= prev_spacing - - normalized_vector_next_x = normalized_vector_next_y = 0 - next_spacing = 0 - while True: - normalized_vector_next_x = ( - point_list[i].coords[0][0]-point_list[(i+1) % len(point_list)].coords[0][0]) - normalized_vector_next_y = ( - point_list[i].coords[0][1]-point_list[(i+1) % len(point_list)].coords[0][1]) - next_spacing = math.sqrt(normalized_vector_next_x*normalized_vector_next_x + - normalized_vector_next_y*normalized_vector_next_y) - if next_spacing < constants.line_lengh_seen_as_one_point: - point_list.pop(i) - currentDistance += next_spacing - continue - - normalized_vector_next_x /= next_spacing - normalized_vector_next_y /= next_spacing - break - - vecx = (normalized_vector_next_x+normalized_vector_prev_x) - vecy = (normalized_vector_next_y+normalized_vector_prev_y) - vec_length = math.sqrt(vecx*vecx+vecy*vecy) - - vecx_forbidden_point = vecx - vecy_forbidden_point = vecy - - # The two sides are (anti)parallel - construct normal vector (bisector) manually: - # If we offset by half we are offseting normal to the next segment - if(vec_length < constants.line_lengh_seen_as_one_point or offset_by_half): - vecx = linesign_child*bisectorline_length*normalized_vector_next_y - vecy = -linesign_child*bisectorline_length*normalized_vector_next_x - - if transfer_forbidden_points: - vecx_forbidden_point = linesign_child * \ - bisectorline_length_forbidden_points*normalized_vector_next_y - vecy_forbidden_point = -linesign_child * \ - bisectorline_length_forbidden_points*normalized_vector_next_x - - else: - vecx *= bisectorline_length/vec_length - vecy *= bisectorline_length/vec_length - - if (vecx*normalized_vector_next_y-vecy * normalized_vector_next_x)*linesign_child < 0: - vecx = -vecx - vecy = -vecy - vecx_forbidden_point = vecx - vecy_forbidden_point = vecy - - assert((vecx*normalized_vector_next_y-vecy * - normalized_vector_next_x)*linesign_child >= 0) - - originPoint = point_list[i] - originPoint_forbidden_point = point_list[i] - if(offset_by_half): - off = currentDistance+next_spacing/2 - if off > line.length: - break - originPoint = line.interpolate(off) - - bisectorline = LineString([(originPoint.coords[0][0]-vecx, - originPoint.coords[0][1]-vecy), - (originPoint.coords[0][0]+vecx, - originPoint.coords[0][1]+vecy)]) - - bisectorline_forbidden_point = LineString([(originPoint_forbidden_point.coords[0][0]-vecx_forbidden_point, - originPoint_forbidden_point.coords[0][1]-vecy_forbidden_point), - (originPoint_forbidden_point.coords[0][0]+vecx_forbidden_point, - originPoint_forbidden_point.coords[0][1]+vecy_forbidden_point)]) - - for edge in previous_edge_list+next_edge_list: - point, priority = calc_transferred_point_graph( - bisectorline, edge['geometry']) - if point is None: - continue - edge['projected_points'].insert(projected_point_tuple( - point=point, point_source=sample_linestring.PointSource.OVERNEXT if overnext_neighbor - else sample_linestring.PointSource.DIRECT), priority) - for edge_forbidden in previous_edge_list_forbidden+next_edge_list_forbidden: - point, priority = calc_transferred_point_graph( - bisectorline_forbidden_point, edge_forbidden['geometry']) - if point is None: - continue - edge_forbidden['projected_points'].insert(projected_point_tuple( - point=point, point_source=sample_linestring.PointSource.FORBIDDEN_POINT), priority) - - i += 1 - currentDistance += next_spacing diff --git a/lib/stitches/sample_linestring.py b/lib/stitches/sample_linestring.py deleted file mode 100644 index 657607175..000000000 --- a/lib/stitches/sample_linestring.py +++ /dev/null @@ -1,342 +0,0 @@ -from enum import IntEnum - -import numpy as np -from shapely.geometry import LineString, Point -from shapely.ops import substring - -from ..stitches import constants, point_transfer - - -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 - # point which must be stitched to avoid to large deviations to the desired path - EDGE_NEEDED = 3 - # NOT_NEEDED = 4 #Legacy - # ALREADY_TRANSFERRED = 5 #Legacy - # ADDITIONAL_TRACKING_POINT_NOT_NEEDED = 6 #Legacy - # EDGE_RASTERING_ALLOWED = 7 #Legacy - # EDGE_PREVIOUSLY_SHIFTED = 8 #Legacy - ENTER_LEAVING_POINT = 9 # Whether this point is used to enter or leave a child - # If the angle at a point is <= constants.limiting_angle this point is marked as SOFT_EDGE - SOFT_EDGE_INTERNAL = 10 - # If the angle at a point is > constants.limiting_angle this point is marked as HARD_EDGE (HARD_EDGES will always be stitched) - HARD_EDGE_INTERNAL = 11 - # If the point was created by a projection (transferred point) of a neighbor it is marked as PROJECTED_POINT - PROJECTED_POINT = 12 - REGULAR_SPACING_INTERNAL = 13 # introduced to not exceed maximal stichting distance - # FORBIDDEN_POINT_INTERNAL=14 #Legacy - SOFT_EDGE = 15 # If the angle at a point is <= constants.limiting_angle this point is marked as SOFT_EDGE - # If the angle at a point is > constants.limiting_angle this point is marked as HARD_EDGE (HARD_EDGES will always be stitched) - HARD_EDGE = 16 - FORBIDDEN_POINT = 17 # Only relevant for desired interlacing - non-shifted point positions at the next neighbor are marked as forbidden - # If one decides to avoid forbidden points new points to the left and to the right as replacement are created - REPLACED_FORBIDDEN_POINT = 18 - DIRECT = 19 # Calculated by next neighbor projection - OVERNEXT = 20 # Calculated by overnext neighbor projection - - -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)) - - # approach from https://stackoverflow.com/a/50772253/4249120 - vectors = np.diff(line.coords, axis=0) - v1 = vectors[:-1] - v2 = vectors[1:] - dot = np.einsum('ij,ij->i', v1, v2) - mag1 = np.linalg.norm(v1, axis=1) - mag2 = np.linalg.norm(v2, axis=1) - cosines = dot / (mag1 * mag2) - angles[1:-1] = np.arccos(np.clip(cosines, -1, 1)) - - return angles - - -def raster_line_string_with_priority_points(line, # noqa: C901 - start_distance, - end_distance, - maxstitch_distance, - minstitch_distance, - 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 - -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) - 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) < 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) - - linecoords = line.coords - - if start_distance > end_distance: - start_distance, end_distance = line.length - \ - start_distance, line.length - end_distance - 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] - - # 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, 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, minstitch_distance, constants.point_spacing_to_be_considered_equal)): - deque_points.pop() - - # 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) - - # aligned line is a line without doubled points. - # I had the strange situation in which the offset "start_distance" from the line beginning - # resulted in a starting point which was already present in aligned_line causing a doubled point. - # A double point is not allowed in the following calculations so we need to remove it: - if (abs(path_coords.coords[0][0] - path_coords.coords[1][0]) < constants.eps and - abs(path_coords.coords[0][1] - path_coords.coords[1][1]) < constants.eps): - path_coords.coords = path_coords.coords[1:] - if (abs(path_coords.coords[-1][0] - path_coords.coords[-2][0]) < constants.eps and - abs(path_coords.coords[-1][1] - path_coords.coords[-2][1]) < constants.eps): - path_coords.coords = path_coords.coords[:-1] - - angles = calculate_line_angles(path_coords) - # 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 - last_point = Point(path_coords.coords[0]) - # 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): - current_distance += last_point.distance(Point(point)) - last_point = Point(point) - while dq_iter < len(deque_points) and deque_points[dq_iter][1] < current_distance+start_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]+start_distance < abs_offset*constants.factor_offset_forbidden_point)): - item = merged_point_list.pop() - merged_point_list.append((point_transfer.projected_point_tuple( - point=item[0].point, point_source=PointSource.FORBIDDEN_POINT), item[1]-start_distance)) - else: - merged_point_list.append( - (deque_points[dq_iter][0], deque_points[dq_iter][1]-start_distance)) - # 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-start_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((point_transfer.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): - # 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 < 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 - merged_point_list.insert(segment_end_index, (point_transfer.projected_point_tuple( - point=aligned_line.interpolate(new_distance), point_source=PointSource.REGULAR_SPACING_INTERNAL), new_distance)) - segment_end_index += 1 - break - - 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): - 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: - 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 offset_by_half: - index_preferred = index_overnext - index_less_preferred = index_direct - else: - index_preferred = index_direct - index_less_preferred = index_overnext - - if index_preferred != -1: - if (index_less_preferred != -1 and index_less_preferred > index_preferred and - (merged_point_list[index_less_preferred][1]-merged_point_list[index_preferred][1]) >= - constants.factor_segment_length_direct_preferred_over_overnext * - (merged_point_list[index_preferred][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_less_preferred - else: - segment_end_index = index_preferred - elif index_less_preferred != -1: - segment_end_index = index_less_preferred - - # 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 ( constants.point_spacing_to_be_considered_equal and distance_right > constants.point_spacing_to_be_considered_equal: - new_point_left_proj = result_list[index][1]-distance_left - if new_point_left_proj < 0: - new_point_left_proj += line.length - new_point_right_proj = result_list[index][1]+distance_right - if new_point_right_proj > line.length: - new_point_right_proj -= line.length - point_left = line.interpolate(new_point_left_proj) - point_right = line.interpolate(new_point_right_proj) - forbidden_point_distance = result_list[index][0].point.distance( - LineString([point_left, point_right])) - if forbidden_point_distance < constants.factor_offset_remove_dense_points*abs_offset: - del result_list[index] - result_list.insert(index, (point_transfer.projected_point_tuple( - point=point_right, point_source=PointSource.REPLACED_FORBIDDEN_POINT), new_point_right_proj)) - result_list.insert(index, (point_transfer.projected_point_tuple( - point=point_left, point_source=PointSource.REPLACED_FORBIDDEN_POINT), new_point_left_proj)) - current_index_shift += 1 - break - else: - distance_left /= 2.0 - distance_right /= 2.0 - return result_list diff --git a/lib/stitches/tangential_fill_stitch_pattern_creator.py b/lib/stitches/tangential_fill_stitch_pattern_creator.py index 4abe498d0..0ee1c0318 100644 --- a/lib/stitches/tangential_fill_stitch_pattern_creator.py +++ b/lib/stitches/tangential_fill_stitch_pattern_creator.py @@ -10,7 +10,6 @@ from .running_stitch import running_stitch from ..debug import debug from ..stitches import constants -from ..stitches import sample_linestring from ..stitch_plan import Stitch from ..utils.geometry import cut, roll_linear_ring, reverse_line_string @@ -109,23 +108,6 @@ def create_nearest_points_list( return children_nearest_points -def calculate_replacing_middle_point(line_segment, abs_offset, max_stitch_distance): - """ - Takes a line segment (consisting of 3 points!) - and calculates a new middle point if the line_segment is - straight enough to be resampled by points max_stitch_distance apart FROM THE END OF line_segment. - Returns None if the middle point is not needed. - """ - angles = sample_linestring.calculate_line_angles(line_segment) - if angles[1] < abs_offset * constants.limiting_angle_straight: - if line_segment.length < max_stitch_distance: - return None - else: - return line_segment.interpolate(line_segment.length - max_stitch_distance).coords[0] - else: - return line_segment.coords[1] - - @debug.time def connect_raster_tree_from_inner_to_outer(tree, node, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half): # noqa: C901