Question: I am trying to create a circle that displays a circle regardless of axis scaling, but placed in data coordinates and whose radius is dependent on the scaling of the y-axis. Based on the transforms tutorial, and more specifically the bit about plotting in physical coordinates, I need a pipeline that looks like this: from matplotlib import pyplot as plt, patches as mpatch, transforms as mtrans fig, ax = plt.subplots() x, y = 5, 10 r = 3 transform = fig.dpi_scale_trans + fig_to_data_scaler + mtrans.ScaledTranslation(x, y, ax.transData) ax.add_patch(mpatch.Circle((0, 0), r, edgecolor='k', linewidth=2, facecolor='w', transform=t))  The goal is to create a circle that's scaled correctly at the figure level, scale it to the correct height, and then move it in data coordinates. fig.dpi_scale_trans and mtrans.ScaledTranslation(x, y, ax.transData) work as expected. However, I am unable to come up with an adequate definition for fig_to_data_scaler. It is pretty clear that I need a blended transformation that takes the y-scale from ax.transData combined with fig.dpi_scale_trans (inverted?) and then uses the same values for x, regardless of data transforms. How do I do that? Another reference that I looked at: https://stackoverflow.com/a/56079290/2988730.  Here's a transform graph I've attempted to construct unsuccessfully: vertical_scale_transform = mtrans.blended_transform_factory(mtrans.IdentityTransform(), fig.dpi_scale_trans.inverted() + mtrans.AffineDeltaTransform(ax.transData)) reflection = mtrans.Affine2D.from_values(0, 1, 1, 0, 0, 0) fig_to_data_scaler = vertical_scale_transform + reflection + vertical_scale_transform # + reflection, though it's optional   It looks like the previous attempt was a bit over-complicated. It does not matter what the figure aspect ratio is. The axes data transform literally handles all of that out-of-the box. The following attempt almost works. The only thing it does not handle is pixel aspect ratio: vertical_scale_transform = mtrans.AffineDeltaTransform(ax.transData) reflection = mtrans.Affine2D.from_values(0, 1, 1, 0, 0, 0) uniform_scale_transform = mtrans.blended_transform_factory(reflection + vertical_scale_transform + reflection, vertical_scale_transform) t = uniform_scale_transform + mtrans.ScaledTranslation(x, y, ax.transData) ax.add_patch(mpatch.Circle((0, 0), r, edgecolor='k', linewidth=2, facecolor='w', transform=t))  This places perfect circles at the correct locations. Panning works as expected. The only issue is that the size of the circles does not update when I zoom. Given mtrans.AffineDeltaTransform(ax.transData) on the y-axis, I find that to be surprising. I guess the updated question is then, why is the scaling part of the transform graph not updating fully when I zoom the axes?

Answer: It appears that the approach I proposed in the question is supposed to work. To create a transform that has data scaling in the y-direction and the same scaling regardless of data in the x-direction, we can do the following:  Create a transform that scales vertically with ax.transData Create a simple reflection transform using Affine2D By reflecting, applying the transform in step 1 and reflecting back, we can make a transform that scales the x-axis the same as y. Finally, we add a ScalesTranslation to place the object at the correct data location  Here is the full solution: from matplotlib import pyplot as plt, patches as mpatch, transforms as mtrans fig, ax = plt.subplots() x, y = 5, 10 r = 3 # AffineDeltaTransform returns just the scaling portion vertical_scale_transform = mtrans.AffineDeltaTransform(ax.transData) reflection = mtrans.Affine2D.from_values(0, 1, 1, 0, 0, 0) # The first argument relies on the fact that `reflection` is its own inverse uniform_scale_transform = mtrans.blended_transform_factory(reflection + vertical_scale_transform + reflection, vertical_scale_transform) t = uniform_scale_transform + mtrans.ScaledTranslation(x, y, ax.transData) # Create a circle at origin, and move it with the transform ax.add_patch(mpatch.Circle((0, 0), r, edgecolor='k', linewidth=2, facecolor='w', transform=t))  This answer is encapsulated in a proposed gallery example: https://github.com/matplotlib/matplotlib/pull/28364  The issue with this solution at time of writing is that AffineDeltaTransform is not updating correctly when axes are zoomed or resized. The issue has been filed in matplotlib#28372, and resolved in matplotlib#28375. Future versions of matplotlib will be able to run the code above interactively.