< SVGObject >
<replicate attributeName="P" spatialfrequency="7" values="x,y,z"/>
<replicate attributeName="Q" spatialfrequency="12" values="a,b,c"/>
< /SVGObject >
< SVGObject >
<replicate repeatCount="12">
<replicateAttribute attributeName="P" values="x,y,z"/>
<replicateAttribute attributeName="Q" values="a,b,c"/>
</replicate>
< /SVGObject >
That is, all attributes will be present during each snapshot made of the object.
Table of Contents
Having spent a good part of two years writing a book about SVG [1] the first author, like others in the SVG community, had a good chance to experiment with its expressive power and to discover places where that power seemed limited.
Among the first of the limitations some of SVG's user community discovered were that it could not easily be tricked into making mesh-gradients, or gradients that remain everywhere smooth but which might provide the smooth shading required, say of the contours of a geographic map.
In 2006, because of experiments I had done with the approximation of non-linear gradients in SVG, I was contacted by researchers at INRIA who were encountering a similar problem in their attempts to visualize systems of partial differential equations using smooth gradient textures [2]. In an attempt to simulate a richer range of gradients types than what SVG afforded, I experimented with the overlay of gradients using transparency [3], masks [4], filter displacements [5], tessellations [6,7], script [8,9] and even with defining a font made of an alphabet of piecewise linear gradients, stretched to fit a Bezier-curve using <textPath> [10]. Andrew Matseevsky wrote several small articles to the svg-developers group talking about his own approach to advanced gradients.[11]








Figure 1: Gradients using (from left to right and top to bottom): transparency, masks, displacement filter, tessellation, tessellation, script, script, and gradient-font.
At SVG Open 2008, I discussed several of these approaches in a paper [12] on various problems on the "edges of SVG." Further description of some of these approaches may be seen therein.
Around this same time frame (the second half of 2008), I became aware of at least two other approaches to the limitations of SVG's gradients. Adobe and INRIA in 2008 published their paper on diffusion curves [13], and Israel Eisenberg circulated several papers including his "tubefy" effect [14] using a script-based approach to generating gradients that are neither linear nor radial. I also developed a small web-essay [15] about several possible extensions to the SVG spec, including discussion of a <contour> tag which would, as in Adobe Illustrator, allow the interpolation between two Bezier curves each with a fill pattern.
Additionally, I had been experimenting with non-rectangular tilings for purposes other than creating textures or fill patterns such as creating game boards or venues for random walks. The triangular and hexagonal tessellations of the plane are among the most familiar of these, but the theory of tessellations is deep and rich and does not include just these familiar repeating tiling patterns [16]. Many of the classic combinatorial games such as go, hex, and bridgit can be easily generalized to graph theoretic variants played on a planar graph. A particular tiling consisting of squares and triangles can be applied to the plane nondeterministically, despite the fact that its geometric dual is a five-regular graph. [17] How might one create such nondeterministic tessellations or even non-rectangular tilings without resorting to script?
In the speculations at [15], another of the shortcomings of SVG that I mused about was the fact that despite the fact that fractals, and fractal-like objects had been discussed by the SVG Working group since its early years, there is still no way to extrapolate from the micro-syntax of <path> into, say, the richer vector-based syntax of something like Turtle graphics [18] that allows direction and rotation in addition to Cartesian coordinates to be included in the path drawing methods. From this reasoning, I proposed a <doodle> tag which would allow various relative direction changes, grouping, iteration and the like. The SVG Working Group considered this proposal and basically argued, I think, that adding more micro-syntax to the path element would overload that element and obscure some of its basic use. Nevertheless, the desire to rotate, replicate and extrapolate were directions that I felt SVG deserved to grow. Other experiments I had performed with quasi-3D effects (sometimes known as 2.5 or 2+ dimensional effects) [19] had me and others in the SVG community including some in the SVG Working Group curious about non-affine transformations and the simulation of 3D effects.
It was, in essence, a confluence of my thinking about the proposed <contour> and <doodle> tags - one which interpolates SVG path objects and the other which replicates path fragments that got me thinking about how one might do at least some of both of these activities at the same time.
Well, it turns out that SVG already has a delightfully compact declarative language for describing motion already, borrowed in part from the SMIL spec. That is, SVG has the <animate> tag and its relatives. They are easy to use and allow device-independent, script-free ways of choreographing changes (over time) in a wide range of features of a wide range of SVG objects [20]. Suppose, instead of changing a single object over time, we "keep" each of the stages of its evolution and lay them down in the document atop one another. This would be done through a new <replicate> that would be rather like a group <g>. Accordingly, the solution as envisioned would be a lot like SVG's <use> but on steroids.
It was foreseen that these copies, or "replicates" as they will be called should be able to be laid out along a specified spatial path just as <animateMotion> (in conjunction with <mpath>) allows objects in SVG/SMIL to move and evolve along any path.
And since SVG's animation module has already been largely implemented by most browsers *[21], it is imagined that the difficulty of implementing <replicate> within browsers may be far easier than some of the alternative proposals under consideration. It is also imagined to be broader in expressive scope than <contour>, <doodle> or diffusion curves because of the wide range of situations in which these concepts, like animation, might be applied.
Can the same content be replicated and animated?
Consider the following list [19] of attributes that might be animated:
How to synchronize changes in multiple attributes?
Consider the following example (from [22])
<circle cx="50%" cy="20" r="5%" fill="blue">
<animate attributeName="cx" dur="2.7" values="5%;95%;5%"
repeatCount="indefinite" />
<animate attributeName="cy" dur="3" values="5%;95%;5%"
repeatCount="indefinite" />
</circle>
In it the period or cycle duration of the variations in the x and y directions are unequal. This particular animation will repeat itself every 27 seconds, since 27 is the smallest number that is evenly divisible by both 3 and 2.7 - a necessary condition for the periodicities of cx and cy to synchronize. It creates movement rather like a billiard ball bouncing off the edges of a rectangle.
The problem for building a replicate tag is that while animation will update the screen exactly so many times as the browser is able or if it can keep up, then so often as delta x or delta y exceed one pixel. The updates are taken in almost infinitesimal steps. In the case of replicates we will most likely (instead) seek to take snapshots of the variation of both variables at the same interval. Though one variable may vary more rapidly than another (which could be controlled through a values list) our physical snapshots must include representations of both variables. That is, there is a difference between time and space which makes it natural to ignore the "dur" variable for space but rather to include a count of the number of replicates desired that would be common across all attributes and hence centralized in the <replicate> tag. That is we will conceptualize replicates in a slightly different way because of this asymmetry between space and time.
In this proposal, snapshot variables are shared across the various attributes being replicated as follows:
< SVGObject >
<replicate repeatCount="80" >
<replicateAttribute attributeName="att1" from="x1" to="x2" />
vreplicateAttribute attributeName="att2" from="y1" to="y2" />
vreplicateAttribute attributeName="att3" values="z1;z2;z3;z4" />
</replicate>
< /SVGObject >
That is, we have chosen to consolidate those characteristics common to all attributes within a parent replicate node. Replicate would resemble animate more if we let each replicate tag declare the frame rate redundantly but it seemed like a source of confusion for authors if a variable which was required to be the same were redundantly declared like:
< SVGObject >
<replicate repeatCount="80" attributeName="att1" from="x1" to="x2" />
<replicate repeatCount="80" attributeName="att2" from="y1" to="y2" />
<replicate repeatCount="80" attributeName="att3" values="z1;z2;z3" />
< /SVGObject >
Clearly we might desire differential rates of change for different values, but we believe these aspects can be controlled thoroughly through values lists, keyTimes and keySplines all of which would be defined as expected following the lead of SVG SMIL.
Animation appears smooth because the time interval between frames is small relative to the changes taking place in an object's appearance or location [23]. One of the joys of SMIL animation is that the author does not need to contemplate issues of frame rate, flicker fusion, and screen-refresh rates. Like with other technologies (including HTML and SVG), SMIL lets us declare what we wish to accomplish and rely on the specification, the markup and the implementation to hash out all these fine details for us. However, because replication, like the individual frames of an animation is intrinsically a discrete rather than an analog phenomenon, we find ourselves departing slightly from that independence with spatial replication. To be sure one can think of adding new clones of an ellipse so frequently that the distances between them are infinitesimal, but this raises several considerations about whether or not the DOM is affected by the addition of so many live objects, how many iterations (controlled by repeatCount) would be sufficient for a blend, for example, to appear smooth, and is there a solution to the analytic formula given by the limiting case of some of these rather complex curves? These and other issues will be revisited later in the paper.
We have built a JavaScript interpreter that works in the following general way, with some notable exceptions *[24].
<replicate> tags are parsed in JavaScript using something resembling *[25] this
var ReplicateElements=document.getElementsByTagNameNS(svgr,"replicate");
for (var i=0;i<ReplicateElements.length;i++){ //loop through each replicate tag
//modify each node being replicated
}
The parent node of the replicate (namely the node to be replicated) is then retrieved and cloned, with any adjusted attributes mentioned within its descendent <replicateAttribute> nodes being adjusted incrementally based on the value range specified for that attribute and the value of repeatCount.
var Original=ReplicateItem.parentNode; // the parent of the <replicate> is the node to be cloned
var repeatCount=ReplicateItem.getAttribute("repeatCount"); //the number of replications of each parent to make
var ReplicateAttributeElements=ReplicateItem.getElementsByTagNameNS(svgr,"replicateAttribute");
That is, the author merely attaches a .js file that finds the replicate tags and begins making the clones as though <replicate> were an actual part of the SVG markup.
Hence the majority of the syntactic recommendations here have actually been implemented in a JavaScript proof of concept. Examples showing the appearance of such code will be provided.
Note: almost all of these examples can be seen "live" at the resource page associated with the conference abstract:
http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/replicate.htm
The SVG spec defines the SVG graphics elements as "the element types that can cause graphics to be drawn onto the target canvas". Those are: 'path', 'text', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'image' and 'use'." We will use the term SVGObject to refer to an arbitrary one of these primitive graphic elements.
<SVGObject>attributeName refers to one of the animatable attributes associated with the SVGObject [this implicitly assumes that all animatable values are also replicatable]. from and to have values that can be animated. Examples would include numeric values for attributes like cx and ry of ellipses, color values such as rgb(255,128,0) or papayawhip for the fill or stroke of SVGObjects, rotational values such as (360,100,100), path coordinate data such as "M0 0 Q 100 100 100 200" etc. such as are already specified in the SVG recommendation.
<replicate repeatCount="n" >
<replicateAttribute attributeName="att1" from="x1" to="x2" />
</replicate>
</SVGObject>
Example 1.1: using an ellipse with a linear gradient: *[26]:
<ellipse rx="80" ry="50" cy="-100" cx="200" fill="url(#f)" stroke-width=".5" stroke="none" >
<replicate repeatCount="50" >
<replicateAttribute attributeName="cy" from="300" to="100" />
</replicate>
</ellipse>

Illustration 1.1
<SVGObject>
<replicate repeatCount="n" >
<replicateAttribute attributeName="att1" from="x11" to="x12" />
<replicateAttribute attributeName="att2" from="x21" to="x22" />
.
.
.
<replicateAttribute attributeName="attn" from="xn1" to="xn2" />
</replicate>
</SVGObject>
<text font-size="12" id="WWW" x="-50" y="300" fill="red">
<replicate id="F" repeatCount="80">
<replicateAttribute attributeName="y" from="150" to="290"/>
<replicateAttribute attributeName="x" from="90" to="200"/>
<replicateAttribute attributeName="font-size" from="9" to="170"/>
<replicateAttribute attributeName="fill"
from="rgb(0,0,0)" to="rgb(0,123,255)" />
</replicate>
TEXT</text>
Example using ellipse and rgb values:
<ellipse rx="100" ry="150" fill="rgb(255,200,0)"
cx="-160" cy="200">
<replicate repeatCount="75" >
<replicateAttribute attributeName="cx" from="400" to="350" />
<replicateAttribute attributeName="cy" from="200" to="300" />
<replicateAttribute attributeName="ry" from="150" to="65" />
<replicateAttribute attributeName="rx" from="100" to="150" />
<replicateAttribute attributeName="opacity" from="1" to=".75" />
<replicateAttribute attributeName="stroke-opacity" from=".5" to="0" />
<replicateAttribute attributeName="stroke-width" from="4" to="0" />
<replicateAttribute attributeName="stroke" from="rgb(100,255,120)" to="rgb(0,124,124)" />
<replicateAttribute attributeName="fill" from="rgb(0,128,255)" to="rgb(225,255,220)" />
</replicate>
</ellipse>
<SVGObject>
<replicate repeatCount="n" >
<replicateAttribute attributeName="att1" values="x11;x12;...;x1a" />
<replicateAttribute attributeName="att2" values="x21;x22;...;x2b" />
.
.
.
<replicateAttribute attributeName="attn" values="xn1;xn2;...;xnz" />
</replicate>
</SVGObject>
Example 3.1: moving a gradient-filled ellipse along a non-linear x-y path as it changes its radii.
<ellipse cy="-50" fill="url(#f)" stroke="none" >
<replicate repeatCount="190" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="cx" values="450; 250; 350" />
<replicateAttribute attributeName="ry" values="100;20;20;150;0" />
<replicateAttribute attributeName="rx" values="150;200;0" />
<replicateAttribute attributeName="opacity" values="1;1;0" />
</replicate>
</ellipse>
<ellipse cx="300" cy="50" fill="url(#g)" stroke-width=".5" stroke="none" >
<replicate repeatCount="70" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="ry" values="120;10" />
<replicateAttribute attributeName="rx" values="50;200;60;70;50" />
</replicate>
</ellipse>
For SMIL follow link to http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/demo9.svg
Example 3.3: Corkscrew, a la Escher (with SMIL)
<rect x="-200" width="40" height="40"
fill="url(#g)">
<replicate repeatCount="200">
<replicateAttribute attributeName="y"
values="200;50;200;50;200;50;200;50" />
<replicateAttribute attributeName="x" values="40;600;40;600" />
</replicate>

Illustration 3.3
For SMIL, follow link to http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/demoB.svg

Illustration
3.4: putting a rotating corkscrew inside a rotating
For SMIL and source code follow link to http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/demoE.svg
<ellipse cy="-1000" cx="400" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >
<replicate repeatCount="180" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="transform"
from="rotate(0 400 200)" to="rotate(180 400 200)" />
<replicateAttribute attributeName="ry" from="100" to="15" />
<replicateAttribute attributeName="rx" from="100" to="10" />
</replicate>
</ellipse>
For source see http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/demoG.svg

Illustration 4.3
For SMIL and source (FF4 or IE/ASV) see http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/demoFE.svg
<ellipse cx="200" cy="-50" rx="30" ry="50" fill="url(#f)" stroke-width=".5" stroke="none" >
<replicate repeatCount="100" >
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="ry" from="100" to="0" />
<replicateAttribute attributeName="rx" from="100" to="0" />
<replicateAttribute attributeName="stroke-width" from="0" to="3" />
<replicateAttribute attributeName="fill"
values=" rgb(255,200,200);rgb(255,255,0);rgb(0,255,0);
rgb(0,255,255); rgb(0,0,255); rgb(255,0,255); rgb(255,255,255)" />
</replicate>
</ellipse>
For SMIL (FF4 or IE/ASV) see http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/color4.svg
Example 5.2
<ellipse cy="200" cx="400" rx="30" ry="50">
<replicate repeatCount="120" >
<replicateAttribute attributeName="cy" from="250" to="170" />
<replicateAttribute attributeName="cx" from="400" to="350" />
<replicateAttribute attributeName="transform"
from="rotate(0 300 200)" to="rotate(900 300 200)" />
<replicateAttribute attributeName="ry" from="100" to="25" />
<replicateAttribute attributeName="rx" from="100" to="30" />
<replicateAttribute attributeName="fill"
values=" rgb(255,0,200);rgb(255,255,0);rgb(0,255,0);
rgb(0,255,255); rgb(0,0,255); rgb(255,0,255);
rgb(255,255,255)" />
</replicate>
</ellipse>

Illustration 5.2
MISSING SYNTAX
<path d="M -10 -10 -20 -20 z" fill-opacity=".4" stroke="black"
stroke-width=".3" stroke-opacity=".3">
<replicate repeatCount="80" >
<replicateAttribute attributeName="d"
from="M 411 73, 487 201, 420 376, 217 240 z"
to="M 511 73, 387 201, 520 376, 317 240 z"
/>
<replicateAttribute attributeName="fill"
from="rgb(45,45,128)" to="rgb(0,233,128)" />
</replicate>
</path>
<path d="M -10 -10 -20 -20 z"
stroke="red" stroke-width=".3" stroke-opacity=".2">
<replicate repeatCount="40" >
<replicateAttribute attributeName="d"
from="M 511 73, C 387 201, 320 376, 117 240 C 287 201, 20 76, 511 73z"
to= "M 411 93, C 220 151, 190 276, 207 250 C 289 101, 230 126, 411 93z"
/>
<replicateAttribute attributeName="fill"
values="rgb(45,45,128);rgb(45,45,48);rgb(203,100,88);rgb(255,255,200)"
/>
</replicate>
</path>
Given a path (defined typically inside the <defs> part of a document) :
<path id="curv" d=pathdata />
We may use that path to define pairs of numeric attibutes of another SVGObject as follows:
<SVGObject>
<replicate repeatCount="n" >
<replicatePath xlink:href="#curv" xattribute="att1" yattribute="att2"/>
<replicatePath xlink:href="#curv" xattribute="p * att3 + q" yattribute="att4"/>
<replicateAttribute attributeName="att5" from="x1" to="x2" />
<replicateAttribute attributeName="att6" from="y1" to="y2" />
<replicateAttribute attributeName="att7" values="z1;z2;z3;z4"/>
</replicate>
</SVGObject>
Those familiar with the <animateMotion> tag in SVG SMIL will note that we have simplified that syntax considerably for this treatment.*[27]
Our JavaScript interpreter has not yet implemented the rotate="auto" attribute from the animationMotion example, though we expect this to be relatively straightforward.
We also have not yet implemented the translation of xattribute and yattribute into color values. We envision the syntax for this to be similar to how the filter primitive feDisplacement associates color values with x-y coordinate values.
Example 7.1. Replicating an ellipse around a smooth curve.
<ellipse cx="300" cy="-300" ry="25" stroke="none" >
<replicate repeatCount="160">
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="cy"/>
<replicateAttribute attributeName="rx" values="10;60;10" />
<replicateAttribute attributeName="fill" values="rgb(128,45,45);
rgb(128,233,0); rgb(128,233,255);rgb(128,45,45);rgb(128,45,45)"/>
</replicate>
</ellipse>
<path id="curve" stroke="black" fill="none" stroke-width="5"
d="M 110,250 C 300,180 400,400 450,200 500,0 -90,320 110,250 z" />
This is the simplest case, like its cousin in the SMIL world: <animateMotion>. The x,y values of the curve being used translate directly into x and y values (in this case cx and cy) of the object being replicated. In this example we have drawn the curve atop the replication so that the reader may see it! Ordinarily it would lie hidden inside a <defs> tag.

Illustration 7.1
Example 7.2. Using the curve to define other attributes of the replicated object. In this example we let the cx values for the ellipse be taken from directly from the curve as in the above example, but instead of taking the y values from the curve, we keep those fixed, but instead let the vertical radius of the ellipse, ry, have its valued determined by the height of the curve. As this example demonstrates, this gives far more precise control over the attributes of an object than would keyTimes and far more natural an interface than that provided by keySplines. The example suggests our claim that allowing linear transforms (multipliers and additive adjustments) to modify these effects would make perfect sense.
<ellipse cy="200" ry="25" stroke="none" >
<replicate repeatCount="160">
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="ry"/>
<replicateAttribute attributeName="rx" values="25;45;25" />
<replicateAttribute attributeName="fill"
values="rgb(40,65,120);rgb(255,233,255); rgb(128,233,0);
rgb(128,200,235);rgb(40,65,120);rgb(40,65,120)" />
</replicate>
</ellipse>
<path id="curve" stroke="#800" fill="none" stroke-width="5"
d="M 110,145 C 300,75 400,295 450,95 500,-105 -90,200 110,145 z" />

Illustration 7.2
As the values of the dark red curve rise toward the top of the screen, the height of the replicated content decreases.
Example 7.3. A more complex path affecting the replication through two replicatePaths.
<ellipse cy="200" ry="25" stroke="none" opacity="1">
<replicate repeatCount="260">
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="rx"/>
<replicatePath xlink:href="#curve" xattribute="cx" yattribute="cy"/>
<replicateAttribute attributeName="opacity" values=".6;.5;.3" />
<replicateAttribute attributeName="fill"
values="rgb(140,65,120);rgb(255,233,55)rgb(128,233,0);
rgb(128,200,235);rgb(40,65,120);rgb(140,65,120)" />
</replicate>
</ellipse>
<path id="curve"
d="
M 380 282 Q 155 222 306.5 252.5 Q 458 283 241 156.5 Q 24 30 163.5 102 Q 303 174 352.5 186 Q 402 198 418 151.5 Q 434 105 431 148 Q 428 191 461.5 233.5 Q 495 276 464.5 164.5 Q 434 53 473 36 Q 512 19 575.5 41 Q 639 63 657.5 130.5 Q 676 198 679 213.5 Q 682 229 657 284 Q 632 339 623 308.5 Q 614 308 600.5 267.5 Q 587 177 577.5 124 Q 568 71 586.5 206.5 Q 605 302 380 282z
"
stroke="#800" fill="none" stroke-width="5" />

Illustration 7.3
Two of the use cases that came to mind as this proposal was being developed included
<svgModifier id="f">
<modifierChild0 attlist0>
<modifierChild1 attlist1>
.
.
.
<modifierChildn attlistn>
</svgModifier>
<SVGObject attlistO href>
<replicate repeatCount="100">
[optional list of <replicateAttribute>s and <replicatePath>s]
<replicateModifier modifierType=x childNum=c attributeName=a values="x1;x2;...;xn" />
[optional list of additional <replicateModifier>s]
</replicate>
</ SVGObject >
Within the replicateModifier we have the following attributes:
<linearGradient id="f" x1="1" y1="0.45" x2="0.15" y2="0.9">
<stop offset="0" stop-color="#420" />
<stop offset=".2" stop-color="#f55"/>
<stop offset=".6" stop-color="#fff"/>
<stop offset="1" stop-color="#ff8"/>
</linearGradient>
<ellipse cy="-1000" cx="350" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >
<replicate repeatCount="100">
<replicateAttribute attributeName="cy" from="300" to="100" />
<replicateAttribute attributeName="transform" from="rotate(0 250 200)" to="rotate(50 450 200)" />
<replicateAttribute attributeName="ry" from="100" to="15" />
<replicateAttribute attributeName="rx" from="100" to="10" />
<replicateModifier modifierType="linearGradientStop" childNum="2" attributeName="stop-color" values=" rgb(244,45,45); rgb(233,233,4); rgb(0,0,255);rgb(244,45,45)" />
</replicate>
</ellipse>
Example 8.2 - Changing two the colors of both of a gradient's stops.
<radialGradient id="f" cx=".8" cy=".6" fx="0" fy=".7">
<stop offset=".6" stop-color="#080"/>
<stop offset="1" stop-color="#4f4"/>
</radialGradient>
<ellipse cy="-1100" cx="200" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >
<replicate repeatCount="90">
<replicateAttribute attributeName="cy" values="300; 150" />
<replicateAttribute attributeName="ry" values="100; 90 ;5" />
<replicateAttribute attributeName="rx" values="100; 150; 140; 10" />
<replicateModifier modifierType="gradientStop" childNum="1"
attributeName="stop-color"
values="rgb(244,45,45);rgb(233,233,4);rgb(30,130,235);rgb(244,45,45)"
/>
<replicateModifier modifierType="gradientStop" childNum="0"
attributeName="stop-color"
values="rgb(244,45,245);rgb(244,45,45); rgb(4,233,4);rgb(30,130,235)"
/>
</replicate>
</ellipse>
Note that we first obtain childNum=1 and then childNum=0 though this is not necessary.
The source code can be seen at http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/modifier4.svg
Example 8.4. Changing the "begin" attribute of replicated animate tags.
<linearGradient id="f" x1="0" x2="1" y1="0" y2=".7">
<stop offset=".1" stop-color="#88f"/>
<stop offset=".4" stop-color="#600"/>
<stop offset=".7" stop-color="#b4b"/>
<stop offset="1" stop-color="#ea4" />
</linearGradient>
<ellipse cy="-1100" cx="200" rx="30" ry="50" fill="url(#f)">
stroke-width=".5" stroke="none" >
<animate attributeName="ry" dur="3" repeatCount="indefinite"
values="100; 40; 100"/>
<animate attributeName="rx" dur="3" repeatCount="indefinite"
values="40; 100; 40"/>
<replicate repeatCount="50">
<replicateAttribute attributeName="cy" values="320; 120" />
<replicateAttribute attributeName="cx" values="300; 550;250" />
<replicateModifier modifierType="animate" childnum="0"
attributeName="begin" values="0;8" />
</replicate>
</ellipse>

Illustration 8.4
A similar example, animated and with source can be seen at http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/modifierB.svg
<g>
<replicate repeatCount="4" >
<replicateAttribute attributeName="transform"
from="scale(1.7), translate(0 0), rotate(0 100 200)"
to="scale(0.9), translate(-100 350), rotate(30 100 200) " />
</replicate>
<g opacity="1">
<replicate repeatCount="7" >
<replicateAttribute attributeName="transform"
from="translate(-50 -50)" to="translate(600 0)" />
</replicate>
<path d="M -10 -10 -20 -20 z" transform="scale(.5)"
stroke="red" stroke-width=".3" stroke-opacity=".2">
<replicate repeatCount="25" >
<replicateAttribute attributeName="d"
from="M 511 63, C 387 201, 320 376, 117 240 C 287 201,
0 76, 511 63z"
to="M 411 73, C 220 151, 190 276, 207 250 C 289 101,
130 126, 411 73z" />
<replicateAttribute attributeName="fill"
values="rgb(45,45,0);rgb(203,100,88);rgb(255,255,200)" />
</replicate>
</path>
</g>
</g>
See example and source code at http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/nestedReplicates6.svg. Examples similar to this one, but with rather interesting animation may beseen at http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/nestedReplicates4.svg and http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/nestedReplicates5.svg.
Example 9.3 While the example of Illustration 9.3 involves a group of content being replicated as a group, sometimes we might have only a single element. In which case, we may effectively nest the replicate tags by placing one below the other as children of the same basic SVGObject.. In this example we make the strokes associated with each of the replicated ellipses more complex by varying the replicating the stroke while varying its width.
<ellipse ry="10" cx="330" cy="240" fill="none">
<replicate repeatCount="35">
<replicateAttribute attributeName="rx" values="1;320;1" />
<replicateAttribute attributeName="ry" values="20;100;20" />
<replicateAttribute attributeName="transform"
values="rotate(0,330,240);rotate(360,330,240)" />
</replicate>
<replicate repeatCount="6" xmlns="http://granite.sru.edu/svgr">
<replicateAttribute attributeName="stroke-width" values="20;0" />
<replicateAttribute attributeName="stroke"
values="rgb(0,50,255);rgb(255,55,0);rgb(255,255,255)"
/>
</replicate>
</ellipse>

Illustration 9.3

Illustration 9.4
The link provided with the abstract at http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/replicate.htm contains most of the examples in this paper and numerous others. A listing of some other noteworthy examples is contained here:
http://granite.sru.edu/~eje8449/vsw-anim-rect-text.svg
http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/eisenberg1.svg
<replicate repeatCount="indefinite" DOMImpact="none">
should be considered to enable this.
xmlns="http://www.w3.org/2000/svg";
xlink="http://www.w3.org/1999/xlink";
svgr="http://granite.sru.edu/svgr";
root=document.documentElement;
function init(evt){
var ReplicateElements=document.getElementsByTagNameNS(svgr,"replicate");
for (var i=0;i<ReplicateElements.length;i++){
replicate(ReplicateElements.item(i), i);
}
// replacing the root with a clone works around non-starting animation bug in Opera 10.60
var newRoot=root.cloneNode(true);
document.removeChild(root);
document.appendChild(newRoot);
root=document.documentElement;
var animateElements = root.getElementsByTagNameNS("xmlns", "animate");
for (var i = 0; i < animateElements.length; i++) {
animateElements.item(i).beginElement();
}
}
function replicate(ReplicateItem, k){
var Original=ReplicateItem.parentNode; // the parent of the <replicate> is the node to be cloned
var ReplicateItemNextSibling;
if (ReplicateItem.nextSibling) ReplicateItemNextSibling=ReplicateItem.nextSibling;
Original.removeChild(ReplicateItem); // remove the <replicate> to prevent it from being cloned
var ReplicateAttributeElements=ReplicateItem.getElementsByTagNameNS(svgr,"replicateAttribute");
var ReplicatePathElements=ReplicateItem.getElementsByTagNameNS(svgr,"replicatePath");
var ReplicateModifierElements=ReplicateItem.getElementsByTagNameNS(svgr,"replicateModifier");
var OriginalNextSibling;
if (Original.nextSibling) OriginalNextSibling=Original.nextSibling;
var repeatCount=ReplicateItem.getAttribute("repeatCount");
for (var n=0;n<repeatCount;n++){
var Clone=Original.cloneNode("true");
(Original.id) ? Clone.setAttribute("id",Original.id+"-"+n) : Clone.setAttribute("id","r"+k+"-"+n);
var t;
for (var i=0;i<ReplicateAttributeElements.length;i++){
var ReplicateAttributeItem=ReplicateAttributeElements.item(i);
if(ReplicateAttributeItem.hasAttribute("keySplines")) t=getKeySplineTime(ReplicateAttributeItem.getAttribute("keySplines").split(/[ *, *]|[ *]/g), t);
else t=n/repeatCount;
var value=getValue(ReplicateAttributeItem, t);
Clone.setAttributeNS(null, ReplicateAttributeItem.getAttributeNS(null,"attributeName"), value);
}
for (var i=0;i<ReplicatePathElements.length;i++){
var ReplicatePathItem=ReplicatePathElements.item(i);
if(ReplicatePathItem.hasAttribute("keySplines")) t=getKeySplineTime(ReplicatePathItem.getAttribute("keySplines").split(/[ *, *]|[ *]/g), t);
else t=n/repeatCount;
var Baseid=ReplicatePathItem.getAttribute("xlink:href");
if (Baseid==null) var Baseid=ReplicatePathItem.getAttributeNS(xlink,"xlink:href");
var PathElement=document.getElementById(Baseid.substring(1,Baseid.length));
var Point=PathElement.getPointAtLength(PathElement.getTotalLength()*t);
Clone.setAttributeNS(null, ReplicatePathItem.getAttributeNS(null,"xattribute"), Point.x);
Clone.setAttributeNS(null, ReplicatePathItem.getAttributeNS(null,"yattribute"), Point.y);
}
for (var i=0;i<ReplicateModifierElements.length;i++){
var ReplicateModifierItem=ReplicateModifierElements.item(i);
if(ReplicateModifierItem.hasAttribute("keySplines")) t=getKeySplineTime(ReplicateModifierItem.getAttribute("keySplines").split(/[ *, *]|[ *]/g), t);
else t=n/repeatCount;
var modifierType=ReplicateModifierItem.getAttributeNS(null,"modifierType");
if (modifierType=="filter"){}
else if (modifierType=="animate"){
var Cnum=ReplicateModifierItem.getAttributeNS(null,"childNum");
var allAnims=Original.getElementsByTagName("animate");
var properAnim=allAnims.item(Cnum);
var value=getValue(ReplicateModifierItem, t);
properAnim.setAttributeNS(null, ReplicateModifierItem.getAttribute("attributeName"), value);
}
else{ //access modifier through fill (eg: fill="url(#g1)")
var fillurl=Original.getAttribute("fill");
var reference=fillurl.split(/[#)]/g)[1];
var newId=reference+"-"+k+"-"+n;
if(document.getElementById(newId)) var newFill=document.getElementById(newId);
else{
var referent=document.getElementById(reference);
var newFill=referent.cloneNode("true");
newFill.setAttribute("id",newId);
var ReferentNextSibling;
if (referent.nextSibling) ReferentNextSibling=referent.nextSibling;
}
if ((modifierType=="linearGradient")||((modifierType=="radialGradient"))){
if (referent.nodeName!=modifierType) return false;
var value=getValue(ReplicateModifierItem, t);
newFill.setAttributeNS(null, ReplicateModifierItem.getAttribute("attributeName"), value);
Clone.setAttributeNS(null, "fill", "url(#"+newId+")");
}
if (modifierType=="gradientStop"){
//something like <replicateModifier modifierType="linearGradientStop" childNum="2" attributeName="offset" values=".05; .95; .05" />
var Cnum=ReplicateModifierItem.getAttributeNS(null,"childNum");
var allStops=newFill.getElementsByTagName("stop");
var properStop=allStops.item(Cnum);
var value=getValue(ReplicateModifierItem, t);
properStop.setAttributeNS(null, ReplicateModifierItem.getAttribute("attributeName"), value);
Clone.setAttributeNS(null, "fill", "url(#"+newId+")");
}
if(ReferentNextSibling) referent.parentNode.insertBefore(newFill, ReferentNextSibling);
else referent.parentNode.appendChild(newFill);
// action: handle patterns
}
}
if(OriginalNextSibling) Original.parentNode.insertBefore(Clone, OriginalNextSibling);
else Original.parentNode.appendChild(Clone);
}
if(ReplicateItemNextSibling) Original.insertBefore(ReplicateItem, ReplicateItemNextSibling);
else Original.appendChild(ReplicateItem);
}
function getKeySplineTime(S, t){
return 3*t*(1-t)*(1-t)*S[1]+3*t*t*(1-t)*S[3]+t*t*t;
}
function getValue(Rep,t){
var value=new Array();
if (Rep.hasAttribute("values")){
var V=Rep.getAttribute("values").split(/ *; */g);
var valueprop=(V.length-1)*t;
var firstindex=(Math.floor(valueprop)<V.length-1)? Math.floor(valueprop) : V.length-2;
var F=V[firstindex].match(/[^\d-]+|-?\d+\.?\d*/g);
var T=V[firstindex+1].match(/[^\d-]+|-?\d+\.?\d*/g);
var t=valueprop-firstindex;
}
else if (Rep.hasAttribute("from")){
var F=Rep.getAttribute("from").match(/[^\d-]+|-?\d+\.?\d*/g);
var T=Rep.getAttribute("to").match(/[^\d-]+|-?\d+\.?\d*/g);
}
else return false;
for(var i=0; i<F.length; i++){
if(isNaN(parseFloat(F[i]))) value.push(F[i]);
else if(F.join("").indexOf("rgb")>-1) value.push(Math.max(parseInt(numValue(F[i], T[i], t)), 0));
else value.push(parseFloat(numValue(F[i], T[i], t)));
}
return value.join("");
}
function numValue(from, to, t){
to=parseFloat(to);
from=parseFloat(from);
var value=from+(to-from)*t;
if(value.toString().indexOf("e")>-1) value=0;
return value;
}
[10] http://srufaculty.sru.edu/david.dailey/svg/gradientfont4.svg (working only in IE/ASV)
[11] Matseevsky, Andrew. Advanced gradient filling. Yahoo groups: svg-developers. March 18,2006. http://tech.groups.yahoo.com/group/svg-developers/message/54915
[12] Dailey, David, P. The Edges of Plausibility: Exploring the boundaries of filters, animation, gradients, patterns and script in SVG. SVG Open 2008. Nuremberg. https://www.svgopen.org/2008/papers/39-The_Edges_of_Plausibility/
[13] Bousseau,
A., Orzan, A., Winnemöller, H., Barla, P., Thollot, J., Salesin, D.
2008. Diffusion Curves: A Vector Representation
for Smooth-Shaded Images. ACM Transactions on
Graphics (Proc. SIGGRAPH), 27(3), page 92:1 - 92:8.
[15] Dailey, David P. Suggestions for additions to the W3C Specification for SVG October 2008. http://srufaculty.sru.edu/david.dailey/svg/Spec.html
[16] See for example http://www.mathpuzzle.com/tilepent.html or http://srufaculty.sru.edu/david.dailey/tiles/tiling.html
[19] See http://srufaculty.sru.edu/david.dailey/svg/clipdrag12.svg, http://srufaculty.sru.edu/david.dailey/svg/balloon.svg and http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/barberPole.svg
[23] The issues governing "flicker fusion" or the perceptual point at which the the human viewer ceases to perceive the flicker of separate frames of an animation is a complex one governed not just by frames per second but also by the nature of what is happening within those frames. The Wikipedia article provides a general introduction to the topic at http://en.wikipedia.org/wiki/Flicker_fusion_threshold
[26] The source code of the linear gradient itself:
<linearGradient id="f" x1="1" y1="0.45" x2="0.15" y2="0.9">
<stop offset="0" stop-color="#ff8"/>
<stop offset=".3" stop-color="#000"/>
<stop offset="1" stop-color="#ff8"/>
</linearGradient>
[27] In <animateMotion> we have a compound construction
<path id="curve" stroke="black" fill="none" stroke-width="5"
d="M 110,200 C 300,130 400,350 450,150 500,-50 -90,270 110,200" />
<ellipse cx="7" cy="-5" rx="20" ry="14" fill="#aaa" stroke="#666"
stroke-width="2" opacity=".8">
<animateMotion dur="2s" rotate="auto" repeatCount="indefinite" >
<mpath xlink:href="#curve"/>
</animateMotion>
</ellipse>