Matplotlib 3D Trisurf图中ax.format_coord()如何返回(x,y,z)而非视角参数
解决Matplotlib plot_trisurf点击时获取笛卡尔坐标的问题
你遇到的核心问题是:plot_surface和plot_trisurf的Axes3D.format_coord方法逻辑完全不同。前者默认会返回鼠标位置对应的笛卡尔(x,y,z)坐标,但后者默认返回的是当前3D视图的方位角、仰角参数,这就是为什么你调用ax.format_coord(x,y)得到的是视角信息而非点坐标。
下面给你两种可行的解决方案:
方案一:自定义format_coord方法
我们可以重写ax.format_coord,让它返回我们需要的(x,y,z)坐标,同时也能让鼠标hover时的右下角显示保持一致:
import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from plyfile import PlyData, PlyElement # 自定义坐标格式化函数 def custom_format_coord(x, y): try: # 将2D屏幕坐标转换为3D数据空间的x、y初始值 xd, yd, _ = ax.proj3d.inv_transform(x, y, 0) # 获取三角网格对象并创建插值器 tri_surf = ax.get_children()[0] interpolator = tri_surf.get_triangulation().get_interpolator(mesh.data['z']) # 插值计算对应的z值 z_val = interpolator(xd, yd) return f'x={xd:.2f}, y={yd:.2f}, z={z_val:.2f}' except: # 点击点不在曲面范围内时的 fallback return f'x={x:.2f}, y={y:.2f}' # 处理点击事件 def onclick(event): if event.inaxes == ax: coord_str = custom_format_coord(event.xdata, event.ydata) print(f'点击位置坐标:{coord_str}') # 如需提取数值,可解析字符串 parts = coord_str.split(', ') if len(parts) == 3: x = float(parts[0].split('=')[1]) y = float(parts[1].split('=')[1]) z = float(parts[2].split('=')[1]) print(f'解析后的坐标:x={x}, y={y}, z={z}') # 读取PLY文件 plydata = PlyData.read("some.ply") mesh = plydata.elements[0] triangles_as_tuples = [(x[0], x[1], x[2]) for x in plydata['face'].data['vertex_indices']] polymesh = np.array(triangles_as_tuples) # 绘制3D三角曲面 fig = plt.figure() ax = fig.gca(projection='3d') ax.plot_trisurf(mesh.data['x'], mesh.data['y'], mesh.data['z'], triangles=polymesh, linewidth=0.2, antialiased=False) # 替换默认的format_coord方法 ax.format_coord = custom_format_coord fig.canvas.mpl_connect('button_press_event', onclick) plt.show()
方案二:直接在点击事件中计算z值
如果不想修改hover显示的格式,也可以单独在点击事件里计算对应点的z坐标:
import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from plyfile import PlyData, PlyElement # 处理点击事件 def onclick(event): if event.inaxes != ax: return # 将屏幕坐标转换为3D数据空间的x、y xd, yd, _ = ax.proj3d.inv_transform(event.xdata, event.ydata, 0) # 获取三角网格并创建插值器 tri_surf = ax.get_children()[0] interpolator = tri_surf.get_triangulation().get_interpolator(mesh.data['z']) try: z_val = interpolator(xd, yd) print(f'点击点坐标:x={xd:.2f}, y={yd:.2f}, z={z_val:.2f}') except ValueError: # 点击点不在曲面范围内时提示 print('点击点不在三角曲面范围内') # 读取PLY文件 plydata = PlyData.read("some.ply") mesh = plydata.elements[0] triangles_as_tuples = [(x[0], x[1], x[2]) for x in plydata['face'].data['vertex_indices']] polymesh = np.array(triangles_as_tuples) # 绘制3D三角曲面 fig = plt.figure() ax = fig.gca(projection='3d') ax.plot_trisurf(mesh.data['x'], mesh.data['y'], mesh.data['z'], triangles=polymesh, linewidth=0.2, antialiased=False) fig.canvas.mpl_connect('button_press_event', onclick) plt.show()
关键说明
ax.proj3d.inv_transform:负责将2D屏幕坐标转换为3D数据空间的坐标,这里先把z设为0,后续通过三角网格插值得到真实的曲面z值。Triangulation.get_interpolator:创建一个插值函数,输入曲面内的(x,y)就能得到对应的z值,若点击点不在任何三角面片内会抛出异常,需要额外处理。
内容的提问来源于stack exchange,提问作者jmac
相关产品推荐
相关产品推荐

