Not wasted for me. I’m a writer. I explain stuff — just for the sheer jollies. And — who knows? — I might trigger one of your famously light-hearted and insouciant animations that brings such songs to my heart.
What kind of songs — ? Um. Let’s not go there…
Ah! So, the typewriter game. Hit a key. The face of the typebar strikes the paper, impressing some glyph upon it. Then the carriage translates at a set displacement, as is the wont for monspaced fonts. At the right margin, a carriage return removes all those displacements, translating back to the start point, then translates to the next row by the same set displacement, but vertical. So goes the catalogue of translations, cyclically applied, uniform: perhaps left-left-left-left-4-times-right-and-down, then again, and again, and again `til arms are bloody stumps, perhaps.
Game pieces:
- An image, rectangular, with arbitrary dimensions w and h.
a. Within its confines, we step-and-repeat, these with constant-magnitude displacements. We’d rather not go off edges. Sizing exercises for steps and margins are in the offing: See2.
of Game play. - A glyph, an image bound to the head of a typebar. This typewriter has one typebar — one glyph. We got the typewriter on the cheap, but it’s not very versatile.
a. Glyphs consist of (maybe very long) position sequences of (x,y) pairs. They mark out circles, ellipses, squares, letter forms — maybe even fancy filigrees.
b. These plots occupy the confines of an origin-centered, ±unit square, a bounding box, two units on a side. The values of these x, y coordinates all fall within the closed interval [-1,…,1]. - Two-channel path images store such glyphs. These occupy G’MIC’s stacks as would any other image. x coordinates occupy channel 0; y coordinates occupy channel 1; a pixel encapsulates the coordinates of one plot.
a. G’MIC’scurve
command consumes glyphs and plays a connect-the-dots game with these plots, drawing the glyph.
Game play:
- Plot glyphs so many times across the image (the sx step count) and and so many times down (the sy step count). sx and sy counts are integers and may or may not be equal.
- Sizing: To reconcile typebar faces and sx × sy step counts with image dimensions w and h, we (1) divide the image height h by sy, (2) the image width w by sx and (3) take the smaller of ratios w/sx or h/sy.
a. That minimum establishes the stride of carriage translations; the stride also establishes the side lengths of the bounding boxes encompassing glyphs. It ensures that cumulative measures of the larger step counts fit within corresponding image dimensions.
b. There is a contention between these sx × sy step counts, the image dimensions w and h and the stride. The image rectangle, perhaps, can be very much wider than it is high — or vice versa. Nonetheless, there are stride specifications where sy >> sx or sx >> sy. Sigh. Oh. Woe and Sorrow. To fit big step counts in correspondingly small image dimensions, the calamaty gives rise to tiny strides and little bounding boxes and unseemingly huge margins. Not pretty, but people can be silly.
Implementation:
A custom command, stampit
in steprepeat.gmic
carries out the game play. For example, here is a 5 × 3 stamping of a glyph rotated at 11° on a 979 × 1104 image.
gmic -m steprepeat.gmic {979*2},{1103*2+1},1,4 stampit. 5,3,11 r2dx. 50%,5 o. stampit_5-3-11.png
Output image [0] as png file 'stampit_5-3-11.png' (1 image 979x1104x1x4).
The smallest stride arises along the horizontal dimension, so there are large top-and-bottom margins.
A custom command, steprepeat
, implemented below, performs the step-and-repeat pattern. It harnesses a fixed path image — the glyph — here rendered orange and generated by a gtutor_mkspectral
/gtutor_specplot
command pair. These ABN Filigree extracts fabricate paths from spinner chains (aka “wheelies”). How they come about is a story full of prodigious interest and indelible charm, but it is not the story here. The story here is playing the typewriter game with glyphs: the step-wise transit of these from place to place and how one might do that without bloodying oneself at a keyboard.
steprepeat.gmic
stampit: check isint(${1=1})" && "\
${1}>0" && "\
isint(${2=1})" && "\
${2}>0" && "\
isnum(${3=0})
stpx,stpy,rot=${1-3} # stepx, stepy and rotation in degrees.
foreach {
-name. canvas
# mkspectral + specplot: Plots the path made by the tip of the last
# spinner on a chain of spinners.
# Path is in an origin-centered, ±unit square and is 2 units
# on a side, each straddling the x or y axes. Arguments: 250
# plots: path computed from three linked spinners each ≈1/3 in
# length, all with 0° orientation and each spinning at 1, 3 and -13
# Hertz, respectively.
-gtutor_mkspectral 250,{0.95/3},0,1,{0.95/3},0,3,{0.95/3},0,-13 # filigree
# -gtutor_mkspectral 120,{0.95/2},0,1,{0.95/2},0,3 # 3-petal rose
# -gtutor_mkspectral 60,{0.95/3},0,1,{0.95/3},0,2,{0.95/3},0,-4 # bowtie
# -gtutor_mkspectral 20,{0.95},0,1 # circle
-gtutor_specplot. # Spectral ⇒ temporal conversion. Selected now a path.
-name. plots
-steprepeat[canvas] [plots],$stpx,$stpy,$rot
-remove[plots]
}
steprepeat : check ${"is_image_arg $1"}" && "\
isint(${2=1})" && "\
${2}>=0" && "\
isint(${3=1})" && "\
${3}>=0" && "\
isint(${3=1})" && "\
${4}>=0
sx,sy,srot=${2-4} # Horizontal, vertical step counts and rotation angle, degrees.
e[^-1] "Step and repeat given path $1 on image$? $2 times across and $3 times down."
pass$1
foreach {
nm={n}
-name. canvas
w,h:=w#$canvas,h#$canvas # canvas dimensions: pels
dx:=round($w/$sx,1,-1) # across step length in pels, nearest lower whole pel.
dy:=round($h/$sy,1,-1) # down step length in pels, nearest lower whole pel.
s:=min($dx,$dy) # minimum across or down step lengths. s: stride. s × s: bounding box.
mx:=($w-$s*$sx)/2 # across margin: (img. width – ( step length × horiz.step count.))/2
my:=($h-$s*$sy)/2 # down margin: (img. height – ( step length × vert. step count.))/2
pass. # path plotted in the origin-centered ±unit square
-name. plots
# Plots for a box that is just a slight smidgen smaller than
# the bounding box. Visualization aid. Rotate, scale, shear,
# translate along with any other path.
-input (-0.99,-0.99^-0.99,0.99^0.99,0.99^0.99,-0.99^-0.99,-0.99)
-name. box
-permute[box] cyzx # swap x,y pairs from width-wise to spectral- (channel-) wise.
# Set up initial transform from ± unit square to bounding box
-moveglyph[plots,box] {$s/2},{-$s/2},{$mx+$s/2},{$my+$s/2},$srot
-repeat {$sy} {
-repeat {$sx} {
-curve[canvas] [plots],4,0,0,1,1,255,200,0,255
-curve[canvas] [box],4,0,0,1,1,0,200,255,64
-moveglyph[plots,box] 1,1,$s,0 # Step plots, box, once, +horizonal
}
-moveglyph[plots,box] 1,1,{-$sx*$s},$s # Step plots, box, -horizontal by one row,
# then step +vertical, once.
}
-keep[canvas]
-name[canvas] $nm
}
remove.
# Compose an affine transformation matrix and apply same
# to a path.
moveglyph : skip ${1=1},${2=1},${3=0},${4=0},${5=0}
sx,sy,tx,ty,r=${1-5}
foreach {
nm={n}
-name. plots
-fill[plots] ">
begin(
M=eye(3);
M[0]=$sx;
M[2]=$tx;
M[4]=$sy;
M[5]=$ty;
$r>0?M=mul(M,rot([0,0,1],$r°),3)
);
(M*[I(x,y),1])[0,2]
"
-name[plots] $nm
}
camtrak:
foreach {
name. canvas
bbs:=min(w#$canvas,h#$canvas)
steps=200
# Make glyph
-gtutor_mkspectral {$steps},0.7,0,1,0.3,0,-4
-name. glyph
-gtutor_specplot[glyph]
# Make path
-gtutor_mkspectral {0.375*$steps},0.9,0,1,0.1,0,-3
-name. track
-gtutor_specplot[track]
# Scale and translate glyph and track into position
-moveglyph[track] {0.4*$bbs},{-0.4*$bbs},{0.4375*w#$canvas},{0.4375*h#$canvas}
-moveglyph[glyph] {0.1*$bbs},{-0.1*$bbs},{I(#$track,0,0)}
# Make (Δx,Δy) displacements to step glyph along track
+shift[track] -1,0,0,0,2
-sub[-1] [-2]
-crop. 1,100%
-name. tangents
# Step and repeat
-repeat {w#$tangents} {
-curve[canvas] [glyph],5,30,50%,0,1,255,64,200
dx,dy:=I(#$tangents,$>+1,0)
-moveglyph[glyph] 1,1,$dx,$dy
}
}
Here’s a key bit. Applying affine transforms to path images can translate them from place to place. Or rotate them around a center point. Or scale them outward or toward critical points. Or shear them vertically or horizontally along center lines. Or do, as an indivisible action, any combination of these affine transformations. Here, the verb “apply”, stands for the action of rewriting the x and y coordinates taken from an initial path image and altering these, in place, to reflect the desired motion. Plotting the revised path image follows, perhaps using curve or some such.
Here’s another key bit. By the agency of revising x and y coordinates, path images retain “memory” of all applied transforms, these back to the initially created tabula rasa. path image. This gives step-and-repeat tasks like the typewriter game a straight forward play:
-
Get glyph A (made by hand, like
box
, or, likeplots
, obtained wholesale from a
gtutor_mkspectral
/gtutor_specplot
or similar glyph-generating toolset). -
Scale and translate glyph A from the origin-centered, ±unit square to the bounding box
interior. By virtue of their rewriting, the path image coordinates retain these
scalings and translations. -
repeat for vertical_steps:
a. repeat for horizontal_steps:
i. plot glyph A
ii. translate glyph A horizontally 1 × stride
Done
b. translate glyph A –(horizontal_steps × stride)
c. translate glyph A vertically 1 × stride
Done
The custom command that does the ‘Apply affine transform’ operation is moveglyph
. It is a wrapper that hides the mechanics of manipulating affine matrices. Give it a scaling, translation or a rotation (or all three). moveglyph
sets up the appropriate affine transform matrix and applies the encoded transforms to the selected image, which is expected to be a path. Plot the path. apply another transform, plot the revised path. Apply another transform. ⇒ ∞. In a sense, the path retains the cumulative effect of the entire sequence of applied transforms.
Affine transforms descend from the garden variety 2×2 rotational/scaling matrices which David illustrated in Post 178 and Post 179 but are “augmented” in the sense that they are denizens of three dimensions and nominally operate on 3D points: (x, y, z) . The outcome of such augmented machinery are projections of 3D points onto 2D surfaces. The extra mechanics permit the inclusion of translation to the pantheon of rotational and scaling transforms, so that affine matrices can, in a uniform way, encode sequences of all three transforms, engendering shapes or motion of a subline character. But users of moveglyph
need not worry about this back story; just provide a path image and ask for some transformation flavor and build up composite transforms in their path images.
Such augmentation opens up new possibilities for ABN Filigrees. In future incantations, that filter will furnish filigreed borders of the ilk shown in Squaring the Circle, except that the border need not be square, nor the filigree element a circle, and the whole enterprise can operate in resolution-independent vector graphics, a way of working that is opening up in G’MIC. The logistics of having a shape follow another shape as a guiding path is not difficult to achieve in this scheme of stepping path images with sequences of affine transforms, as the following toy, camtrak
illustrates (see steprepeat.gmic
, above, for the implementation):
gmic -m steprepeat.gmic 2048,2048,1,3 camtrak. r2dx[0] 50%,5 o. filigree.jpg
Paths:
Glyph and Track
EDIT: Originally posted with wrong glyph-and-track graphic.