Вычислительная практика II, ММФ, БГУ
Лаврова О.А., апрель 2020
import numpy as np
import math as mth
import matplotlib.pyplot as plt
%matplotlib nbagg
Для построения анимации будем использовать модуль animation библиотеки matplotlib
import matplotlib.animation as anim
Непосредственно анимацию построим с помощью функции FuncAnimation
FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)
Функция FuncAnimation имеет два обязательных аргумента fig и func.
fig -- графическое окно, в котором будет отображаться анимация.
func -- функция одного аргумента, которая будет вызываться в каждом кадре.
#help(anim.FuncAnimation)
Построим анимацию линейной функции $y=x$ по значениям $x$, изменяющимся от $0$ до $10$ с шагом $1$.
Для начала создадим графическое окно для анимации
fig1 = plt.figure();
В графическом окне создадим графическую область и зададим пределы по осям
ax1 = plt.axes();
plt.axis([0, 11, 0, 11]);
В графической области создадим объект типа Line2D, координаты которого пока не определены
line, = ax1.plot([], [],'r');
Определим функцию одного аргумента at_frame1, которая будет вызываться в каждом кадре. Аргументом функции будет номер кадра. Функция добавляет к графическому объекту line из графической области для анимации точку с координатами $(t,t)$ и возвращает измененный графический объект line.
def at_frame1(t):
"""добавляет к графическому объекту line новую точку с координатами (t,t) и возвращает измененный графический объект
Arguments:
t -- номер кадра, неотрицательное целое число
Returns: графический объект типа Line2D
"""
x_coord = list(line.get_xdata());
y_coord = list(line.get_ydata());
x_coord += [t];
y_coord += [t];
line.set_data(x_coord, y_coord)
return line
Строим анимацию. frames=11 означает, что всего будет 11 кадров, значения которых будут изменяться от 0 до 10. Для каждого кадра будет вызываться функция at_frame1 со значением аргумента, равным номеру кадра.
fig1 = plt.figure();
ax1 = plt.axes();
plt.axis([0, 11, 0, 11]);
line, = ax1.plot([], [],'r');
anim.FuncAnimation(fig1, at_frame1, frames=11, repeat=False)
Style Guide for Python Code (https://www.python.org/dev/peps/pep-0008/)
Строки документирования (documentation strings, docstrings)
В контексте постановки задачи из Лабраторной работы 1 необходимо построить анимацию движения тела по траектории $(s_{x}(t),s_{y}(t))$ для $t \in [0,T]$.
Определим исходные данные задачи и построим массивы для покоординатного представления траектории движения
h_start = 10 # высота положения тела в момент запуска
T = 2.5 # время полета
s_end = 5.5e1 # горизонтальное перемещение тела за время полета
h_end = 1.2E+1 # высота положения тела в конечный момент движения
g = 9.807 # ускорение свободного падения
v0_x = s_end/T
v0_y = (h_end-h_start+g/2*T**2)/T
t = np.arange(0,T,0.01)
s_x = v0_x*t
s_y = h_start+v0_y*t-g*t**2/2
Создадим матрицу $s$ из двух столбцов для хранения координат траектории движения
s = np.transpose(np.array([s_x, s_y]))
fig2 = plt.figure();
ax2 = plt.axes();
plt.axis([0, s_end+1, 0, h_end+10]);
line1, = ax2.plot([], [],'k'); # отображение траектории
line2, = ax2.plot([], [],'ro'); # отображение тела
coords = list(line1.get_data())
coords
Определим функцию одного аргумента at_frame2, которая будет вызываться в каждом кадре. Аргументом функции будет массив из $x$-ой и $у$-ой кординаты точки траектории. Функция добавляет к графическому объекту line1 из графической области для анимации точку с координатами $(x,y)$. Такжу функция задает графический объект line2 одной точкой с текущими координатами $(x,y)$.
def at_frame2(t):
"""добавляет к графическому объекту line1 новую точку с координатами (t[0],t[1]) и
задает графичекий объект line2 точкой с координатами (t[0],t[1])
Arguments:
t -- массив из двух элементов
Returns: два графических объета типа Line2D
"""
x_coord = list(line1.get_xdata());
y_coord = list(line1.get_ydata());
x_coord += [t[0]];
y_coord += [t[1]];
line1.set_data(x_coord, y_coord)
line2.set_data(t)
return line1, line2
Строим анимацию. frames=s означает, что всего будет столько кадров, сколько строк матрицы $s$. Для каждого кадра будет вызываться функция at_frame2 со значением аргумента, равным массиву со значениями в текущей строке матрицы.
fig2 = plt.figure();
ax2 = plt.axes();
plt.axis([0, s_end+1, 0, h_end+10]);
line1, = ax2.plot([], [],'k'); # отображение траектории
line2, = ax2.plot([], [],'ro'); # отображение тела
anim.FuncAnimation(fig2, at_frame2, frames=s, repeat=False, interval=10)
Кривая задана траекторией движения тела из Лабораторной работы 1 как $(s_{x}(t),s_{y}(t))$ для $t \in [0,T]$. Начальная точка траектории с координатами $(s_{x}(0),s_{y}(0))$ является неподвижной точкой, обозначим ее через $A$. Подвижная точка $B$ движется последовательно по кривой от конечной точки траектории $(s_{x}(T),s_{y}(T))$ к начальной точке $A$.
Необходимо построить анимацию движения секущей прямой, проходящей через точки $A$ и $B$ до момента, когда секущая прямая становится касательной прямой к заданной кривой при совпадении координат точек $A$ и $B$.
Перед началом анимации графическая область должна содержать следующие графические объекты:
Начальное состояние графической области реализуем с помощью пользовательской функции init(), которая будет передана в качестве значения аргумента init_func функции FuncAnimation.
fig3 = plt.figure(); # создание графического окна для анимации
ax3 = plt.axes(); # создание графической области для анимации
s = np.transpose(np.array([s_x, s_y]))
def init():
"""Создает начальное состояние графической области перед началом анимации"""
curve, = ax3.plot(s[:,0], s[:,1],'k'); # траектория движения
A = np.array([s[0,0], s[0,1]]);
point_A, = ax3.plot(A[0],A[1],'ro'); # неподвижная точка
B = np.array([s[-1,0], s[-1,1]]);
point_B, = ax3.plot(B[0],B[1],'bo'); # подвижная точка
secant_p = [A + (B - A)*t for t in [-2, 2]]; # векторно-параметрическое уравнение прямой
secant_p = np.array(secant_p)
secant_line, = ax3.plot(secant_p[:,0], secant_p[:,1],'g'); # секущая прямая
plt.legend(['Curve','Unmovable point A','Мovable point B','Secant line through A and B'])
plt.axis([-5, s_end+5, 0, h_end+10]);
return None
def at_frame3(t):
"""Should be defined"""
return None
anim.FuncAnimation(fig3, at_frame3, frames=s[::-1], init_func=init, repeat=False, interval=10)
Напишите пользовательскую функцию at_frame3(t), которая будет вызываться в каждом кадре анимации, полагая, что t является массивом текущей координаты подвижной точки $B$. Обратите внимание, что при совпадении координат точек $A$ и $B$ уравнение для задания секущей прямой через две точки возвращает только точку $A$ и уравнение должно быть заменено на уравнение касательной в точке $A$. Для функции at_frame3(t) напишите строки документирования.