首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >2交互式多边形编辑器,matplotlib不起作用

2交互式多边形编辑器,matplotlib不起作用
EN

Stack Overflow用户
提问于 2021-02-21 18:38:32
回答 1查看 80关注 0票数 0

在教程Poly Editor之后,我创建了2个多边形,添加到图中,然后实例化了2个交互式多边形。我希望当我调整单独的多边形时,它会单独工作。然而,它们是相互冲突的。

然后,我尝试了在原始代码中进行一些更改,但仍然没有产生成功的结果。如果你熟悉的话,我希望你能帮我。我真的很感谢你提前告诉我。

下面是我调整后的代码:

代码语言:javascript
复制
"""
===========
Poly Editor
===========

This is an example to show how to build cross-GUI applications using
Matplotlib event handling to interact with objects on the canvas.
"""
import numpy as np
from matplotlib.lines import Line2D
from matplotlib.artist import Artist


def dist(x, y):
"""
Return the distance between two points.
"""
d = x - y
return np.sqrt(np.dot(d, d))


def dist_point_to_segment(p, s0, s1):
"""
Get the distance of a point to a segment.
  *p*, *s0*, *s1* are *xy* sequences
This algorithm from
http://geomalgorithms.com/a02-_lines.html
"""
v = s1 - s0
w = p - s0
c1 = np.dot(w, v)
if c1 <= 0:
    return dist(p, s0)
c2 = np.dot(v, v)
if c2 <= c1:
    return dist(p, s1)
b = c1 / c2
pb = s0 + b * v
return dist(p, pb)


class PolygonInteractor:
"""
A polygon editor.

Key-bindings

  't' toggle vertex markers on and off.  When vertex markers are on,
      you can move them, delete them

  'd' delete the vertex under point

  'i' insert a vertex at point.  You must be within epsilon of the
      line connecting two existing vertices

"""

showverts = True
epsilon = 5  # max pixel distance to count as a vertex hit

def __init__(self, ax, poly):
  if poly.figure is None:
      raise RuntimeError('You must first add the polygon to a figure '
                         'or canvas before defining the interactor')
  self.ax = ax
  self.poly = poly

  x, y = zip(*self.poly.xy)
  self.line = Line2D(x, y,
                     marker='o', markerfacecolor='r',
                     animated=True)
  self.ax.add_line(self.line)
  self._ind = None  # the active vert

def connect(self):
  'connect to all the events we need'
  self.cid         = self.poly.add_callback(self.poly_changed)
  self.ciddraw     = self.poly.figure.canvas.mpl_connect('draw_event', self.on_draw)
  self.cidpress    = self.poly.figure.canvas.mpl_connect('button_press_event', self.on_button_press)
  self.cidrelease  = self.poly.figure.canvas.mpl_connect('button_release_event', self.on_button_release)
  self.cidkeypress = self.poly.figure.canvas.mpl_connect('key_press_event', self.on_key_press)
  self.cidmotion   = self.poly.figure.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)

def on_draw(self, event):
  self.background = self.poly.figure.canvas.copy_from_bbox(self.poly.figure.bbox)
  self.ax.draw_artist(self.poly)
  self.ax.draw_artist(self.line)
  # do not need to blit here, this will fire before the screen is
  # updated

def poly_changed(self, poly):
  """This method is called whenever the pathpatch object is called."""
  # only copy the artist props to the line (except visibility)
  vis = self.line.get_visible()
  Artist.update_from(self.line, poly)
  self.line.set_visible(vis)  # don't use the poly visibility state

def get_ind_under_point(self, event):
  """
  Return the index of the point closest to the event position or *None*
  if no point is within ``self.epsilon`` to the event position.
  """
  # display coords
  xy = np.asarray(self.poly.xy)
  xyt = self.poly.get_transform().transform(xy)
  xt, yt = xyt[:, 0], xyt[:, 1]
  d = np.hypot(xt - event.x, yt - event.y)
  indseq, = np.nonzero(d == d.min())
  ind = indseq[0]

  if d[ind] >= self.epsilon:
      ind = None

  return ind

def on_button_press(self, event):
  """Callback for mouse button presses."""
  if not self.showverts:
      return
  if event.inaxes is None:
      return
  if event.button != 1:
      return
  
  self._ind = self.get_ind_under_point(event)

def on_button_release(self, event):
  """Callback for mouse button releases."""
  if not self.showverts:
      return
  if event.button != 1:
      return
  
  self._ind = None

def on_key_press(self, event):
  """Callback for key presses."""
  if not event.inaxes:
      return
  if event.key == 't':
      self.showverts = not self.showverts
      self.line.set_visible(self.showverts)
      if not self.showverts:
          self._ind = None
  elif event.key == 'd':
      ind = self.get_ind_under_point(event)
      if ind is not None:
          self.poly.xy = np.delete(self.poly.xy,
                                   ind, axis=0)
          self.line.set_data(zip(*self.poly.xy))
  elif event.key == 'i':
      xys = self.poly.get_transform().transform(self.poly.xy)
      p = event.x, event.y  # display coords
      for i in range(len(xys) - 1):
          s0 = xys[i]
          s1 = xys[i + 1]
          d = dist_point_to_segment(p, s0, s1)
          if d <= self.epsilon:
              self.poly.xy = np.insert(
                  self.poly.xy, i+1,
                  [event.xdata, event.ydata],
                  axis=0)
              self.line.set_data(zip(*self.poly.xy))
              break
  if self.line.stale:
      self.poly.figure.canvas.draw_idle()

def on_mouse_move(self, event):
  """Callback for mouse movements."""
  if not self.showverts:
      return
  if self._ind is None:
      return
  if event.inaxes is None:
      return
  if event.button != 1:
      return
  
  x, y = event.xdata, event.ydata

  self.poly.xy[self._ind] = x, y
  if self._ind == 0:
      self.poly.xy[-1] = x, y
  elif self._ind == len(self.poly.xy) - 1:
      self.poly.xy[0] = x, y
  self.line.set_data(zip(*self.poly.xy))

  self.poly.figure.canvas.restore_region(self.background)
  self.poly.figure.draw_artist(self.poly)
  self.poly.figure.draw_artist(self.line)
  self.poly.figure.canvas.blit(self.poly.figure.bbox)

def disconnect(self):
  'disconnect all the stored connection ids'
  self.poly.figure.canvas.mpl_disconnect(self.ciddraw)
  self.poly.figure.canvas.mpl_disconnect(self.cidpress)
  self.poly.figure.canvas.mpl_disconnect(self.cidrelease)
  self.poly.figure.canvas.mpl_disconnect(self.cidmotion)
  self.poly.figure.canvas.mpl_disconnect(self.cidkeypress)

if __name__ == '__main__':
  import matplotlib.pyplot as plt
  from matplotlib.patches import Polygon
  from matplotlib.collections import PatchCollection
  import matplotlib


# Fixing random state for reproducibility
np.random.seed(19680801)

fig, ax = plt.subplots()

N, LP = (4, 3)
poly_4_pts = Polygon(np.random.rand(N, 2), True, fill=None, animated=True)
poly_2_pts = Polygon(np.random.rand(LP, 2), True, fill=None, animated=True)

ax.add_patch(poly_4_pts)
fig.patches.extend([poly_2_pts])
ax.add_patch(poly_2_pts)

poly2 = PolygonInteractor(ax, poly_2_pts)
poly2.connect()

poly1 = PolygonInteractor(ax, poly_4_pts)
poly1.connect()

ax.set_title('Click and drag a point to move it')
ax.set_xlim((-2, 2))
ax.set_ylim((-2, 2))

plt.show()
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-28 00:42:52

我认为问题是你在恢复背景时没有恢复动画艺术家。请参见有关对here进行blitting的教程。我可以添加一些修改来恢复添加到图形中的所有动画对象,因为轮廓线也是动画的,所以你应该在main中声明它们,而不是在类中,然后恢复类中的所有动画。下面是我的代码和结果:

代码语言:javascript
复制
"""
===========
Poly Editor
===========

This is an example to show how to build cross-GUI applications using
Matplotlib event handling to interact with objects on the canvas.
"""
import numpy as np
from matplotlib.lines import Line2D
from matplotlib.artist import Artist


def dist(x, y):
    """
    Return the distance between two points.
    """
    d = x - y
    return np.sqrt(np.dot(d, d))


def dist_point_to_segment(p, s0, s1):
    """
    Get the distance of a point to a segment.
      *p*, *s0*, *s1* are *xy* sequences
    This algorithm from
    http://geomalgorithms.com/a02-_lines.html
    """
    v = s1 - s0
    w = p - s0
    c1 = np.dot(w, v)
    if c1 <= 0:
        return dist(p, s0)
    c2 = np.dot(v, v)
    if c2 <= c1:
        return dist(p, s1)
    b = c1 / c2
    pb = s0 + b * v
    return dist(p, pb)


class PolygonInteractor:
    """
    A polygon editor.

    Key-bindings

      't' toggle vertex markers on and off.  When vertex markers are on,
          you can move them, delete them

      'd' delete the vertex under point

      'i' insert a vertex at point.  You must be within epsilon of the
          line connecting two existing vertices

    """

    showverts = True
    epsilon = 5  # max pixel distance to count as a vertex hit

    def __init__(self, ax, poly, contour, animated_artists=()):
        if poly.figure is None:
            raise RuntimeError('You must first add the polygon to a figure '
                               'or canvas before defining the interactor')
        self.ax = ax
        canvas = poly.figure.canvas
        self.poly = poly

        self.line = contour

        self._ind = None  # the active vert

        canvas.mpl_connect('draw_event', self.on_draw)
        canvas.mpl_connect('button_press_event', self.on_button_press)
        canvas.mpl_connect('key_press_event', self.on_key_press)
        canvas.mpl_connect('button_release_event', self.on_button_release)
        canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
        self.canvas = canvas

        self._artists = []
        for a in animated_artists:
            self.add_artist(a)
        #self.add_artist(self.line)

    def add_artist(self, art):
        if art.figure != self.canvas.figure:
            raise RuntimeError
        art.set_animated(True)
        self._artists.append(art)

    def on_draw(self, event):
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)
        self.ax.draw_artist(self.poly)
        self.ax.draw_artist(self.line)
        self._draw_animated()
        self.canvas.blit(self.ax.bbox)
        # do not need to blit here, this will fire before the screen is
        # updated

    def _draw_animated(self):
        """Draw all of the animated artists."""
        fig = self.canvas.figure
        for a in self._artists:
            fig.draw_artist(a)

    def get_ind_under_point(self, event):
        """
        Return the index of the point closest to the event position or *None*
        if no point is within ``self.epsilon`` to the event position.
        """
        # display coords
        xy = np.asarray(self.poly.xy)
        xyt = self.poly.get_transform().transform(xy)
        xt, yt = xyt[:, 0], xyt[:, 1]
        d = np.hypot(xt - event.x, yt - event.y)
        indseq, = np.nonzero(d == d.min())
        ind = indseq[0]

        if d[ind] >= self.epsilon:
            ind = None

        return ind

    def on_button_press(self, event):
        """Callback for mouse button presses."""
        if not self.showverts:
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return
        self._ind = self.get_ind_under_point(event)

    def on_button_release(self, event):
        """Callback for mouse button releases."""
        if not self.showverts:
            return
        if event.button != 1:
            return
        self._ind = None

    def on_key_press(self, event):
        """Callback for key presses."""
        if not event.inaxes:
            return
        if event.key == 't':
            self.showverts = not self.showverts
            self.line.set_visible(self.showverts)
            if not self.showverts:
                self._ind = None
        elif event.key == 'd':
            ind = self.get_ind_under_point(event)
            if ind is not None:
                self.poly.xy = np.delete(self.poly.xy,
                                         ind, axis=0)
                self.line.set_data(zip(*self.poly.xy))
        elif event.key == 'i':
            xys = self.poly.get_transform().transform(self.poly.xy)
            p = event.x, event.y  # display coords
            for i in range(len(xys) - 1):
                s0 = xys[i]
                s1 = xys[i + 1]
                d = dist_point_to_segment(p, s0, s1)
                if d <= self.epsilon:
                    self.poly.xy = np.insert(
                        self.poly.xy, i+1,
                        [event.xdata, event.ydata],
                        axis=0)
                    self.line.set_data(zip(*self.poly.xy))
                    break
        if self.line.stale:
            self.canvas.draw_idle()

    def on_mouse_move(self, event):
        """Callback for mouse movements."""
        if not self.showverts:
            return
        if self._ind is None:
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return
        x, y = event.xdata, event.ydata

        self.poly.xy[self._ind] = x, y
        if self._ind == 0:
            self.poly.xy[-1] = x, y
        elif self._ind == len(self.poly.xy) - 1:
            self.poly.xy[0] = x, y
        self.line.set_data(zip(*self.poly.xy))

        self.canvas.restore_region(self.background)
        self._draw_animated()
        self.canvas.draw_idle()
        # self.ax.draw_artist(self.poly)
        # self.ax.draw_artist(self.line)
        self.canvas.blit(self.ax.bbox)


if __name__ == '__main__':
    import matplotlib.pyplot as plt
    from matplotlib.patches import Polygon

    theta = np.arange(0, 2*np.pi, 0.1)
    r = 1.5

    xs = r * np.cos(theta)
    ys = r * np.sin(theta)

    poly = Polygon(np.column_stack([xs, ys]), animated=True)
    xc1, yc1 = zip(*poly.xy)
    poly_contour = Line2D(xc1, yc1, marker='o', markerfacecolor='r',
                          animated=True)

    poly2 = Polygon(np.column_stack([xs+3, ys+3]), animated=True)
    xc2, yc2 = zip(*poly2.xy)
    poly_contour2 = Line2D(xc2, yc2, marker='o', markerfacecolor='r',
                          animated=True)

    fig, ax = plt.subplots()
    ax.add_patch(poly)
    ax.add_line(poly_contour)
    ax.add_patch(poly2)
    ax.add_line(poly_contour2)
    p = PolygonInteractor(ax, poly, poly_contour, [poly, poly2, poly_contour, poly_contour2])
    p2 = PolygonInteractor(ax, poly2, poly_contour2, [poly, poly2, poly_contour, poly_contour2])

    ax.set_title('Click and drag a point to move it')
    ax.set_xlim((-2, 6))
    ax.set_ylim((-2, 6))
    plt.show()

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66301388

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档