summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/stitches/ConnectAndSamplePattern.py9
-rw-r--r--lib/stitches/LineStringSampling.py273
-rw-r--r--lib/stitches/StitchPattern.py16
-rw-r--r--lib/stitches/auto_fill.py10
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)):