龙空技术网

Python数学编程 第六章 绘制几何图形和分形 第一节

语音刺客 271

前言:

今天看官们对“python运动轨迹”大致比较着重,小伙伴们都需要了解一些“python运动轨迹”的相关文章。那么小编也在网摘上收集了一些关于“python运动轨迹””的相关知识,希望大家能喜欢,小伙伴们快快来学习一下吧!

在这一章,我们首先学习matplotlib中的patches,用它可以绘制各种几何图形,如圆、三角形和多边形。然后,我们将学习matplotlib的动画支持,并比那些一个程序动态演示抛物轨迹。在最后一节中,我们将需学习如何绘制分形图(一种重复应用简单的几何变换得到复杂的几何图形)。开始吧!

6.1 使用matplotlib的patches绘制几何图形

在matplotlib中,patches可以用来绘制几何图形,图形的每一部分都可视为一个块(patches).。例如,为了把一个圆添加到绘图中,你可以指定圆的半径和圆心。这与我们之前使用matplotlib的方式非常不同,之前我们只需要提供点的x坐标和y坐标即可,在使用patches的特点编写程序之前,我们需要先了解一下matplotlib是如何绘图的。考虑以下程序,其使用matplotlib绘制了三个点(1,1),(2,2),(3,3):

 >>> import matplotlib.pyplot as plt >>> x = [1,2,3] >>> y = [1,2,3] >>> plt.plot(x,y) [<matplotlib.lines.Line2D object at 0x000001605C4B7A58>] >>> plt.show()

这个程序创建一个matplotlib窗口,显示一条穿过给定的三个点的直线。在程序中,调用plt.plot()函数会创建一个Figure对象,在该对象中创建了坐标系,最后在坐标系中绘制数据点(见图6-1)。

图6-1 三点直线

下面的程序重新绘制了这个图,只是这次我们明确创建了Figure对象并添加了坐标轴,而不只是调用plot()函数并依赖它创建上述对象:

 >>> import matplotlib.pyplot as plt >>> x = [1,2,3] >>> y = [1,2,3] >>> fig = plt.figure()          # ① >>> ax = plt.axes()             # ② >>> plt.plot(x,y) [<matplotlib.lines.Line2D object at 0x000001605CF712E8>] >>> plt.show()

这里,我们在①处使用figure()函数创建Figure对象(fig),然后在②处使用axes()函数创建坐标轴。axes()函数同时将坐标轴添加到Figure对象中。最后两行代码与之前的程序相同。这一次,当我们调用plot()函数时,它会看到一个带有坐标系的Figure对象已经存在,因此直接根据提供的数据绘制图形。

除了手动创建Figure和Axes对象之外,还可以使用pyplot模块中的两个不同函数(gcf()函数和gca()函数)来获取对当前对象Figure和Axes对象的引用。当调用gcf()函数时,它返回当前Figure对象的引用;当调用gca()函数时,它返回当前Axes对象的引用。这两个该函数都有一个有趣的特性:如果对象不存在,它们将分别别创建相应的对象。我们在本章中使用这些函数时,它们的工作原理将变得更加清晰。

6.1.1 绘制一个圆

为了绘制一个圆,你可以添加一个Circle块到Axes对象中,如下例所示:

 ''' Example of using matplotlib's Circle patch '''  import matplotlib.pyplot as plt  def create_circle():     circle = plt.Circle((0, 0), radius = 0.5)       #  ①     return circle def show_shape(patch):     ax = plt.gca()                                  # ②     ax.add_patch(patch)     plt.axis('scaled')     plt.show()  if __name__ == '__main__':     c= create_circle()                              # ③     show_shape(c)

在这个程序中,我们将Circle块对象的创建和图中的块的添加分成两个函数:create_circle()和show_shape()。在create_circle()函数中,我们创建一个圆心在(0,0)且半径为0.5的圆,方法是在①处创建一Circle对象,将圆心坐标(0,0)作为一个元组传递,并将半径0.5使用同名的关键字(radius)进行传递。create_circle()函数将返回创建的Circle对象。

为了使show_shape()函数可以作用于任何matplotlib块,首先在②处用gca()函数获取当前Axes对象的引用,接着使用add_patch()函数添加传递给它的块,最后调用show()函数显示图形。我们这里使用scaled函数调用axis()函数,这个参数能够告诉matplotlib自动调节数轴的取值范围,我们需要在所有用块自动调节数轴范围的程序中使用这个语句。当然,你也可以指定固定的取值界限,正如第二章看到的那样。比如,axis(xmin=x,xmax=y,ymin=m,ymax=n)。

在③处,我们调用create_circle()函数,并使用标签c指代其返回的Circle对象。然后,调用show_shape()函数,将c作为参数输入。当运行程序时,你将看到一个matplotlib窗口显示的圆。

正如你看到的,这个圆看上去并不圆。这是由于自动宽高比决定了x轴和y轴的长度比例。如果在②后插入语句ax.set_aspect('equal'),你会发现这个圆将变得更加符合实际。set_aspect()函数用来设置图形的宽高比。使用equals参数可以使matplotlib将x轴和y轴的长度比设置为1:1。

可以使用ec和fc关键字参数修改块中的边缘颜色和表面颜色(填充颜色)。例如fc='g'和ec = 'r'将创建一个边缘颜色为红色和内部填充为绿色的圆。

圆心在(0,0)半径为0.5的圆

matplotlib支持其他各种块,如椭圆(Ellipse)、多边形(POlygon)和矩形(Rectangle)。

6.1.2 创建动画图形

有时我们可能想要创建具有动态形状的图形,matplotlib的动画支持可以帮我们做到这一点。后面我们将创建一个电话版本的抛物轨迹绘制程序。

首先来看一个简单的例子,我们将绘制一个matplotlib图形,该图形从一个小的圆开始,无限的增长到某个半径(除非matplotlib窗口关闭)。

 ''' A growing circle '''  from matplotlib import pyplot as plt from matplotlib import animation  def create_circle():     circle = plt.Circle((0,0), radius=0.05)     return circle  def update_radius(i, circle):     circle.radius = i*0.5     return circle  def create_animation():     fig = plt.gcf()                             # ①     ax = plt.axes(xlim=(-10,10),ylim=(-10,10))     ax.set_aspect('equal')     circle = create_circle()     ax.add_patch(circle)                        # ②     anim = animation.FuncAnimation(             # ③         fig, update_radius, fargs = (circle,), frames=30, interval=50)     plt.title('Simple Circle Animation')     plt.show()  if __name__ == '__main__':     create_animation()

首先我们从matplotlib包中导入animation模块。create_animation()函数在这里执行了核心功能,在①处它使用gcf()函数获得当前Figure对象的引用,然后创建一个坐标系(x轴和y轴的区间均为-10到10)。在这之后,创建一个半径为0.05、圆心在(0,0)处的Circle对象,并在②处将这个对象添加到坐标系中。然后,在③处我们创建了FuncAnimation对象,它传递了将要创建的动画的数据。

(1)fig。它是当前Figure对象。

(2)update_radius。这个函数负责绘制每一帧。它需要两个参数——一个在调用时传递给它的帧编号,以及更新每一帧时的块对象。这个函数也返回一个块对象。

(3)fargs。这个元组包含了所有(除了帧编号)要传递给update_radius()函数的参数。如果没有要传递的此类参数,则不需要指定该关键字。

(4)frames。frames指动画中的帧数,也是update_radius()函数被调用的次数。这里我们任意取了30帧。

(5)interval。interval值两帧之间的时间间隔(毫秒)。如果共话显示速度太慢,则减小这个值,否则增大这个值。

接下来我们使用title()函数设置标题,最后用show()显示图形。

如前所述,update_radius()函数负责更新在每一帧都会发生变化的圆的属性。这里我们设置半径为i*0.5,其中i是帧编号。你将会看到每一帧逐渐增大的圆(共30帧),一次最大的圆的半径为15。因为坐标轴设置在-10到10之间,这就出现了圆形超出图形范围之外的效果。运行程序时,你将看到第一个动画图形。如下所示。

你会注意到动画会一直持续,直到关闭matplotlib窗口,这是默认情况,你可以在创建FuncAnimation对象时通过设置关键字参数repeat=False来改变此行为。

FunAnimation对象和持久化

你可能会注意到在圆的动画的程序中,我们用标签anim来指代创建的FuncAnimation对象,即使我们不再其他地方使用它。这是因为matplotlib当前的行为存在问题,他不存储任何对FuncAnimation对象额的引用,使该对象容易受到Python中垃圾回收机制的影响,这意味着动画将不会被创建。我们可以通过创建一个标签指代这个对象来阻止这一问题的发生。

动画圆

6.1.3 抛物运动动画演示

在第二章中,我们绘制了一个球在抛物运动中的轨迹。这里,基于这个图象,使用matplotlib动画来支持制作动画轨迹,从而使得这一演示更接近于实际生活中看到的球的运动情况。

 ''' Animation thr trajectory of an object in projectile mmotion '''  from matplotlib import pyplot as plt from matplotlib import animation import math g = 9.8  def get_intervals(u, theta):     t_flight = 2*u*math.sin(theta)/g     intervals = []     starts = 0     interval = 0.005     while starts < t_flight:         intervals.append(starts)         starts += interval     return intervals  def update_position(i, circle, intervals, u, theta):     t = intervals[i]     x = u*math.cos(theta)*t     y = u*math.sin(theta)*t - 0.5*g*t**2     circle.center = x, y     return circle def create_animation(u, theta):     intervals = get_intervals(u, theta)      xmin = 0     xmax = u*math.cos(theta)*intervals[-1]     ymin = 0     tmax = u*math.sin(theta)/g     ymax = u*math.sin(theta)*tmax - 0.5*g*tmax**2       # ①      fig = plt.gcf()     ax = plt.axes(xlim=(xmin,xmax), ylim=(ymin,ymax))      circle = plt.Circle((xmin, ymin), 1.0)     ax.add_patch(circle)     anim = animation.FuncAnimation(         fig, update_position, fargs=(circle, intervals, u, theta),         frames=len(intervals), interval=1, repeat=False)     plt.title('Projectile Motion')     plt.xlabel('X')     plt.ylabel('Y')     plt.show()  if __name__ == '__main__':     try:         u = float(input('Enter the initial velocity (m/s): '))         theta  = float(input('Enter the angle of projection (degree): '))     except ValueError:         print('You entered an invalid input')     else:         thete =math.radians(theta)         create_animation(u, theta)

create_animation()函数有两个参数:u和theta,这两个参数对应于初始速度和抛射角度,他们是程序的初始输入。get_intervals()函数用来获取世纪那间隔,据此可以计算抛物运动的x和y坐标,这个函数是通过使用在第二章中用到的相同逻辑来实现的,那时我们单独定义了一个frange()函数。

为了给动画设置坐标界限,我们需要找到x和y的最大值和最小值。最小值都为0,及每一个坐标的初始值。x的最大值为球飞行结束时的坐标值,即列表intervals的最后一个时间间隔。y坐标的最大值为球飞行处于最高点的值,即在①处,我们可以使用如下公式进行计算:

得到这些值后,我们在②处创建坐标系,将合适的坐标界限作为参数输入。接下来的两个语句中,我们在(xmin,ymin)处创建一个半径为1.0的原来表示求(xmin, ymin分别表示x轴和y轴的最小坐标值),并将其添加到图形的Axes对象中。

接下来在③处创建FuncAnimation对象,以当前的图形对象和以下参数作为输入。

(1)update_position。这个函数将更改每一帧中圆的圆心。这里的想法是为每个时间间隔创建一个新的帧,因此我们将帧的数量设置为时间间隔的大小。我们在低i个时间间隔计算球在这一瞬间的x和y坐标,并将圆心设置为这些值。

(2)fargs。update_position()函数需要使用的时间间隔列表、间隔、初始速度和theta,他们都需要通过此关键字参数指定。

(3)frames。因为我们将在每一个时间间隔绘制一帧,所以将帧的数目设置为intervals列表的大小。

(4)repeat。如同我们在第一个动画例子中所讨论的,默认情况下动画将无限重复。这们并不希望看到这一点,所以将这个关键字设置为False。

运行程序,它会询问初始输入然后创建动画。

初始时刻球的位置

过程时刻球的位置

过程时刻球的位置

最终时刻球的位置

标签: #python运动轨迹