in graspologic/layouts/nooverlap/_quad_node.py [0:0]
def _do_contraction(self) -> None:
logger.info("contracting nodes:%d" % (len(self.nodes)))
node_list = self.nodes
nodes_by_size = sorted(node_list, key=lambda n: n.size, reverse=True)
num_nodes = len(node_list)
nodes_around_the_edge = self.get_nodes_near_lines(
self.get_top_quad_node().nodes
)
num_nodes_around_edge = len(nodes_around_the_edge)
# cells = {}
for idx, node_to_move in enumerate(nodes_by_size):
# move to its original spot node_to_move.original_x, node_to_move.original_y
# then move it toward where it is until it does not overlap with anything already placed.
prev_x, prev_y = node_to_move.original_x, node_to_move.original_y
new_x, new_y = node_to_move.x, node_to_move.y
ov_idx = 0
ov_idx, overlapping_node = is_overlapping_any_node_and_index(
node_to_move,
node_to_move.original_x,
node_to_move.original_y,
nodes_around_the_edge + nodes_by_size,
ov_idx,
idx + num_nodes_around_edge,
)
if overlapping_node is None:
new_x, new_y = prev_x, prev_y
if node_to_move.x == node_to_move.original_x:
# this is needed just in case the min x node is overlapping.
# then the orginal X is eual to the X where is moves and that give us a divide by zero
# when calculating the slope
# We wiggle it just a little bit to prevent an error
node_to_move.x += _EPSILON
# print ("contracting: %d, node_to_move %s, overlapping: %s" % (idx, str(node_to_move.to_list()), overlapping_node))
while (
overlapping_node is not None
and node_to_move.x != node_to_move.original_x
):
# slope doesn't change leave as original_xy
slope_ca = (node_to_move.y - node_to_move.original_y) / (
node_to_move.x - node_to_move.original_x
)
if node_to_move.node_id == overlapping_node.node_id:
raise Exception(
"They should not be the same node!! %s" % (node_to_move.node_id)
)
if node_to_move.original_x == new_x:
new_x += _EPSILON
a = dist_original_to_over = distance.euclidean(
[node_to_move.original_x, node_to_move.original_y],
[overlapping_node.x, overlapping_node.y],
)
b = dist_from_original_to_new = distance.euclidean(
[node_to_move.original_x, node_to_move.original_y], [new_x, new_y]
)
c = dist_from_new_to_overlapping = distance.euclidean(
[new_x, new_y], [overlapping_node.x, overlapping_node.y]
)
node_to_move.color = "#FF0004" # RED
overlapping_node.color = "#F1FD00" # Yellow
# print ("not None, a: %g, b: %g, c: %g" %(a, b, c), node_to_move.node_id, node_to_move.size, overlapping_node.size)
# print ("original(%g,%g), current(%g,%g), overlap(%g,%g)" %(node_to_move.original_x, node_to_move.original_y, new_x, new_y, overlapping_node.x, overlapping_node.y))
angle_c = math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b))
len_c_new = node_to_move.size + overlapping_node.size + _EPSILON
angle_a_new = math.asin(a * math.sin(angle_c) / len_c_new)
angle_b_new = 180 - math.degrees(angle_c) - math.degrees(angle_a_new)
new_len_b = (
len_c_new * math.sin(math.radians(angle_b_new)) / math.sin(angle_c)
)
# print ("slope: %g, angle c: %g, new angle a: %g, newlenC: %g, new angle a: %g, new lenB %g" %(slope_ca, math.degrees(angle_c), math.degrees(angle_a_new), len_c_new, math.degrees(angle_a_new), new_len_b))
x_new_plus = node_to_move.original_x + math.sqrt(
new_len_b ** 2 / (1 + slope_ca ** 2)
)
x_new_neg = node_to_move.original_x - math.sqrt(
new_len_b ** 2 / (1 + slope_ca ** 2)
)
x_plus_diff = x_new_plus - new_x
x_neg_diff = x_new_neg - new_x
# print ("both outsize, plus diff: %g, minus diff: %g" %(x_plus_diff, x_neg_diff))
if abs(x_plus_diff) < abs(x_neg_diff):
prev_x, prev_y = new_x, new_y
new_x = x_new_plus
new_y = prev_y - slope_ca * prev_x + slope_ca * x_new_plus
else:
prev_x, prev_y = new_x, new_y
new_x = x_new_neg
new_y = prev_y - slope_ca * prev_x + slope_ca * x_new_neg
# print ("before: idx: %d, node: %s" %(ov_idx, str(overlapping_node.to_list()) ))
ov_idx, overlapping_node = is_overlapping_any_node_and_index(
node_to_move,
new_x,
new_y,
nodes_around_the_edge + nodes_by_size,
0,
idx + num_nodes_around_edge,
)
# print ("after: idx: %d, node: %s" %(ov_idx, str(overlapping_node is not None) ))
node_to_move.x = new_x
node_to_move.y = new_y
# if idx > 20: # only do a few in the first quad
# break
return