The Edges of Plausibility

Exploring the boundaries of filters, animation, gradients, patterns and script in SVG


Abstract


As an expressive medium, SVG with its filters, gradients, patterns, animation and scripting offers the author great flexibility.

This paper considers the extent to which several special effects may be produced by the combination of various of these aspects of SVG's arsenal. It is compendium of general and specialized solutions or approaches to a broad range of problems, as well as some suggestions for where the SVG standard might be expanded.

More generally the paper will seek to explore some of the limitations or boundaries of filters, gradients, and SMIL animation. In general, the investigations have proceeded from the premise, following some sense of parsimony or elegance, that if something can be done with declarative animation, that is preferable to the use of script. Another general premise at play is that less markup is better than more for the same task -- reuse through either grouping, the use tag, or script is beneficial if it results in smaller and more readable source code.

Among the topics covered will be short case studies showing how to make or use the following:


Table of Contents

Introduction
simulated cylindrical rotation
animating the positions of stops
moving parts of the surface
dragging a texture through a clipPath
linear motion that appears nonlinear
circular motion with SMIL but without rotation
nondeterministic landscapes:
text animated along a curvilinear path
animated posterization of a bitmap
natural textures
recycled animations
non-rectangular tilings
gradients that are neither linear nor radial
clippaths to simulate nonaffine warping
animations that traverse nondeterministic bezier patterns
Conclusion
References

The SVG specifications give the author many choices in how particular effects might be accomplished. Sometimes, owing to a bounty of options, we have more than one way to do things. Other times, perhaps for the same reason, it may be difficult, at first glance to determine whether or not a given effect may be accomplished with the various tools at one's disposal. In the examples that follow, I seek to explore, briefly, approaches to a wide assortment of problems, some practical, some frivolous, in hopes both of illustrating how broad the applicability of SVG is, and of displaying some solutions to problems that others may find instructive in some way.

It is important to remind the reader now (and perhaps again) that effects involving SMIL will only work (at the time of this writing) in the Opera and IE/ASV browsers and that support for filters outside those browsers is limited. Almost all these examples work both in IE/ASV and Opera, though very many do not yet work in Safari or Firefox because of their use of either SMIL or filters which those browsers do not yet support. No cause for concern, though. All browsers will, in time, support SVG including its more ambitious parts. It simply makes sense!

Suppose we wished to illustrate a cylinder in SVG. An intrinsically three dimensional object, the cylinder as viewed from a face-on projection, is merely a rectangle. From this perspective, to distinguish the cylinder from the rectangle we might typically do so using a linear gradient containing three or more stops:

            
<linearGradient id="V">
<stop stop-color="#111" offset="0"/>
<stop stop-color="#a41" offset=".1"/>
<stop stop-color="#f41" offset=".4"/>
<stop stop-color="#a41" offset=".7"/>
<stop stop-color="#111" offset="1"/>
</linearGradient>
<rect id="BAR" x="300" y="0" height="100%" width="15%" fill="url(#V)"/>

Figure 1.  A static cylinder using a linearGradient\

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


Now to convey the impression that such a cylinder is rotating, we might imagine several approaches. It could be done either by moving parts of its ostensible surface around one another, by animating and inserting stops into a gradient or by moving gradient tiles through a clipPath.

In the example of the above code we have three stops in a linear gradient, so that colors are arranged in the order black-red-black. Suppose we could move the red stop from left to right, then for example, vary its color to blue and then move that gradient from left to right and repeat this process, sliding stops leftward, and then swapping colors. This might present the impression of a rotating cylinder which is painted differently on its two opposite sides.

                        
<linearGradient id="V">
<stop stop-color="#111" offset="0"/>
<stop stop-color="#f41" offset=".4">
<animate attributeName="offset" dur="2" values="0;.9" repeatCount="indefinite" />
<animate attributeName="stop-color" dur="4" values="red;red;blue;blue" keyTimes="0;2;2;4" fill="freeze" repeatCount="indefinite" />
</stop>
<stop stop-color="#111" offset="1"/>
</linearGradient>
<rect id="BAR" x="100" y="0" height="100%" width="15%" fill="url(#V)"/>

The concept is that the objects are synchronized so that the top and bottom ones move together but the middle one is one-half cycle behind the others.

Figure 2. Sliding stops; swapping colors.

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


The above illustrates the basic concept. It is easy to see that this approach appears rather jerky: the visual center of the gradient (the stop) moves uniformly from left to right and then suddenly jumps (despite the color change) to the opposite side again.

We might attempt to adjust the number of stops (to make the transitions smoother) and also to allow the colors to be "passed" from stop to stop, by animating the colors of the stops thusly:

                    
<linearGradient id="V" gradientTransform="rotate(0)">
<stop stop-color="#111" offset="0">
<animateColor attributeName="stop-color" dur="4"
values="yellow; #118; #black; red" repeatCount="indefinite"/>
</stop>
<stop stop-color="#911" offset="0">
<animate attributeName="offset" id="one" dur="2"
values="0;.7" repeatCount="indefinite" />
<animateColor attributeName="stop-color" dur="4"
values="#118; #black; red;yellow" repeatCount="indefinite"/>
</stop>
<stop stop-color="#911" offset="0">
<animate attributeName="offset" id="two" dur="2"
values=".7;1" repeatCount="indefinite" />
<animateColor attributeName="stop-color" dur="4"
values=" #black; red; yellow; #118" repeatCount="indefinite"/>
</stop>
<stop id="FIN" stop-color="#990" offset="1">
<animateColor attributeName="stop-color" dur="4"
values="red;yellow; #118; #f8f" repeatCount="indefinite"/>
</stop>
</linearGradient>
<rect id="BAR" x="300" y="0" height="150%" width="15%" fill="url(#V)"/>

Unfortunately, the effect as illustrated here remains unconvincing.

Continuing the approach a bit further with the use of script, the following example, is the best instance of this approach I've been able to create:

Figure 3. Sliding stops; swapping colors.

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


The script goes into the DOM and retrieves all the stops within the gradient and swaps them thusly:

                
Colors=["red","yellow","black", "#118"]
count=0
function rebuild(evt){
var N=Colors.length
count=(count+1)%N
var F=document.getElementById("V")
var stops=F.getElementsByTagName("stop")
for(i=0;i<stops.length;i++){
stops.item(i).setAttribute("stop-color", Colors[(N+count-i)%N])
}
var P=stops.item(1).firstChild.nextSibling
P.setAttribute("begin","indefinite")
P.beginElement();
}

The animation in one of the stops serves as a sort of coxswain, both coordinating the passing of colors and the restarting of the other animations.

                
<linearGradient id="V" gradientTransform="rotate(0)">
<stop stop-color="#111" offset="0"/>
<stop stop-color="#911" offset="0">
<animate attributeName="offset" id="one" dur="2" onend="rebuild(evt)"
values="0;.7" repeatCount="1" />
</stop>
<stop stop-color="#911" offset="0">
<animate attributeName="offset" id="two" dur="2" begin="one.begin"
values=".7;1" repeatCount="1" />
</stop>
<stop id="FIN" stop-color="#990" offset="1"/>
</linearGradient>

An approach without script would be more elegant, but as yet, I have not pulled it off to my satisfaction.

In 2007 Jeff Schiller posed an interesting question in the svg-developers forum in Yahoo-groups. The question was whether or not one could (without script) animate a series of objects that move first in front of and later behind one another. Following the discussion that ensued, Bruce Rindahl posted a link to some code that accomplished the task. That approach can be used to simulate cylindrical rotation. This listing preserves the essence of that approach by allowing two rectangles to sit atop one another with the top most rectangle becoming invisible during precisely half of its lifespan, hence allowing the middle object to appear in front:

                    
<rect x="205" width="100" height="60%" y="20%" fill="#97f" opacity=".9">
<animate attributeName="x" dur="4"
values="205;300;205" repeatCount="indefinite" />
</rect>

<rect x="205" width="100" height="60%" y="20%" fill="#ae7" opacity=".8">
<animate attributeName="x" dur="4" begin="2"
values="205;300;205" repeatCount="indefinite" />
</rect>

<rect x="205" width="100" height="60%" y="20%" fill="#97f" opacity=".8">
<animate attributeName="x" dur="4"
values="205;300;205" repeatCount="indefinite" />
<animate attributeName="visibility" dur="4"
values="visible;hidden" repeatCount="indefinite" />
</rect>

The concept is that the objects are synchronized so that the top and bottom ones move together but the middle one is one-half cycle behind the others.

Figure 4. A carousel shell-game

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


I rather doubt that the XSLT translation from DocBook to XHTML will preserve a working illustration, so if your browser cannot see the SMIL animation above, then you may also look at a working example: here.

The approach that seems to have been most successful consists of drawing, on a rectangle, a gradient for which the left and right sides match. That is, if we were to identify the left and right edges of the rectangle as if folding gently into a cylinder, the resulting gradient would be continuous around the cylinder. Then, all that is required is that we move that rectangle through a viewing window (a clipPath) that is half as wide as the rectangle, taking care to loop the rectangle back to its starting position when it is done. I first devised this approach in about 2001 at this html page using bitmapped graphics where the left and right edges had been mushed together so as to remove the seam when a copy of the image was pasted beside itself. The ideas of wrapping edges of the computer screen is at least as old as the video game Asteroids and the underlying concepts date back to the early days of topology centuries ago.

Here's the basic premise implemented in code:

                    
<linearGradient id="W">
<stop stop-color="#aaa" offset="0"/>
<stop stop-color="#533" offset=".25"/>
<stop stop-color="#aaa" offset=".50"/>
<stop stop-color="#533" offset=".75"/>
<stop stop-color="#aaa" offset="1"/>
</linearGradient>
<clipPath id="CP">
<rect x="45%" y="0" height="100%" width="10%"/>
</clipPath>
<rect y="0" height="100%" width="20%" fill="url(#W)" clip-path="url(#CP)">
<animate attributeName="x" dur="2s" values="35%;45%" repeatCount="indefinite" />
</rect>

Notice that the rectangle is exactly twice as wide as the clipPath and that the rect is dragged so that it always covers the clipPath. When it reaches its rightmost extent (45%) it resets promptly back to its leftmost extent (35%). Its rightmost extent coincides with the left edge of the clipPath -- the hole through which we view it.

The above code is illustrated by the example here:

Figure 5. Dragging through a clipPath

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


If this example is not visible in your browser (for example it doesn't show up in DocBook apparently because of the clipPath) then take a look here.

Some final improvements are natural.

First we may fill the rectangle with a more interesting texture. For this I chose a barber pole coloration: red, white and blue stripes.

       
<g id="base">
<rect x="0" y="0" height="40" width="30%" fill="#e12"/>
<rect x="0" y="40" height="35" width="30%" fill="#ddd"/>
<rect x="0" y="75" height="40" width="30%" fill="#11c"/>
<rect x="0" y="115" height="35" width="30%" fill="#ddd"/>
</g>

Second, instead of using a gradient, we will use a pattern to fill our cylinder, so that rather than sliding a rectangle with texture through a clipPath, we may apply an animateTransform to translate the pattern leftward.

                    
<pattern id="barber" patternUnits="userSpaceOnUse" width="30%" height="150" >
<use xlink:href="#bshop" transform="translate(0,-50)">
<animateTransform attributeName="transform" type="translate"
dur="2s" from="0 0" to="-150 0" repeatCount="indefinite"/>
</use>
</pattern>
<rect x="60%" y="0" height="100%" width="10%" fill="url(#barber)"/>

Third, we may skew the gradient so that it appears to wrap around the cylinder helictically. This necessitates replicating the stripes contained in "base" a few times so that the skew does not produce undefined areas in the corners of the rectangle to which it is applied.

                    
<g id="bshop" transform="skewY(45)">
<use xlink:href="#base"/>
<use xlink:href="#base" transform="translate(0,-150)"/>
<use xlink:href="#base" transform="translate(0,150)"/>
<use xlink:href="#base" transform="translate(0,-300)"/>
</g>

Fourth, since the resulting figure, is thus far, very flat in appearance, we may overlay a masking region, in this case, a rectangle with bright transparency in the middle and darkness on either edge:

                    
<linearGradient id="gradient1" >
<stop offset="0" stop-color="black"/>
<stop offset="0.3" stop-color="white" stop-opacity="0"/>
<stop offset="0.4" stop-color="white" stop-opacity=".8"/>
<stop offset="0.6" stop-color="white" stop-opacity=".0"/>
<stop offset="1" stop-color="black"/>
</linearGradient>
<rect x="60%" y="0" width="10%" height="100%" fill="url(#gradient1)"/>

This rectangle has the same dimensions as the rectangle that has been striped with the #barber pattern, and being appended later in the DOM, it lays directly above that striped rectangle.

Finally, we add a bit more texture to the basic tiling of the pattern. This is because if one implements all of the above, the resulting barber-pole (true to the popular illusion the moving barber pole presents) appears to have its strips simply moving downward. Without a bit of additional texture, the drawing is so smooth that the grain of the cylinder cannot be perceived.

                        
<g id="base">
<rect x="0" y="0" height="40" width="30%" fill="#e12"/>
<rect x="0" y="40" height="35" width="30%" fill="#ddd"/>
<rect x="0" y="75" height="40" width="30%" fill="#11c"/>
<rect x="0" y="115" height="35" width="30%" fill="#ddd"/>
<circle cx="120" cy="22" r="2" fill="brown"/>
<circle cx="140" cy="72" r="1" fill="black"/>
<circle cx="60" cy="122" r="1" fill="brown"/>
<circle cx="40" cy="252" r="1" fill="yellow"/>
<ellipse cx="80" cy="32" rx=".5" ry="17" fill="#282"/>
</g>

By adding small "imperfections" (small ovals and circles) to its otherwise smooth surface, the eye will be able to see that we are, in fact, rotating leftward, rather than merely dragging and infinite striped tiling downward.

The resulting effect, as animated by SMIL is, now, rather convincing.

Figure 6. Rotating barber pole

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


By allowing objects to move over an animated gradient that uses a displacement filter to adjust their positions, a variety of nonlinear motion may be accomplished without script.

First, let's look at the movement of a simple unfiltered object whose x and y coordinates are propelled by SMIL (the live code can be seen at this location) :

                
<rect x="0" y="5" height="90" width="90" fill="none" stroke="#888" stroke-width="8"/>
<rect id="r" height="50" width="50" fill="url(#V)"/>
<circle r="5" fill="#39d" >
<animate attributeName="cy" dur="6.5s" values="90; 10; 90"
repeatCount="indefinite"/>
<animate attributeName="cx" dur="4.7s" values="0; 90; 0"
repeatCount="indefinite"/>
</circle>

So long as the periodicities of cx and cy (in this case and ) are not multiples of one another (or of a large common divisor) then the movement of the object will resemble the bouncing of a billiard ball. This sort of piecewise linear motion is typical of this sort of animation. We could with relative ease, append circular and linear components to the motion by having one of these linear animations turn itself off at the end of an iteration and then, in turn activate an animateTransform which would rotate the object circularly for a certain duration, but the class of scriptless motions afforded is still rather constrained. In the next section, we'll look at how time signatures can change the nature of the motion further, but in the meantime consider how an object might be deformed through an feDisplacement filter as it moves over a gradient:

                    
<defs id="DEF">
<linearGradient id="V" gradientTransform="rotate(25)" >
<stop stop-color="#000" offset=".3"/>
<stop stop-color="#a60" offset=".5" />
<stop stop-color="#000" offset=".96"/>
</linearGradient>
<filter id="D" filterUnits="userSpaceOnUse" x="-25%" width="150%" >
<feImage xlink:href="#r" result="M" />
<feDisplacementMap in="SourceGraphic" in2="M" scale="27"
xChannelSelector="R" yChannelSelector="G"/>
</filter>
</defs>
<rect x="0" y="5" height="90" width="90" fill="none" stroke="#888" stroke-width="8"/>
<rect id="r" x="0" y="5" height="90" width="90" fill="url(#V)"/>
<g id="F" transform="translate(13,13)" fill="yellowgreen" >
<circle r="4" id="B" fill="inherit" >
<animate attributeName="cy" dur="8.5s" values="80; -5; 80"
repeatCount="indefinite"/>
<animate attributeName="cx" dur="10.7s" values="-10; 75; -10"
repeatCount="indefinite"/>
</circle>
</g>
<use xlink:href="#B" opacity=".75" filter='url(#D)' fill="purple"/>


In this example (which can be seen live at this location), two circles are animated in the same way. One is yellow-green, the other purple. The purple one, though, has its path "disrupted" by an feDisplacementMap: as it slides over a ramp that goes from black to orange to black again, its x and y positions are displaced according to the magnitude of the redness and greenness underneath. One may observe from the example that the balls remain in synchrony and appear in about the same location when they are atop black content, but that their locations diverge most when the purple ball is over the brightest part of the orange ramp.

The possibilities for non-scripted but highly nonlinear motion based on such examples are clearly numerous, as one considers groupings, rotations, reuses, and patterns associated with various types of complex and composite gradients. We close this section with one more example (visible here, if the embedded version should not be visible) that take the above code but merely adds an animateTransform to rotate the angle of the gradient. Clearly, by using both SMIL and filters, one will need either Opera 9.5, IE/ASV, Batik, or possibly Renesis to view. It has been confirmed to work in the first two of these environments.

Figure 7. Rotating gradient to deflect bouncing balls

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


We might make an object traverse a circle in two obvious ways: we might use an <animateTransform> thusly:

                
<ellipse cx="180" cy="175" rx="30" ry="30" fill="blue">
<animateTransform attributeName="transform" type="rotate" dur="2" from="360,280,200" to="0,280,200" repeatCount="indefinite"/>
</ellipse>


We might have it traverse a circular path (as defined by an arc) thusly:

                
<path d="M200 100 A 150,150 0 1,0 201,99 z " id="C"
opacity="0.5" fill="#080" stroke="#8f8" stroke-width="5" />

<circle cx="7" cy="-5" r="20" fill="#8888ff" stroke="#44aa88">
<animateMotion dur="2s" rotate="auto" fill="freeze" repeatCount="indefinite" >
<mpath xlink:href="#C"/>
</animateMotion>
</ellipse>



But there is another approach that allows the consideration of a vast number of different curves as paths for movement. By adjusting the time signature of a SMIL animation, linear interpolation between two points can become nonlinear. This may be accomplished using the keySplines attribute, in combination with a set of keyTimes. Basically, this allows us to change the approach gradient, in time, of a particular keyTime, hence allowing a warp of the way the object traverses locations over time. Two examples are given.

In the first, presented because it is a bit simpler, we allow the "cx" attribute of a moving object (in this case an ellipse, though any object will work just as well) to oscillate from left to right in the normal way, but we make the vertical component "cy" oscillate with a warp defined on its time signature:

                
<circle cx="50%" cy="20" r="5%" fill="blue">
<animate attributeName="cx" dur="3" values="5%; 95%;5%"
repeatCount="indefinite" />
<animate attributeName="cy" dur="6s" values="5%; 95%;5%" keyTimes="0;0.5; 1"
calcMode="spline" keySplines="1 0 0 1; 0 0 1 1" repeatCount="indefinite" />
</circle>



The resulting behavior, over time, looks as follows:

Figure 8. Some key times, using keySplines

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


Here, the path followed during the first half of the animation is the path on which points 1,2,3,4,5, and 6 lie. After the midpoint of the animation, the blue circle returns to its initial position via the line 6, 7, 8, 9, 10. If calcMode=”spline” were not invoked, the animation would follow the path (10,9,8,7,6,7,8,9,10) repeatedly. The keySplines attribute contains a series of two control points in the (timein, timeout) plane, or four integers for every inter-keyTimes interval: in other words, we will have 4(n-1) integers for n keyTimes. Each pair of control points defines an approach gradient between the associated pair of key values. In this example, we will begin at (t=0, y=5%) and follow the spline attracted first by (t=1, y=5%) (all the while x is increasing linearly); then attracted by (t=0, y=95%) and finally ending up (at the end of the first transition) at (t=1, y=95%) namely halfway through the animation at position #6. (since x with half the duration has already been reset to its endpoint, 5%). The animation will speed up considerably between positions #3 and #4 since that is when the time deformation is greatest. The animation continues back along the two lines, since the keySplines path “0 0 1 1” does not alter the time sequence in the second half of the animation.

The second example contrasts conventional animation (the green ball is unwarped over time) with time-warped animation (the blue one has both its "cx" and "cy" attributes warped in the "cy" attribute is warped in the above example. In it the green ball traverses the black diamond, while the blue one follows the circular arc. We note the apparent change in speed for the blue ball -- I currently know of no way to normalize this.


It is fairly easy to create series of random piecewise monotonic functions from segments of bezier curves. Filling these with gradients can generate appealing animation on-the-fly.

In the inexpensive animation of television in the 1960's one can probably recall how the heroes might be chased by the villains across a backdrop that was used to give the illusion of motion. If one watched carefully, we could notice that the backdrop repeated itself every few seconds. The animation of the backdrop was run cyclically, presumably to save on the cost of human animators' time. We may accomplish something better with script. The example I will discuss is rather script intensive and I could not get my word-processor (Oxygen/DocBook) to insert the working example here, but it may be seen on the web at this location. The basic premise begins with a path that has been filled with a gradient:

                
<linearGradient id="foot" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0" stop-color="#b87"/>
<stop offset=".1" stop-color="#b63"/>
<stop offset=".70" stop-color="#c86"/>
<stop offset=".90" stop-color="#bc7"/>
</linearGradient>

<path id="foothills" d="M 0 500 400 400 800 500 z" fill="url(#foot)" stroke="none"/>



We will seek to redefine that path into a more complex sequence of Bezier segments that are randomly created, and changed on-the-fly so as to simulate the movement of background scenery. To do this involves a small trick: let us generate a series of (x,y) pairs as JavaScript objects. Suppose we would like those objects to become the contour of a mountainous horizon, then we wish the points to be ordered from left to right so that the resulting edge that will connect their top appears as a function y=f(x) which is everywhere defined over the domain x. We would clearly like our points to be connected by curves that are not just line segments (bezier segments seem quite more appealing) and we also do not want the intervals along the x-axis to all be uniform. What we may do then, is to generate a series of points uniformly over a rectangle, and then to sort those points (as rows in a spreadsheet) according to their x axis. This will select randomly from all discrete-valued functions defined over that domain x range rectangle. That the sort function can be done in n log n time signature, means that the operation will be fast.

                
function draw(peaks,hi,up){
Hills=document.getElementById("foothills")
Hi=hi //the height of the mountains from the ground
UP=up //the elevation of the ground
var points=3*peaks
for (i=0;i<points;i++){
Peaks[i]= {
x: Math.floor(Math.random()*rightedge),
y: Math.floor(Math.random()*hi)+up
}
}
Peaks.sort(compare)
s=render(Peaks,0)
Hills.setAttribute("d",s)
}
function compare(x,y){
if (x.x<y.x) return -1
else return 1
}

The customizable sort function of JavaScript is apparently borrowed from Java, though one can certainly see hints of it in the unix shell sort utility from a few decades before. The first time I saw it I was left agog by its extensibility. The above code creates an array of x-y point objects placed in order along the x axis. It now remains simply to render these points as a series of bezier curves. The "render" function, shown below, actually builds the string that defines the "d" attribute of the path used as the outline of the foothills. It merely interleaves a series of control and end points of cubic splines, stitching the ends together when done.

                
function render(Arr,lo){
var low=Math.floor(bottomedge*.9)
var s="M "+0+" "+(low)+" C"
s+=7+" "+(low - Arr[0].y)+" "
for (i=1;i<Arr.length;i++){
s+=Arr[i].x+" "+(low - Arr[i].y)+" "
}
s+="L "+(rightedge)+" "+low+" Q "+(rightedge/2)+" "+(low)+" "+40+" "+(low)+" z"
return s
}

Having thus created an array of x-y points that is ordered on the x-axis, we may animate the drawing by pushing a new random point onto the end of the array, shifting one off from the beginning of the array and then moving all the points in between, by some speed factor, a little to the left.

                    
function animate(){
onestep()
window.setTimeout("animate()",10)
}
function onestep(){
var speed=1
for (j=0;j<Peaks.length;j++){
Peaks[j].x-=speed
}
if(Peaks[3].x<0){
st=rightedge
for (k=0;k<3;k++){
newx=st+Math.ceil(80*Math.random()/Peaks.length)
var Q={x: newx, y: Math.floor(Math.random()*Hi+UP)}
st=newx
Peaks.push(Q);
R=Peaks.shift()
}
}
s=render(Peaks,R.y)
Hills.setAttribute("d",s)
}

In the version visible on the web, several different layers of mountain can be likewise constructed, with taller mountains behind and foothills in front. The objects in the foreground can then be made to move by faster, simulating several layers of separate animation.

Animating the start-offset of text laid out along a path results in an appealing textual effect. We begin with three observations: first text may be laid out along a curve (see Text on a Path); second, the horizontal offset along that curve may be specified by an attribute (startOffset). Finally, that attribute (like most in SVG) is animateable:

                
<path id="curve1" d="M 10 100 C 200 30 300 250 350 50"
stroke="black" fill="none" stroke-width="5" />
<text id="T" style="font-family:ariel;font-size:16">
<textPath xlink:href="#curve1" startOffset ="10">
<animate attributeName="startOffset" dur="7s" from="0" to="390"
repeatCount="indefinite" />
Hello, here is some text crawling along a bezier curve.
</textPath>
</text>

Figure 10. Illustration of text moving on a path

(Note[2016] see examples at 

http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/edges_of_plausibility.htm)


In the illustration of Figure 10, we can see (if the browser supports SMIL) that the text moves all the way across the path before resetting itself. If we duplicate the text, making two copies of the string itself, and then attempt to animate it so that it flows continually, the effect can be achieved by letting the startOffset loop from -370 to about 20 in Internet Explorer/ASV. However, the specification does not allow negative values for startOffset, so I am not sure if the effect seen here (working in IE/ASV but not in Opera) can be accomplished or not.

The use of a composite filter involving feComponentTransfer, feGaussianBlur and feColorMatrix can create a smoothed posterization resembling a vectorization of a bitmapped graphic.The <feComponentTransfer> primitive allows the independent redefinition of each of the four color channels: R,G, B, and A (alpha). It allows the adjustment of brightness and contrast through application of any of a variety of different functions to any or all channels of an image. The types of adjustment allowed include identity, table, discrete, linear, and gamma. Discrete can be used to posterize an image (that is to reduce it to fewer color values). Linear is used for simple brightening and darkening (or contrast adjustment) while table can be used to remap the function (like discrete) only continuously. Let's explore what some of these effects do, in isolation:

                    
<filter id="discrete">
<feComponentTransfer>
<feFuncR type="discrete">
<animate attributeName="tableValues" values="0 0 1;0 1 1; 0 0 1" dur="3s" repeatCount="indefinite"/>
</feFuncR>
<feFuncG type="discrete" tableValues="0 1"/>
<feFuncB type="discrete" tableValues="0"/>
</feComponentTransfer>
</filter>
<image id="M3" x="250" xlink:href="p17.jpg" filter="url(#discrete)" width="250" height="250" />

In this example (code above, live here, and illustrated below), we show a mapping of values in the R, G, and B channels of a single image. This filter maps the red values in the interval [0,1] to one of the three values as follows: (0 to .33) --> 0; (.67 to 1.0) --> 1; and (.33 to .67) --> x, where the value of x is chosen by the animation to oscillate between 0 and 1. In this way we may see the effect of changing the discretization function over time. The green channel is mapped to either 0% or 100% green with the threshold between these levels being chosen halfway between the endpoints. The blue channel (relatively insignificant in this particular image) is dampened to black (removing its effect altogether). The source image has no alpha channel (i.e., it is everywhere opaque), hence there is no need to modify that channel.


One of the ostensible reasons for posterizing a bitmapped image is for the sake of compression: creating an image with fewer bits per pixel. Another is as an intermediate stage in converting a bitmap to a series of vectors. This last process, involving edge tracing or some similar method for recognizing the boundaries between areas of similar pixel values is an area of considerable interest in the research. Open source tools such as Inkscape and NIH Image both provide some forms of bitmap to vector conversion, as does the proprietary tool Adobe Streamline. Doing this with SVG in the browser is a bit of an academic exercise, since SVG 1.1 gives us no way of knowing where, in an image, certain edges might be, or of knowing what the color value is at a given pixel value. I gather though that the SVG working group has considered ways of doing that, and if it is possible to load SVG images into the HTML <canvas> tag, then we might be able to interrogate pixel values from there. At any rate suppose we were interested in taking the image above as discretized (3 values on the red channel, 2 on green and 1 on blue), and drawing a smooth contour around the monochromatic regions. The fact that pixellation occurs (in this image around the eyes and mouth) complicates the matter of drawing a smooth curve around monochromatic regions. This might be addressed by applying either an averaging function (like the mosaic filter in Adobe Photoshop (TM)), or a Gaussian blur. The latter would seem to hold more promise for this particular purpose (and it exists within SVG).

At this web site, we may see an example of first providing a Gaussian blur (the standard deviation is animated from 1 to 8 pixels in radius) followed by a feComponentTransfer involving discrete remappings of the color channels. The animations of the red and green channel as well as that of the blur are kept out of synchrony with one another so we can get some sense of the range of topographies induced by this combination of filters. Relevant code is illustrated along with a few representative screen shots:

                
<filter id="discrete">
<feGaussianBlur stdDeviation="5">
<animate attributeName="stdDeviation" values="1;8;1" dur="5s" repeatCount="indefinite"/>
</feGaussianBlur>
<feComponentTransfer>
<feFuncR type="discrete" tableValues="0 .25 .75 1">
<animate attributeName="tableValues" values="0 .1 .2 .7 .9 1 1 ;0 .1 1 .7 .9 1 1 ;0 .1 .2 .7 .9 1 1 " dur="3s" repeatCount="indefinite"/>
</feFuncR>
<feFuncG type="discrete" tableValues="0 1">
<animate attributeName="tableValues" values="0 .5 1;.2 .8 0 ;0 .5 1 " dur="4.7s" repeatCount="indefinite"/>
</feFuncG>
<feFuncB type="discrete" tableValues="0"/>
</feComponentTransfer>
</filter>


Several textures including wood grain, masonry and filigree can be created using composite filters that include feTurbulence. Illustrated below are several examples of textural effect created in SVG.


In fact, the topic is interesting enough, that I have invited students and others to suggest alternative themes, improvements and the like. Please e-mail the author if you have additions to the collections found here, if you have any additional ideas. Most of these begin just with the <feTurbulence> filter to fill a rectangle with "interesting" noise. Other filters that may blur, sharpen, or recolor are then layered. I'll discuss two of these with a bit of detail (fire and clouds) since they are indicative of the wide range of possibilities.

To make some clouds, I'll first begin with a rectangle filled with turbulence:

                
<filter id="twoF">
<feTurbulence baseFrequency=".01" numOctaves="4" seed="200"/>
</filter>
<rect x="25%" y="25%" filter="url(#twoF)" height="50%" width="50%"/>

Figure 14. rectangle with feTurbulence


Next, we add some animation to both the scale of the turbulence (to convey a sense of motion to the clouds) and the seed (the numeric seed that determines which part of the turbulence function is actually accessed). By varying the seed, we generate a class of similar maps, but ensure that the actual shape of the clouds will vary over time.

                
<filter id="twoF">
<feTurbulence baseFrequency=".02" numOctaves="4" seed="200">
<animate attributeName="baseFrequency" dur="3s" end="R.mousedown" fill="freeze"
values=".001 .003;.002 .002;.001 .003" repeatCount="indefinite"/>
<animate attributeName="seed" dur="30s"
values="300;310" repeatCount="indefinite"/>
</feTurbulence>
</filter>
<rect id="R" x="25%" y="25%" filter="url(#twoF)" height="45%" width="45%"/>

While the above effects do not change the nature of the static appearance of the rectangle, they certainly vary it as seen in the browser. Next, since the colors are still too polychromatic for clouds, we might dampen the color by selectively altering the three channels with an feComponentTransfer that moves both red and green toward white, but allows the blue channel to oscillate, hence conveying a golden tinge to the clouds.

                
<feComponentTransfer>
<feFuncR type="discrete" tableValues="1"/>
<feFuncG type="discrete" tableValues="1"/>
<feFuncB type="linear" slope="1" intercept="0">
<animate attributeName="slope" values="-1;1;-1" dur="3s" repeatCount="indefinite"/>
<animate attributeName="intercept" values="1;0;1" dur="2s" repeatCount="indefinite"/>
</feFuncB>
</feComponentTransfer>

The overall effect as animated can be seen on the web here.

We turn now to a very different effect, the example of the fire created by distorting a radial gradient through displacement by turbulence. Let us begin with a rough fire-like shape and fill it with a rough fire-like gradient. We need not be too careful with either, since the filter effects will tend to make the original barely recognizable anyway. But starting with a hint of a proper drawing, will serve to give an impressionist suggestion of what we had in mind.

                
<defs>
<radialGradient id="gradient1" cy="70%" fy="100%" r="70%">
<animate attributeName="cx" dur="1.5s"
values="20%; 75%; 20%" repeatCount="indefinite"/>
<stop offset="0" stop-color="#8cf"/>
<stop offset="0.14" stop-color="#f40"/>
<stop offset="0.7" stop-color="yellow"/>
<stop offset="0.8" stop-color="orange"/>
<stop offset="0.96" stop-color="#482"/>
<stop offset="1" stop-color="#eee" stop-opacity=".5"/>
</radialGradient>
</defs>
<rect id="Q" x="0" y="0" width="100%" height="100%" fill="#213"/>
<path id="P" transform=" scale(1,1.3) translate(5,25)" fill="url(#gradient1)"
d = "M 100 50 140 70 120 80 100 50 M 121 173 165 67 176 113 195 92 230 123 254 79 268 39 273 91 286 51 323 166 336 125 337 77 365 138 363 212 104 212 101 122 z"
/>

Figure 15. fire-like path with fire-like gradient


For the effect of simulating how the hottest point of a fire tends to move rather suddenly from place to place, as complex reactions of wind, fuel, and heat interact we allow the center of the radial gradient (cx) to oscillate from left to right rather swiftly.

Now, is the time to perform the magic. We apply a filter which consists of a displacement map that overlays the path and its gradient over a turbulence filter, with the displacement offsetting the x and y locations of our drawing based on the chromatic values of the (seemingly random) turbulence.

                
<filter id="turbF" x="0%" width="150%" y="0%" height="200%">
<feTurbulence baseFrequency=".04" numOctaves="1" result="turb" />
<feDisplacementMap scale="100" in="SourceGraphic" in2="turb" xChannelSelector="R" yChannelSelector="B" />
</filter>

Figure 16. distorting by turbulence


Finally, we animate various aspects of the turbulence and the distortion. Here we choose the baseFrequency of the turbulence, which controls its "crinkliness," and the scale of the displacement which determines how far pixels will be moved.

                
<filter id="turbF" x="0%" width="150%" y="0%" height="200%">
<feTurbulence baseFrequency=".04" numOctaves="1" result="turb">
<animate attributeName="baseFrequency" dur="5s"
values=".03; .02; .01; .04; .03" repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="turb" xChannelSelector="R" yChannelSelector="B">
<animate attributeName="scale" dur="1.9s"
values="100; 200; 100" repeatCount="indefinite"/>
</feDisplacementMap>
</filter>

The resulting effect shown here has a cartoonish quality reminiscent of Maleficent in Disney's Sleeping Beauty. That effect can be made less cartoonish by increasing the number of octaves to the turbulence, but at considerable cost to the speed of the animation.

Figure 17. animating fire


The above may be seen at this location, and a considerably more outrageous version (which uses spreadMethod="reflect" on the gradient) can be see here.

One may also produce a variety of aquatic themes (perhaps as antidote to all the fire?) using combinations of animated gradients, feDisplacementMap, feFlood, feColorMatrix and feTurbulence. The reader is referred to this collection for further study, but one of my older examples deserves special mention. In the example located at http://srufaculty.sru.edu/david.dailey/svg/waves.html, a distortion filter is applied to a bitmap. The distortion gets its magnitude data from a series of concentric waves (radial gradients) that ripple outward from the center. In order to accomplish this, script was involved to allow gradient stops to propel themselves outward, with outermost gradients being removed and relocated at the center of the gradient. As it runs in IE/ASV, the user may choose a custom image from her own hard drive, since IE has not yet decided that users should not be able to access their own file space with a file upload for local consumption. (For whatever reason, the WHATWG seems to have declared that to be intrinsically naughty, and the HTMLWG seems content to follow along.) The example is also noteworthy for its use of HTML widgets like selects, checkboxes and radios to allow for user control and modification of the animation.


After the objects involved in an animation have been dynamically created through script and the animation has run its course, might we save some of the effort in recreating a new animation from scratch, by simply adjusting an attribute or two, and restarting the animation? The answer is yes.

The premise here may seem a bit far-fetched, but the underlying concept has arisen in broad enough contexts in my own work, that it may prove worth the reader's patience to take a breath and endure. Let us suppose that we have gone to some lengths in terms of data structures and DOM calls to create a certain collection of animated objects. The objects themselves are, perhaps complex, and the animations associated with each object may be individualized (by random and user-based events). What to do when the animation runs its life cycle? Suppose, for the sake of entertainment, we do not simply want our animations to "loop," over and over (like unwashed socks), but would like to recreate them, freshened in some way. One way, that might be recommended from traditional programming would be to store all the relevant data in a data structure (and if it becomes big -- a database) with the rendering being strictly derived from the data structure, rather than the other way around. But suppose, for a minute, that the overhead of creating the data structure (especially including human time in programming) is relatively large. Might we find it easier to simply retread our tires, than to remold them?

For this example, the premise is somewhat simplified for sake of illustration. We have a collection of randomly generated caterpillars (each with its own DNA, coloration, and behavior) and then suddenly, during that awkward phase known as metamorphosis, they become butterflies (for example). The DNA remains consistent, and the expression of those chromosomes, through coloration and behavior, bears some resemblance to the original. But overall, most observers (at least from the human species) will perceive that a major transformation has occurred. Let's build a simplified version of this scenario. We begin with a "prototype group" containing three proto-stars (paths) and animations to reposition and resize the group:

                
<g id="Proto">
<animateMotion begin="indefinite" dur="2s" rotate="auto" fill="freeze"
repeatCount="1" path="M 0 100 600 400" onend="rebirth(evt)"/>
<animateTransform attributeName="transform" type="scale" dur="4s"
begin="indefinite" values=".25;1" repeatCount="1"/>
<path stroke-width="1.5" fill-rule="evenodd" />
<path stroke-width="1.5" fill-rule="evenodd" />
<path stroke-width="1.5" fill-rule="evenodd" />
</g>

Let us suppose that we have a program which, in response to a mouse click, clones the above prototype and builds a series, called a "composite star," of three n-pointed stars, each having a randomly chosen number of points, n, with gradually diminishing radii, from back to front. Further let us suppose that the stroke and fill colors associated with each of the stars is randomized, and that the composite star is created and centered at the locus of the mouseclick. Assume, furthermore, that an animation, customized for each composite star, is developed so that the composite star moves with a randomly generated speed along a randomly constructed curve leading from its start position to some final random ending position. By the time all this has been accomplished, this composite star has had a goodly amount of "personality" built for it. The illustration below displays several incarnations of one composite star which might preserve its coloration patterns, its path of motion, rate of speed, nature of rotation, and so forth. All that changes from one incarnation to the next is the shape of the individual stars from which it is composed.


The particular script which might create such stars and such random paths need not concern us here though the reader may view this example at http://srufaculty.sru.edu/david.dailey/svg/svgopen2008/makestars3.svg . In the example, the animation progresses deterministically: a composite star reaches the end of its path and then restarts. The key point here is that the stars have been defined with quite a bit of structure. The following illustration gives an idea of how different coloring patterns and pathways (representing a "species" ) might actually exemplify more intra-individual similarity than might be masked by the vagaries of metamorphosis.


So, returning to our basic question here: how might we resurrect one of these composite stars, with new shape, but without having to rebuild its animateMotion (including the path data), its color scheme, and the physical instantiation of the path itself? The first two obvious and strictly imperative approaches would be as follows: 1. store all relevant characteristics of objects (six colors, three Bezier points, three star-shaped paths, speed, time of onset, etc.) in a data structure, of some sort, in script. Whenever an animation (which will be far easier to program with SMIL since the animation runs along a Bezier curve) reaches its conclusion, then retrieve that data, destroy the previous object and recreate a new one that preserves the majority of characteristics of the old one, re-embedding the animateMotion into the object and then embedding the newly construction incarnation into the DOM. A second and similar approach would do all this without creating the data structure in script but merely to interrogate the object's properties prior to its recreation. This approach in the midst of a complex animation would be discouraged because of the expensive nature (timewise) of all the DOM calls it would entail.

By using declarative techniques (SMIL) in combination with script, we may dramatically reduce the overhead in terms both of data structures, DOM calls, and most importantly, perhaps, program complexity. Note, from the above program listing, that the animateMotion that each composite star contains (with modified path and duration data) will, upon termination, invoke a rebirth(evt) function. The evt.target of that function is the custom animation itself as embedded into the cloned group. From it we may find the group (<g>) and hence all things associated with the group.

            
function rebirth(evt){
var G=evt.target.parentNode
var Os=G.getElementsByTagName("path")
var r=Math.random()*60+25
for (var i=0;i<3;i++){
var s=makeStar(r-(i*15))
Os.item(i).setAttribute("d", s)
}
evt.target.nextSibling.beginElement()
evt.target.beginElement()
}

We find the three paths associated with the group, and simply modify their "d" attributes and their radii (r). The object is modified, but neither the object itself, nor the animation within, is removed from the DOM. We need, then, merely to restart any animations associated with the group, including the evt.target itself, using the beginElement() command from JavaScript. The fully functional code may be seen at http://srufaculty.sru.edu/david.dailey/svg/svgopen2008/makestars4.svg, while another allied example which moves composite stars through a clipPaths leading parallel to animateMotion paths can be seen here.

The pattern tag in SVG allows the rectangular tessellation (or tiling) of the plane with whatever material we place inside a rectangle. How might we tessellate more freely? A few remarks should first be made about tiling. First there are the familiar three tilings offered by the regular n-gons: equilateral triangles, squares, and regular hexagons. Beyond these, there are a large variety (containing numerous open problems) of tilings that involve other shapes (or collections of shapes). Often tessellations are divided into the categories periodic, nonperiodic, and aperiodic. The periodic tilings include the 3 tilings by the regular n-gons, but they also involve tilings of more than one shape such as a tiling involving squares of unit length and equilateral triangles. Paul Bourke's treatment provides further insights into this rich area, as does the Wikipedia article. Nonperiodic tilings (also called non-deterministic tilings since the locus of individual tiles is not determined, a priori) are numerous in kind with familiar examples including the use of the 60 degree rhombus, the half-hexagon (a regular hexagon cut into two trapezoids and sometimes used in deck and patio tilings), and a variant of the snub square tiling, in which the restriction that squares never abut one another is relaxed. The aperiodic tilings (of which the tilings of Penrose are most familiar) are those in which periodicity may never occur (unlike the nonperiodic tilings where periodicity is certainly one of the possibilities).

For the periodic tilings, the fact that the tiling repeats (via simple translation) means that we may use SVG's <pattern> tag to embed each such tiling in the plane.

Table 1. Two periodic tilings using <pattern>

Figure 21. Several composite stars along individualized pathways


Figure 22. A curious non-equilateral pentagon


The code used for such tiling is quite straightforward, though a bit tedious to render in SVG since each of the polygons must be replicated sufficiently often that the entire pattern rectangle is covered so that the left side wraps to the right and the top to the bottom, toroidally. In the case of the combination of rectangles and triangles, at left, the pattern space is filled with no fewer than 17 separate path statements:

                
<pattern id="Pattern" patternUnits="userSpaceOnUse" width="109" height="109" >
<path d="M 0 0 L 200 0 100 173.2 z" fill="url(#sand)"/>
<path d="M 200 0 L 100 173.2 273.2 273.2 373.2 100 z" fill="url(#backsky)"/>
<path d="M 0 0 L 100 173.2 -73.2 273.2 -173.2 100 z" fill="url(#backsky)"/>
<path d="M 100 173.2 L 273.2 273.2 100 373.2 z" fill="url(#mountain)"/>
<path d="M 100 173.2 L -73.2 273.2 100 373.2 z" fill="url(#sand)"/>
<path d="M 200 0 L 100 173.2 273.2 273.2 373.2 100 z" fill="url(#backsky)" transform="translate(-273.2,273.2)"/>
<path d="M 273.2 273.2 L 373.2 446.4 200 546.4 100 373.2 z" fill="url(#backsky)"/>
<path d="M 200 546.4 L 100 373.2 0 546.4 z" fill="url(#mountain)"/>
<path d="M 273.2 273.2 473.2 273.2 373.2 100 z " fill="url(#mountain)"/>
<path d="M 273.2 273.2 473.2 273.2 373.2 446.4 z " fill="url(#sand)"/>
<path d="M 200 0 L 100 173.2 273.2 273.2 373.2 100 z" fill="url(#backsky)" transform="translate(273.2,273.2)"/>
<path d="M 0 0 L 100 173.2 -73.2 273.2 -173.2 100 z" fill="url(#backsky)" transform="translate(546.4,0)"/>
<path d="M 100 173.2 L -73.2 273.2 100 373.2 z" fill="url(#sand)" transform="translate(546.4,0)"/>
<path d="M 100 173.2 L 273.2 273.2 100 373.2 z" fill="url(#mountain)" transform="translate(273.2,-273.2)"/>
<path d="M 100 173.2 L -73.2 273.2 100 373.2 z" fill="url(#sand)" transform="translate(273.2,-273.2)"/>
<path d="M 100 173.2 L 273.2 273.2 100 373.2 z" fill="url(#mountain)" transform="translate(273.2,273.2)"/>
<path d="M 100 173.2 L -73.2 273.2 100 373.2 z" fill="url(#sand)" transform="translate(273.2,273.2)"/>
</pattern>

The pentagonal tiling at right uses a bit of sleight of hand: I simply used a bitmap that I had drawn many years ago:

                
<defs>
<pattern id="TilePattern" patternUnits="userSpaceOnUse" width="62" height="36" >
<image id="tile" xlink:href="smalltile3.png" height="36" width="62"/>
</pattern>
<clipPath id="CP"><use xlink:href="#Q" /></clipPath>
</defs>

<text x="75" y="25" font-size="15" fill="black" > -- The pattern space</text>
<text x="10" y="100" font-size="15" fill="black" > used in &lt;pattern&gt; -- </text>
<ellipse fill="url(#TilePattern)" cx="40%" cy="35%" rx="30%" ry="20%" stroke="black" stroke-width="1"/>
<rect id="Q" x="0" y="0" width="62" height="36" fill="none" stroke="black" strokewidth="4" />
<g clip-path="url(#CP)">
<use xlink:href="#tile" />
</g>

The use of <pattern> to thusly create interesting effects of non-rectangular tilings may be seen in the following examples:

Triangular tiling with animated gradients: Sunrise in Tokyo http://srufaculty.sru.edu/david.dailey/svg/triangles6.svg
Triangular tiling with animated gradients: Kissing triangles http://srufaculty.sru.edu/david.dailey/svg/triangles7.svg
Rotating squares and triangles: Recombinant Scrambled Egg http://srufaculty.sru.edu/david.dailey/svg/svgopen2008/patternTile3.svg

In certain cases, we may wish to tile nonperiodically or to fill each of several tiles with different content. In either of these cases, <pattern> will prove insufficient, and we will need to rely on script instead. By laying out each of a collection of tiles individually, each may be uniquely positioned, and each may be individually filled, stroked, and animated. The code for tiling in this way need not be overly complex. In the example at here, in which each triangular tile is given its own pulsating gradient, begins by placing two equilateral triangles in a group:

                
<g id="gg" transform="scale(0.5)">
<path id="P" d="M -100 173.2 L 100 173.2 L 0 0 z" fill="url(#g)"/>
<path id="P2" d="M 0 0 L 100 173.2 L 200 0 z" fill="url(#g2)" />
</g>

We then locate the group by id, and proceed to clone it, stamp both of its triangles with a unique id, verify that we have not yet run out of room on the screen and add a transform to lay the content out from side-to-side and row-by-row.

                
id++
if (ys*id>bottomedge/scale) {off++; id=0}
var clone=gg.cloneNode("true")
newg("X"+id) //this customizes the gradient of the first triangle
newg("Y"+id) //this customizes the gradient of the second triangle
var x=(off - (id%2)/2)*xs
var y=Math.floor(ys*id)
clone.setAttributeNS(null,"transform","scale("+scale+") translate("+x+" "+y+")")


Laying out hexagonal tilings is very similar. Examples of square, triangular, and hexagonal tilings laid out by script so as to enable individualized treatments of the individual tiles can be seen in these examples.

Because the gradients specified by SVG are either radial or linear, it is natural to investigate ways of transcending this limitation. One approach involves tessellation of piecewise conformant gradient tiles, another is to use symbols that are small pieces of linear gradient as a font and to lay those out along a text path. For the first approach, let us begin with a small isosceles triangle and fill it with a linear gradient. We may then rotate and replicate it so as to simulate a radial gradient as shown in the illustration:


The script which generates this is quite simple and can be seen (with a bit of animated flair) at this location. The key aspect that has served to keep the script simple, is that the prototype is are cloned and rotated, meaning that the applicable gradient receives the new rotation as well:

                
function startup(evt) {
Root = document.documentElement;
P = document.getElementById("P");
ys=300;
angle=40
P1={x:0,y:0}
P2={x:xwide*(ys+angle),y:-ys}
P3={x:-xwide*(ys+angle),y:-ys}
P.setAttribute("d","M "+P1.x+" "+P1.y+" "+P2.x+" "+P2.y+" "+P3.x+" "+P3.y+" z")
for (var c=0;c<(360/angle);c++) newAngle(angle*c)
}
function newAngle(a){
var clone=P.cloneNode("true")
clone.setAttributeNS(null,"transform","translate(300 250) rotate("+a+" 0 0) scale("+.5+") ")
Root.appendChild(clone);
}


Clearly if the angle is reduced so that more than 9 triangles are used, the approximation to a radial gradient becomes that much better. If, however, the overall direction of the change in the gradient is not to be uniformly rotated as above, then this simplicity disappears and we must independently customize new gradients for each triangle as illustrated below and instantiated at this location.


It should be noted that using this approach, it is a bit difficult to rotate the individual gradients just the right amount, since the apparent left-right offset varies depending on the direction the isosceles triangle points. Furthermore, there appear to be browser differences in terms of the way the rotation of a gradient is handled with Opera, IE/ASV and FF handling the situation a bit differently. Safari, as of this writing, appears not to handle the rotations of gradients.

I have also experimented a bit with stitching together pieces of linear and radial gradients, so that upon the edge where they meet, the derivatives are continuous, but as yet, these experiments have not proven fruitful. However, another approach was suggested by discussion a year or so ago involving graphics laid out like text along a path.


The above svg illustration renders similarly in oXygen and IE/ASV, though Opera 9.5 appears not yet to support free graphics in a font definition. The rather simple source code can be seen below, and a live web version (with animation on the gradient) can be seen here.

                
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="180 20 200 190">
<defs>
<linearGradient id="g" gradientTransform="rotate(90 .5 .5)">
<stop offset="0" stop-color="#880"/>
<stop offset=".4" stop-color="#000" />
<stop offset=".9" stop-color="#ff8"/>
<stop offset="1" stop-color="#880"/>
</linearGradient>
<font horiz-adv-x="100">
<font-face font-family="GradientP" units-per-em="100"/>
<glyph unicode="p" >
<path d="M 20 280 L 230 280 230 -160 20 -160z" fill="url(#g)" />
</glyph>
</font>
</defs>
<path id="P1" fill="none" stroke="none" stroke-width="2" d="M 100 110 C 300 310 300 -90 500 110"/>
<text font-family="GradientP" font-size="10" >
<textPath xlink:href="#P1" method="stretch"> ppppppppppppppppppppppppppppppppppppppppppppp </textPath>
</text>
</svg>


Clearly the ability to define new classes of gradients through script exists, though considerable work would need to be done for any desired effect to be produced. The use of the feTurbulence filter provides another approach for investigation, as some of the examples in earlier sections should make clear.

SVG comes with transformations of type rotate, translate, scale, skew and matrix. The first four of these transformations are each special cases of the matrix transform which may emulate any of the others. All of these transformations are called "affine," and all affine transformations have in common that they transform a given rectangle as input into a parallelogram (of any size, shape, translation and orientation).

Figure 26. A bitmap: once unchanged and twice transformed


                
<image xlink:href="p99.jpg" height="250" width="200" preserveAspectRatio="none"/>
<image xlink:href="p99.jpg" height="250" width="200" preserveAspectRatio="none"
transform="translate(200,0) skewX(50) rotate(50) scale(.35 1.55)"/>
<image xlink:href="p99.jpg" height="250" width="200" preserveAspectRatio="none"
transform="translate(0,250) skewX(65) rotate(0) scale(1.0 .205)"/>


But suppose we wish to warp an image in some other way. If one wished, for example to develop a "morphing" utility that allows a point-by-point deformation of one image into another, then more fluid distortions are likely to be useful. The approach presented here uses individualized distortions on pieces of the image as it has been decomposed into clipPaths. The complete example is rather complex and may be seen at this location, but I will present an overview of the strategy used in hopes to help the reader make sense of the rather lengthy code seen there. We begin by partitioning the image into four triangular clipPaths. Each triangle (T1 through T4) consists of one point, P5, at the center of the image, and the other two points along one of its four edges. The "warping" effect we seek is one which, while leaving all points on the outside of the rectangle fixed, remaps the center point P5 to a new point P6, chosen by the user through mouse activity.


In order to accomplish this, each triangle (as a path) is subjected to a matrix transform. The matrix transform, M, is chosen such that M preserves the locus of each of the points on the perimeter but so that the matrix multiplication M x P5 = P6. Let us consider T1=path(P1,P5,P3) as it is affected by M. M =[

a b c
d e f
0 0 1

] and M x P1 = P1 ; M x P2 = P2 and M x P5 = P6. Pj (for j = 1,2,5,6) may be represented as [xj, yj, 1] . The result is ultimately six equations with six unknowns, and unique solutions for a,b,c,d,e, and f may be found for a given value of P6. The equations are a bit tedious to calculate (using pedestrian approaches took about 1 page of work for each of the four triangles) but it is work, that ultimately an undergraduate without much math beyond introductory college algebra can follow and perhaps perform. The working example at this location is noteworthy for how fast it works in Safari, Opera and IE/ASV. (It works in FF3 as well, but a bit sluggishly). Below is a comparison of two screen shots, before and after mouse movement.


Clearly, if the number of clipPaths were increased, the appearance might become smoother (and overcome the pyramidal effect seen along the edges of the triangles) but the performance in speed would suffer. For each clipPath, a separate copy of the image has been made and clipped, hence resulting in large allocation of RAM. The trade off between time and smoothness has not been investigated systematically, but the speed with which the example runs in Safari, Opera and ASV suggests that this approach has much fruit yet to be harvested.

Another approach that I have investigated is the use of feDisplacement and feOffset filters to warp images, but the fact that the underlying filters are either radial or linear tends to impose a similar restriction on the transformations as does the affineness of the native transformations. Additionally, it should be observed that it is not (to my way of understanding) trivial to figure out quite how far an feDisplacement will actually send a given pixel, despite the quantitative nature of the transform. (See displace2, displace4, displace7, and offsets7 for experiments along these lines.)

This investigates methods of stitching together various segments of knots to create a non-deterministic fabric of traversable threads. Let me explain. Some years ago I proved a couple of theorems. One demonstrated that three coloration of four-regular planar graphs was NP-complete [Dailey1]. Another [Dailey2] showed that the coursing of trichromatic data through planar graphs could be used to build logic gates (and, or, neither-nor and the like). The notion that locally very simple networks can, in some global way, contain enough complexity to model the most complex of problems has intrigued me for years. It also happens that many highway networks can be viewed as four-regular planar graphs (the five or six-way intersection is rather the anomaly, while interstate overpasses have added a nonplanar nature to terrestrial travel). One may consider the ability to construct random examples of such graphs, therefore, as beneficial to algorithm testing both in areas of computational complexity and traffic routing. With that in mind, I became interested in collections of tiles (each containing small four regular subgraphs) which when tiled together in the plane might generate interesting and representative classes of four-regular planar graphs. Some of my earlier investigations into these matters (for example quasi-random wallpaper, or tiling the plane with knots ) used a collection of bitmaps that tessellated seamlessly in any orientation. Selecting random tiles, or random orientations of a given tile, produces interesting classes of graphs and networks. I suspect, but have yet to prove that the 3-coloration of such graphs (as constructed from a small collection of tiles) is NP-complete.


A class of interesting combinatorial problems emerges. Suppose we are given an n-gon (preferably one that tiles, at least in combination with others). And for each of the tiles, we identify along each of its n edges k>0 points. How many distinct pairings (despite rotation) of these n*k points are there, so that each of the points on the perimeter of the n-gon has exactly one connection elsewhere in the n-gon. The resulting tile set will tile quite interestingly, if non-determinism (in choice of tile and perhaps in choice of rotation) is allowed. For two points on each side of the exterior of a triangle we have 15 = 5! / 4*2 possible tiles (of which 8 are shown here), including rotation, or 7 distinct tiles not including rotation (the seven unique tilings are shown here, with the last being a sixty degree rotation of the first.


For two points on the exterior of each side of a square there are 34 possible tiles (these were constructed manually for the example shown at quasi-random wallpaper, ). The problems with using bitmaps for the tilings are that they take a long time to draw by hand, and once drawn the connective structure (that affiliates one point on the edge with another) is not available to the program. I hence rebuilt the 34 square tiles in SVG, and experimented with embedding them (using various subsets) and rotating them in this example . These can be illustrated (with one inexplicably missing) as shown.


Rather than drawing all these tiles manually, it was desirable that they be constructed through code. This project was undertaken as a proof of concept for the triangular tiles. For these, the tiles to be drawn were identified by the edge pairings:

                
pair[0]=[0,3,1,4,2,5] //three crossings
pair[1]=[0,2,1,4,3,5] //two crossings
pair[2]=[0,5,1,3,2,4] //one crossing
pair[3]=[0,4,1,5,2,3] //one crossing
pair[4]=[0,5,1,4,2,3] //no crossings
pair[5]=[0,5,1,2,3,4] //three corners
pair[6]=[0,1,2,3,4,5] //three sides
pair[7]=[0,4,1,3,2,5] //rotation of 1
pair[8]=[0,3,1,5,2,4] //rotation of 1
pair[9]=[0,2,1,5,3,4] //rotation of 2
pair[10]=[0,4,1,2,3,5] //rotation of 2
pair[11]=[0,1,2,4,3,5] //rotation of 3
pair[12]=[0,2,1,3,4,5] //rotation of 3
pair[13]=[0,1,2,5,3,4] //rotation of 4
pair[14]=[0,3,1,2,4,5] //rotation of 4


Pairings #5 and #6 (shown as the third and first pictures in Figure 29 above) are self-symmetric so that rotating them makes no difference. Likewise with pairing #0 (tile #7 in Figure 29). Other tilings all have nontrivial rotations and are, hence worthy of being overtly constructed. We may then use the three control points in the center of the circles for construction of cubic bezier curves to connect the various endpoints. For the purpose of tiling, we wish each of the curves to be perpendicular to both of the edges it connects. The appropriateness of this approach is demonstrated in this example. We then merely need to tile these 15 tiles together randomly as illustrated in the current state of progress for this approach. A small oval is then animated and set to follow a path. It is animated along the bezier segment. The animation terminates, and consistent with the methods discussed in the section on "Reusing animations" earlier in this paper, it then activates a function which determines which edge it has ended up on. I did not succeed, yet, in getting the oval to identify which of the segments within the new triangle it actually abuts against (in part since half of the triangles are rotated 60 degrees for purposes of tiling). And to avoid the problem that small loops often exist in the roadway thusly constructed, I have left the example as it is. An illustration of what the page looks like is shown below. Ultimately, the application becomes more relevant as the braidings become more interconnected, such as would be the case using some of the more densely connected fabrics shown in the earlier example, but the majority of the problem we set out to solve has been accomplished.


Overall, the simplest conclusion that really summarizes the findings presented here is that SVG with its paths, clipPaths, transformations, filters and transparency, its flexible means of declarative animation, and the ability to script (with familiar access through XML DOM) gives the expressive author a vast array of tools for doing a wide variety of rather disparate things. What becomes hard at times, is not the XML syntax nor its requirement that we occasionally utter some mysterious incantation related to XML namespaces (which I may finally understand upon reaching my 80th birthday) but the determination of whether or not something can be done with the arsenal of tools. And if it can be done, which of many possible approaches might be best. As has hopefully been illustrated, the number of conceptually different approaches to a given problem can be, at times, quite large, requiring major adjustments, not just in code, but in thinking.

Implications for future research include improving the approaches to nondeterministic path traversal, further refining the enumeration of edge-joined tilings as discussed, further play with irregular tessellation, and a lot of extra work with displacement filters and the tiling of non-linear gradients. The work outlined here charts what I might call beginnings in several directions, with much distance to traverse in each of these directions remaining to be done.

For SVG as a standard, what suggestions might emerge from this paper are several:

  1. Some types of gradients that are more flexible than just linear and radial would really be handy. The contour gradients of many applications that parallel (or run perpendicular to) a bounding shape might help (certainly for the task of stitching things together into larger smooth textures.) It might be nice to take n user defined points in the plane and to define from that a set of contour lines which would run like altitude lines, around those points providing arbitrary gradient maps over a region.

  2. Transforms of the non-affine sort would certainly be useful. While it is encouraging that one can simulate these, a proper simulation would require too many small regions to be practical. Cylindrical and spherical warps obviously come to mind, but any extension of the conic warp attempted in the section on clipPaths would be useful.

  3. Some types of random noise other than what is provided through feTurbulence could be useful in allowing the class of natural textures that we can construct to expand.

  4. Additional filters (such as one can purchase as add-ons to Adobe Photoshop for example) would be handy. The convolution filters allowed are flexible, but for the average user, things like "plasticize" or "chromatize" might be more straightforward.

  5. Additional 3-D constructs could be added. In addition to non-affine transforms, which some might view as special cases of 3-D constructs, there is currently no compelling standard for 3-D work on the web. Adding a bit of this into SVG might serve to fill a void without stepping on any exposed toes.

  6. Extension of the declarative animation model to include occasional imperative constructs: for example, might we not benefit from having the ability to define random durations or random x-y loci in our declarative markup without having to rely upon script? Likewise, a construct that allows us to specify that a certain object might move in such and such a direction until it encounters an edge or another object, might be quite a powerful extension to the quality of declarative code within SVG.

  7. In conjunction with the posterization of bitmaps, the ability to read color values at given pixel locations back into script is quite important. Likewise, the ability to use a "file upload" capability to bring images from user-drive-space into an SVG application is crucial. It appears that the HTML WG (at least WHATWG) has decided this is to become verboten (perhaps it causes too much work for browser makers to worry about security problems) but in the 15 years that Internet Explorer has allowed it in HTML it seems not to have crashed the Internet. Perhaps SVG could enable this in some way, since the HTML bus seems intent on squashing this ability?

  8. Non rectangular <pattern> spaces. It's clear that most of this can be simulated through rectangular ones. And it is also quite likely that nondeterministic tilings will require script rather than markup for quite some time to come. But SVG could enable the non-programmer to produce interesting visual effects by simply enabling the choice of any of the uniform tilings. Some of these might actually prove to be practical since the hexagonal tiling for example is often used in simulation of battlefield scenarios since distance in that graph is slightly more similar to Euclidean than is distance in square grids.

And of course, we have the simple request that the offset of a text should be expressible as a negative number -- it seems rather inconsequential in contrast to some of the rest of this wish list.