Skip to content

Instantly share code, notes, and snippets.

@pozitron57
Created June 18, 2024 15:02
Show Gist options
  • Save pozitron57/d3b26ab870051e5b3def72233c4756f6 to your computer and use it in GitHub Desktop.
Save pozitron57/d3b26ab870051e5b3def72233c4756f6 to your computer and use it in GitHub Desktop.
Animation for kinematics constraints problem https://phys.pro/problems/1189.py
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import rc, rcParams
# Parameters {{{
alpha = 30/np.pi
alpha = np.degrees(alpha)
dt = 0.7
v1 = 1.6
## Неверный, но веселый результат
#v2x = v1 * np.cos(alpha)
#v2y = v1 * (np.sin(alpha) + np.tan(alpha))
v2x = v1 * (np.cos(alpha) - 1)
v2y = v1 * np.sin(alpha)
#}}}
# Клин общие настройки
base_length = 1
A = (0, 0) # Левый угол
B = (-base_length, 0) # Правый угол
height = base_length * np.tan(alpha)
C = (0, height) # Верхний угол
# Брусок (прямоугольник) общие настройки
rect_width = base_length / 6
rect_height = base_length / 8
# Блок (круг) общие настройки
r = rect_height / 2
times = np.linspace(0, (base_length/np.cos(alpha) - rect_width - r) / v1, 300)
ccc = '#555555'
# Общие настройки для стрелок
ct = '#3344BB'
arrow_lw=1.5
start_time = np.amax(times) * 0.19
scale = 10
for idx, dt in enumerate(times):
# Plot setup
rcParams['font.size'] = 15
fig, ax = plt.subplots(figsize=(7.63, 2.42))
fig.subplots_adjust(left=.15, bottom=.15, right=.95, top=.95)
plt.rcParams.update({
"text.usetex": False,
"mathtext.fontset": "custom",
"mathtext.it": "STIX Two Text:italic",
"mathtext.rm": "STIX Two Text",
"mathtext.sf": "STIX Two Text",
"font.sans-serif": "STIX Two Text",
})
### Клин
x = [A[0] - v1*dt, B[0] - v1*dt, C[0] - v1*dt, A[0] - v1*dt]
y = [A[1], B[1], C[1], A[1]]
plt.plot(x, y, color=ccc, linewidth=2)
plt.fill(x, y, 'white', edgecolor=ccc, zorder=20)
### Пунктиром начальное положение клина
x = [A[0], B[0], C[0], A[0]]
y = [A[1], B[1], C[1], A[1]]
plt.plot(x, y, color=ccc, ls='--', linewidth=1)
#plt.fill(x, y, 'white', edgecolor=ccc)
# Матрица поворота для прямоугольника
origin = np.array([0, 0])
rotation_matrix = np.array([[np.cos(alpha), np.sin(alpha)], [-np.sin(alpha), np.cos(alpha)]])
rect_points = np.array([[0, 0],
[rect_width, 0],
[rect_width, rect_height],
[0, rect_height]])
# Поворот
rect_rotated = np.dot(rect_points, rotation_matrix)
# Смещение прямоугольника в левый угол треугольника (координаты -1, 0)
rect_rotated[:, 0] -= base_length
rect_rotated[:, 0] += v2x * dt
rect_rotated[:, 1] += v2y * dt
# Координаты для построения
rect_x_rotated = rect_rotated[:, 0].tolist() + [rect_rotated[0, 0]]
rect_y_rotated = rect_rotated[:, 1].tolist() + [rect_rotated[0, 1]]
# Координаты середины правой стороны прямоугольника
midpoint_x = (rect_rotated[1, 0] + rect_rotated[2, 0]) / 2
midpoint_y = (rect_rotated[1, 1] + rect_rotated[2, 1]) / 2
# Построение прямоугольника
plt.plot(rect_x_rotated, rect_y_rotated, color=ccc, linewidth=2)
# Блок (круг)
circle = plt.Circle((C[0] - v1*dt, C[1]), r, color=ccc, fill=False, linewidth=2)
plt.gca().add_patch(circle)
# Наклонная нить до сдвига
line_x = [C[0] - v1*dt - r * np.sin(alpha), midpoint_x]
line_y = [C[1] + r * np.cos(alpha), midpoint_y]
plt.plot(line_x, line_y, color=ccc, linewidth=1)
# Горизонтальная нить до сдвига
line_x = [0.35, C[0]-v1*dt]
line_y = [height + r, height + r]
plt.plot(line_x, line_y, color=ccc, linewidth=1)
# Смещение подписей Δx
offset = -0.01 * base_length/np.sin(alpha)
# Стрелка Δx от начального положения клина
if dt>start_time:
plt.annotate(
'', # Пустая строка для текста
xy=(C[0]-v1*dt, height/2), # Конечная точка стрелки
xytext=(C[0], height/2), # Начальная точка стрелки
arrowprops=dict(
arrowstyle='<->', # Двусторонняя стрелка
lw=arrow_lw, # Толщина линии
color=ct, # Цвет стрелки
shrinkA=0, # Убрать отступ в начальной точке
shrinkB=0, # Убрать отступ в конечной точке
mutation_scale=scale # Масштаб головки стрелки
)
)
plt.text(-v1*dt/2, height/2 - offset, r'$\Delta x$', color=ct, ha='center')
# Стрелка Δx текущего положения угла клина к бруску
start = (B[0] + v2x * dt, B[1] + v2y * dt)
end = (B[0] - v1 * dt, B[1])
# Координаты середины стрелки
mid_x = (start[0] + end[0]) / 2
mid_y = (start[1] + end[1]) / 2
# Вектор стрелки
arrow_vector = np.array([end[0] - start[0], end[1] - start[1]])
# Нормализация вектора стрелки
norm_arrow_vector = arrow_vector / np.linalg.norm(arrow_vector)
# Вектор, перпендикулярный стрелке
perpendicular_vector = np.array([-norm_arrow_vector[1], norm_arrow_vector[0]])
# Смещение текста перпендикулярно стрелке
text_x = mid_x + offset * perpendicular_vector[0]
text_y = mid_y + offset * perpendicular_vector[1]
if dt>start_time:
plt.annotate(
'', # Пустая строка для текста
xy=end, # Конечная точка стрелки
xytext=start, # Начальная точка стрелки
zorder=21,
arrowprops=dict(
arrowstyle='<->', # Двусторонняя стрелка
lw=arrow_lw, # Толщина линии
color=ct, # Цвет стрелки
shrinkA=0, # Убрать отступ в начальной точке
shrinkB=0, # Убрать отступ в конечной точке
mutation_scale=scale # Масштаб головки стрелки
)
)
plt.text(text_x, text_y, r'$\Delta x$', color=ct,
ha='center', rotation=np.degrees(alpha))
# СТЕНА И ПОЛ
wall_x = [0.35, 0.35]
wall_y = [-0.01, height + r * 1.5]
plt.plot(wall_x, wall_y, 'k-', linewidth=8)
# Построение пола floor
plt.plot([0.40, -v1*np.amax(times) - base_length*1.1], [-0.02, -0.02], 'k-', linewidth=6)
plt.gca().set_aspect('equal', adjustable='box')
plt.xlim([-v1*np.amax(times) - base_length*1.01, 0.4])
#plt.ylim([-0.03, 0.75])
plt.axis('off')
plt.xticks([])
plt.yticks([])
plt.savefig(f'/Users/slisakov/Yandex.disk.localized/documents/scripts/1514_school/1189/{idx:03d}.png',
dpi=200, bbox_inches='tight')
plt.close()
# Create animation with ffmpeg:
# ffmpeg -framerate 30 -i %03d.png -c:v libx264 -pix_fmt yuv420p 1189.mp4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment