Inspired by the neat post from @Clayton Shonkwiler, I thought it would be cool to generalize his approach. I started by defining three families of curves:
(* A typical elliptic orbit. *)
ellipticOrbit[t_,a_:1,b_:1]:={a Cos[t],b Sin[t]};
(* The circle of radius b rolls on the outside of the circle of radius a. Setting a=b results in a cardioid, setting a=2b results in a nephroid. *)
epicycloidOrbit[t_,a_:1,b_:1]:={(a+b)Cos[t]-b Cos[(a/b+1)t],(a+b)Sin[t]-b Sin[(a/b+1)t]};
(* The circle of radius b rolls on the outside of the circle of radius a. The point P is at a distance c from the center of the circle of radius b. *)
epitrochoidOrbit[t_,a_:1,b_:.25,c_:.5]:={(a+b)Cos[t]-c Cos[(a/b+1)t],(a+b)Sin[t]-c Sin[(a/b+1)t]};
This next bit of code lays out k copies of a curve f around a circle of radius r:
compositeOrbits[t_,k_:4,f_:ellipticOrbit,r_:2,curvePars__]:=Table[
r ReIm[Exp[2 I i Pi/k]]+f[t-(2 i Pi/k),##]&@@{curvePars},
{i,0,k-1}
];
To generate an animation in your notebook evaluate:
animatedCurves[n_:12,k_:4,f_:ellipticOrbit,r_:1,curvePars___]:=Animate[
Graphics[{
Thickness[.005],
Table[
{FaceForm[{RGBColor["#E5F6C6"],Opacity[.2]}],
EdgeForm[{RGBColor["#E5F6C6"],Thickness[.002]}],
Polygon[compositeOrbits[t+i,k,f,r,curvePars]],
RGBColor["#E5F6C6"],
Point/@compositeOrbits[t+i,k,f,r,curvePars]},
{i,0,2Pi(1-1/n),2Pi/n}
]},
Background->RGBColor["#5D414D"],
PlotRange->If[
Length@{curvePars}==0,
{{-4r,4r},{-4r,4r}},
{{-1.5(r+Total[{curvePars}]),1.5(r+Total[{curvePars}])},{-1.5(r+Total[{curvePars}]),1.5(r+Total[{curvePars}])}}
],
AspectRatio->Automatic
],
{t,0,2Pi},
AnimationRate->Pi/30
];
The first argument determines the number of polygons, the second argument determines the number of curves, the third argument sets the type of curve, the fourth determines their distance from the origin, and the remaining arguments are curve parameters.
To export this as a GIF use the following function. After the first argument the argument structure is the same as before. Remember to set your directory before exporting to make it easier to locate your GIF:
gifAnimatedCurves[name_String,n_:12,k_:4,f_:ellipticOrbit,r_:1,curvePars___]:=Export[
name<>".gif",
Table[
Graphics[{
Thickness[.005],
Table[
{FaceForm[{RGBColor["#E5F6C6"],Opacity[.2]}],
EdgeForm[{RGBColor["#E5F6C6"],Thickness[.002]}],
Polygon[compositeOrbits[t+i,k,f,r,curvePars]],
RGBColor["#E5F6C6"],
Point/@compositeOrbits[t+i,k,f,r,curvePars]},
{i,0,2Pi(1-1/n),2Pi/n}
]},
Background->RGBColor["#5D414D"],
PlotRange->If[
Length@{curvePars}==0,
{{-4r,4r},{-4r,4r}},
{{-1.5(r+Total[{curvePars}]),1.5(r+Total[{curvePars}])},{-1.5(r+Total[{curvePars}]),1.5(r+Total[{curvePars}])}}
],
AspectRatio->Automatic
],
{t,0,4Pi/n,.05}
]
];
For example, in the GIF at the top of this post each vertex of a triangle traverses a different cardioid. The animation was generated by:
gifAnimatedCurves["animation", 12, 3, epicycloidOrbit, 1.5, .5, 0.5]
With this approach any 2D parametric curve, with any number of parameters, can be used to generate animations of this type.