­

原 matplotlib动画入门(3):弹球

  • 2019 年 10 月 6 日
  • 筆記

介绍

在平面上画一个方框代表墙壁,框内有一个运动的弹球,当弹球碰到墙壁时就弹回去,小球不停的运动。

代码

新建一个文件particle.py,增加如下代码:

先引入相应的包

import numpy as np  import matplotlib.pyplot as plt  import matplotlib.animation as animation

新建一个ParticleBox类

class ParticleBox:      '''      初始化方法      init_state是一个数组:[x,y,vx,vy],四个值分别表示x坐标,y坐标,x方向的速度,y方向的速度      bounds 是箱子的边界: [xmin, xmax, ymin, ymax]      size: 球的半径      '''      def __init__(self,              init_state = [1,0,0.5,1],              bounds = [-2, 2, -2, 2],              size = 0.04):          self.init_state = np.asarray(init_state, dtype=float)          self.size = size          self.state = self.init_state.copy()          self.bounds = bounds        '''      每一帧动画调用一次step函数,画出小球的位置。      dt是每一帧代表的时间,其乘以速度就是运动的距离      '''      def step(self, dt):            '''           x = x + dt * vx           y = y + dt * vy          '''          self.state[:2] += dt * self.state[2:]            '''          如果弹球碰到墙,就弹回来          '''          crossed_x1 = (self.state[0] < self.bounds[0] + self.size)          crossed_x2 = (self.state[0] > self.bounds[1] - self.size)          crossed_y1 = (self.state[1] < self.bounds[2] + self.size)          crossed_y2 = (self.state[1] > self.bounds[3] - self.size)            if crossed_x1:              self.state[0] = self.bounds[0] + self.size          if crossed_x2:              self.state[0] = self.bounds[1] - self.size          if crossed_y1:              self.state[1] = self.bounds[2] + self.size          if crossed_y2:              self.state[1] > self.bounds[3] - self.size          #反方向运动          if crossed_x1 | crossed_x2 :              self.state[2] *= -1          if crossed_y1 | crossed_y2 :              self.state[3] *= -1   

每一帧动画调用一次animate函数,i表示帧号。

def animate(i):      global box, rect      box.step(dt)      rect.set_edgecolor('k')      particles.set_data(box.state[0], box.state[1])      particles.set_markersize(4)      return particles, rect

生成边框、小球以及让它动起来

#随机取四个数作为起始位置和速度  init_state = np.random.random(4)  box = ParticleBox(init_state, size=0.04)  #每帧代表0.1秒  dt = 0.1    #画图  fig = plt.figure()  fig.subplots_adjust(left=0, right=1, bottom=0, top=1)  ax = fig.add_subplot(111, aspect='equal', autoscale_on=False,                       xlim=(-3.2, 3.2), ylim=(-2.4, 2.4))    particles, = ax.plot([], [], 'bo', ms=4)  rect = plt.Rectangle(box.bounds[::2],                       box.bounds[1] - box.bounds[0],                       box.bounds[3] - box.bounds[2],                       ec='none', lw=2, fc='none')  ax.add_patch(rect)    ani = animation.FuncAnimation(fig, animate, frames=600,                                interval=1, blit=False)    plt.show()

完整代码为:

import numpy as np  import matplotlib.pyplot as plt  import matplotlib.animation as animation    class ParticleBox:      '''      init_state is an array:[x,y,vx,vy]      bounds 箱子的边界: [xmin, xmax, ymin, ymax]      size: 球的半径      '''      def __init__(self,              init_state = [1,0,0.5,1],              bounds = [-2, 2, -2, 2],              size = 0.04):          self.init_state = np.asarray(init_state, dtype=float)          self.size = size          self.state = self.init_state.copy()          self.bounds = bounds        '''      每一帧动画调用一次step函数,画出小球的位置。      dt是每一帧代表的时间,其乘以速度就是运动的距离      '''      def step(self, dt):            '''           x = x + dt * vx           y = y + dt * vy          '''          self.state[:2] += dt * self.state[2:]            '''          如果碰到墙,就弹回来          '''          crossed_x1 = (self.state[0] < self.bounds[0] + self.size)          crossed_x2 = (self.state[0] > self.bounds[1] - self.size)          crossed_y1 = (self.state[1] < self.bounds[2] + self.size)          crossed_y2 = (self.state[1] > self.bounds[3] - self.size)            if crossed_x1:              self.state[0] = self.bounds[0] + self.size          if crossed_x2:              self.state[0] = self.bounds[1] - self.size          if crossed_y1:              self.state[1] = self.bounds[2] + self.size          if crossed_y2:              self.state[1] > self.bounds[3] - self.size            if crossed_x1 | crossed_x2 :              self.state[2] *= -1          if crossed_y1 | crossed_y2 :              self.state[3] *= -1        def animate(i):      global box, rect      box.step(dt)      rect.set_edgecolor('k')      particles.set_data(box.state[0], box.state[1])      particles.set_markersize(4)      return particles, rect    #随机取四个数作为起始位置和速度  init_state = np.random.random(4)  box = ParticleBox(init_state, size=0.04)  #每帧代表0.1秒  dt = 0.1    #画图  fig = plt.figure()  fig.subplots_adjust(left=0, right=1, bottom=0, top=1)  ax = fig.add_subplot(111, aspect='equal', autoscale_on=False,                       xlim=(-3.2, 3.2), ylim=(-2.4, 2.4))    particles, = ax.plot([], [], 'bo', ms=4)  rect = plt.Rectangle(box.bounds[::2],                       box.bounds[1] - box.bounds[0],                       box.bounds[3] - box.bounds[2],                       ec='none', lw=2, fc='none')  ax.add_patch(rect)    ani = animation.FuncAnimation(fig, animate, frames=600,                                interval=1, blit=False)    plt.show()

执行命令

python3 particle.py