Search code examples
algorithmmathgeometrycomputational-geometryvoronoi

Intersection points in Fortune's algoritm for computing Voronoi diagram


I am finding it very difficult to follow fortune's algorithm, I have gone through all the resources available on the net and fairly understood the theory behind it. But when I am implementing it on my own the little details which were left out in the sources have been a real pain for me. The intersection points, which I understood are the center of a circle with two given points on it and a horizontal tangent with equation y = c but working through the equations I am unable to get the co-ordinates of the center(the intersection point). Can someone please help me on how to find the co-ordinates of the intersection points.


Solution

  • Is this what you are asking about?

    INPUT:
    two given points:
    [x1, y1]
    [x2, y2]
    and a real number: 
    c
    where y = c is a horizontal line 
    
    CALCULATION of the coordinates of the centre of the circle
    that passes through the two given points [x1, y1] and [x2, y2] 
    and is tangent to the horizontal line y = c:
    
        k = (x2 - x1) / (y2 - y1)
        x_P = x1 + k * (c - y1)
        t = sqrt((x_P - x1)^2 + (c - y1)^2) * math.sqrt((x_P - x2)^2 + (c - y2)^2)
        t = sqrt(t)
        x_center = x_P - t * k / abs(k)
        y_center = (y1 + y2) / 2  - k * (x_center - (x1 + x2)/2)     
    

    Geometric Construction. Let us have two points A = [x1, y1] and B = [x2, y2] and let y = c bet he horizontal line, where y1 < c and y2 < c. We are looking for the center O = [x_center, y_center] of the circle C that passes through the points A and B and is tangent to the line y = c.

    1. Write the equation of the line AB that passes through the points A and B. This allows us to find the coordinates of the intersection point P = [x_P, y_P] between the lines AB and y = c. Without loss of generality, assume that point B is between points A and P.

    2. Our next goal is to find the coordinates of the point T at which the circle C is tangent to the line y = c. To do that, we will first find be the distance t = PT between the points P and T. After that, we simply have to move distance t from the point P along the line y = c, which means we find the x coordinate x_T of T as either x_T = x_P - t or x_T = x_P + t (there are two solutions). The y coordinate of T is y_T = c.

    3. Consider the two triangles PAT and PTB. They share a common angle angle TPA = angle BPT. Furthermore, because y = c is tangent to the circle C circumscribed around the triangle ABT, we can deduce that angle PTA = angle PBT. Therefore triangles PAT and PTB are similar.

    4. The similarity between PAT and PTB yields the similarity identity PT / PA = PB / PT which can be rewritten as t^2 = PT^2 = PA * PB. Thus, we can find that t = sqrt(PA * PB) (this is what t is in the code).

    5. Finally, we find that x_T = x_P - t or x_T = x_P + t, whichever is closer to the segment AB. The center O of the circle C is the intersection of the vertical line x = x_T and the orthogonal bisector of segment AB (the line through the midpoint of AB and perpendicular to AB)

    Remark: There is a degenerate case when the line AB is parallel to the line y = c. Then the picture is more symmetric and one can use a slightly different method, which I have included in the code below

    Test code in python:

    import numpy as np
    import math
    
    
    def find_center(A, B, c):
        x1 = A[0]
        y1 = A[1]
        x2 = B[0]
        y2 = B[1]
        if abs(y1 - y2) < 1e-7:
            radius = ((2*c - y1 - y2)**2 + (x2 - x1)**2) / (8*(c - y1))
            x_center = (x1 + x2) / 2
            y_center = c - radius 
        else:
            k = (x2 - x1) / (y2 - y1)
            x_P = x1 + k * (c - y1)
            t = math.sqrt((x_P - x1)**2 + (c - y1)**2) * math.sqrt((x_P - x2)**2 + (c - y2)**2)
            t = math.sqrt(t)
            x_center = x_P - t * k / abs(k)
            y_center = (y1 + y2) / 2  - k * (x_center - (x1 + x2)/2)     
        return np.array([x_center, y_center])
    
    A = np.array([0, 0])
    B = np.array([3, 1.5])
    c = 5
    
    O = find_center(A, B, c)
    
    print('print the distances between the calculated center and the two points and the horizontal line: ')
    print((np.linalg.norm(A - O), np.linalg.norm(B - O),  c - O[1]))
    

    Edit. Here is another method, which also takes care of the degenerate case when y1 = y2, i.e. the points A and B form a line parallel to y = c.

    Construction. All points from the plane that are at an equal distance from the line y = c and the point A = [x1, y1] form a parabola with directrix y = c and focus A. So simply write the equation of this parabola:

    distance(X, A)^2 = distance(X, {y=c})^2
    
    or as a quadratic equation:
    
    (x - x1)^2  +  (y - y1)^2 = (c - y)^2
    
    which simplifies to 
    
    y = ( x^2 - 2*x1* x + x1^2 + y1^2 - c^2 ) / (y1 - c)
    

    The same applies to point B = [x2, y2] and line y = c, where the equation of the parabola with focus B and directrix a = c is

    y = ( x^2 - 2*x2* x + x2^2 + y2^2 - c^2 ) / (y2 - c)
    

    Therefore, the points that are at equal distance from A, B and y = c should be the two intersection points of the two parabolas. In terms of equations, you simply have to solve the equation

     ( x^2 - 2*x1* x + x1^2 + y1^2 - c^2 ) / (y1 - c) =  ( x^2 - 2*x2* x + x2^2 + y2^2 - c^2 ) / (y2 - c)
    

    for the unknown x coordinate of O. Then the y coordinate is found by plugging the solution in one of the parabola equations above.

    Here is some python code:

    import numpy as np
    import math
    
    def sqe(a):
        def sqe(a):
        if abs(a[0]) < 1e-7:
            return - a[2] / a[1], - a[2] / a[1]
        D = math.sqrt(a[1]**2 - 4*a[0]*a[2])
        return ( - a[1] - D ) /(2*a[0]), ( - a[1] + D ) / (2*a[0])
    
    def find_center(A, B, c):
        x1 = A[0]
        y1 = A[1]
        x2 = B[0]
        y2 = B[1]     
        a1 = np.array([1, -2*x1, x1**2 + y1**2 - c**2])
        a2 = np.array([1, -2*x2, x2**2 + y2**2 - c**2])
        x_center = sqe((y1-c)*a2 - (y2-c)*a1)[1]
        y_center = (a1[0]*x_center**2 + a1[1]*x_center + a1[2])  / (2*y1-2*c)      
        return np.array([x_center, y_center])
    
    A = np.array([0, 0])
    B = np.array([3, 0.5])
    c = 5
    
    O = find_center(A, B, c)
    print('print the distances between the calculated center and the two points and the horizontal line: ')
    print((np.linalg.norm(A - O), np.linalg.norm(B - O),  c - O[1]))