"""Complex analysis for plane geometry.
"""
import numpy as np
# --- sampling ---
[docs]def complex_sample(size=None):
"""Wraps random_sample() to get corresponding number of complex valued samples
where real and imaginary part are identically uniformly distributed on [0,1]"""
return np.random.random_sample(size) + 1j * np.random.random_sample(size)
# --- conversion ---
[docs]def to_array(z):
"""Corresponding element in R^2"""
return np.array([z.real, z.imag])
[docs]def to_complex(x):
"""Converts 2D-array to complex number"""
return complex(x[0], x[1])
[docs]def homogeneous(z):
"""Homogeneous coordinates in CP^2 interpreting z as affine coordinates."""
if np.isinf(z):
return np.array([1., 0.])
else:
return np.array([z, 1.])
# --- plane geometry ---
[docs]def scalar_product(z1, z2):
"""Real scalar product of the corresponding elements in R^2"""
return (z1.conjugate() * z2).real
[docs]def determinant(z1, z2):
"""Real determinant of the corresponding elements in R^2"""
return (z1.conjugate() * z2).imag
[docs]def rel_angle(u, v):
"""Positive angle in [0,2*pi) between complex numbers u,v"""
alpha = np.angle(v/u)
return alpha if alpha >= 0 else 2*np.pi + alpha
# --- cross-ratios ---
[docs]def cr(z1,z2,z3,z4):
"""Complex cross-ratio (z1 - z2)/(z2 - z3) * (z3 - z4)/(z4 - z1)"""
z1, z2, z3, z4 = map(homogeneous, [z1, z2, z3, z4])
a = np.linalg.det(np.array([z1,z2]))
b = np.linalg.det(np.array([z2,z3]))
c = np.linalg.det(np.array([z3,z4]))
d = np.linalg.det(np.array([z4,z1]))
if b == 0. or d == 0.:
return complex('infinity')
else:
return (a*c)/(b*d)
# def cross_ratio(z1, z2, z3, z4):
# return ((z1-z2)*(z3-z4))/((z2-z3)*(z4-z1))
[docs]def fourth_point_from_cross_ratio(z, z1, z2, q):
"""Get z12 in cr(z, z1, z12, z2) = q"""
a = (z-z1)/(z2-z)
return (a*z2 + q*z1)/(a + q)
# --- circumcircles ---
[docs]def triangle_area_from_lengths(a, b, c):
"""Numerically stable version of Heron's formula
for the area of a tringagle with side lengths a,b,c. """
return np.sqrt( (a+(b+c))*(c-(a-b))*(c+(a-b))*(a+(b-c)) ) / 4
[docs]def circumcircle_center(z1, z2, z3):
x, y, z = map(lambda x: np.array([x.real,x.imag]), [z1, z2, z3])
D = 2*( x[0]*(y[1]-z[1]) + y[0]*(z[1]-x[1]) + z[0]*(x[1]-y[1]))
s_x = x[0]**2 + x[1]**2
s_y = y[0]**2 + y[1]**2
s_z = z[0]**2 + z[1]**2
u = ( s_x*(y[1]-z[1]) + s_y*(z[1]-x[1]) + s_z*(x[1]-y[1]) ) / D
v = ( s_x*(z[0]-y[0]) + s_y*(x[0]-z[0]) + s_z*(y[0]-x[0]) ) / D
return np.array([u,v])
[docs]def circumcircle_radius(z1, z2, z3):
x, y, z = map(lambda x: np.array([x.real,x.imag]), [z1, z2, z3])
l_xy, l_yz, l_zx = map( np.linalg.norm, (x-y,y-z,z-x) )
return (l_xy*l_yz*l_zx) / ( 4*triangle_area_from_lengths(l_xy, l_yz, l_zx) )
# --- quads ---
[docs]def diagonals_intersection(z1, z2, z3, z4):
n1 = 1j * (z3 - z1)
n2 = 1j * (z4 - z2)
A = np.array([[n1.real, n1.imag],
[n2.real, n2.imag]])
b = np.array([scalar_product(n1,z1),
scalar_product(n2,z2)])
x = np.linalg.solve(A,b)
return complex(*x)
[docs]def intersect_edges(z1, z2, z3, z4):
n1 = 1j * (z2 - z1)
n2 = 1j * (z4 - z3)
A = np.array([[n1.real, n1.imag],
[n2.real, n2.imag]])
b = np.array([scalar_product(n1,z1),
scalar_product(n2,z3)])
x = np.linalg.solve(A,b)
return complex(*x)