Issue
I want to add a bunch of circles with equal aspect ratio to my plot, but the ratio of the datalimits of my x and y axis aren't equal. Therefore I tried to just create a regular circle of radius 1 first (which will be stretched if the datalimits ratio is, say 2:1).
from matplotlib.patches import Circle
circle = Circle(xy,0.5,transform=ax.transAxes) # if r=0.5, then the ellipsis will touch the borders of the x and y axis.
Then I find out the aspect ratio of my datalimits so I can set the width of circle
to the aspect ratio.
def get_data_aspect(ax):
xlim = ax.get_xlim()
ylim = ax.get_ylim()
return (xlim[1]-xlim[0])/(ylim[1]-ylim[0])
data_aspect = get_data_aspect(ax)
According to my logic, if I divide the width of my squished circle by this aspect ratio, it should compensate the streched width of my circle and make it an actual circle:
circle.set(width=circle.width/data_aspect)
Then I can scale the circle with any factor I want to make it bigger or smaller
a = 0.1
circle.set(width=circle.width*a, height=circle.height*a)
Yet for some reason this does not work and I can't figure out why. Since matplotlib.patches
(as far as I know), never call plt.autoscale()
when created or changed, resizing of the datalimits shouldn't occur. I don't know what else to try or why my math is wrong.
Full code:
def get_data_aspect(ax):
xlim = ax.get_xlim()
ylim = ax.get_ylim()
return (xlim[1]-xlim[0])/(ylim[1]-ylim[0])
def add_Circle(ax,xy,d,asp):
asp_is = get_data_aspect(ax)
circle = Circle(xy,0.5,transform=ax.transAxes)
circle.set(width=circle.width/asp_is)
circle = ax.add_patch(circle)
return circle
def LJ_hamonic_plot():
fig, ax = plt.subplots(figsize=(7,4))
sig,eps = 3,1
r_min = 2**(1/6) * sig
k = (36 * eps * 2**(2/3))/sig**2
r = np.linspace(r_min-1,r_min+3,1000)
U_LJ = 4 * eps *((sig/r)**12 - (sig/r)**6)
U_harm = k/2 * (r - r_min)**2 -eps
start, end = -1,3
r_new = np.linspace(start,end,1000)
ax.plot(r_new, U_LJ, label=r'$\mathrm{harmonisch}$', color='blue', linewidth=0.7)
# ax.set_ylim(*ax.get_ylim())
ax.set_ylim(-1.2,0.5)
ax.set_xlim(-0.76,end)
ax.plot(r_new, U_harm, label=r'$\mathrm{anharmonisch}$', color='red', linewidth=0.7)
ax.set_ylabel(r'$V(\Delta r)$', size=12, labelpad=7)
ax.set_xlabel(r'$\Delta r = r - r_0$', size=12)
ax.xaxis.set_minor_locator(AutoMinorLocator())
ax.xaxis.set_major_locator(MaxNLocator(6))
ax.yaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_major_locator(MaxNLocator(6))
ax.xaxis.set_ticks_position('bottom')
ax.tick_params(axis='both',which='both',labelsize=12, direction='in')
ax.legend(prop={'size': 12})
# make molecule model
asp = get_data_aspect(ax)
print(asp)
c1 = add_Circle(ax,(0.5,0.5),1/2,1)
plt.show()
# plt.savefig('harmonic_fit.svg',dpi=500, bbox_inches='tight')
LJ_hamonic_plot()
Solution
Try trans = (fig.dpi_scale_trans + transform.ScaledTranslation(x, y, ax.transData))
where x and y are x- and y-coordinates of the center of the circle while keeping the argument xy
as (0, 0) in matplotlib.patches.Circle
.
Utilising this, I have plotted a few circles with different radii at different positions:
Please look at the function get_transform(x, y)
in the code below which implements correct positioning of the circle and correct transformation required to plot the circle:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import MaxNLocator, AutoMinorLocator
from matplotlib.patches import Circle
import matplotlib.transforms as transform
def LJ_hamonic_plot():
fig, ax = plt.subplots(figsize=(7,4))
sig,eps = 3,1
r_min = 2**(1/6) * sig
k = (36 * eps * 2**(2/3))/sig**2
r = np.linspace(r_min-1,r_min+3,1000)
U_LJ = 4 * eps *((sig/r)**12 - (sig/r)**6)
U_harm = k/2 * (r - r_min)**2 -eps
start, end = -1,3
r_new = np.linspace(start,end,1000)
ax.plot(r_new, U_LJ, label=r'$\mathrm{harmonisch}$', color='blue', linewidth=0.7)
# ax.set_ylim(*ax.get_ylim())
ax.set_ylim(-1.2,0.5)
ax.set_xlim(-0.76,end)
ax.plot(r_new, U_harm, label=r'$\mathrm{anharmonisch}$', color='red', linewidth=0.7)
ax.set_ylabel(r'$V(\Delta r)$', size=12, labelpad=7)
ax.set_xlabel(r'$\Delta r = r - r_0$', size=12)
ax.xaxis.set_minor_locator(AutoMinorLocator())
ax.xaxis.set_major_locator(MaxNLocator(6))
ax.yaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_major_locator(MaxNLocator(6))
ax.xaxis.set_ticks_position('bottom')
ax.tick_params(axis='both',which='both',labelsize=12, direction='in')
ax.legend(prop={'size': 12})
def get_transform(x, y):
# x and y are the x- and y-coordinates of the center of the circle.
trans = (fig.dpi_scale_trans + transform.ScaledTranslation(x, y, ax.transData))
return(trans)
# Plot some circles
for i, j in zip(np.arange(-3, 10), np.arange(-5, 8)):
circle = Circle((0.0, 0.0), 0.5/(abs(int(i)) + 2) , transform=get_transform(i/5, j/15), alpha=0.75)
ax.add_patch(circle)
plt.show()
LJ_hamonic_plot()
For more details, please check the documentation for matplotlib's advanced transformations to understand how you can create a circle while maintaining the aspect ratio of the figure.
Answered By - medium-dimensional
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.