File size: 6,029 Bytes
3d1f2c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import numpy as np
import sympy as sp

from scipy.stats import linregress
from ellipse import LsqEllipse


def line_intersection(data, pair, w, h):
    #1e-7 sum in case there are two identical coordinate values
    key1, key2 = pair
    x1, y1, x2, y2 = [], [], [], []
    for count, point in enumerate(data[key1]):
        x1.append(point['x']*w + count*1e-7)
        y1.append(point['y']*h + count*1e-7)
    for count, point in enumerate(data[key2]):
        x2.append(point['x']*w + count*1e-7)
        y2.append(point['y']*h + count*1e-7)
        

    slope1, intercept1, r1, p1, se1 = linregress(x1, y1)
    slope2, intercept2, r2, p2, se2 = linregress(x2, y2)
    
    x_intersection = (intercept2 - intercept1) / (slope1 - slope2)
    y_intersection = slope1 * x_intersection + intercept1

    return x_intersection, y_intersection

def line_polynomial_intersection(x1, y1, x2, y2):
    # Interpolate the two sets of points to create polynomial functions
    if not all(x == x1[0] for x in x1):

        if len(x1) > 2:
            poly1 = np.poly1d(np.polyfit(x1, y1, 2))
            poly2 = np.poly1d(np.polyfit(x2, y2, 1))

            c, b, a = poly1.coeffs
            coef2 = poly2.coeffs
            e, d = coef2 if len(coef2) == 2 else [0, coef2[0]]

            x1 = ((e - b) + np.sqrt((b - e) ** 2 - 4 * c * (a - d))) / (2 * c)
            x2 = ((e - b) - np.sqrt((b - e) ** 2 - 4 * c * (a - d))) / (2 * c)
            y1 = d + e * x1
            y2 = d + e * x2

            return [[x1, y1], [x2, y2]]

        elif len(x1) == 2:
            poly1 = np.poly1d(np.polyfit(x1, y1, 1))
            poly2 = np.poly1d(np.polyfit(x2, y2, 1))

            coef1 = poly1.coeffs
            coef2 = poly2.coeffs

            b, a = coef1 if len(coef1) == 2 else [0, coef1[0]]
            d, c = coef2 if len(coef2) == 2 else [0, coef2[0]]

            x1 = (c - a) / (b - d)
            y1 = a + b * x1

            return [[x1, y1]]
    else:
        return []

def find_ellipse_line_intersections(cx, cy, w, h, theta, a, b):
        x, y = sp.symbols('x y')

        # Equations for the ellipse and the line
        ellipse_eq = ((sp.cos(theta) * (x - cx) - sp.sin(theta) * (y - cy))**2 / w**2 + 
                     (sp.sin(theta) * (x - cx) + sp.cos(theta) * (y - cy))**2 / h**2 - 1)
        line_eq = a + b * x - y

        # Solve the system of equations
        solutions = sp.solve((ellipse_eq, line_eq), (x, y))
        try:
            intersections = [(float(sol[0].evalf()), float(sol[1].evalf())) for sol in solutions]
        except:
            intersections = []
        return intersections


def ellipse_intersection(data, pair, w, h):
    
    key1, key2 = pair
    x1, y1, x2, y2 = [], [], [], []
    
    #Ellipse should be first one of the pair
    for count, point in enumerate(data[key1]):
        x1.append(point['x']*w + count*1e-7)
        y1.append(point['y']*h + count*1e-7)
    for count, point in enumerate(data[key2]):
        x2.append(point['x']*w + count*1e-7)
        y2.append(point['y']*h + count*1e-7)
        
    if len(x1) > 4:
        X = np.array(list(zip(x1, y1)))
            
        reg = LsqEllipse().fit(X)
        
        try:
            center, width, height, phi = reg.as_parameters()
        except:
            return []
        
        if not isinstance(phi, complex):
            coeffs = np.polyfit(x2, y2, 1)
            intersection = find_ellipse_line_intersections(center[0], center[1], width, height, -phi, coeffs[1], coeffs[0])
            
        else:
            intersection = line_polynomial_intersection(x1, y1, x2, y2)
    else:
        intersection = line_polynomial_intersection(x1, y1, x2, y2)
    
    return intersection
    

def find_tangent_points(center, width, height, theta, external_point):
    
    def point_to_ellipse_coords(p, center, theta):
        h, k = center
        x, y = p
        x_1, y_1 = x - h, y - k
        x_2, y_2 = x_1 * np.cos(theta) + y_1 * np.sin(theta), x_1 * np.sin(theta) - y_1 * np.cos(theta)
        
        return x_2, y_2
        
    def feasibility_list(point_list, a, b):
        f_list = []
        smaller, larger = sorted([1-1e-7, 1+1e-7])
        for point in point_list:
            x, y = point
            if smaller <= x**2/a**2 + y**2/b**2 <= larger:
                f_list.append(point)
        return f_list
            
    def ellipse_coords_to_point(p, center, theta):
        x_2, y_2 = p
        h, k = center
        x_1 = x_2 * np.cos(theta) + y_2 * np.sin(theta)
        y_1 = x_2 * np.sin(theta) - y_2 * np.cos(theta)
        x = x_1 + h
        y = y_1 + k
        
        return x, y
    
    # Extract ellipse parameters
    a = width 
    b = height

    px, py = point_to_ellipse_coords(external_point, center, theta)

    y1 = ((a*b)**2*py + np.sqrt(-a**2*b**6*px**2 + a**2*b**4*px**2*py**2 + b**6*px**4)) / (a**2*py**2 + b**2*px**2)
    y2 = ((a*b)**2*py - np.sqrt(-a**2*b**6*px**2 + a**2*b**4*px**2*py**2 + b**6*px**4)) / (a**2*py**2 + b**2*px**2)

    x1_1 = (b*px + np.sqrt(4*a**2*y1*(py-y1) + b**2*px**2)) / (2*b)
    x2_1 = (b*px - np.sqrt(4*a**2*y1*(py-y1) + b**2*px**2)) / (2*b)
    x1_2 = (b*px + np.sqrt(4*a**2*y2*(py-y2) + b**2*px**2)) / (2*b)
    x2_2 = (b*px - np.sqrt(4*a**2*y2*(py-y2) + b**2*px**2)) / (2*b)
    
    points_to_check = [(x1_1, y1), (x2_1, y1), (x1_2, y2), (x2_2, y2)]
    feasible_points = feasibility_list(points_to_check, a, b)

    points_untransformed = [ellipse_coords_to_point(p, center, theta) for p in feasible_points]
    
    return points_untransformed


def are_points_collinear(point1, point2, point3, tolerance=1e-5):
    x1, y1 = point1
    x2, y2 = point2
    x3, y3 = point3

    # Check if the slopes are equal with a tolerance
    return abs((y2 - y1) * (x3 - x2) - (y3 - y2) * (x2 - x1)) < tolerance