使用相同的数据相关缩放创建混合变换

问题描述 投票:0回答:1

我正在尝试创建一个圆,无论轴缩放如何,它都显示一个圆,但放置在数据坐标中,其半径取决于 y 轴的缩放。基于变换教程,更具体地说,关于在物理坐标中绘制,我需要一个如下所示的管道:

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))

目标是创建一个在图形级别正确缩放的圆,将其缩放到正确的高度,然后在数据坐标中移动它。

fig.dpi_scale_trans
mtrans.ScaledTranslation(x, y, ax.transData)
按预期工作。但是,我无法为
fig_to_data_scaler
提供足够的定义。

很明显,我需要一个混合变换,将

ax.transData
fig.dpi_scale_trans
(反转?)相结合,然后对
x
使用相同的值,无论数据转换如何。我该怎么做?

我查看的另一个参考文献:https://stackoverflow.com/a/56079290/2988730


这是我尝试构建但未成功的转换图:

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
python matplotlib transform
1个回答
0
投票

计算图形的长宽比

aspect_ratio = fig.get_figheight() / fig.get_figwidth()

长宽比的计算方式为高度除以宽度。该比率用于确保缩放变换在应用于圆时保持正确的比例。

创建变换以正确缩放数据坐标中的半径

transform = mtrans.Affine2D().scale(aspect_ratio, 1.0) + ax.transData
  • mtrans.Affine2D()
    :这将创建一个新的仿射变换,它是平移、缩放、旋转和剪切变换的组合。
  • scale(aspect_ratio, 1.0)
    :此方法沿 x 和 y 轴按指定因子缩放变换。按长宽比缩放 x 轴,按 1.0 缩放 y 轴,以确保根据图形的长宽比正确缩放圆的半径。
  • ax.transData
    :此转换将数据坐标映射到显示坐标。这将自定义缩放与从数据坐标到显示坐标的现有转换相结合。

变换调整数据坐标中圆的半径,因此在图形上渲染时它看起来正确缩放,这一点很重要,因为如果不考虑图形的纵横比,图形的纵横比可能会扭曲对象,导致圆显示为椭圆形 .

import matplotlib.pyplot as plt
import matplotlib.patches as mpatch
import matplotlib.transforms as mtrans
from typing import Tuple

def create_scaled_circle(ax: plt.Axes, center: Tuple[float, float], radius: float) -> mpatch.Circle:
    fig = ax.figure
    x, y = center

    # Calculate aspect ratio of the figure
    aspect_ratio = fig.get_figheight() / fig.get_figwidth()

    # Create a transform that scales the radius correctly in data coordinates
    transform = mtrans.Affine2D().scale(aspect_ratio, 1.0) + ax.transData

    circle = mpatch.Circle(center, radius, transform=transform, facecolor='none', edgecolor='red')
    return circle

单一用例

fig, ax = plt.subplots()
circle1 = create_scaled_circle(ax, (5, 10), 3)
circle2 = create_scaled_circle(ax, (7, 15), 5)
ax.add_patch(circle1)
ax.add_patch(circle2)
ax.set_xlim(0, 20)
ax.set_ylim(0, 20)
plt.show()

enter image description here

多个测试用例

# Define 10 test cases with different centers, radii, axis limits, and quadrants
test_cases = [
    ((5, 10), 3, (-20, 20), (-20, 20)),  # Quadrant I
    ((-7, 15), 5, (-20, 20), (-20, 20)),  # Quadrant II
    ((-10, -5), 2, (-20, 20), (-20, 20)),  # Quadrant III
    ((15, -10), 4, (-20, 20), (-20, 20)),  # Quadrant IV
    ((8, 8), 3, (-30, 30), (-30, 30)),  # Near origin, all quadrants
    ((-12, -12), 6, (-20, 20), (-20, 20)),  # Quadrant III
    ((3, -18), 1.5, (-20, 20), (-20, 20)),  # Quadrant IV
    ((-6, 6), 2.5, (-15, 15), (-15, 15)),  # Quadrant II
    ((9, -9), 4.5, (-25, 25), (-25, 25)),  # Quadrant IV
    ((-11, 7), 3.5, (-20, 20), (-20, 20)),  # Quadrant II
]

# Create subplots for each test case
fig, axs = plt.subplots(5, 2, figsize=(15, 25))

for ax, (center, radius, xlim, ylim) in zip(axs.flatten(), test_cases):
    circle = create_scaled_circle(ax, center, radius)
    ax.add_patch(circle)
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
    ax.set_aspect('equal')  # Ensuring equal aspect ratio for display

plt.tight_layout()
plt.show()

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.