Hello there,
There was a time where epicycles were used to build real machines to reproduce (and forecast) orbits of planets. Now tracing these curves allows to create completely useless but funny animations (for the maths, see View of Tracing closed curves with epicycles). I mean, these animations do not help you to understand Fourier Series, as considering the reconstruction with graphics on the complex plane is too complicated (showing things with graphs of real functions is simpler). However, seeing these twirling circles is really funny ! Here is a Scilab implementation with metropolitan France curve (script below, uncomment line 54 to have a closer view, focused on the center of fourth circle).
q = http_get("https://raw.githubusercontent.com/gregoiredavid/france-geojson/refs/heads/master/metropole.geojson");
pos = q.geometry.coordinates(1)(1)(1:$-1,:);
N= size(pos,1);
// approximate mercator projection
pos(:,2)=(pos(:,2)-mean(pos(:,2)))*1.5;
pos(:,1)=(pos(:,1)-mean(pos(:,1)));
// compute fft of complexified curve
u=fft(pos(:,1)+%i*pos(:,2))/N;
// sort Fourier coefficients w.r.t. decreasing modulus
[Rs,ks]=gsort(abs(u));
w=ks-1;
w(w>N/2)=w(w>N/2)-N;
tspan = linspace(0,2*%pi,1000);
set(gca(),"data_bounds",[-9 9 -9 9],"tight_limits","on",...
"background",0,"margins",[0 0 0 0],"isoview","on");
set(gcf(),"background",0,"axes_size",[800 800]);
scale = 4;
thick = 2;
for NM=[10 50 100 200 300 400]
sref = u(ks(1:NM)).'*exp(w(1:NM)*%i*tspan);
xref = real(sref)';
yref = imag(sref)';
col = [addcolor(coolwarm(NM)) 0];
R = [Rs(1:NM);0];
circles = [zeros(NM+1,2) 2*R 2*R zeros(NM+1,1) 360*64*ones(NM+1,1)];
delete(gca().children)
l = 1;
for t = tspan
gcf().immediate_drawing = "off"
s = [0; cumsum(u(ks(1:NM)).*exp(w(1:NM)*%i*t))];
x = real(s); y = imag(s);
circles(:,1:2)=[x-R y+R];
if t == tspan(1)
hb = plot(x,y,"-o","marksize",4,"thickness",thick);
hc = xarcs(circles', col);
hc.thickness = thick;
hr = plot(xref(1),yref(1),"r","thickness",thick)
ht = xstring(0,1,sprintf("N = %d",NM));
ht.font_foreground=color("white");
ht.font_size = 4;
else
hb.data = [x,y];
hc.data = circles;
hr.data = [xref(1:l) yref(1:l)];
end
gca().data_bounds=[x(4)+scale*[-1 1] y(4)+scale*[-1 1]];
gcf().immediate_drawing = "on"
l=l+1;
end
end