in graspologic/layouts/nooverlap/_quad_node.py [0:0]
def _do_contraction_with_given_nodes(self, node_list: List[_Node]) -> None:
logger.info("contracting nodes:%d" % (len(node_list)))
nodes_by_size = sorted(node_list, key=lambda n: n.size, reverse=True)
nodes_around_the_edge: List[_Node] = []
num_nodes_around_edge = len(nodes_around_the_edge)
# cells = {}
for idx, node_to_move in enumerate(nodes_by_size):
if idx % 100 == 0:
logger.info(f"processing {idx}")
# 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 == 0.0:
# this is needed just in case the min x node is overlapping.
# then the orginal X is equal 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 make the math work
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]
)
# 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))
denominator = 2 * a * b
if 0 == denominator:
denominator = 0.00000001
value = (a ** 2 + b ** 2 - c ** 2) / denominator
if value >= 1:
value = 0.999999
elif value <= -1:
value = -0.999999
angle_c = math.acos(value)
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