matplotlib - 使用 matplotlib 绘制固定长度的线段(切线)以保留纵横角
问题描述
背景:我试图在梯度噪声图上将梯度显示为固定长度的线。每个“梯度”都可以看作是给定点的切线。问题是,即使我确保线条具有相同的长度,纵横比也会拉伸它们:
生成这个的完整代码:
from math import sqrt, floor, modf, sin
import matplotlib.pyplot as plt
mix = lambda a, b, x: a*(1-x) + b*x
interpolant = lambda t: ((6*t - 15)*t + 10)*t*t*t
rng01 = lambda x: modf(sin(x) * 43758.5453123)[0]
def _gradient_noise(t):
i = floor(t)
f = t - i
s0 = rng01(i) * 2 - 1
s1 = rng01(i + 1) * 2 - 1
v0 = s0 * f;
v1 = s1 * (f - 1);
return mix(v0, v1, interpolant(f))
def _plot_noise(n, interp_npoints=100):
xdata = [i/interp_npoints for i in range(n * interp_npoints)]
gnoise = [_gradient_noise(x) for x in xdata]
plt.plot(xdata, gnoise, label='gradient noise')
plt.xlabel('t')
plt.ylabel('amplitude')
plt.grid(linestyle=':')
plt.legend()
for i in range(n + 1):
a = rng01(i) * 2 - 1 # gradient slope
norm = sqrt(1 + a**2)
norm *= 4 # 1/4 length
vnx, vny = 1/norm, a/norm
x = (i-vnx/2, i+vnx/2)
y = (-vny/2, vny/2)
plt.plot(x, y, 'r-')
plt.show()
if __name__ == '__main__':
_plot_noise(15)
红线图位于 for 循环中。
hypot(x[1]-x[0], y[1]-y[0])
为每个向量提供一个常数.25
,对应于我的目标长度 (¼)。这意味着我的段实际上在给定方面的正确长度。这也可以通过以下方式“验证” .set_aspect(1)
:
我尝试了几件事,例如将坐标转换为显示坐标plt.gca().transData.transform(...)
(plt.gca().transData.inverted().transform(...)
无论如何,这样做也可能实际上也会改变角度。
总结一下:我正在寻找一种方法来显示具有固定长度(在 x 数据坐标系中表示)并在 xy 数据坐标系中定向(旋转)的线条。
解决方案
欢迎来到 SO。第一个问题问得真好。一旦我复制了情节并检查了数学,这让我怀疑自己的理智有多么强烈......
但是,您自己确定了核心问题:问题是在您的代码中,渐变线的长度是在数据坐标中确定的,而它应该取决于绘图的纵横比。
因此,如果您希望渐变线在显示空间中具有一致的长度,那么您需要在计算然后范数时通过绘图的纵横比(或它的倒数)重新缩放 thedx
或组件:dy
import matplotlib.pyplot as plt
from math import sqrt, floor
mix = lambda a, b, x: a*(1-x) + b*x
interpolant = lambda t: ((6*t - 15)*t + 10)*t*t*t
rng01 = lambda x: ((1103515245*x + 12345) % 2**32) / 2**32
def _gradient_noise(t):
i = floor(t)
f = t - i
s0 = rng01(i) * 2 - 1
s1 = rng01(i + 1) * 2 - 1
v0 = s0 * f;
v1 = s1 * (f - 1);
return mix(v0, v1, interpolant(f))
def _plot_noise(n, interp_npoints=100):
xdata = [i/interp_npoints for i in range(n * interp_npoints)]
gnoise = [_gradient_noise(x) for x in xdata]
fig, ax = plt.subplots()
ax.plot(xdata, gnoise, label='gradient noise')
ax.set_xlabel('t')
ax.set_ylabel('amplitude')
ax.grid(linestyle=':')
ax.legend(loc=1)
x0, x1, y0, y1 = ax.axis()
aspect = (y1 - y0) / (x1 - x0)
for i in range(n + 1):
dy = rng01(i) * 2 - 1 # gradient slope
dx = 1
norm = sqrt(dx**2 + (dy / aspect)**2)
# norm *= 4 # 1/4 length
vnx, vny = dx/norm, dy/norm
x = (i-vnx/2, i+vnx/2)
y = (-vny/2, vny/2)
ax.plot(x, y, 'r-')
plt.show()
if __name__ == '__main__':
_plot_noise(15)
推荐阅读
- electron - Electron NSIS 防止重新安装
- django - 在 views.py 中设置注册功能(尝试运行服务器时出现错误消息)
- angular - 重新安装后 TSC 挂起编译
- c++ - 无法使用在同一类中使用成员函数的线程进行编译
- python - 为什么 Spacy 的 NER 训练器返回令牌而不返回实体?
- r - 如何获得第一个和最后一个时间顺序之间的差异?
- angular - 如何允许某些带有角度的 URL 并从中收集数据
- javascript - html/javascript 中的画布线模糊
- swift - 如何在 Swift 中将此日期字符串格式“2018-03-30T14:36:10.093”格式化为日期
- html - 如何结合活动按钮和悬停?