# Animate a lattice with Python

Fellowing instructions found in a youtube tutorial, I managed to draw a slinky.

See my .blend (note that I did not add the curve modifier as I want my slinky to be along an axis, see below). Now, I'd like to animate it so I could illustrate how a longitudinal wave propagates (as show in this video). For simplicity, let's consider a sinusoidal wave: the displacement field is xi(x, t) = xi0 cos(2 pi f t - 2 pi x / lambda) where f is the frequency of the wave and lambda is the wavelength (lambda f = v where v is the velocity of the wave).

My question is: how can I use Python to add keyframes so that the vertices of the lattice (and the slinky) are moved according to the displacement field? Well, that was not easy for a beginner like me but I think I found a way. Explanations about lattice_index_from_uvw can be found in this question of mine. Just run the script once and then the handler bpy.app.handlers.frame_change_pre does the job.

import bpy
from math import cos

pi = 3.1415

lat = bpy.data.lattices['Lattice']
obj = bpy.data.objects['Lattice']

# Space

def lattice_index_from_uvw(lat, u, v, w):
totu = lat.points_u
totv = lat.points_v
return w * (totu * totv) + (v * totu) + u

points = obj.data.points

Delta_x = points[lattice_index_from_uvw(lat, 1, 0, 0)].co_deform.x - points[lattice_index_from_uvw(lat, 0, 0, 0)].co_deform.x

x0 = [points[lattice_index_from_uvw(lat, u, 0, 0)].co_deform.x for u in range(lat.points_u)]

A = Delta_x / 3
Lambda = 10 * Delta_x

# Time

scn = bpy.context.scene

frame_start = scn.frame_start
frame_end = scn.frame_end

Delta_t = 1
T = frame_end - frame_start

def sine_wave(x, t, A, T, Lambda):
return A * cos(2 * pi * ( (t / T) - (x / Lambda)))

def update_wave(t):
for u in range(lat.points_u):
for v in range(lat.points_v):
for w in range(lat.points_w):
points[lattice_index_from_uvw(lat, u, v, w)].co_deform.x = x0[u] + sine_wave(x0[u], t, A, T, Lambda)

def my_handler(scene):
print(scene.frame_current)
update_wave(scene.frame_current * Delta_t)

bpy.app.handlers.frame_change_pre.append(my_handler)