PnLCalib / utils /utils_geometry.py
2nzi's picture
Upload 63 files
3d1f2c9
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