Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def Astroid(x, y):
# Solve k^4+2*k^3-(x^2+y^2-1)*k^2-2*y^2*k-y^2 = 0 for positive root k.
# This solution is adapted from Geocentric::Reverse.
p = Math.sq(x)
q = Math.sq(y)
r = (p + q - 1) / 6
if not(q == 0 and r <= 0):
# Avoid possible division by zero when r = 0 by multiplying equations
# for s and t by r^3 and r, resp.
S = p * q / 4 # S = r^3 * s
r2 = Math.sq(r)
r3 = r * r2
# The discrimant of the quadratic equation for T3. This is zero on
# the evolute curve p^(1/3)+q^(1/3) = 1
disc = S * (S + 2 * r3)
u = r
if (disc >= 0):
T3 = S + r3
# Pick the sign on the sqrt to maximize abs(T3). This minimizes loss
# of precision due to cancellation. The result is unchanged because
# of the way the T is used in definition of u.
def C1f(eps, c):
eps2 = Math.sq(eps)
d = eps
c[1] = d*((6-eps2)*eps2-16)/32
d *= eps
c[2] = d*((64-9*eps2)*eps2-128)/2048
d *= eps
c[3] = d*(9*eps2-16)/768
d *= eps
c[4] = d*(3*eps2-5)/512
d *= eps
c[5] = -7*d/1280
d *= eps
c[6] = -7*d/2048
C1f = staticmethod(C1f)
def Astroid(x, y):
# Solve k^4+2*k^3-(x^2+y^2-1)*k^2-2*y^2*k-y^2 = 0 for positive root k.
# This solution is adapted from Geocentric::Reverse.
p = Math.sq(x)
q = Math.sq(y)
r = (p + q - 1) / 6
if not(q == 0 and r <= 0):
# Avoid possible division by zero when r = 0 by multiplying equations
# for s and t by r^3 and r, resp.
S = p * q / 4 # S = r^3 * s
r2 = Math.sq(r)
r3 = r * r2
# The discrimant of the quadratic equation for T3. This is zero on
# the evolute curve p^(1/3)+q^(1/3) = 1
disc = S * (S + 2 * r3)
u = r
if (disc >= 0):
T3 = S + r3
# Pick the sign on the sqrt to maximize abs(T3). This minimizes loss
# of precision due to cancellation. The result is unchanged because
def C1pf(eps, c):
eps2 = Math.sq(eps)
d = eps
c[1] = d*(eps2*(205*eps2-432)+768)/1536
d *= eps
c[2] = d*(eps2*(4005*eps2-4736)+3840)/12288
d *= eps
c[3] = d*(116-225*eps2)/384
d *= eps
c[4] = d*(2695-7173*eps2)/7680
d *= eps
c[5] = 3467*d/7680
d *= eps
c[6] = 38081*d/61440
C1pf = staticmethod(C1pf)
if outmask & Geodesic.DISTANCE:
s12 = 0 + s12x # Convert -0 to 0
if outmask & Geodesic.REDUCEDLENGTH:
m12 = 0 + m12x # Convert -0 to 0
if outmask & Geodesic.AREA:
# From Lambda12: sin(alp1) * cos(bet1) = sin(alp0)
salp0 = salp1 * cbet1
calp0 = math.hypot(calp1, salp1 * sbet1) # calp0 > 0
# real alp12
if calp0 != 0 and salp0 != 0:
# From Lambda12: tan(bet) = tan(sig) * cos(alp)
ssig1 = sbet1; csig1 = calp1 * cbet1
ssig2 = sbet2; csig2 = calp2 * cbet2
k2 = Math.sq(calp0) * self._ep2
# Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0).
A4 = Math.sq(self._a) * calp0 * salp0 * self._e2
ssig1, csig1 = Geodesic.SinCosNorm(ssig1, csig1)
ssig2, csig2 = Geodesic.SinCosNorm(ssig2, csig2)
C4a = list(range(Geodesic.nC4_))
self.C4f(k2, C4a)
B41 = Geodesic.SinCosSeries(False, ssig1, csig1, C4a, Geodesic.nC4_)
B42 = Geodesic.SinCosSeries(False, ssig2, csig2, C4a, Geodesic.nC4_)
S12 = A4 * (B42 - B41)
else:
# Avoid problems with indeterminate sig1, sig2 on equator
S12 = 0
if (not meridian and
omg12 < 0.75 * math.pi and # Long difference too big
sbet2 - sbet1 < 1.75): # Lat difference too big
# Use tan(Gamma/2) = tan(omg12/2)
if outmask & Geodesic.REDUCEDLENGTH:
m12 = 0 + m12x # Convert -0 to 0
if outmask & Geodesic.AREA:
# From Lambda12: sin(alp1) * cos(bet1) = sin(alp0)
salp0 = salp1 * cbet1
calp0 = math.hypot(calp1, salp1 * sbet1) # calp0 > 0
# real alp12
if calp0 != 0 and salp0 != 0:
# From Lambda12: tan(bet) = tan(sig) * cos(alp)
ssig1 = sbet1; csig1 = calp1 * cbet1
ssig2 = sbet2; csig2 = calp2 * cbet2
k2 = Math.sq(calp0) * self._ep2
# Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0).
A4 = Math.sq(self._a) * calp0 * salp0 * self._e2
ssig1, csig1 = Geodesic.SinCosNorm(ssig1, csig1)
ssig2, csig2 = Geodesic.SinCosNorm(ssig2, csig2)
C4a = list(range(Geodesic.nC4_))
self.C4f(k2, C4a)
B41 = Geodesic.SinCosSeries(False, ssig1, csig1, C4a, Geodesic.nC4_)
B42 = Geodesic.SinCosSeries(False, ssig2, csig2, C4a, Geodesic.nC4_)
S12 = A4 * (B42 - B41)
else:
# Avoid problems with indeterminate sig1, sig2 on equator
S12 = 0
if (not meridian and
omg12 < 0.75 * math.pi and # Long difference too big
sbet2 - sbet1 < 1.75): # Lat difference too big
# Use tan(Gamma/2) = tan(omg12/2)
# * (tan(bet1/2)+tan(bet2/2))/(1+tan(bet1/2)*tan(bet2/2))
# with tan(x/2) = sin(x)/(1+cos(x))
else:
self._f = 1.0/f
self._f1 = 1 - self._f
self._e2 = self._f * (2 - self._f)
self._ep2 = self._e2 / Math.sq(self._f1) # e2 / (1 - e2)
self._n = self._f / ( 2 - self._f)
self._b = self._a * self._f1
# authalic radius squared
if self._e2 == 0:
self._c2 = (Math.sq(self._a) + Math.sq(self._b))/2
elif self._e2 > 0:
self._c2 = (Math.sq(self._a) +
Math.sq(self._b) * Math.atanh(math.sqrt(self._e2)) /
math.sqrt(abs(self._e2)))/2
else: # self._e2 < 0:
self._c2 = (Math.sq(self._a) +
Math.sq(self._b) * math.atan(math.sqrt(-self._e2)) /
math.sqrt(abs(self._e2)))/2
self._etol2 = Geodesic.tol2_ / max(0.1, math.sqrt(abs(self._e2)))
if not(Math.isfinite(self._a) and self._a > 0):
raise ValueError("Major radius is not positive")
if not(Math.isfinite(self._b) and self._b > 0):
raise ValueError("Minor radius is not positive")
self._A3x = list(range(int(Geodesic.nA3x_)))
self._C3x = list(range(int(Geodesic.nC3x_)))
self._C4x = list(range(int(Geodesic.nC4x_)))
self.A3coeff()
self.C3coeff()
self.C4coeff()
# Inverse.
dummy, m12a, m0, dummy, dummy = self.Lengths(
self._n, math.pi + bet12a, sbet1, -cbet1, sbet2, cbet2,
cbet1, cbet2, dummy, False, C1a, C2a)
x = -1 + m12a/(self._f1 * cbet1 * cbet2 * m0 * math.pi)
if x < -real(0.01):
betscale = sbet12a / x
else:
betscale = -self._f * Math.sq(cbet1) * math.pi
lamscale = betscale / cbet1
y = (lam12 - math.pi) / lamscale
if y > -Geodesic.tol1_ and x > -1 - Geodesic.xthresh_:
# strip near cut
if self._f >= 0:
salp1 = min(1.0, -x); calp1 = - math.sqrt(1 - Math.sq(salp1))
else:
if x > -Geodesic.tol1_:
calp1 = max(0.0, x)
else:
calp1 = max(-1.0, x)
salp1 = math.sqrt(1 - Math.sq(calp1))
else:
# Estimate alp1, by solving the astroid problem.
#
# Could estimate alpha1 = theta + pi/2, directly, i.e.,
# calp1 = y/k; salp1 = -x/(1+k); for _f >= 0
# calp1 = x/(1+k); salp1 = -y/k; for _f < 0 (need to check)
#
# However, it's better to estimate omg12 from astroid and use
# spherical formula to compute alp1. This reduces the mean number of
# Newton iterations for astroid cases from 2.24 (min 0, max 6) to 2.12
# of precision due to cancellation. The result is unchanged because
# of the way the T is used in definition of u.
T3 += cmp(T3, 0) * math.sqrt(disc) # T3 = (r * t)^3
# N.B. cbrt always returns the real root. cbrt(-8) = -2.
T = Math.cbrt(T3) # T = r * t
# T can be zero; but then r2 / T -> 0.
u += T
if T != 0:
u += (r2 / T)
else:
# T is complex, but the way u is defined the result is real.
ang = math.atan2(math.sqrt(-disc), -(S + r3))
# There are three possible cube roots. We choose the root which
# avoids cancellation. Note that disc < 0 implies that r < 0.
u += 2 * r * math.cos(ang / 3)
v = math.sqrt(Math.sq(u) + q) # guaranteed positive
# Avoid loss of accuracy when u < 0.
# u+v, guaranteed positive
if u < 0:
uv = q / (v - u)
else:
uv = u + v
w = (uv - q) / (2 * v) # positive?
# Rearrange expression for k to avoid loss of accuracy due to
# subtraction. Division by 0 not possible because uv > 0, w >= 0.
k = uv / (math.sqrt(uv + Math.sq(w)) + w) # guaranteed positive
else: # q == 0 && r <= 0
# y = 0 with |x| <= 1. Handle this case directly.
# for y small, positive root is k = abs(y)/sqrt(1-x^2)
k = 0
return k
Astroid = staticmethod(Astroid)