如何通过鼠标单击或滚轮拖放,旋转任何形状?

时间:2019-09-10 11:57:34

标签: python matplotlib

通过发布以前的question,我学会了如何拖放无任何抱怨的形状。现在,我试图添加一个使用滚轮旋转此形状的函数。

到目前为止,该程序执行了应有的操作,但不幸的是出现了无法忍受的错误。两个主要问题是:

1。平移后不想要的旋转形状重置:

通过拖放已经旋转的形状,形状将旋转回零度角。如果我再次使用滚轮,则形状会跳回到应该的位置。

我的建议是换行:

    xdx = [i+dx for i,_ in self.geometry]
    ydy = [i+dy for _,i in self.geometry]

收件人:

    xdx = [i+dx for i,_ in self.newGeometry]
    ydy = [i+dy for _,i in self.newGeometry]

希望它不会绘制初始形状。不幸的是,它导致了一种“二次”翻译。

2。形状不会绕其本地原点旋转:

该行:

   x0, y0 = self.newGeometry[0]

定义形状的本地原点,该原点应该是旋转中心。因此,不允许在任何旋转过程中更改此坐标。通过比较旋转前后的坐标,我发现偏差在不断增加。

完整的MWE包含以下六个功能:

import matplotlib.pyplot as plt
import numpy as np

class DraggablePolygon:
    lock = None
    def __init__(self):
        print('__init__')
        self.press = None
        self.angle = 0

        fig = plt.figure()
        ax = fig.add_subplot(111)

        self.geometry = [[0.0,0.0],[0.1,0.05],[0.2,0.15],[0.3,0.20],[0.4,0.25],[0.5,0.30],
                [0.6,0.25],[0.7,0.15],[0.8,0.05],[0.9,0.025],[1.0,0.0]]

        self.newGeometry = [[0.0,0.0]]

        poly = plt.Polygon(self.geometry, closed=True, fill=False, linewidth=3, color='#F97306')
        ax.add_patch(poly)
        self.poly = poly

    def connect(self):
        'connect to all the events we need'
        print('connect')
        self.cidpress = self.poly.figure.canvas.mpl_connect(
            'button_press_event', self.on_press)
        self.cidrelease = self.poly.figure.canvas.mpl_connect(
            'button_release_event', self.on_release)
        self.cidmotion = self.poly.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_motion)
        self.cidscroll = self.poly.figure.canvas.mpl_connect(
                'scroll_event', self.rotate_on_scroll)


    def on_press(self, event):
        'on button press we will see if the mouse is over us and store some data'
        print('on_press')
        if event.inaxes != self.poly.axes: return
        if DraggablePolygon.lock is not None: return
        contains, attrd = self.poly.contains(event)
        if not contains: return

        x0, y0 = self.newGeometry[0]
        print('x0 = '+str(x0)+', '+'y0 = '+str(y0)+' -> on_press')
        self.press = x0, y0, event.xdata, event.ydata
        DraggablePolygon.lock = self

    def on_motion(self, event):
        'on motion we will move the rect if the mouse is over us'
        if DraggablePolygon.lock is not self:
            return
        if event.inaxes != self.poly.axes: return
        x0, y0, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = event.ydata - ypress

        xdx = [i+dx for i,_ in self.geometry]
        ydy = [i+dy for _,i in self.geometry]

        self.newGeometry = [[a, b] for a, b in zip(xdx, ydy)]
        self.poly.set_xy(self.newGeometry)
        self.poly.figure.canvas.draw()

    def on_release(self, event):
        'on release we reset the press data'
        print('on_release')
        if DraggablePolygon.lock is not self:
            return

        self.press = None
        DraggablePolygon.lock = None
        self.geometry = self.newGeometry
        print('x0 = '+str(self.geometry[0][0])+', '+'y0 = '+str(self.geometry[0][1])+' -> on_release')
        self.poly.set_xy(self.geometry)
        self.poly.figure.canvas.draw()

    def rotate_on_scroll(self, event, degree=1):
        if event.button == 'up':
            self.angle += degree*(np.pi/180)
        elif event.button == 'down':
            self.angle -= degree*(np.pi/180)

        x0, y0 = self.newGeometry[0]
        print('x0 = '+str(x0)+', '+'y0 = '+str(y0)+' -> pre_scroll')

        qx = []
        qy = []
        for i in range(len(self.geometry)):
            qx.append(x0 + np.cos(self.angle) * (self.geometry[i][0] - x0)
        - np.sin(self.angle) * (self.geometry[i][1] - y0))
            qy.append(y0 + np.sin(self.angle) * (self.geometry[i][0] - y0)
        + np.cos(self.angle) * (self.geometry[i][1] - y0))
        self.newGeometry = np.column_stack((qx, qy))

        print('x0 = '+str(self.newGeometry[0][0])+', '+'y0 = '+str(self.newGeometry[0][1])+' -> after_scroll')
        print('delta_x = '+str(x0-self.newGeometry[0][0])+', '+'delta_y = '+str(y0-self.newGeometry[0][1])+' -> deviation during scroll')
        self.poly.set_xy(self.newGeometry)
        self.poly.figure.canvas.draw()

dp = DraggablePolygon()
dp.connect()

plt.show()

我希望形状旋转时不会出现任何“跳跃”,并且会围绕其原点。感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

看起来像问题在于newGeometrygeometry的角色在这里没有明确定义。不过,以下方法应该可以工作。

import matplotlib.pyplot as plt
import numpy as np

class DraggablePolygon:
    lock = None
    def __init__(self):
        print('__init__')
        self.press = None
        self.angle = 0

        fig = plt.figure()
        ax = fig.add_subplot(111)

        self.geometry = [[0.0,0.0],[0.1,0.05],[0.2,0.15],[0.3,0.20],[0.4,0.25],[0.5,0.30],
                [0.6,0.25],[0.7,0.15],[0.8,0.05],[0.9,0.025],[1.0,0.0]]

        self.newGeometry = self.geometry

        poly = plt.Polygon(self.geometry, closed=True, fill=False, linewidth=3, color='#F97306')
        ax.add_patch(poly)
        self.poly = poly

    def connect(self):
        'connect to all the events we need'
        print('connect')
        self.cidpress = self.poly.figure.canvas.mpl_connect(
            'button_press_event', self.on_press)
        self.cidrelease = self.poly.figure.canvas.mpl_connect(
            'button_release_event', self.on_release)
        self.cidmotion = self.poly.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_motion)
        self.cidscroll = self.poly.figure.canvas.mpl_connect(
                'scroll_event', self.rotate_on_scroll)


    def on_press(self, event):
        'on button press we will see if the mouse is over us and store some data'
        print('on_press')
        if event.inaxes != self.poly.axes: return
        if DraggablePolygon.lock is not None: return
        contains, attrd = self.poly.contains(event)
        if not contains: return
        self.geometry = self.newGeometry
        x0, y0 = self.newGeometry[0]
        print('x0 = '+str(x0)+', '+'y0 = '+str(y0)+' -> on_press')
        self.press = x0, y0, event.xdata, event.ydata
        DraggablePolygon.lock = self

    def on_motion(self, event):
        'on motion we will move the rect if the mouse is over us'
        if DraggablePolygon.lock is not self:
            return
        if event.inaxes != self.poly.axes: return
        x0, y0, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = event.ydata - ypress

        xdx = [i+dx for i,_ in self.geometry]
        ydy = [i+dy for _,i in self.geometry]

        self.newGeometry = [[a, b] for a, b in zip(xdx, ydy)]
        self.poly.set_xy(self.newGeometry)
        self.poly.figure.canvas.draw()

    def on_release(self, event):
        'on release we reset the press data'
        print('on_release')
        if DraggablePolygon.lock is not self:
            return

        self.press = None
        DraggablePolygon.lock = None
        self.geometry = self.newGeometry
        print('x0 = '+str(self.geometry[0][0])+', '+'y0 = '+str(self.geometry[0][1])+' -> on_release')
        self.poly.set_xy(self.geometry)
        self.poly.figure.canvas.draw()

    def rotate_on_scroll(self, event, degree=1):
        if event.button == 'up':
            self.angle += degree*(np.pi/180)
        elif event.button == 'down':
            self.angle -= degree*(np.pi/180)

        x0, y0 = self.newGeometry[0]
        print('x0 = '+str(x0)+', '+'y0 = '+str(y0)+' -> pre_scroll')

        qx = []
        qy = []
        for i in range(len(self.geometry)):
            qx.append(x0 + np.cos(self.angle) * (self.newGeometry[i][0] - x0)
                        - np.sin(self.angle) * (self.newGeometry[i][1] - y0))
            qy.append(y0 + np.sin(self.angle) * (self.newGeometry[i][0] - x0)
                        + np.cos(self.angle) * (self.newGeometry[i][1] - y0))
        self.newGeometry = np.column_stack((qx, qy))

        print('x0 = '+str(self.newGeometry[0][0])+', '+'y0 = '+str(self.newGeometry[0][1])+' -> after_scroll')
        print('delta_x = '+str(x0-self.newGeometry[0][0])+', '+'delta_y = '+str(y0-self.newGeometry[0][1])+' -> deviation during scroll')
        self.poly.set_xy(self.newGeometry)
        self.poly.figure.canvas.draw_idle()
        self.angle = 0

dp = DraggablePolygon()
dp.connect()

plt.show()