1

The equation of an ellipse is:

sqrt((x-a1)**2 + (y-b1)**2) + np.sqrt((x-a2)**2 + (y-b2)**2) = c

The focii are (a1, b1) and (a2, b2). c is also known. How do I draw this in python using matplotlib?

Thanks for your help.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
Vladimir Vargas
  • 1,744
  • 4
  • 24
  • 48

2 Answers2

3

You can represent the ellipse parametrically in some variable t. You could look into Wikipedia to see how this can be done, for instance.

In the following code, I've derived the parameters necessary for the parametric form from the parameters that you've supplied.

# Example focii and sum-distance
a1 = 1
b1 = 2
a2 = 5
b2 = 7
c = 9

# Compute ellipse parameters
a = c / 2                                # Semimajor axis
x0 = (a1 + a2) / 2                       # Center x-value
y0 = (b1 + b2) / 2                       # Center y-value
f = np.sqrt((a1 - x0)**2 + (b1 - y0)**2) # Distance from center to focus
b = np.sqrt(a**2 - f**2)                 # Semiminor axis
phi = np.arctan2((b2 - b1), (a2 - a1))   # Angle betw major axis and x-axis

# Parametric plot in t
resolution = 1000
t = np.linspace(0, 2*np.pi, resolution)
x = x0 + a * np.cos(t) * np.cos(phi) - b * np.sin(t) * np.sin(phi)
y = y0 + a * np.cos(t) * np.sin(phi) + b * np.sin(t) * np.cos(phi)

# Plot ellipse
plt.plot(x, y)

# Show focii
plt.plot(a1, b1, 'bo')
plt.plot(a2, b2, 'bo')

plt.axis('equal')
plt.show()

This gives what you need:

Ellipse plot

Praveen
  • 6,872
  • 3
  • 43
  • 62
  • As a physicist, if there is any theoretical simplification of the problem, I do it. That's why I like this answer. I wondered if this code that you showed me has been already implemented in some module. It seems that it isn't. Thanks for your time. – Vladimir Vargas Feb 16 '17 at 12:01
1

you need 2 lists or arrays of X, Y such that the elements satisfy the ellipse equation

the usual ellipse plotting solutions parameterize the ellipse equation by central (or focal) angle to make the X,Y functions single valued for the angle in 0 to 2pi

I showed a solution in Drawing elliptical orbit in Python (using numpy, matplotlib) Y as a function of X with a hack to "feel out" the xrange, then piece together the dual Y solutions for each x

just the minimum mods to that code to put in your equation, would fail for a1 = a2

the symbolic solution takes a minute or so runtime

import numpy as np
import matplotlib.pyplot as plt
from sympy import *  

# sqrt((x-a1)**2 + (y-b1)**2) + np.sqrt((x-a2)**2 + (y-b2)**2) = c
coeffs = [1, 0, -1, 0, 4]
xs = [coeffs[0], coeffs[2]]

def ysolv(coeffs):
    x,y,a1,b1,a2,b2,c = symbols('x y a1 b1 a2 b2 c', real = True)
    ellipse = sqrt((x-a1)**2 + (y-b1)**2) + sqrt((x-a2)**2 + (y-b2)**2) - c
    y_sols = solve(ellipse, y)
    print(*y_sols, sep='\n')

    num_coefs = [(a, f) for a, f in (zip([a1,b1,a2,b2,c], coeffs))]
    y_solsf0 = y_sols[0].subs(num_coefs)
    y_solsf1 = y_sols[1].subs(num_coefs)
    print(y_solsf0, '\n', y_solsf1)

    f0 = lambdify([x], y_solsf0)
    f1 = lambdify([x], y_solsf1)
    return f0, f1

f0, f1 = ysolv(coeffs)

y0 = [f0(x) for x in xs]
y1 = [f1(x) for x in xs]

def feeloutXrange(f, midx, endx):
    fxs = []
    x = midx
    while True:
        try: f(x)
        except:
            break
        fxs.append(x)
        x += (endx - midx)/200
    return fxs

midx = (min(xs) + max(xs))/2    

xpos = feeloutXrange(f0, midx, max(xs))
xnegs = feeloutXrange(f0, midx, min(xs))
xs_ellipse = xnegs[::-1] + xpos[1:]

y0s = [f0(x) for x in xs_ellipse]
y1s = [f1(x) for x in xs_ellipse]

ys_ellipse = y0s + y1s[::-1] + [y0s[0]] # add y start point to end to close drawing

xs_ellipse = xs_ellipse + xs_ellipse[::-1] + [xs_ellipse[0]] # added x start point  

plt.plot(xs_ellipse, ys_ellipse)
plt.show()

(-c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*(a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2 - c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 + b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x + a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 + b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*(b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2))
(c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*(a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2 - c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 + b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x + a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 + b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*(b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2))
sqrt(-48*x**2 + 192)/8 
 -sqrt(-48*x**2 + 192)/8

enter image description here

the other answers used the parametric transform approach

I especially like showing sympy solving the equation for you rather than someone hand solving it

the symbolic expression only needs be found once for a particular Ellipse parameterization, then the symbolic expression can simply be hard coded:

"""   
for Ellipse equation:
sqrt((x-a1)**2 + (y-b1)**2) + sqrt((x-a2)**2 + (y-b2)**2) = c   

sympy solution to Ellipse equation, only have to run once to get y_sols
symbolic expression to paste into ysolv below

#def symEllipse():
#    x,y,a1,b1,a2,b2,c = symbols('x y a1 b1 a2 b2 c', real = True)
#    ellipse = sqrt((x-a1)**2 + (y-b1)**2) + sqrt((x-a2)**2 + (y-b2)**2) - c
#    y_sols = solve(ellipse, y)
#    print(*y_sols, sep='\n')

"""

coeffs = [1, 1, -1, -1, 3]
xs = [coeffs[0], coeffs[2]]

def ysolv(coeffs):

    x,y,a1,b1,a2,b2,c = symbols('x y a1 b1 a2 b2 c', real = True)

    y_sols = [
        (-c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*
          (a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2
           - c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 +
           b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x +
           a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 +
           b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*
           (b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2)),
          (c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*
          (a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2
           - c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 +
           b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x + 
           a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 + 
           b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*
           (b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2))
          ]
    num_coefs = [(a, f) for a, f in (zip([a1,b1,a2,b2,c], coeffs))]
    y_solsf0 = y_sols[0].subs(num_coefs)
    y_solsf1 = y_sols[1].subs(num_coefs)
    print(y_solsf0, '\n', y_solsf1)

    f0 = lambdify([x], y_solsf0)
    f1 = lambdify([x], y_solsf1)
    return f0, f1
Community
  • 1
  • 1
f5r5e5d
  • 3,656
  • 3
  • 14
  • 18
  • Could you help me understand why this is better than the parametric approach? It seems unduly complicated... – Praveen Feb 16 '17 at 06:18
  • not necessarily "better", I was making the point that a pure x, y coordinate solution is not "impossible" – f5r5e5d Feb 16 '17 at 06:49
  • @f5r5e5d thanks for your answer. Indeed this shows the great power of sympy and gives a very nice solution. – Vladimir Vargas Feb 16 '17 at 11:59