def get_bounds(center, size):
    x_scale = 1.6
    upper_left = Point(center.x - size * x_scale, center.y + size)
    upper_right = Point(center.x + size * x_scale, center.y + size)
    lower_left = Point(center.x - size * x_scale, center.y - size)
    lower_right = Point(center.x + size * x_scale, center.y - size)
    bounds = [(upper_left, upper_right), (lower_left, lower_right), (upper_right, lower_right),
              (upper_left, lower_left)]
    return bounds
def find_intersection_of_parabolas(parabola1, parabola2, sweep_line):
    if parabola1.x == parabola2.x:
        py = (parabola1.y + parabola2.y) / 2.0
        px = 1.0 * (parabola1.x ** 2 + (parabola1.y - py) ** 2 - sweep_line ** 2) / (2 * parabola1.x - 2 * sweep_line)
        return Point(px, py)
    elif parabola2.x == sweep_line:
        py = parabola2.y
        px = 1.0 * (parabola1.x ** 2 + (parabola1.y - py) ** 2 - sweep_line ** 2) / (2 * parabola1.x - 2 * sweep_line)
        return Point(px, py)
    elif parabola1.x == sweep_line:
        py = parabola1.y
        px = 1.0 * (parabola2.x ** 2 + (parabola2.y - py) ** 2 - sweep_line ** 2) / (2 * parabola2.x - 2 * sweep_line)
        return Point(px, py)
    else:
        return calculate_parabolas_intersection(parabola1, parabola2, sweep_line)
def find_circle_center(a, b, c):  # function from internet
    # check if bc is a "right turn" from ab
    if ((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)) > 0:
        return None, None

    # Joseph O'Rourke, Computational Geometry in C (2nd ed.) p.189
    A = b.x - a.x
    B = b.y - a.y
    C = c.x - a.x
    D = c.y - a.y
    E = A * (a.x + b.x) + B * (a.y + b.y)
    F = C * (a.x + c.x) + D * (a.y + c.y)
    G = 2 * (A * (c.y - b.y) - B * (c.x - b.x))

    if G == 0:
        return None, None  # Points are co-linear

    # point o is the center of the circle
    ox = 1.0 * (D * E - B * F) / G
    oy = 1.0 * (A * F - C * E) / G

    # o.x plus radius equals max x coord
    x = ox + math.sqrt((a.x - ox) ** 2 + (a.y - oy) ** 2)
    o = Point(ox, oy)

    return x, o
def intersection_of_lines(line1, line2):
    p1 = ((line1[0].x * line1[1].y - line1[0].y * line1[1].x) * (line2[0].x - line2[1].x) - (line1[0].x - line1[1].x)
          * (line2[0].x * line2[1].y - line2[0].y * line2[1].x)) / \
         ((line1[0].x - line1[1].x) * (line2[0].y - line2[1].y) - (line1[0].y - line1[1].y) * (line2[0].x - line2[1].x))
    p2 = ((line1[0].x * line1[1].y - line1[0].y * line1[1].x) * (line2[0].y - line2[1].y) - (line1[0].y - line1[1].y)
          * (line2[0].x * line2[1].y - line2[0].y * line2[1].x)) / \
         ((line1[0].x - line1[1].x) * (line2[0].y - line2[1].y) - (line1[0].y - line1[1].y) * (line2[0].x - line2[1].x))
    return Point(p1, p2)
    def calculate_voronoi_diagram(self):
        while not self.events.empty():
            event = self.events.pop()
            if isinstance(event, Circle):
                self.handle_circle_event(event)
            else:
                self.handle_point_event(event)
            self.scenes.append(Scene([PointsCollection(self.points, color='red'),
                                      PointsCollection(self.get_voronoi_points(), color='blue')],
                                     [LinesCollection(self.get_voronoi_lines()),
                                      LinesCollection(lines_from_bounds(get_bounds(Point(0, 0), 30)), color='black'),
                                      LinesCollection([[(event.x, -30), (event.x, 30)]], color='purple')]))

        self.finish_edges()
        self.bound(Point(0, 0), 30)
        self.scenes.append(Scene([PointsCollection(self.points, color='red'),
                                  PointsCollection(self.get_voronoi_points(), color='blue')],
                                 [LinesCollection(self.get_voronoi_lines()),
                                  LinesCollection(lines_from_bounds(get_bounds(Point(0, 0), 30)), color='black')]))
def calculate_parabolas_intersection(parabola1, parabola2, sweep_line):
    z0 = 2.0 * (parabola1.x - sweep_line)
    z1 = 2.0 * (parabola2.x - sweep_line)

    a = 1.0 / z0 - 1.0 / z1
    b = -2.0 * (parabola1.y / z0 - parabola2.y / z1)
    c = 1.0 * (parabola1.y ** 2 + parabola1.x ** 2 - sweep_line ** 2) / z0 - 1.0 * (
            parabola2.y ** 2 + parabola2.x ** 2 - sweep_line ** 2) / z1

    py = 1.0 * (-b - math.sqrt(b * b - 4 * a * c)) / (2 * a)
    px = 1.0 * (parabola1.x ** 2 + (parabola1.y - py) ** 2 - sweep_line ** 2) / (2 * parabola1.x - 2 * sweep_line)
    return Point(px, py)
    def create_bounding_box(self, points):
        for pts in points:
            point = Point(pts[0], pts[1])
            self.events.push(point)
            if point.x < self.LEFT:
                self.LEFT = point.x
            if point.y < self.BOTTOM:
                self.BOTTOM = point.y
            if point.x > self.RIGHT:
                self.RIGHT = point.x
            if point.y > self.TOP:
                self.TOP = point.y

        dx = (self.RIGHT - self.LEFT + 1) / 5.0
        dy = (self.TOP - self.BOTTOM + 1) / 5.0
        self.LEFT = self.LEFT - dx
        self.RIGHT = self.RIGHT + dx
        self.BOTTOM = self.BOTTOM - dy
        self.TOP = self.TOP + dy
def intersect(point, arc):
    if arc is None:
        return None
    if arc.point.x == point.x:
        return None

    y1 = 0.0
    y2 = 0.0

    if arc.prev is not None:
        y1 = find_intersection_of_parabolas(arc.prev.point, arc.point, point.x).y
    if arc.next is not None:
        y2 = find_intersection_of_parabolas(arc.point, arc.next.point, point.x).y

    if (arc.prev is None or y1 <= point.y) and (arc.next is None or point.y <= y2):
        py = point.y
        px = (arc.point.x ** 2 + (arc.point.y - py) ** 2 - point.x ** 2) / (2 * arc.point.x - 2 * point.x)
        res = Point(px, py)
        return res
    return None
    def insert_arc(self, point):
        if self.beach_line is None:
            self.beach_line = Arc(point)
            return

        if self.insert_arc_among_existing(point):
            return

        arc = self.beach_line
        while arc.next is not None:
            arc = arc.next
        arc.next = Arc(point, arc)

        x = self.LEFT
        y = (arc.next.point.y + arc.point.y) / 2.0
        start = Point(x, y)

        seg = Edge(start)
        arc.lower_edge = arc.next.upper_edge = seg
        self.voronoi.append(seg)