summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLex Neva <github.com@lexneva.name>2022-04-21 23:09:05 -0400
committerKaalleen <reni@allenka.de>2022-05-04 19:18:33 +0200
commit6ca1af0c88728bd91f1775d0056a66d9272b3a1b (patch)
tree1c4c388a79d03fff15593bba91684dde8800c7f9 /lib
parentcbcaa0ac0ef43c4bb9da3aa49abd23c1e0d4b360 (diff)
avoid anytree dependency
Diffstat (limited to 'lib')
-rw-r--r--lib/stitches/point_transfer.py80
-rw-r--r--lib/stitches/tangential_fill_stitch_line_creator.py169
-rw-r--r--lib/stitches/tangential_fill_stitch_pattern_creator.py231
-rw-r--r--lib/utils/dotdict.py2
4 files changed, 261 insertions, 221 deletions
diff --git a/lib/stitches/point_transfer.py b/lib/stitches/point_transfer.py
index 553ffbda..c0d519ef 100644
--- a/lib/stitches/point_transfer.py
+++ b/lib/stitches/point_transfer.py
@@ -15,7 +15,7 @@ projected_point_tuple = namedtuple(
def calc_transferred_point(bisectorline, child):
"""
- Calculates the nearest interserction point of "bisectorline" with the coordinates of child (child.val).
+ 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.
"""
@@ -39,7 +39,7 @@ def calc_transferred_point(bisectorline, child):
return point, priority
-def transfer_points_to_surrounding(treenode, used_offset, offset_by_half, to_transfer_points, to_transfer_points_origin=[], # noqa: C901
+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):
"""
@@ -64,18 +64,24 @@ def transfer_points_to_surrounding(treenode, used_offset, offset_by_half, to_tra
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))
+ 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 = treenode.children
- siblings_tuple = treenode.siblings
+ 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 = []
@@ -85,38 +91,39 @@ def transfer_points_to_surrounding(treenode, used_offset, offset_by_half, to_tra
if transfer_to_child:
for child in childs_tuple:
- if not child.already_rastered:
+ 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 subchild in child.children:
- if not subchild.already_rastered:
- child_list.append(subchild)
+ 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 sibling.already_rastered:
+ 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 subchild in sibling.children:
- if not subchild.already_rastered:
- neighbor_list.append(subchild)
+ for nibling in tree[sibling]:
+ if not tree.nodes[nibling].already_rastered:
+ neighbor_list.append(nibling)
- if transfer_to_parent and treenode.parent is not None:
- if not treenode.parent.already_rastered:
+ 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(treenode.parent)
+ neighbor_list.append(current_node.parent)
if transfer_forbidden_points:
- neighbor_list_forbidden.append(treenode.parent)
+ neighbor_list_forbidden.append(current_node.parent)
if overnext_neighbor:
- if treenode.parent.parent is not None:
- if not treenode.parent.parent.already_rastered:
- neighbor_list.append(treenode.parent.parent)
+ 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
@@ -130,7 +137,7 @@ def transfer_points_to_surrounding(treenode, used_offset, offset_by_half, to_tra
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):
+ if point_source_list:
point_source_list.pop()
if len(point_list) == 0:
return
@@ -243,34 +250,35 @@ def transfer_points_to_surrounding(treenode, used_offset, offset_by_half, to_tra
originPoint_forbidden_point.coords[0][1]-vecy_forbidden_point)])
for child in child_list:
- point, priority = calc_transferred_point(bisectorline_child, child)
+ current_child = tree.nodes[child]
+ point, priority = calc_transferred_point(bisectorline_child, current_child)
if point is None:
continue
- child.transferred_point_priority_deque.insert(projected_point_tuple(
+ 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:
- point, priority = calc_transferred_point(
- bisectorline_forbidden_point_child, child)
+ current_child = tree.nodes[child]
+ point, priority = calc_transferred_point(bisectorline_forbidden_point_child, current_child)
if point is None:
continue
- child.transferred_point_priority_deque.insert(projected_point_tuple(
+ 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:
- point, priority = calc_transferred_point(
- bisectorline_neighbor, neighbor)
+ current_neighbor = tree.nodes[neighbor]
+ point, priority = calc_transferred_point(bisectorline_neighbor, current_neighbor)
if point is None:
continue
- neighbor.transferred_point_priority_deque.insert(projected_point_tuple(
+ 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:
- point, priority = calc_transferred_point(
- bisectorline_forbidden_point_neighbor, neighbor)
+ current_neighbor = tree.nodes[neighbor]
+ point, priority = calc_transferred_point(bisectorline_forbidden_point_neighbor, current_neighbor)
if point is None:
continue
- neighbor.transferred_point_priority_deque.insert(projected_point_tuple(
+ current_neighbor.transferred_point_priority_deque.insert(projected_point_tuple(
point=point, point_source=sample_linestring.PointSource.FORBIDDEN_POINT), priority)
i += 1
diff --git a/lib/stitches/tangential_fill_stitch_line_creator.py b/lib/stitches/tangential_fill_stitch_line_creator.py
index 6b141611..deb87659 100644
--- a/lib/stitches/tangential_fill_stitch_line_creator.py
+++ b/lib/stitches/tangential_fill_stitch_line_creator.py
@@ -1,6 +1,6 @@
from enum import IntEnum
-from anytree import AnyNode, LevelOrderGroupIter, PreOrderIter
+import networkx as nx
from depq import DEPQ
from shapely.geometry import MultiLineString, Polygon
from shapely.geometry import MultiPolygon
@@ -10,6 +10,13 @@ from shapely.ops import polygonize
from ..stitches import constants
from ..stitches import tangential_fill_stitch_pattern_creator
+from ..utils import DotDict
+
+
+class Tree(nx.DiGraph):
+ # This lets us do tree.nodes['somenode'].parent instead of the default
+ # tree.nodes['somenode']['parent'].
+ node_attr_dict_factory = DotDict
def offset_linear_ring(ring, offset, resolution, join_style, mitre_limit):
@@ -126,15 +133,15 @@ def take_only_valid_linear_rings(rings):
return LinearRing()
-def make_tree_uniform_ccw(root):
+def make_tree_uniform_ccw(tree):
"""
Since naturally holes have the opposite point ordering than non-holes we
make all lines within the tree "root" uniform (having all the same
ordering direction)
"""
- for node in PreOrderIter(root):
- if node.id == "hole":
- node.val.coords = list(node.val.coords)[::-1]
+ for node in nx.dfs_preorder_nodes(tree, 'root'):
+ if tree.nodes[node].type == "hole":
+ tree.nodes[node].val = LinearRing(reversed(tree.nodes[node].val.coords))
# Used to define which stitching strategy shall be used
@@ -144,7 +151,7 @@ class StitchingStrategy(IntEnum):
SPIRAL = 2
-def check_and_prepare_tree_for_valid_spiral(root):
+def check_and_prepare_tree_for_valid_spiral(tree):
"""
Takes a tree consisting of offsetted curves. If a parent has more than one child we
cannot create a spiral. However, to make the routine more robust, we allow more than
@@ -153,27 +160,34 @@ def check_and_prepare_tree_for_valid_spiral(root):
childs. If the routine returns false even under the mentioned weaker conditions the
tree cannot be connected by one spiral.
"""
- for children in LevelOrderGroupIter(root):
- if len(children) > 1:
- count = 0
- child_with_children = None
- for child in children:
- if not child.is_leaf:
- count += 1
- child_with_children = child
- if count > 1:
+
+ def process_node(node):
+ children = set(tree[node])
+
+ if len(children) == 0:
+ return True
+ elif len(children) == 1:
+ child = children.pop()
+ return process_node(child)
+ else:
+ children_with_children = {child for child in children if tree[child]}
+ if len(children_with_children) > 1:
+ # Node has multiple children with children, so a perfect spiral is not possible.
+ # This False value will be returned all the way up the stack.
return False
- elif count == 1:
- child_with_children.parent.children = [child_with_children]
- else: # count == 0 means all childs have no children so we take only the longest child
- max_length = 0
- longest_child = None
- for child in children:
- if child.val.length > max_length:
- max_length = child.val.length
- longest_child = child
- longest_child.parent.children = [longest_child]
- return True
+ elif len(children_with_children) == 1:
+ children_without_children = children - children_with_children
+ child = children_with_children.pop()
+ tree.remove_nodes_from(children_without_children)
+ return process_node(child)
+ else:
+ # None of the children has its own children, so we'll just take the longest.
+ longest = max(children, key=lambda child: tree[child]['val'].length)
+ shorter_children = children - {longest}
+ tree.remove_nodes_from(shorter_children)
+ return process_node(longest)
+
+ return process_node('root')
def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, offset_by_half, strategy, starting_point): # noqa: C901
@@ -213,25 +227,29 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance,
ordered_poly = orient(poly, -1)
ordered_poly = ordered_poly.simplify(
constants.simplification_threshold, False)
- root = AnyNode(
- id="node",
- val=ordered_poly.exterior,
- already_rastered=False,
- transferred_point_priority_deque=DEPQ(iterable=None, maxlen=None),
- )
- active_polys = [root]
+ tree = Tree()
+ tree.add_node('root',
+ type='node',
+ parent=None,
+ val=ordered_poly.exterior,
+ already_rastered=False,
+ transferred_point_priority_deque=DEPQ(iterable=None, maxlen=None),
+ )
+ active_polys = ['root']
active_holes = [[]]
- for holes in ordered_poly.interiors:
- active_holes[0].append(
- AnyNode(
- id="hole",
- val=holes,
- already_rastered=False,
- transferred_point_priority_deque=DEPQ(
- iterable=None, maxlen=None),
- )
- )
+ # We don't care about the names of the nodes, we just need them to be unique.
+ node_num = 0
+
+ for hole in ordered_poly.interiors:
+ tree.add_node(node_num,
+ type="hole",
+ val=hole,
+ already_rastered=False,
+ transferred_point_priority_deque=DEPQ(iterable=None, maxlen=None),
+ )
+ active_holes[0].append(node_num)
+ node_num += 1
while len(active_polys) > 0:
current_poly = active_polys.pop()
@@ -239,7 +257,7 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance,
poly_inners = []
outer = offset_linear_ring(
- current_poly.val,
+ tree.nodes[current_poly].val,
offset,
resolution=5,
join_style=join_style,
@@ -248,9 +266,9 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance,
outer = outer.simplify(constants.simplification_threshold, False)
outer = take_only_valid_linear_rings(outer)
- for j in range(len(current_holes)):
+ for hole in current_holes:
inner = offset_linear_ring(
- current_holes[j].val,
+ tree.nodes[hole].val,
-offset, # take negative offset for holes
resolution=5,
join_style=join_style,
@@ -275,11 +293,10 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance,
MultiPolygon(poly_inners))
if not result.is_empty and result.area > offset * offset / 10:
- result_list = []
if result.geom_type == "Polygon":
result_list = [result]
else:
- result_list = list(result)
+ result_list = list(result.geoms)
for polygon in result_list:
polygon = orient(polygon, -1)
@@ -295,29 +312,31 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance,
if poly_coords.is_empty:
continue
- node = AnyNode(
- id="node",
- parent=current_poly,
- val=poly_coords,
- already_rastered=False,
- transferred_point_priority_deque=DEPQ(
- iterable=None, maxlen=None
- ),
- )
+ node = node_num
+ node_num += 1
+ tree.add_node(node,
+ type='node',
+ parent=current_poly,
+ val=poly_coords,
+ already_rastered=False,
+ transferred_point_priority_deque=DEPQ(iterable=None, maxlen=None),
+ )
+ tree.add_edge(current_poly, node)
active_polys.append(node)
hole_node_list = []
for hole in polygon.interiors:
- hole_node = AnyNode(
- id="hole",
- val=hole,
- already_rastered=False,
- transferred_point_priority_deque=DEPQ(
- iterable=None, maxlen=None
- ),
- )
+ hole_node = node_num
+ node_num += 1
+ tree.add_node(hole_node,
+ type="hole",
+ val=hole,
+ already_rastered=False,
+ transferred_point_priority_deque=DEPQ(iterable=None, maxlen=None),
+ )
for previous_hole in current_holes:
- if Polygon(hole).contains(Polygon(previous_hole.val)):
- previous_hole.parent = hole_node
+ if Polygon(hole).contains(Polygon(tree.nodes[previous_hole].val)):
+ tree.nodes[previous_hole].parent = hole_node
+ tree.add_edge(hole_node, previous_hole)
hole_node_list.append(hole_node)
active_holes.append(hole_node_list)
for previous_hole in current_holes:
@@ -325,23 +344,23 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance,
# contained in the new holes they
# have been merged with the
# outer polygon
- if previous_hole.parent is None:
- previous_hole.parent = current_poly
+ if tree.nodes[previous_hole].parent is None:
+ tree.nodes[previous_hole].parent = current_poly
+ tree.add_edge(current_poly, previous_hole)
- # print(RenderTree(root))
- make_tree_uniform_ccw(root)
+ make_tree_uniform_ccw(tree)
if strategy == StitchingStrategy.CLOSEST_POINT:
(connected_line, connected_line_origin) = tangential_fill_stitch_pattern_creator.connect_raster_tree_nearest_neighbor(
- root, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half)
+ tree, 'root', offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half)
elif strategy == StitchingStrategy.INNER_TO_OUTER:
(connected_line, connected_line_origin) = tangential_fill_stitch_pattern_creator.connect_raster_tree_from_inner_to_outer(
- root, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half)
+ tree, 'root', offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half)
elif strategy == StitchingStrategy.SPIRAL:
- if not check_and_prepare_tree_for_valid_spiral(root):
+ if not check_and_prepare_tree_for_valid_spiral(tree):
raise ValueError("Geometry cannot be filled with one spiral!")
(connected_line, connected_line_origin) = tangential_fill_stitch_pattern_creator.connect_raster_tree_spiral(
- root, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half)
+ tree, offset, stitch_distance, min_stitch_distance, starting_point, offset_by_half)
else:
raise ValueError("Invalid stitching stratety!")
diff --git a/lib/stitches/tangential_fill_stitch_pattern_creator.py b/lib/stitches/tangential_fill_stitch_pattern_creator.py
index 084b1d01..1b085ed6 100644
--- a/lib/stitches/tangential_fill_stitch_pattern_creator.py
+++ b/lib/stitches/tangential_fill_stitch_pattern_creator.py
@@ -3,7 +3,7 @@ from collections import namedtuple
import numpy as np
import trimesh
-from anytree import PreOrderIter
+import networkx as nx
from depq import DEPQ
from shapely.geometry import MultiPoint, Point
from shapely.geometry.polygon import LineString, LinearRing
@@ -54,7 +54,7 @@ def cut(line, distance):
def connect_raster_tree_nearest_neighbor( # noqa: C901
- tree, used_offset, stitch_distance, min_stitch_distance, close_point, offset_by_half):
+ tree, node, used_offset, stitch_distance, min_stitch_distance, close_point, offset_by_half):
"""
Takes the offsetted curves organized as tree, connects and samples them.
Strategy: A connection from parent to child is made where both curves
@@ -77,20 +77,21 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
placed at this position
"""
- current_coords = tree.val
+ current_node = tree.nodes[node]
+ current_coords = current_node.val
abs_offset = abs(used_offset)
result_coords = []
result_coords_origin = []
# We cut the current item so that its index 0 is closest to close_point
- start_distance = tree.val.project(close_point)
+ start_distance = current_coords.project(close_point)
if start_distance > 0:
current_coords = cut(current_coords, start_distance)
- tree.val = current_coords
+ current_node.val = current_coords
- if not tree.transferred_point_priority_deque.is_empty():
+ if not current_node.transferred_point_priority_deque.is_empty():
new_DEPQ = DEPQ(iterable=None, maxlen=None)
- for item, priority in tree.transferred_point_priority_deque:
+ for item, priority in current_node.transferred_point_priority_deque:
new_DEPQ.insert(
item,
math.fmod(
@@ -98,7 +99,7 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
current_coords.length,
),
)
- tree.transferred_point_priority_deque = new_DEPQ
+ current_node.transferred_point_priority_deque = new_DEPQ
stitching_direction = 1
# This list should contain a tuple of nearest points between
@@ -106,8 +107,8 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
# distance along the current geometry, and the belonging subtree node
nearest_points_list = []
- for subnode in tree.children:
- point_parent, point_child = nearest_points(current_coords, subnode.val)
+ for subnode in tree[node]:
+ point_parent, point_child = nearest_points(current_coords, tree.nodes[subnode].val)
proj_distance = current_coords.project(point_parent)
nearest_points_list.append(
nearest_neighbor_tuple(
@@ -141,7 +142,7 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
end_distance,
stitch_distance,
min_stitch_distance,
- tree.transferred_point_priority_deque,
+ current_node.transferred_point_priority_deque,
abs_offset,
offset_by_half,
False)
@@ -149,8 +150,8 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
assert len(own_coords) == len(own_coords_origin)
own_coords_origin[0] = sample_linestring.PointSource.ENTER_LEAVING_POINT
own_coords_origin[-1] = sample_linestring.PointSource.ENTER_LEAVING_POINT
- tree.stitching_direction = stitching_direction
- tree.already_rastered = True
+ current_node.stitching_direction = stitching_direction
+ current_node.already_rastered = True
# Next we need to transfer our rastered points to siblings and childs
to_transfer_point_list = []
@@ -172,6 +173,7 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
# to use "-used_offset" for stitching_direction==-1
point_transfer.transfer_points_to_surrounding(
tree,
+ node,
stitching_direction * used_offset,
offset_by_half,
to_transfer_point_list,
@@ -188,6 +190,7 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
if offset_by_half:
point_transfer.transfer_points_to_surrounding(
tree,
+ node,
stitching_direction * used_offset,
False,
to_transfer_point_list,
@@ -232,6 +235,7 @@ def connect_raster_tree_nearest_neighbor( # noqa: C901
item = nearest_points_list[cur_item]
(child_coords, child_coords_origin) = connect_raster_tree_nearest_neighbor(
+ tree,
item.child_node,
used_offset,
stitch_distance,
@@ -324,7 +328,7 @@ def get_nearest_points_closer_than_thresh(travel_line, next_line, thresh):
def create_nearest_points_list(
- travel_line, children_list, threshold, threshold_hard, preferred_direction=0):
+ travel_line, tree, children_list, threshold, threshold_hard, preferred_direction=0):
"""
Takes a line and calculates the nearest distance along this line to
enter the childs in children_list
@@ -357,13 +361,13 @@ def create_nearest_points_list(
weight_reversed_order = 0
for child in children_list:
result = get_nearest_points_closer_than_thresh(
- travel_line, child.val, threshold
+ travel_line, tree.nodes[child].val, threshold
)
if result is None:
# where holes meet outer borders a distance
# up to 2*used offset can arise
result = get_nearest_points_closer_than_thresh(
- travel_line, child.val, threshold_hard
+ travel_line, tree.nodes[child].val, threshold_hard
)
assert result is not None
proj = travel_line.project(result[0])
@@ -378,13 +382,13 @@ def create_nearest_points_list(
)
result = get_nearest_points_closer_than_thresh(
- travel_line_reversed, child.val, threshold
+ travel_line_reversed, tree.nodes[child].val, threshold
)
if result is None:
# where holes meet outer borders a distance
# up to 2*used offset can arise
result = get_nearest_points_closer_than_thresh(
- travel_line_reversed, child.val, threshold_hard
+ travel_line_reversed, tree.nodes[child].val, threshold_hard
)
assert result is not None
proj = travel_line_reversed.project(result[0])
@@ -404,7 +408,7 @@ def create_nearest_points_list(
weight_in_order / 2, max(0, weight_in_order - 10 * threshold)
)
if weight_in_order == weight_reversed_order:
- return (1, result_list_in_order)
+ return 1, result_list_in_order
elif preferred_direction == -1:
# Reduce weight_reversed_order to make reversed
# stitching more preferred
@@ -438,7 +442,8 @@ def calculate_replacing_middle_point(line_segment, abs_offset, max_stitch_distan
return line_segment.coords[1]
-def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance, min_stitch_distance, close_point, offset_by_half): # noqa: C901
+def connect_raster_tree_from_inner_to_outer(tree, node, used_offset, stitch_distance, min_stitch_distance, close_point,
+ offset_by_half): # noqa: C901
"""
Takes the offsetted curves organized as tree, connects and samples them.
Strategy: A connection from parent to child is made as fast as possible to
@@ -462,20 +467,21 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
at this position
"""
- current_coords = tree.val
+ current_node = tree.nodes[node]
+ current_coords = current_node.val
abs_offset = abs(used_offset)
result_coords = []
result_coords_origin = []
- start_distance = tree.val.project(close_point)
+ start_distance = current_coords.project(close_point)
# We cut the current path so that its index 0 is closest to close_point
if start_distance > 0:
current_coords = cut(current_coords, start_distance)
- tree.val = current_coords
+ current_node.val = current_coords
- if not tree.transferred_point_priority_deque.is_empty():
+ if not current_node.transferred_point_priority_deque.is_empty():
new_DEPQ = DEPQ(iterable=None, maxlen=None)
- for item, priority in tree.transferred_point_priority_deque:
+ for item, priority in current_node.transferred_point_priority_deque:
new_DEPQ.insert(
item,
math.fmod(
@@ -483,19 +489,20 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
current_coords.length,
),
)
- tree.transferred_point_priority_deque = new_DEPQ
+ current_node.transferred_point_priority_deque = new_DEPQ
# We try to use always the opposite stitching direction with respect to the
# parent to avoid crossings when entering and leaving the child
parent_stitching_direction = -1
- if tree.parent is not None:
- parent_stitching_direction = tree.parent.stitching_direction
+ if current_node.parent is not None:
+ parent_stitching_direction = tree.nodes[current_node.parent].stitching_direction
# Find the nearest point in current_coords and its children and
# sort it along the stitching direction
stitching_direction, nearest_points_list = create_nearest_points_list(
current_coords,
- tree.children,
+ tree,
+ tree[node],
constants.offset_factor_for_adjacent_geometry * abs_offset,
2.05 * abs_offset,
parent_stitching_direction,
@@ -528,7 +535,7 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
end_offset,
stitch_distance,
min_stitch_distance,
- tree.transferred_point_priority_deque,
+ current_node.transferred_point_priority_deque,
abs_offset,
offset_by_half,
False
@@ -545,7 +552,7 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
current_coords.length - end_offset,
stitch_distance,
min_stitch_distance,
- tree.transferred_point_priority_deque,
+ current_node.transferred_point_priority_deque,
abs_offset,
offset_by_half,
False
@@ -554,8 +561,8 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
assert len(own_coords) == len(own_coords_origin)
- tree.stitching_direction = stitching_direction
- tree.already_rastered = True
+ current_node.stitching_direction = stitching_direction
+ current_node.already_rastered = True
to_transfer_point_list = []
to_transfer_point_list_origin = []
@@ -563,7 +570,7 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
# TODO: maybe do not take the first and the last
# since they are ENTER_LEAVING_POINT points for sure
if (
- not offset_by_half
+ not offset_by_half
and own_coords_origin[k] == sample_linestring.PointSource.EDGE_NEEDED
or own_coords_origin[k] == sample_linestring.PointSource.FORBIDDEN_POINT):
continue
@@ -579,6 +586,7 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
# need to use "-used_offset" for stitching_direction==-1
point_transfer.transfer_points_to_surrounding(
tree,
+ node,
stitching_direction * used_offset,
offset_by_half,
to_transfer_point_list,
@@ -595,6 +603,7 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
if offset_by_half:
point_transfer.transfer_points_to_surrounding(
tree,
+ node,
stitching_direction * used_offset,
False,
to_transfer_point_list,
@@ -642,10 +651,8 @@ def connect_raster_tree_from_inner_to_outer(tree, used_offset, stitch_distance,
# geometry. Hence we need to insert the child geometry points
# here before the next point of own_coords.
item = nearest_points_list[cur_item]
- (
- child_coords,
- child_coords_origin,
- ) = connect_raster_tree_from_inner_to_outer(
+ (child_coords, child_coords_origin) = connect_raster_tree_from_inner_to_outer(
+ tree,
item.child_node,
used_offset,
stitch_distance,
@@ -820,14 +827,14 @@ def connect_raster_tree_spiral(
"""
abs_offset = abs(used_offset)
- if tree.is_leaf:
+ if not tree['root']: # if node has no children
return sample_linestring.raster_line_string_with_priority_points(
- tree.val,
+ tree.nodes['root'].val,
0,
- tree.val.length,
+ tree.nodes['root'].val.length,
stitch_distance,
min_stitch_distance,
- tree.transferred_point_priority_deque,
+ tree.nodes['root'].transferred_point_priority_deque,
abs_offset,
offset_by_half,
False)
@@ -836,86 +843,92 @@ def connect_raster_tree_spiral(
result_coords_origin = []
starting_point = close_point.coords[0]
# iterate to the second last level
- for node in PreOrderIter(tree, stop=lambda n: n.is_leaf):
- ring1 = node.val
- ring2 = node.children[0].val
-
- part_spiral = interpolate_linear_rings(ring1, ring2, stitch_distance, starting_point)
- node.val = part_spiral
+ for node in nx.dfs_preorder_nodes(tree, 'root'):
+ if tree[node]:
+ ring1 = tree.nodes[node].val
+ child = list(tree.successors(node))[0]
+ ring2 = tree.nodes[child].val
+
+ part_spiral = interpolate_linear_rings(ring1, ring2, stitch_distance, starting_point)
+ tree.nodes[node].val = part_spiral
+
+ for node in nx.dfs_preorder_nodes(tree, 'root'):
+ if tree[node]:
+ current_node = tree.nodes[node]
+ (own_coords, own_coords_origin) = sample_linestring.raster_line_string_with_priority_points(
+ current_node.val,
+ 0,
+ current_node.val.length,
+ stitch_distance,
+ min_stitch_distance,
+ tree.nodes[node].transferred_point_priority_deque,
+ abs_offset,
+ offset_by_half,
+ False)
- for node in PreOrderIter(tree, stop=lambda n: n.is_leaf):
- (own_coords, own_coords_origin) = sample_linestring.raster_line_string_with_priority_points(
- node.val,
- 0,
- node.val.length,
- stitch_distance,
- min_stitch_distance,
- node.transferred_point_priority_deque,
- abs_offset,
- offset_by_half,
- False)
-
- point_transfer.transfer_points_to_surrounding(
- node,
- -used_offset,
- offset_by_half,
- own_coords,
- own_coords_origin,
- overnext_neighbor=False,
- transfer_forbidden_points=False,
- transfer_to_parent=False,
- transfer_to_sibling=False,
- transfer_to_child=True)
-
- # We transfer also to the overnext child to get a more straight
- # arrangement of points perpendicular to the stitching lines
- if offset_by_half:
point_transfer.transfer_points_to_surrounding(
+ tree,
node,
-used_offset,
- False,
+ offset_by_half,
own_coords,
own_coords_origin,
- overnext_neighbor=True,
+ overnext_neighbor=False,
transfer_forbidden_points=False,
transfer_to_parent=False,
transfer_to_sibling=False,
transfer_to_child=True)
- # Check whether starting of own_coords or end of result_coords can be removed
- if not result_coords:
- result_coords.extend(own_coords)
- result_coords_origin.extend(own_coords_origin)
- elif len(own_coords) > 0:
- if Point(result_coords[-1]).distance(Point(own_coords[0])) > constants.line_lengh_seen_as_one_point:
- lineseg = LineString([result_coords[-2], result_coords[-1], own_coords[0], own_coords[1]])
- else:
- lineseg = LineString([result_coords[-2], result_coords[-1], own_coords[1]])
- (temp_coords, _) = sample_linestring.raster_line_string_with_priority_points(lineseg,
- 0,
- lineseg.length,
- stitch_distance,
- min_stitch_distance,
- DEPQ(),
- abs_offset,
- offset_by_half,
- False)
- if len(temp_coords) == 2: # only start and end point of lineseg was needed
- result_coords.pop()
- result_coords_origin.pop()
- result_coords.extend(own_coords[1:])
- result_coords_origin.extend(own_coords_origin[1:])
- elif len(temp_coords) == 3: # one middle point within lineseg was needed
- result_coords.pop()
- result_coords.append(temp_coords[1])
- result_coords.extend(own_coords[1:])
- result_coords_origin.extend(own_coords_origin[1:])
- else: # all points were needed
+ # We transfer also to the overnext child to get a more straight
+ # arrangement of points perpendicular to the stitching lines
+ if offset_by_half:
+ point_transfer.transfer_points_to_surrounding(
+ tree,
+ node,
+ -used_offset,
+ False,
+ own_coords,
+ own_coords_origin,
+ overnext_neighbor=True,
+ transfer_forbidden_points=False,
+ transfer_to_parent=False,
+ transfer_to_sibling=False,
+ transfer_to_child=True)
+
+ # Check whether starting of own_coords or end of result_coords can be removed
+ if not result_coords:
result_coords.extend(own_coords)
result_coords_origin.extend(own_coords_origin)
- # make sure the next section starts where this
- # section of the curve ends
- starting_point = result_coords[-1]
+ elif len(own_coords) > 0:
+ if Point(result_coords[-1]).distance(Point(own_coords[0])) > constants.line_lengh_seen_as_one_point:
+ lineseg = LineString([result_coords[-2], result_coords[-1], own_coords[0], own_coords[1]])
+ else:
+ lineseg = LineString([result_coords[-2], result_coords[-1], own_coords[1]])
+ (temp_coords, _) = sample_linestring.raster_line_string_with_priority_points(lineseg,
+ 0,
+ lineseg.length,
+ stitch_distance,
+ min_stitch_distance,
+ DEPQ(),
+ abs_offset,
+ offset_by_half,
+ False)
+ if len(temp_coords) == 2: # only start and end point of lineseg was needed
+ result_coords.pop()
+ result_coords_origin.pop()
+ result_coords.extend(own_coords[1:])
+ result_coords_origin.extend(own_coords_origin[1:])
+ elif len(temp_coords) == 3: # one middle point within lineseg was needed
+ result_coords.pop()
+ result_coords.append(temp_coords[1])
+ result_coords.extend(own_coords[1:])
+ result_coords_origin.extend(own_coords_origin[1:])
+ else: # all points were needed
+ result_coords.extend(own_coords)
+ result_coords_origin.extend(own_coords_origin)
+ # make sure the next section starts where this
+ # section of the curve ends
+ starting_point = result_coords[-1]
assert len(result_coords) == len(result_coords_origin)
return result_coords, result_coords_origin
diff --git a/lib/utils/dotdict.py b/lib/utils/dotdict.py
index acd575b9..12cf6e79 100644
--- a/lib/utils/dotdict.py
+++ b/lib/utils/dotdict.py
@@ -15,7 +15,7 @@ class DotDict(dict):
def update(self, *args, **kwargs):
super(DotDict, self).update(*args, **kwargs)
- self.dotdictify()
+ self._dotdictify()
def _dotdictify(self):
for k, v in self.items():