def _do_contraction_with_given_nodes()

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