在matplotlib中更新箭头3D点

时间:2016-11-16 16:42:57

标签: python-3.x matplotlib graph 3d

我正在尝试使用matplotlib来绘制3D树的图形。我希望用户能够拖动每个节点,以便他们可以以他们选择的任何方式查看树。我的计划是最终进行子类化,以便向节点添加数据并创建树。

我在拖动节点时更新图表时遇到问题。我找到了一种使用以下方法更新散点的位置的方法:

System.out.println("Doorspace is: " + itemd (door, ld, hd, doorspace));

但似乎无法找到更新箭头位置和矢量的方法。

我在检索鼠标位置方面遇到了困难,目前我总是找到它:

self.plot.set_offsets([x,y])  
self.plot.set_3d_properties([z], 'z')  
编辑:感谢ImportanceOfBeingErnest而不是尝试更新图表,我将其删除并制作了新版本。

ecoor == {'elevation': 30.0, 'azimuth': -60.0}

以下是原始代码:

# PlotNode._plot changes
self.plot.remove()
self.plot = axes.scatter([x], [y], [z])

# PlotInterNode._plot changes (both vector updates)
arlen=length(xc - x, yc - y, zc - z)
edge.remove()
edge = axes.quiver(x, y, z, xc - x, yc - y, zc - z, length=arlen,
                   arrow_length_ratio=.5/arlen, pivot='tail')
self.edges.update({childname:(childnode, edge)})

编辑:我需要在3D中检索鼠标位置是

import math
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

class PlotNode(object):
    """
    Class for graph node.
    """

    def __init__(self, name, parent):
        """
        Initializes PlotNode.
        """
        self.__name__ = name
        self.parent = parent
        self.coordinates = self._make_coordinates()
        x, y, z = self.coordinates
        self.plot = axes.scatter([x], [y], [z])
        self.pressed = False
        self.move = False

    def name(self):
        return self.__name__

    COOR = (0, 0, 0)
    def _make_coordinates(self):
        """
        Finds coordinates from a file or, if not exist, calculate new coordinates.
        """
        if PlotNode.COOR[1] == PlotNode.COOR[0]:
            PlotNode.COOR = (PlotNode.COOR[0] + 1, 0, 0)
        elif PlotNode.COOR[2] == PlotNode.COOR[1]:
            PlotNode.COOR = (PlotNode.COOR[0], PlotNode.COOR[1] + 1, 0)
        else:
            PlotNode.COOR = (PlotNode.COOR[0], PlotNode.COOR[1], PlotNode.COOR[2] + 1)
        return (PlotNode.COOR[0], PlotNode.COOR[1], PlotNode.COOR[2])

    def _plot(self):
        """
        Plots node onto graph.
        """
        x, y, z = self.coordinates
        #updates the plot coordinates
        self.plot.set_offsets([x,y])
        self.plot.set_3d_properties([z], 'z')
        #updates the parent
        if self.parent:
            self.parent._plot(self.name())
        self.plot.figure.canvas.draw()

    def press(self, event):
        """
        Mouse press event.
        """
        if event.inaxes != self.plot.axes or not self.plot.contains(event)[0]:
            return False
        self.pressed = True
        axes.disable_mouse_rotation() #Make sure node moves instead of plot rotation
        return True

    def release(self, event):
        """
        Mouse release event.
        """
        if event.inaxes != self.plot.axes or not self.pressed:
            return False
        self.pressed = False
        if self.move:
            self.move = False
            x, y, z = self.coordinates
            ecoor = to_dict(axes.format_coord(event.xdata, event.ydata))
            xe, ye, ze = ecoor.get('x', x), ecoor.get('y', y), ecoor.get('z', z)
            self.coordinates = (xe, ye, ze)
            self._plot()
        else:
            self.open()
        axes.mouse_init() #Make plot rotation avaliable again
        return True

    def motion(self, event):
        """
        Mouse motion event.
        """
        if event.inaxes != self.plot.axes or not self.plot.contains(event)[0]:
            return False
        if not self.pressed:
            return False
        self.move = True
        x, y, z = self.coordinates
        ecoor = to_dict(axes.format_coord(event.xdata, event.ydata))
        xe, ye, ze = ecoor.get('x', x), ecoor.get('y', y), ecoor.get('z', z)
        self.coordinates = (xe, ye, ze)
        self._plot()
        return True

    def open(self):
        print('openned!') #to be changed

class PlotInterNode(PlotNode):
    """
    Class for graph folder node.
    """
    def __init__(self, name, parent=None):
        """
        Initializes PlotDir.
        """
        self.edges = {}
        PlotNode.__init__(self, name, parent)

    def _plot(self, childname=None):
        """
        Plots node onto graph.
        """
        if childname:
            x, y, z = self.coordinates
            childnode, edge = self.edges.get(childname)
            xc, yc, zc = childnode.coordinates
            ##update the vector
            arlen=length(xc - x, yc - y, zc - z)
            ##update the arrow length
        else:
            x, y, z = self.coordinates
            for childname in self.edges:
                _, edge = self.edges.get(childname)
                ##update the position of each edge
            super()._plot()
        self.plot.figure.canvas.draw()

    def traverse(self):
        """
        Generator that traverses the tree rooted at this node.
        """
        yield self
        for child in self.edges:
            try:
                for node in self.edges.get(child)[0].traverse():
                    yield node
            except AttributeError:
                yield self.edges.get(child)[0]


def select_node(root, event):
    """
    Single event function to handle all node movement.
    """
    if event.name == 'button_press_event':
        event_fn = lambda self: PlotNode.press(self, event)
    elif event.name == 'button_release_event':
        event_fn = lambda self: PlotNode.release(self, event)
    elif event.name == 'motion_notify_event':
        event_fn = lambda self: PlotNode.motion(self, event)
    for node in root.traverse():
        if event_fn(node):
            return #if act on node then end

select_ids = []
def connect_select(root):
    """
    Connects select_node to events.
    """
    select_ids.append(figure.canvas.mpl_connect('button_press_event',  lambda event: select_node(root, event)))
    select_ids.append(figure.canvas.mpl_connect('button_release_event', lambda event: select_node(root, event)))
    select_ids.append(figure.canvas.mpl_connect('motion_notify_event', lambda event: select_node(root, event)))

def to_dict(string):
    """
    Converts a string to a dictionary.
    """
    dictionary = {}
    for st in string.split(','):
        st = st.strip().split('=')
        if st[0] not in dictionary:
            try:
                dictionary.update({st[0]:float(st[1])})
            except ValueError:
                st[1] = st[1].split(' ')[0]
                dictionary.update({st[0]:float(st[1])})
    return dictionary

def length(x, y, z):
    """
    Returns hypotenuse.
    """
    ret = math.sqrt(math.pow(x, 2) + math.pow(y, 2) + math.pow(z, 2))
    if not ret:
        ret = 1
    return ret

figure = pyplot.figure()
axes = figure.add_subplot(111, projection='3d')
root = PlotInterNode('root')
def make_children(node, child_type, number):
    x, y, z = node.coordinates
    for i in range(number):
        child = child_type(str(i), node)
        xc, yc, zc = child.coordinates
        arlen = length(xc - x, yc - y, zc - z)
        edge = axes.quiver(x, y, z, xc - x, yc - y, zc - z, length=arlen, arrow_length_ratio=.5/arlen, pivot='tail')
        node.edges.update({child.name():(child, edge)})
def node_depth(node, depth):
    if not depth:
        make_children(node, PlotNode, 3)
    else:
        make_children(node, PlotInterNode, 3)
        for child in node.edges:
            node_depth(node.edges.get(child)[0], depth-1)
node_depth(root, 3)
connect_select(root)
pyplot.show()
在我打电话之前

axes.button_pressed = None

1 个答案:

答案 0 :(得分:1)

感谢ImportanceOfBeingErnest而不是尝试更新绘图,我删除了它并创建了一个新的。

# PlotNode._plot changes
self.plot.remove()
self.plot = axes.scatter([x], [y], [z])

# PlotInterNode._plot changes (both vector updates)
arlen=length(xc - x, yc - y, zc - z)
edge.remove()
edge = axes.quiver(x, y, z, xc - x, yc - y, zc - z, length=arlen,
               arrow_length_ratio=.5/arlen, pivot='tail')
self.edges.update({childname:(childnode, edge)})

第二部分:
所有我需要在3D中检索鼠标位置是添加

axes.button_pressed = None
在我打电话之前

axes.format_coord(event.xdata, event.ydata)