You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Matplotlib中绘制指定两点间、可指定波数且兼容多子图的波浪箭头

实现Matplotlib多子图兼容的波浪箭头绘制方法

之前做可视化需求时,正好需要在Matplotlib里给指定两点加带自定义波浪数的箭头,还得兼容多子图场景。参考了Ian Roberts提出的一个实用但已关闭的问题,加上hayk的优质解答,我花了不少时间调整代码适配多子图,现在把这个通用方法分享出来,大家不用再踩坑啦!

核心功能

  • 支持在任意两点间绘制带自定义波浪数量的箭头
  • 完美兼容Matplotlib多子图布局
  • 可自定义箭头样式、波浪幅度等参数

实现代码

import matplotlib.pyplot as plt
import numpy as np

def draw_wavy_arrow(ax, x_start, y_start, x_end, y_end, num_waves=3, wave_amplitude=0.05, **arrow_kwargs):
    """
    在指定Axes上绘制两点间的波浪箭头
    
    参数:
        ax: matplotlib.axes.Axes 对象,要绘制箭头的子图
        x_start, y_start: 箭头起点坐标
        x_end, y_end: 箭头终点坐标
        num_waves: 波浪的数量,默认3
        wave_amplitude: 波浪的幅度,默认0.05(相对坐标轴范围)
        **arrow_kwargs: 传递给plt.arrow的额外参数,比如color、linewidth等
    """
    # 获取坐标轴范围,计算相对幅度的实际值
    x_range = ax.get_xlim()[1] - ax.get_xlim()[0]
    y_range = ax.get_ylim()[1] - ax.get_ylim()[0]
    abs_wave_amplitude = wave_amplitude * np.sqrt(x_range**2 + y_range**2)
    
    # 计算两点间的向量与长度
    dx = x_end - x_start
    dy = y_end - y_start
    length = np.sqrt(dx**2 + dy**2)
    
    # 生成沿线段分布的归一化参数与波浪偏移
    t = np.linspace(0, 1, 100 * num_waves)
    wave = abs_wave_amplitude * np.sin(2 * np.pi * num_waves * t)
    
    # 计算垂直于线段的单位向量,用于生成波浪偏移方向
    perp_x = -dy / length
    perp_y = dx / length
    
    # 计算波浪线上的所有点坐标
    x_points = x_start + dx * t + wave * perp_x
    y_points = y_start + dy * t + wave * perp_y
    
    # 绘制波浪线
    ax.plot(x_points, y_points, **arrow_kwargs)
    
    # 在终点绘制箭头(稍微缩短起点避免和波浪线重叠)
    arrow_x_start = x_points[-20]
    arrow_y_start = y_points[-20]
    arrow_dx = x_end - arrow_x_start
    arrow_dy = y_end - arrow_y_start
    ax.arrow(arrow_x_start, arrow_y_start, arrow_dx, arrow_dy, 
             head_width=0.02 * np.sqrt(x_range**2 + y_range**2), 
             head_length=0.03 * np.sqrt(x_range**2 + y_range**2),
             fc=arrow_kwargs.get('color', 'black'), ec=arrow_kwargs.get('color', 'black'))

# 示例用法
if __name__ == "__main__":
    # 创建1行2列的多子图布局
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
    
    # 第一个子图:5个波浪的红色箭头
    ax1.plot([0, 1], [0, 1], 'k--')
    draw_wavy_arrow(ax1, 0, 0, 1, 1, num_waves=5, wave_amplitude=0.08, color='red', linewidth=2)
    ax1.set_title('5个波浪的箭头')
    ax1.set_xlim(-0.1, 1.1)
    ax1.set_ylim(-0.1, 1.1)
    
    # 第二个子图:2个波浪的蓝色箭头
    ax2.plot([0, 0.5], [1, 0], 'b--')
    draw_wavy_arrow(ax2, 0, 1, 0.5, 0, num_waves=2, wave_amplitude=0.05, color='blue', linewidth=1.5)
    ax2.set_title('2个波浪的箭头')
    ax2.set_xlim(-0.1, 0.6)
    ax2.set_ylim(-0.1, 1.1)
    
    plt.tight_layout()
    plt.show()

使用说明

  1. 调用时必须传入目标子图的ax对象,这是兼容多子图的核心
  2. num_waves可以自由调整波浪数量,数值越大波浪越密集
  3. wave_amplitude是相对幅度,会根据子图坐标轴范围自动计算实际偏移,避免不同尺度子图中波浪大小失衡
  4. 通过**arrow_kwargs可以传递Matplotlib线条和箭头的样式参数,比如颜色、线宽等

内容的提问来源于stack exchange,提问作者user46147

火山引擎 最新活动