remove sample_linestring and point_transfer

pull/1548/head
Lex Neva 2022-05-02 14:41:34 -04:00 zatwierdzone przez Kaalleen
rodzic bd8cb0d1ff
commit 8a1f70a6cd
3 zmienionych plików z 0 dodań i 863 usunięć

Wyświetl plik

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

Wyświetl plik

@ -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 (<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]
# 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)):
return_point_list.append(result_list[i][0].point.coords[0])
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
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:
index += current_index_shift
distance_left = result_list[index][0].point.distance(
result_list[index-1][0].point)/2.0
distance_right = result_list[index][0].point.distance(
result_list[(index+1) % len(result_list)][0].point)/2.0
while distance_left > 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

Wyświetl plik

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