I find TomNorway's approach better, it has more flexibility in other cases than the accepted answer. I tested the code and made some quick fixes for even more applicability by creating a class.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
class LinesAngles:
def __init__(self, line1, line2, radius=None, flip=False, obtuse=False, reverse=False, dec=0, fontsize=8, title=""):
"""
line1: list of two points, of shape [[x0, y0], [x1, y1]]
line2: list of two points, of shape [[x0, y0], [x1, y1]]
radius: None, float or tuple of floats. If None, is set to half the length
of the shortest line orgio: If True, draws the arc around the point (0,0). If False, estimates
the intersection of the lines and uses that point.
flip: If True, flips the arc to the opposite side by 180 degrees
obtuse: If True, uses the other set of angles. Often used with reverse=True.
reverse: If True, reverses the two angles so that the arc is drawn "the opposite way around the circle"
dec: The number of decimals to round to
fontsize: fontsize of the angle label
title: Title of the plot
"""
self.line1 = line1
self.line2 = line2
self.lines = [line1, line2]
self.radius = radius
self.flip = flip
self.obtuse = obtuse
self.reverse = reverse
self.dec = dec
self.fontsize = fontsize
self.title = title
def halfangle(self,a, b) -> float:
"""
Gets the middle angle between a and b, when increasing from a to b
a: float, angle in degrees
b: float, angle in degrees
returns: float, angle in degrees
"""
if b < a:
b += 360
return (a + b)/2 % 360
def get_arc_patch(self, lines: list):
"""
For two sets of two points, create a matplotlib Arc patch drawing
an arc between the two lines.
lines: list of lines, of shape [[(x0, y0), (x1, y1)], [(x0, y0), (x1, y1)]]
returns: Arc patch, and text for the angle label
"""
linedata = [np.array(line.T) for line in lines]
scales = [np.diff(line).T[0] for line in linedata]
scales = [s[1] / s[0] for s in scales]
# Get angle to horizontal
angles = np.array([np.rad2deg(np.arctan(s/1)) for s in scales])
if self.obtuse:
angles[1] = angles[1] + 180
if self.flip:
angles += 180
if self.reverse:
angles = angles[::-1]
angle = abs(angles[1]-angles[0])
if self.radius is None:
lengths = np.linalg.norm(lines, axis=(0,1))
self.radius = min(lengths)/2
# Solve the point of intersection between the lines:
t, s = np.linalg.solve(np.array([line1[1]-line1[0], line2[0]-line2[1]]).T, line2[0]-line1[0])
intersection = np.array((1-t)*line1[0] + t*line1[1])
# Check if radius is a single value or a tuple
try:
r1, r2 = self.radius
except:
r1 = r2 = self.radius
arc = Arc(intersection, 2*r1, 2*r2, theta1=angles[1], theta2=angles[0])
half = self.halfangle(*angles[::-1])
sin = np.sin(np.deg2rad(half))
cos = np.cos(np.deg2rad(half))
r = r1*r2/(r1**2*sin**2+r2**2*cos**2)**0.5
xy = np.array((r*cos, r*sin))
xy = intersection + xy/2
textangle = half if half > 270 or half < 90 else 180 + half
textkwargs = {
'x':xy[0],
'y':xy[1],
's':str(round(angle, self.dec)) + "°",
'ha':'center',
'va':'center',
'fontsize':self.fontsize,
'rotation':textangle
}
return arc, textkwargs
def plot(self) -> None:
"""!
Plot the lines and the arc
"""
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
for line in self.lines:
x,y = line.T
ax.plot(x,y)
ax.axis('equal')
arc, angle_text = self.get_arc_patch(self.lines)
ax.add_artist(arc)
ax.set(title=self.title)
ax.text(**angle_text)
plt.show()
For using it you just create the instance and the plot function.
# lines are formatted like this: [(x0, y0), (x1, y1)]
line1 = np.array([(1,-2), (3,2)])
line2 = np.array([(2,2), (2,-2)])
default = LinesAngles(line1, line2, title="Default")
#Plot single pair of lines
default.plot()

If you still want to plot multiple cases, I created a function that accepts the instances and plots automatically to the subplots you need.
# lines are formatted like this: [(x0, y0), (x1, y1)]
line1 = np.array([(1,-2), (3,2)])
line2 = np.array([(2,2), (2,-2)])
default = LinesAngles(line1, line2, title="Default")
flip = LinesAngles(line1, line2, title='flip=True', flip=True)
obtuse = LinesAngles(line1, line2, title='obtuse=True, reverse=True', obtuse=True, reverse=True)
radius = LinesAngles(line1, line2, title='radius=(2,1)', radius=(2,1))
#Plot single pair of lines
default.plot()
#Plot multiple line pairs
multiple_plot(default, flip, obtuse, radius, num_subplots=4)

Thanks to TomNorway for his answer, all credit to him I only made some modifications.