matplotlib의 FuncAnimation 메서드를 활용하면 개별 프레임을 일일이 파일로 저장하는 번거로운 과정 없이도 곧바로 동영상 형태의 애니메이션을 만들 수 있습니다. 이 글에서는 입자(particle)의 위치를 지속적으로 갱신하는 사용자 정의 함수 update를 작성하고, 마지막에 scatter 객체를 반환하도록 구성하는 방법을 살펴보겠습니다.
구현 단계
- 입자의 초기 위치(position), 속도(velocity), 힘(force), 크기(size)를 설정합니다.
figsize=(7, 7)옵션으로 새 figure를 생성하거나 기존 figure를 활성화합니다.xlim과ylim을 지정하여 현재 figure에 axes를 추가하고, 해당 축을 현재 작업 축으로 만듭니다.- 입자들의 초기 위치를 기반으로 scatter 플롯을 그립니다.
- 지정한 함수 func를 반복적으로 호출하여 애니메이션을 생성합니다.
FuncAnimation클래스에 입자 위치를 갱신하는 사용자 정의 메서드를 전달하면 됩니다. plt.show()를 호출해 완성된 애니메이션을 화면에 표시합니다.
예제 코드
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
dt = 0.005
n = 20
L = 1
particles = np.zeros(n, dtype=[("position", float, 2),
("velocity", float, 2),
("force", float, 2),
("size", float, 1)])
particles["position"] = np.random.uniform(0, L, (n, 2))
particles["velocity"] = np.zeros((n, 2))
particles["size"] = 0.5 * np.ones(n)
fig = plt.figure(figsize=(7, 7))
ax = plt.axes(xlim=(0, L), ylim=(0, L))
scatter = ax.scatter(particles["position"][:, 0], particles["position"][:, 1])
def update(frame_number):
particles["force"] = np.random.uniform(-2, 2., (n, 2))
particles["velocity"] = particles["velocity"] + particles["force"] * dt
particles["position"] = particles["position"] + particles["velocity"] * dt
particles["position"] = particles["position"] % L
scatter.set_offsets(particles["position"])
return scatter,
anim = FuncAnimation(fig, update, interval=10)
plt.show()
코드 설명
위 예제에서는 구조화된 NumPy 배열을 사용해 20개의 입자 정보를 한 번에 관리합니다. 매 프레임마다 update 함수가 호출되면서 무작위로 생성된 힘이 입자에 적용되고, 이 힘이 속도와 위치에 순차적으로 반영됩니다. % L 연산은 경계 조건(boundary condition) 역할을 하여, 영역 밖으로 나간 입자가 반대편 경계에서 다시 등장하도록 만들어 줍니다.
FuncAnimation(fig, update, interval=10)에서 interval=10은 프레임 사이의 간격을 10밀리초로 지정하는 옵션입니다. 즉, 초당 약 100프레임 속도로 애니메이션이 재생됩니다. 프레임을 이미지 파일로 저장하거나 별도로 합치는 작업 없이, matplotlib 창에서 바로 부드러운 입자 운동 애니메이션을 확인할 수 있다는 점이 이 방법의 가장 큰 장점입니다.
실행 결과
코드를 실행하면 무작위한 힘의 영향을 받으며 자유롭게 움직이는 20개의 입자가 실시간으로 애니메이션되는 모습을 볼 수 있습니다. 입자들은 경계 조건에 따라 시뮬레이션 영역 내부를 끊임없이 순환합니다.