kopia lustrzana https://github.com/inkstitch/inkstitch
avoid anytree dependency
rodzic
cbcaa0ac0e
commit
6ca1af0c88
|
@ -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
|
||||
|
|
|
@ -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!")
|
||||
|
||||
|
|
|
@ -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
|
||||
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)
|
||||
node.val = part_spiral
|
||||
part_spiral = interpolate_linear_rings(ring1, ring2, stitch_distance, starting_point)
|
||||
tree.nodes[node].val = part_spiral
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -19,7 +19,6 @@ stringcase
|
|||
tinycss2
|
||||
flask
|
||||
fonttools
|
||||
anytree
|
||||
depq
|
||||
trimesh
|
||||
scipy==1.7.3
|
||||
|
|
Ładowanie…
Reference in New Issue