The following program draws a rotated ellipse from an equation given in general form:
import matplotlib as mpl
import matplotlib.pyplot as plt
x_range = np.linspace(-6, +6)
y_range = np.linspace(-10, +1)
x, y = np.meshgrid(x_range, y_range)
A, B, C, D, E, F = 0.25, 0.40, 0.25, 1.50, 2.00, 1.50
f = lambda x, y, A, B, C, D, E, F: A*x**2 + B*x*y +C*y**2 + D*x + E*y + F
equation = f(x, y, A, B, C, D, E, F)
fig, ax = plt.subplots(1, 1, figsize = (4, 4), tight_layout = True)
ax.contour(x, y, equation, levels = [0], colors = color)
plt.show()
Is there a simple way to draw a point on this ellipse,no matter where it is ?
My question comes from the fact that I use Geogebra
sometimes and, in Geogebra
, if you draw an ellipse by the input 0.25*x^2 + 0.40*x*y + 0.25*y^2 + 1.5*x + 2*y + 1.5 = 0
, you can easily put a point where you want on the ellipse (and even move it), using the simple instruction Point on Object
...
I wonder if something similar could be implemented in Python, starting from the small program that I wrote a little above?
Your code draws the ellipse in quite a roundabout way -- it draws a contour of f == 0
for the values in the 2d array equation
, which is obtained by calculating the value of f
at every point on the grid defined by x
and y
.
That said, ax.contour
returns a QuadContourSet
from which you can extract the coordinates being plotted like so:
qcs = ax.contour(x, y, equation, levels = [0], colors = color)
pts = qcs.collections[0].get_paths()[0].vertices
pts
is a 2d array of shape (N, 2)
. Any row of this array is a point on your ellipse. For example:
ax.plot(pts[5, 0], pts[5, 1], 'xk')
plots the black x below:
If you want to find a point for any arbitrary x
value, you simply need to obtain the y
value which solves the equation f(x, ...) == 0
. scipy.optimize.fsolve
can do this for you. Since fsolve
takes a function F
and solves the problem F(X, args) == 0
, we need to redefine our function so that the first argument is the unknown coordinate we're trying to find, and supply the rest as args
to fsolve
:
from scipy.optimize import fsolve
def get_ellipse_point(x, A, B, C, D, E, F, y0):
def ellipse_func(y, x, A, B, C, D, E, F):
return A*x**2 + B*x*y +C*y**2 + D*x + E*y + F
y_pt = fsolve(ellipse_func, y0, (x, A, B, C, D, E, F)
return (x, y_pt[0])
y0
is your initial guess of the point's y
. Calling this function, for example, to find points with x == 0
gives:
# Initial guess of y is -10, which will find the point at the bottom of the ellipse
pt_x0_bottom = get_ellipse_point(0, A, B, C, D, E, F, -10) # (0, -7.16227766016838)
# Initial guess of y is +10, which will find the point at the top of the ellipse
pt_x0_top = get_ellipse_point(0, A, B, C, D, E, F, 10) # (0, -0.8377223398316207)
We can plot these points to make sure we have the correct answer:
ax.plot(*pt_x0_bottom, 'ob') # Plot bottom point with blue circle
ax.plot(*pt_x0_top, '^g') # Plot top point with green triangle
Note that I used the def
keyword to define the function of the ellipse. I could have defined it with a lambda like you did to get the same result, but that is not a good practice (See Is it pythonic: naming lambdas)