G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing
GREYC CNRS ENSICAEN UNICAEN

A Full-Featured Open-Source Framework for Image Processing



Latest stable version: 3.4.3        Current pre-release: 3.5.0 (2024/12/20)

Tutorial

Wheelie Animations

WheeliesInterludeDeep DiveFrom Wheelies, Arabesques
From Arabesques, WheeliesWheelie Animations


     17. A 1911 Goudy Bookletter Ampersand via 1,024 delta wheelies
Fourteen years ago, Santiago Ginnobili wondered on YouTube if, somewhere in the Universe, a planetary orbit might exist resembling Homer Simpson. This — being YouTube — induced Dr. Ginnobili to post the prerequisite demo video. It did indeed demonstrate a system of epicycles tracing the elder Simpson.

Explanations for finding just the right epicycles — a thousand in number, Dr. Ginnobili attested — were missing. And to good effect. Any explanation would have been fatal to the sheer panache of Dr. Ginnobili's cyclic capriccio. He trusted that, by the sweet by-and-by, YouTube would serve up some sod or two who could sacrifice brio for technical prosaics and thereby make good with actual epicyclic computations.

Indeed, over the sweet by-and-by YouTube did not disappoint. Improvements in compression resolutions and a lifting of play time constraints have given rise to a number of longer running epicyclic hits. These, unkinked, lay bare circular paths that are bashed a bit. There are even YouTubers who have taken up in fair dinkum ways the lot of explaining epicyclic computations:

 1.  Grant Sanderson ( 3Blue1Brown ):  But what is the Fourier Transform? A visual introduction. and its companion But what is a Fourier series? From heat flow to drawing with circles.
 2.  Burkard Polster ( Mathologer ): Epicycles, complex Fourier series and Homer Simpson's orbit,  uses Santiago Ginnobili's epicyclic rendering of Homer Simpson as a jumping off point as to what may be going on with all of those circles.
 3.  Daniel Shiffman ( Coding Train ) runs through a JavaScript implementation.

Implementations in other languages abound, including G'MIC. If you have made your way from wheelies through to deep dives, assembled from wheelies arabesques and deconstructed arabesques into wheelies you know already what these gyrating line segments are. Chaotic in appearance — yes, but appearances deceive: these segments dance a ballet choreographed with rocket-launch precision. They are delta wheelies, linked end-to-end, no more, no less, in a manner prescribed by the original wheelie chain recipe. For some instant in time, these delta wheelies add up vectorially to the cardinal sum evaluated at just that same instant, as they must, for the deltas are differenced from that cardinal sum — are just a picturesque re-expression of that sum — which must necessarily land at the same point.

To be sure, these visualized delta wheelie chains are much, much, MUCH! longer than the single-digit linkages we explored back in Wheelies, but we know that, in principle, delta wheelie chains with a million, million links are principally no different than those with just a few: they are just the pairwise differencing of the N terms of some cardinal sum ordered in one of its N! ways, and that such a cardinal sum has an analogous frequency domain spectral image. Digitize a former circle kinked up to look like Homer Simpson, or the 1911 Goudy Bookletter ampersand, or Jean-Baptiste Joseph Fourier, and fft will fetch for you the cardinal sum. Then difference terms for the delta wheelie chain using any permutation you care to and with however many links you need for the fidelity you want.

The particulars:

      1.  Digitize the path: obtain discrete samples at (presumably) equal time steps.
      2.  Through the discrete forward Fourier transform, find the frequency domain image of the path these samples trace.
      3.  Compute the delta wheelies through some pair-wise differencing scheme of spectral coefficients.
      4.  Animate the delta wheelie chain and the plotting of the arabesque. The animations mesh automagically. Take your bow; bask in the applause. People think you've done something magical, but, recalling Heinlein's Lazarus Long, you know it to be just engineering.

We could use any permutation of the ordinals, but there is whimsy in differencing "from the origin to the Nyquist rate." The large, low rotational rate delta wheelies serve visually as roots for the small-but-frantically spinning high frequency deltas.

spectralpermutation
15. Selection order for alternate clockwise and counterclockwise wheelies, large to small magnitude (usually...)

The implementaton which follows glides over digitization. Sometime shortly after this writing (January 2023) there will be a cheatsheet on the topic of calling scripts and executables from G'MIC, for which a Python 3 script that parses Structured Vector Graphics files demonstrate the technique. For the impatient, a G'MIC exercises discussion hosted on the discuss.pixls.us discourse server embodies that cheatsheet. Alternatively, you may download the CSV file of the 1911 Goudy Bookletter ampersand for a playpen digitized path; you know you've always wanted one.

Download wheelie_animchain.gmic ):

wheelie_animchain.gmic
  1 gtutor_mkchainanim :
  2    # Argument 1: image, 1×2n×1×2.  n=Nyquist Sample Rate.
  3    # Argument 2: positive integer. Animation frames.
  4    # Argument 3: positive integer. Lower passband, Hertz.
  5 
  6    -check ${is_image_arg\ $1}" && ${2=300}>0 && ${3=32}>0"
  7    -pass$1
  8    -name[0] canvas
  9    -name[1] spectral
 10    fcnt=$2
 11    wcnt=$3
 12 
 13    # Scale and clear zero frequency, eliding any
 14    # offset from the complex plane origin. As a
 15    # consequence, the root wheelie originates
 16    # at the origin.
 17 
 18    -div[spectral] {h#$spectral}
 19    -eval I[#$spectral,0]=vector2(0)
 20 
 21    # Image 'coefficients' for dynamic array (da);
 22    # storage for frequency domain ordinals.
 23 
 24    0
 25    -name. coefficients
 26 
 27    # Scan spectral image and set 'wfound' to the count of
 28    # non-zero, (meaningful) coefficients. Note that many
 29    # coefficients in the spectral image may have zero-length
 30    # and, thus, no bearing on the arabesque. The dynamic
 31    # array ('coefficients') acquires the non-zero
 32    # coefficients, converted to polar form (r ∠ θ). Scan
 33    # alternately down the counterclockwise (m%2==0) and
 34    # clockwise (m%2!=0) portions of the frequency axis,
 35    # toward the Nyquist Sampling Rate mid-point. 'o' obtains
 36    # the rotational rate in each loop; 'j' is the index to
 37    # the currently assayed ordinal — it alternates between
 38    # clockwise and counterclockwise indices; 'm' is the
 39    # loopcounter.
 40 
 41    wfound={">
 42             wc=get('wcnt',0,0);
 43             sz=0;
 44             repeat(2*wc,m,
 45                    sw=h#$spectral;
 46                    if(
 47                       m%2==0,
 48                       j=int(m/2)+1;
 49                       o=j,
 50                       j=sw-(int(m/2)+1);
 51                       o=-(int(m/2)+1)
 52                      );
 53                    rad=norm2(I(#$spectral,0,j));
 54                    if(rad>0,
 55                       ang=atan2(
 56                                  i(#$spectral,0,j,0,1),
 57                                  i(#$spectral,0,j,0,0)
 58                                );
 59                       ang<0?ang=ang+2*pi;
 60                       da_push(#$coefficients,[rad,ang,o])
 61                     );
 62                   );
 63             da_size(#$coefficients)
 64            "}
 65 
 66    -if $wfound
 67 
 68       # The coefficient array (da) may still be
 69       # manipulated as an image; we strip the penultimate
 70       # and last pixel of the dynamic array image,
 71       # containing administrative size and capacity
 72       # counts. The remaining three channel image contains
 73       # a permutation of non-zero cardinal sum
 74       # coefficients: radius (channel zero), orientation
 75       # (channel 1) and rotational rate (channel 2). By
 76       # virtue of our "ends-to-the-middle" scan, we have
 77       # prepared a coefficient permutation where the
 78       # rotational rates alternate in sign and increase in
 79       # magnitude.
 80 
 81       -crop[coefficients] 0,0,0,0,0,{$wfound},0,3
 82       -shift[coefficients] 0,1,0,0,0
 83 
 84       # During animation, rotors spin delta 
 85       # wheelies by fixed angular increments.
 86  
 87       -split[coefficients] c,2
 88       -name. rotors
 89       -fill[rotors] "begin(framecount=get('fcnt',0,0));
 90                      [i(0,y,0,1),(2*pi*i(0,y,0,0))/framecount]"
 91 
 92       # Backplate imaging: Generate arabesque
 93       # from the spectral image. This is a reference
 94       # image to compare what delta wheelie chain
 95       # draws.
 96 
 97       -mul[spectral] {h#$spectral}
 98       +split[spectral] c
 99       -ifft[-2,-1]
100       -append[-2,-1] c
101       -name. plots
102       -fill[plots] "begin(
103                            sw=min(w#$canvas,h#$canvas)/2;
104                            id=eye(3);
105                            id[0]=sw;
106                            id[2]=sw;
107                            id[4]=-sw;
108                            id[5]=sw;
109                            store('amat',id,3,3)
110                          );
111                          (id*[I(x,y),1])[0,2]
112                    "
113       -permute[plots] cyzx
114       -eval "PV=crop(#$plots);
115              polygon(#$canvas,
116                      -int(size(PV)/2),
117                      PV,
118                      0.375,
119                      0xaaaaaaaa,
120                      75,85,110,96
121                     )"
122       -remove[spectral,plots]
123       -store[canvas] canvastemplate
124 
125       # Allocate plot storage for tracing the tip
126       # of the wheeli echain.
127       
128       -input[0] 1,$fcnt,1,2
129       -name[0] trace
130 
131       # Animation loop…
132       -repeat $fcnt k=$>
133 
134          # 0. Assemble (cumulate)
135          #    & scale delta wheelie
136          #    chain [dwchain].
137 
138          +split[coefficients] c
139          -polar2complex[-2,-1]
140          -append[-2,-1] c
141          -cumulate[-1] y
142          -crop[-1] 0,0,0,0,0,{2*h-1},0,{s-1},3
143          -name. dwchain
144          -fill[dwchain] "begin(id=get('amat',9));(id*[I(x,y),1])[0,2]"
145 
146          # 1. Copy position of delta wheelie tip
147          #    into path trace buffer.
148 
149          -eval I(#$trace,0,$k,0)=I(#$dwchain,0,h#$coefficients-1)
150          -input[trace] $canvastemplate
151          -if $k>0
152 
153             # 2. if: Trace buffer needs at least two
154             #    plots of the wheelie tip path.
155 
156             +crop[trace] 0,0,0,0,0,$k,0,1
157             -crop[-1] 0,0,0,0,0,{2*h-1},0,{s-1},3
158             -permute[-1] cyzx
159 
160             # 3. Draw wheelie tip trace.
161 
162             -eval "PV=crop(#-1);
163                    sz=size(PV);
164                    polygon(#$canvas,
165                            -int(sz/2),
166                             PV,
167                             1.00,
168                             0xffffffff,
169                             40,50,50,255
170                           )"
171             -remove[-1]
172          -fi
173 
174          # 4. Draw delta wheelie chain.
175 
176          -permute[dwchain] cyzx
177          -eval "PV=crop(#$dwchain);
178                 sz=size(PV);
179                 polygon(#$canvas,
180                         -int(sz/2),
181                              PV,
182                              1.00,
183                              0xffffffff,
184                              60,100,200,255
185                        )"
186 
187          # 5. Draw red dot hinges.
188 
189          -eval "PV=crop(#$dwchain,0,0,0,0,2,h#$coefficients,1,1);
190                 sz=size(PV);
191                 for(j=0,
192                     j<(sz/2),
193                     j++,
194                     ellipse(#$canvas,
195                             PV[2*j,2],
196                             1.5,
197                             1.5,
198                             0,
199                             1,
200                             240,25,210,255
201                            )
202                    )"
203          -remove[dwchain]
204          -name[canvas] frame
205             
206          # 6. Increment delta wheelies
207          #    for next frame.
208 
209          -add[coefficients] [rotors]
210       -done # Animation loop 
211 
212       # 7. Drawn all frames. Remove
213       #    all not labeled 'frame'.
214  
215       -remove[^frame]
216    -else
217       -error "Spectral image argument has no coefficients."
218 -fi
    
Weeds, for those seeking further distractions
  Line    Remark  
  1-11 Argument management: The image selected by the command sets some of the non-dynamic characteristics of the animation; frame dimensions and number of color channels. One could easily select an image with imagery. Such would then form an unanimated "backplate." In classic cel animation, the backplate is furthest from the camera, occupies the bottom of the cel stack and is retained from shot to shot. The upper cells carry the animation, being mostly transparent and swapped with successor cels from shot to shot.

The command's first argument is a spectral representation of a plot path; note that it is expected to be arrayed along the image y axis. This is a convention born of convenience, as a common provider of such plot paths, input_csv stacks the path along the y image dimension. -permute[xoriented] yxzc can appropriately rearrange spectral representations stacked along the x image dimension.

The second argument sets the number of frames to be rendered for the animation. How long that may be in seconds depends upon what frame rate may be given to some output renderer. If that output renderer is told to make an animation at 30 frames a second, then 300 frames will provide for a ten second animation.

The third passband argument are for those who find joy in sloppy tracing effects. Technically, it deletes all but the given number positive and negative coefficients; a passband of 30 Hertz elides all but thirty counterclockwise and thirty clockwise coefficients, retaining sixty coefficients all told. In practice, this removes the high frequency components from the path. Barring a path initially comprised only of low frequencies, this gives rise to an animation that rounds off sharp corners and the like, as sharp corners contain high frequency components — for such is what makes corners sharp.          
  18-19 Centering the arabesque: We adjust the spectral domain image by eliminating the f=0 spectral coefficient. A strict fellow may rightfully carp that this destroys data. That is true, but we are in the service of Art, where compositional adjustments are common. This adjustment removes any offset phasor of the arabesque from the origin so that the spatial conversion of this spectral data by ifft centers the image on the origin. That is, the deletion of the f=0 coefficient in the spectral domain gives rise to the average position of zero for all plots in the spatial domain.  
  24-25 Dynamic Array Coefficient Cache: We establish a dynamic array container image to hold harvested non-zero cardinal sum coefficients. We start with an empty image. Dynamic array internals will set image dimensions, as needed, when vectors of particular dimensions are pushed onto the stack. See 41-64 commentary.
  41-64 Finding Cardinal Sum Coefficients: We harness a math expression to set the command line variable wfound to the number of wheelies found; that is, the number of non-zero coefficients of the cardinal sum. A spectral image may be predominantly populated with zero coefficients, so we set out to find and cache — in a dynamic array — only the non-zero ones.

Whilst we are busying ourselves with this coefficient filtering, we also order found items in a particular permutation, that of alternating positive and negative coefficients while proceeding from low to high rotational rates, this as described in the text. When we plot the delta wheelie chain in the animation loop, 138-144, 178,187, the slowly rotating, large radii delta wheelies will be toward the root end of the chain, with the more rapidly rotating deltas near the tip. This plotting convention appears in most epicycle (nèe "delta wheelie") renderings.

Also, whilst we are at it, we represent coeffcients in polar form, storing the same as dynamic array vectors of the form: [+radius, ±orientation, ±rotational_rate] for each non-zero coefficient. This layout is in the service of our animation loop, to wit: animate the orientation angles of the cardinal sum coefficients by adding "smidgen angles," — delta angles derived from the rotational rate. Incrementing by smidgen angles becomes straightforward addition when the coefficients are in polar form. See 81-91, 138-146 and 209.
  81-91 Tomb-Raiding the Dynamic Array: With the number of coefficients ascertained (wfound is positive), we deconstruct the image underlying the dynamic array, turning it into a "coefficient vector" and a "rotor vector". We: (1) trim away dynamic array administrative fields by means of crop; see 81. (2) by means of shift and the Dirichlet boundary rule, we introduce a dummy constant coefficient term, r=0, ∠=0°, to the cardinal sum. This doesn't change the sum — it is effectively zero — but in the course of converting cardinal sum coefficients to wheelie chain hinge points, this dummy term becomes the very first plot point of the chain. See Culminate Coefficients: 138-144

As a first step in finding "smidgen angles", we split out channel three, ±rotational_rate, from the coefficient vector, making a proto "rotor vector"; see 87-91. The fill mathematical expression applied to the rotor vector accomplishes two tasks: (1) It implements smidgen angle conversion: (2π × f) ÷ framecount for f, the rotational rate of each vector, scaling the vector elements by , then dividing by the frame count. This leaves the rotor vector holding "smidgen angles," by which the animation loop advances the orientations of cardinal sum terms. It also (2) injects an additional, zeroed-out, channel into the rotor vector, converting its elements into [r=0,∠Δθ] polar values. Such values may be added to the coefficient vector in a straightforward manner without altering the radius component; see 209. The means of fabricating such a zeroed-out channel stems from a side-effect of the pixel accessor function i(#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions). This pixel accessor invocation: i(0,y,0,1) references a channel that does not initially exist in rotor vector. Recall that the split of proto rotors from the coefficient vector produced single channel elements, these just rotational rates. In the absence of an explicitly specified boundary_condition, G'MIC creates an out-of-bounds channel and uses the default Dirichlet boundary rule; off-image pixels are set to constant black.
  97-121 Plot a Back Plate Image: We create and store a back plate image. It portrays the spatial plot points encoded in the Argument 1 [spectral] image, this so that the user might compare the wheelie-generated trace with the actual image. We recover the plot points of the actual image by means of the inverse, discrete time Fourier transform of [spectral], ifft; this brings about a set of plot points realizing an image plotted on the complex plane.

To render these plot points, we harness the mathematical expression function polygon(). Particulars follow closely on that of rendering cardinal sums in From Wheelies, Arabesques; the transform of plot points into pixel image space, suitable for consumption by polygon() takes place through an affine transform. See the anim_simple.gmic annotations for how this transform to screen space comes about.

Once polygon() has rendered the plot points on the back plate, we employ the store command to stash the back plate off the image list and into a named storage variable, canvastemplate. In the animation loop, we restore this image via input  at the start of each frame, upon which we further render wheelie chains and tip tracery; see the Animation loop... 150.
  124-129 Tracing the Wheelie Chain Tip: Before entering the animation loop, we set up storage to retain plot points of the wheelie tip's per-frame travels. Accumulating coefficients at the top of the animation loop, 138-144 expediently finds all the wheelie chain hinge points; the last hinge point corresponds to the chain's tip. On each frame, we add that tip plot to [trace] for subsequent rendering; see 176-185.  This overlay trace rendering allows a visual comparison between the wheelie chain drawing and the benchmark arabesque scribed on the back plate. See 97-121.   
  131-210 Animation Loop: In the animation loop, we: (1) culminate coefficients, creating wheelie chain hinge points; see 138-144 (2) update the trace buffer with the current tip position for this frame; see 149 (3) Fetch a copy of the back plate from off image list storage; see 150 (4) with sufficient trace points accumulated, render the wheelie chain tip trace from plot points accumulated from previous frames; see 153-171 (5) render "hinges" at the joins of the wheelie chain; see 189-202 (6) Increment coefficient orientations by rotor vector angles; see 209 and 81-91.  
  138-144 Culminate Coefficients: A very expedient way to convert a cardinal sum into a wheelie chain is to just accumulate the coefficients into a progressive sum by means of G'MIC's cumulate command; see 141. Each intermediary sum locates a hinge point between successive delta wheelies; rendering line segments from hinge point to hinge point visualizes the delta wheelie chain itself.

We actually animate the cardinal sum by adding pre-computed "smidgen angles" to the constant angular component of each cardinal sum term; these "smidgen angles" are scaled from the rotational rate of each term; see Tomb-Raiding the Dynamic Array, 81-91. For each frame in the animation loop we step the orientations of each cardinal sum term by a "smidgin angle", see 209, culminate the adjusted cardinal sum terms, then render the trace, chain, and hinge markers. The culmination method here follows from the distinction between delta wheelies and cardinal sum terms; the line segment between hinges elucidates a pairwise difference between successive cardinal sum terms and is the delta wheelie "connecting" the paired terms. The [dwchain] image houses the hinge plot points. As noted, these are regenerated at the top of the animation loop, once every frame, after stepping the orientations.
  149-170 Mirroring Paths for Fun and Profit: The last hinge point corresponds to the wheelie chain's tip. For each frame, we copy this last hinge point from the coefficient array to the end of the trace buffer. If more than one trace point has accumulated, we render the entire point set via polygon().

The two crop commands, 156-157, may mystify. When given k plot points to render the connecting k–1 line segments, polygon() automatically closes the loop with a k th additional segment, this running from point k–1 to 0: the last to the first point. Strictly speaking, that is exactly what polygon() has been designed to do: make closed polygons. That being so, if one should still wish for (insist on?) "open" polygons, sans closing segments, then the textbook recommendation suggests calling polygon() k–1 times for k plots, passing it each time successive pairs of plot points. That works, but is not an optimal approach. It is quite a bit faster to call polygon()once — with, say, 2×k plots than k–1 times with point pairs, as that doubled data set still bypasses much overhead from polygon() setup-and-teardown. The doubled data set, 2×k, arises from mirroring the path, so as to first walk forward along the unmirrored plots, and then trace steps backward along the mirrored plots. polygon() still closes the polygon, but since the first and last points in the data set coincide, there is no visual effect and we seem to have an open polygon. This doubled data set, with one half the plot points the mirror of the other half, furnishes a means of overcoming a polygon() shortcoming: out of the box, it draws single pixel wide lines. With a mirrored dataset, one can displace the outbound half in one direction along miter lines and the other half in the opposite direction; a supplied width parameter controls centerline displacement. There are details: the size of the bisecting angle between successive line segments affects the amount of displacement; it is necessary to scale the furnished width as a function of the bisecting angle. Look for particulars in an upcoming cheatsheet on variable-width line plotting with polygon().

The second crop at line 157 mirrors the data set; how this is so may not be immediately clear. The command trims images, to be sure, but not just to make smaller images. One may also crop from smaller to larger dimensions, and in that case, boundary policies come into play. In the present case, we crop a column vector image with height h to a double-height image: 2×h. G'MIC needs to know how to fill the new pixels: the mirror boundary policy, selected by the last crop argument of 3, instructs G'MIC to fill the new space with a mirror of the old. We get a walk-back data set in one fell swoop.                
  174-204 Annotating the Frame: From this point forward, we embark upon a succession polyon()or ellipse() -based plottings to annotate the back plate with the dynamic elements of the animation. At 150, the top of the animation loop, we input the named image variable $canvastemplate, introducing a copy of the animation back plate drawn at 97-121; see the article Plot a Back Plate Image and the store command. With a fresh copy of the back plate, we harness polygon()to render the current wheelie chain trace, 162-171, and the wheelie chain proper, 176-185. we employ ellipse() to dot the hinge plot points.

These three drawing routines all employ the hinge plot points inhabiting the [dwchain] vector image computed at the top of the animation; see 138-144, Culminate Coefficients. We name the backplate frame to enroll the image into the animation sequence. After we have exited the animation loop, 215, we invoke remove [^frame] to purge the image list of everything but animation frames.    
  209 Stepping the Animation: Adding the [rotors] and [coefficients] column vector images steps the orientation angles of the cardinal sum, adding the celebrated "smidgen angles" 2πfΔt to each term. See the run-up to animation loop, 81-91, Tomb-Raiding the Dynamic Array , for the setup behind this addition.

If you wish to reproduce the lead-in animation to this article, you'd write (something) like:

animate_ampersand:
    -command scripts/wheelie_animchain.gmic # Load gtutor_mkanimchain command.
    -input_csv images/goudyampersand.csv,0  # See 'download the CSV' link.
    -name. ampersand                        # Name for plot points image.
    -mul[ampersand] 2                       # Scale plots to suite — or not.
    -split[ampersand] x                     # Real, imaginary ⇒ two images
    -fft[ampersand,ampersand_c1]            # spatial ⇒ spectral
    -append[ampersand,ampersand_c1] c       # recombine real, imaginary
    -input 680,680,1,3,lerp([110,220,90],[255,255,210],y/(h-1))
                                            # Make blended background canvas
    -name. canvas                           # Convenient canvas name
    -gtutor_mkchainanim[canvas] [ampersand],720,{(h#$ampersand/2)}
                                            # 720 frames, highpass @ Nyquist freq.
    -resize2dx 50%,5                        # scale frames: cheap antialiasing.
    -output[frame] ampersand.mp4,30,h264    # All animation images named 'frame'
So:

$ gmic -command animate_ampersand.gmic animate_ampersand

drops a 720 frame, 340×340 H264 encoded .mp4 video named ampersand.mp4 in your current directory, one that runs at 30 frames per second. animate_ampersand assumes that your plotting files reside in an image subdirectory and the G'MIC scripts retrieved from this Cookbook article reside in a script directory; put them where you like and amend animate_ampersand accordingly.

gtutor_mkchainanim, is listed and annotated here; pull down the blue detail blocks for that and the companion annotations. For the impatient, the arguments given to gtutor_mkchainanim are:

Argument #Notes
 1.  A 1D spectral domain image, 1×2n×1×2 dimensions, encoding the line drawing and from which the animated wheelie chain derives. ½ the height of the spectral image, n, reflects the Nyquist Sampling Rate of the spectral data set. This spectral image typically comes from a discrete time, forward Fourier transform fft of 2D path plots; these are taken to be [real, imaginary] position vector coordinates plotting the line drawing on the complex plane.
 2.  A positive integer, sets the number of constituent frames in the animation and defaults to 300. Such a number accounts for about thirty seconds at ≈29.97 frames per second.
 3.  A  positive integer, sets the low frequency passband width in Hertz; its useful range is 1 ⇒ n–1. Lower passband settings supress high frequency content in the line drawing's path, giving rise to low-resolution, "loopy" paths. In this example, the math expression {(h#$ampersand/2)} sets the bandpass argument to one half the height of the spectral image, 1024, the Nyquist Sampling rate. This essentially admits all frequencies. An argument of 16 admits only the lowest 16 positive and negative rotation rates, 32 samples for a low-resolution, "loopy" path.  
 Selection  The selected image establishes the format of animation. It serves as a backplate for a series of Porter/Duff "over" compositing operations. Backplate imagery persists, unanimated, throughout the run. Draw on it what you may, but be aware that a preimage of the path goes on the backplate as well.
 Output  The command places on the G'MIC image list a series of animation frames tagged with the name frame.

Another way to regard argument 3 may be: "What's the slowest rotational rate I can make use of for a particular fidelity ?" The limit given to argument 3 directs gtutor_mkchainanim to remove from the chain those clockwise and counterclockwise wheelies rotating at faster rates: an argument of 8 limits the chain to wheelies rotating eight times per period or less: leaving as many as sixteen links, all told, a doubling allowing for both clockwise and counterclockwise rotating wheelies.

PassbandResultsPassbandResults
128 32
 16  8
  4  2

18. Ampersands Shedding Higher Frequencies
Unsurprisingly, the effect recalls the related attenuation of progressively bandpass-limited images, rather like a calligrapher's cumulative prostration over scribing — yet again! — some damn 1911 Goudy Bookletter Ampersand, and the increasingly imperfect regards held for that objurgated technical writer insisting upon it — and apparently obsessed with the thing. Indeed, the ampersand that is bandpass-limited to 128 frequency domain samples comes to the fore with just 12½ % of the 1024 samples generating the frontpiece image. Yet the degradation is barely detectable. Even the 32 sample rendition, at 3⅛ % of the original sampling, can pass for a Goudy Ampersand from a tired hand. Further eliminations lead to sudden, scrawling collapse, yet just eight samples can still pass for some kind of ampersand, though Frederic Goudy likely would not recognize it as any design that came from his hand.

It is one thing to behold Homer Simpson emerging from a Ptolemaic system of deferents and epicycles. That's cool! But it is a coolness that is somehow not one's own. It can be appreciated, that's all, but not possessed. For that there needs to be a preoccupying curiosity to glean how what is came to be. That grants title, and with that prize in hand there arises a playfulness for what, some day, might transpire. Even if these are just wheelies, you can make them do anything. They're yours.

Previous:From Arabesques, Wheelies                                        

Updated: Sun 22-January-2023 22:22:58 UTC Commit: 846e5989e3a4
G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing

G'MIC is an open-source software distributed under the CeCILL free software licenses (LGPL-like and/or
GPL-compatible). Copyrights (C) Since July 2008, David Tschumperlé - GREYC UMR CNRS 6072, Image Team.