Drawing an ellipse at an angle between two points in Python

Question:

I’m trying to draw an ellipse between two points. So far, I have it mostly working:

Arc Angle

The issue comes with setting the ellipse height (ellipse_h below).

    x = center_x + radius*np.cos(theta+deg)
    y = center_y - ellipse_h * radius*np.sin(theta+deg)

In this example, it’s set to -0.5:
Ellipse Height

Can anyone please help me rotate the ellipse height with the ellipse? Thank you!

import numpy as np
import matplotlib.pyplot as plt

def distance(x1, y1, x2,  y2):
    return np.sqrt(np.power(x2 - x1, 2) + np.power(y2 - y1, 2) * 1.0)

def midpoint(x1, y1, x2,  y2):
    return [(x1 + x2) / 2,(y1 + y2) / 2]

def angle(x1, y1, x2, y2):
    #radians
    return np.arctan2(y2 - y1, x2 - x1)

x1 = 100
y1 = 150
x2 = 200
y2 = 190
ellipse_h = -1
x_coords = []
y_coords = []

mid = midpoint(x1, y1, x2, y2)
center_x = mid[0]
center_y = mid[1]
ellipse_resolution = 40
step = 2*np.pi/ellipse_resolution

radius = distance(x1, y1, x2, y2) * 0.5
deg = angle(x1, y1, x2, y2)
cos = np.cos(deg * np.pi /180)
sin = np.sin(deg * np.pi /180)

for theta in np.arange(0, np.pi+step, step):
    x = center_x + radius*np.cos(theta+deg)
    y = center_y - ellipse_h * radius*np.sin(theta+deg)
    x_coords.append(x)
    y_coords.append(y)

plt.xlabel("X")
plt.ylabel("Y")
plt.title("Arc between 2 Points")

plt.plot(x_coords,y_coords)
plt.scatter([x1,x2],[y1,y2])
plt.axis('equal')

plt.show()
Asked By: Dr. Pontchartrain

||

Answers:

A simple solution is to describe the ellipse by its standard parametric equation, as you effectively did. However, under the assumption that it is centered on the origin of the coordinate system, it becomes straightforward to then apply a rotation to its points using a 2d rotation matrix and finally apply a translation to position it on its true center. This gives the following:

import numpy as np
import matplotlib.pyplot as plt

# extreme points along the major axis
x1, y1 = 100, 150
x2, y2 = 200, 190

# along minor axis 
height = 15 

# number of points
n = 100

# center
x_c, y_c = (x1 + x2)/2, (y1 + y2)/2

# width (major axis) and height (minor) of the ellipse halved
a, b = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)/2, height/2

# rotation angle
angle = np.arctan2(y2 - y1, x2 - x1)

# standard parametric equation of an ellipse
t = np.linspace(0, 2*np.pi, n)
ellipse = np.array([a*np.cos(t), b*np.sin(t)])  

# 2d rotation matrix
R = np.array([[np.cos(angle), -np.sin(angle)],
              [np.sin(angle),  np.cos(angle)]])  

# apply the rotation to the ellipse
ellipse_rot = R @ ellipse

plt.plot(x_c + ellipse_rot[0], y_c + ellipse_rot[1], 'r' ) 
plt.scatter([x1, x2], [y1, y2], color='k')
plt.axis('equal')
plt.show()

See the output for different heights:

ellipse_outputs

Following your comment, for the limiting case of the circle, you need to specify height = np.sqrt((x2 - x1)**2 + (y2 - y1)**2), so that a = b.

Hope this helps !

Answered By: Yacola