0

我是 Python 编程新手。我正在从事一个涉及 3D 曲面图的项目,我想在单击鼠标时获取 3D 曲面上的点的坐标。我曾尝试在线检查类似的东西,并且我找到了一种巧妙的方法来为 2D 绘图执行此操作。(二维图的数据提示

我的问题是这可以扩展到 3D 绘图吗?我的直觉是,我可以从类似于链接中给出的解决方案的曲面图中获得 x、y 坐标,这取决于当前视图可以映射到 3D 坐标点(听起来对吗?)。但是,我无法找到一种方法来做到这一点。

4

1 回答 1

0

好的,我想出了解决方案!可能对某人有用,因此将其发布在这里。它是堆栈溢出的两种解决方案的组合。

解决方案 1 解决方案 2

我使用 ax.format_coord(x,y) 来获取 z 值。使用 ax.format_coord(mouseevent.xdata,mouseevent.ydata) 您可以在字符串 ('x=0.222, y=0.452, z=0.826') 中获取 x、y、z 值,您可以从中提取值。( 2 )

def getz(x,y,ax):
    s = ax.format_coord(x,y)
    out = ""
    for i in range(s.find('z')+2,len(s)-1):
        out = out+s[i]
    return float(out)

class DataCursor(object):
    """A simple data cursor widget that displays the x,y location of a
    matplotlib artist when it is selected."""
    def __init__(self, artists, tolerance=5, offsets=(-20, 20), 
                 template='z: %0.2f', display_all=False):
    """Create the data cursor and connect it to the relevant figure.
    "artists" is the matplotlib artist or sequence of artists that will be 
        selected. 
    "tolerance" is the radius (in points) that the mouse click must be
        within to select the artist.
    "offsets" is a tuple of (x,y) offsets in points from the selected
        point to the displayed annotation box
    "template" is the format string to be used. Note: For compatibility
        with older versions of python, this uses the old-style (%) 
        formatting specification.
    "display_all" controls whether more than one annotation box will
        be shown if there are multiple axes.  Only one will be shown
        per-axis, regardless. 
    """
    self.template = template
    self.offsets = offsets
    self.display_all = display_all
    if not cbook.iterable(artists):
        artists = [artists]
    self.artists = artists
    self.axes = tuple(set(art.axes for art in self.artists))
    self.figures = tuple(set(ax.figure for ax in self.axes))

    self.annotations = {}
    for ax in self.axes:
        self.annotations[ax] = self.annotate(ax)

    for artist in self.artists:
        artist.set_picker(tolerance)
    for fig in self.figures:
        fig.canvas.mpl_connect('pick_event', self)

def annotate(self, ax):
    """Draws and hides the annotation box for the given axis "ax"."""
    annotation = ax.annotate(self.template, xy=(0, 0), ha='right',
            xytext=self.offsets, textcoords='offset points', va='bottom',
            bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
            arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')
            )
    annotation.set_visible(False)
    return annotation

def __call__(self, event):
    """Intended to be called through "mpl_connect"."""
    # Rather than trying to interpolate, just display the clicked coords
    # This will only be called if it's within "tolerance", anyway.
    x, y = event.mouseevent.xdata, event.mouseevent.ydata
    z = getz(x,y,self.axes[0]) # Just a small change here.
    # print str(x) + ' ' + str(y) + ' ' + str(z)
    annotation = self.annotations[event.artist.axes]
    if x is not None:
        if not self.display_all:
            # Hide any other annotation boxes...
            for ann in self.annotations.values():
                ann.set_visible(False)
        # Update the annotation in the current axis..
        annotation.xy = x, y
        annotation.set_text(self.template % (z))
        annotation.set_visible(True)
        event.canvas.draw()


if __name__ == '__main__':
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D

    plt.figure()
    ax = Axes3D(fig)
    X = np.arange(-5, 5, 0.25)
    Y = np.arange(-5, 5, 0.25)
    X, Y = np.meshgrid(X, Y)
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R)
    surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1)
    DataCursor([surf])

    plt.show()

希望这可以帮助。谢谢!

于 2013-11-11T06:48:17.430 回答