A proposal for adding declarative drawing to SVG

<replicate>:space = <animate>:time (approximately)


Abstract


This paper will focus upon recent investigations into the possibility of extending the SVG spec to include a <replicate> tag for the purposes of extending functionality into three primary areas:

The <replicate> tag (and allied tags like <replicateAttribute>, <replicatePath> and <replicateModifier>) are seen as having a similar relationship to 2D space that <animate> and its cousins from the SMIL family have to time. That is, <replicate> can be used to perform declarative construction of clusters of related shapes.

The paper will present experiments and suggestions concerning plausible syntax, a proof-of-concept involving the implementation in JavaScript of some of these ideas, lots of visual examples to illustrate the power of such a construct, and speculation about how <replicate> might be implemented and how it might combine with other aspects of SVG like gradients, groups, animation, the DOM, and with other instances of <replicate> as in nesting.

Various discussions concerning extension of the class of gradients available to SVG have taken place over the years both within the SVG WG, at conferences, in discussion lists and in private conversations. A variety of approaches have been outlined varying from experiments with gradients that are neither linear nor radial, to proposed gradients that would behave like contour maps, to diffusion curves. All share a frustration with the current limited set and with the difficulty of extending those (even via script) to richer classes. Much of the current proposal is based on analogy to how SMIL allows declarative animation of objects over time. Let us allow declarative replication of objects over space.

SMIL enables objects to change attributes like opacity, color, size, shape and location over time. Suppose instead of changing the attributes of a single object, we were to superimpose freeze frames of those changes atop one another in the drawing space, This is what <replicate> is intended for. While we may vary several attributes at once of the same object using SMIL, and each of these attributes may have its own timing frequency, spatial replication requires drawing an object at certain intervals, hence requiring a synchronization and flattening of the temporal diversity. That is, while we might replicate and interpolate several attributes of any object as multiple snapshots of it are taken, all attributes will be present during each of those snapshots. Hence, rather than borrowing syntax from animate that would suggest:

< SVGObject >
<replicate attributeName="P" spatialfrequency="7" values="x,y,z"/>
<replicate attributeName="Q" spatialfrequency="12" values="a,b,c"/>
< /SVGObject >

as it is done with the <animate> tag, the spatialfrequecy or repeatCount of snapshots will remain constant for all attributes being varied by the author. That is, the syntax of the examples explored in this proposal follows the following paradigm (resembling that of filters somewhat) instead:

< 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.

We present examples of replications of objects by varying their x and y positions, changing their colors, their sizes and shapes and show results of investigations of how these types of variations can interact with gradients and animations.

Already, we have accumulated a good amount of evidence that this class of declarative drawing would add considerable diversity and richness to SVG. It will also be argued that because of the direct parallels to SMIL,

Links:   examples of syntax and results


Table of Contents

Background - frustrations with gradients, tilings, lack of 3D, adjacency
New solution
Aims
Differences between time and space
Smoothness in time and space
Syntax, Varieties and Examples
Case 1: Simple replication of an SVGObject over a single attribute using "from" and "to"
Case 2: Replication of an SVGObject over multiple attributes using "from" and "to"
Case 3: Interpolation over multiple lists of values
Case 4: Replicating while rotating, scaling or translating
Case 5: Interpolating across multiple color values
Case 6: Path interpolation
Case 7. Using paths to define attribute values (extending the SMIL concepts of animateMotion and mpath)
Case 8: Using <replicateModifier> to clone and adjust gradients, animations, patterns, filters, clipPaths, and masks as we replicate
Case 9: Nested replications; Defining more complex textures and patterns
A gallery: Additional examples and curiosities
Additional issues and complexities
Indefinite numbers of replicates
Impact on DOM
Source Code
Conclusions
Bibliography

Background - frustrations with gradients, tilings, lack of 3D, adjacency.

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 aFigure 1 bFigure 1 cFigure 1 dFigure 1 eFigure 1 fFigure 1 gFigure 1 h

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.

New solution

The basic premise behind the <replicate> tag probably emerged as follows. As someone who used Adobe Illustrator quite a bit more than 20 years ago, I always appreciated that package's ability to "tween" two Bezier curves. This was the basic logic behind the <contour> tag: to allow the gradual interpolation between any two shapes (of properly aligned subcommands), including the color values in each. It was like morphing between two shapes but making a stop-action, freeze-frame still by superimposing each of the transitions. I remember a bit of frustration I had that the movement from one curve to another always followed a line.

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.

Aims

It was intended to borrow, for this proposal, as much as possible from the already extant SVG SMIL spec for changing SVG elements over time. But these changes would happen across space instead of over time.

That is, SVG should provide a set of declarative tools that enable replications of SVG objects that mutate a bit in shape, location and quality, as they are replicated across one or more series of attribute values.

It is thought that SMIL is easier for people to do than scripting (even for experienced programmers, my students and I can report 100% to 500% speedups in development time for developing animations using SMIL vs. scripted animations). So why should a replication module based on the basic principles of SMIL animation not have the same intrinsic ease of use?

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.

Differences between time and space

As one contemplates time and space, our ordinary cognitive experience with both suggests that they don't have a lot in common. We use time for planning, playing and reminiscing, and we use space for storing junk automobiles. Time and space don't feel quite the same. However in the field of data visualization, it is common to let the time of an animation represent some fourth dimension of a vector space, or to plot time along a spatial axis. Data analysts are often quite comfortable with conversions between time and space.

So I will confess that when I began these explorations of developing a workable syntax for the <replicate> tag, I thought that the translations from time-based animation to space-based replication would be more transparent syntactically than they were.

The primary differences arose in attempting to figure out a) which tags and attributes were suitable for replication b) how to coordinate the replication of objects that change concurrently over multiple attributes and c) conceptualizing a "smooth" transition.

Can the same content be replicated and animated?

Consider the following list [19] of attributes that might be animated:

  1. The cx, cy, rx and ry of an ellipse

  2. The height of an image

  3. The opacity of an image

  4. The scale of an feDisplacementMap

  5. The focal points and radius of a reflected radial gradient

  6. The offset and stop-opacity of a stop in a radial gradient

  7. The baseFrequency or seed of an feTurbulence

  8. The height of a rectangle contained in a pattern

  9. The values of an feColorMatrix

While the list consists only to illustrate some of the breadth of applicability of <animate>, we may notice that while modifying or morphing the values of cx, cy, rx, and ry simultaneously for an ellipse might be perfectly sensible, it is hard to imagine why one might want to replicate values of an feColorMatrix, though animation of those values would make imminent sense for a stationary object. What might we accomplish by doing that that we would not accomplish by simply changing the chroma of an underlying shape? That is, certain attributes seem more yoked to space than others, and those appear to be more natural targets for replication. On the other hand, in the examples that follow, we will see the utility of replicating along the offsets of gradients and opacity, so there is no clear reason at present to deny these capabilities, as they will likely follow whatever codebase and spec that one would develop.

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.

Smoothness in time and space

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.

Syntax, Varieties and Examples

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.

Syntax:

<SVGObject> 
<replicate repeatCount="n" >
<replicateAttribute attributeName="att1" from="x1" to="x2" />
</replicate>
</SVGObject>

repeatCount is a required attribute whose value is either a positive integer > 1 or the string "indefinite" as discussed later in this paper.

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.

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

Illustration 1.1

Syntax:

<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>

Example 2.1: using text:

<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>

Note: the JavaScript program we have built for the proof of concept does not yet handle named color values or even hexadecimal color values, but only rgb values as included above.

While using from and to can define a broad range of interesting tasks including some interesting new tri-colored gradients, and a range of 3-dimensional effects such as cylinders, teardrops, spheres and cones, a variety of shapes and effects require that just as in animation, destinations that do not lie on a straight line path from "from" to "to" be visited en route. The syntax is just as one would expect from the case in <animate>.

Syntax:

<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>

The values attribute is filled by a semi-colon delimited string of replicateable values just as with from and to and just as with the values attribute in SVG SMIL.

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>

Three more examples of the use of multivalued changes in attribute values are included to illustrate some of the range of expressiveness of this concept.

Example 3.2: A spinning top. Animating its gradient as an oval is replicated: (Note: Opera has a bug with userSpaceOnUse gradients. FF renders fine, while FF4 beta handles even the animation properly.)

<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

Illustration 3.3

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

Illustration 3.4

Illustration 3.4: putting a rotating corkscrew inside a rotating then applying to a rotating oval.

For SMIL and source code follow link to http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/demoE.svg

The ability to interpolate between strings like from="rotate(0 400 200)" and to="rotate(1240 400 200)" gives rise to some very nice flexibility. While example 3.3 shows that we may replicate along non-linear x-y paths, the paths are piecewise linear. Rotating as we change locations, frees us from that constraint. (Using keySplines, as discussed, later can accomplish some of the same work.)

The syntactic considerations are already covered under cases 2 and 3 above, so we merely present three examples.

Example 4.1:

<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

unfolding leaves

Illustration 4.3

For SMIL and source (FF4 or IE/ASV) see http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/demoFE.svg

Again, there is nothing new syntactically about this case, having been already covered by the syntax of Case 2 and Case 3. However, the implications for classes of interesting gradients are noteworthy.

Example 5.1: Rotating raindrop. (Note: a rotating translucent gradient has been applied over the top of this spectral element).

<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

Illustration 5.2

For those familiar with SMIL animation, the ability to create a smooth animation between two different paths, is one of the most pleasant of effects. Doing that with <replicate> is rather what the thrust of the <contour> proposal [15] is all about.

Syntax:

MISSING SYNTAX

Example 6.1: Interpolating between a convex quadrilateral and a concave one. In this example we have moved all the points one hundred pixels to the right except for the second one that moves 100 pixels to the left, for ease of reading the numeric values.

<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>

Example 6.2

<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>

SVG SMIL has a wonderful feature: the ability to move objects over time along arbitrary paths. Because calculating the x,y coordinates of Bezier and other smooth non-functional curves would be beyond the mathematical expertise of many programmers and because scripting is beyond the programming expertise of many of those who use pictures as a part of their communication, this gives us a way of creating animations that traverse smooth naturalistic paths reminiscent of the way the real world works. It is natural to expect our replications to follow smooth Bezier paths as well. Well one of the limitations of SVG's animation module however is that although we may allow the x and y positions of an object to be governed by an arbitrary path, this is not true of others of the object's attibutes like its radius, its width, or it chromatic values. Why not?

Well, since we are, in this paper, not confined to the SVG specification, we may invent a method by which the author may use the x,y values taken from arbitrary paths to control any pair of numeric values associated with the object being replicated! In short the <replicatePath> tag (or an analogous <animatePath> command for SMIL) acts like a bivariate version of the univariate <replicateAttribute> by controlling two variables at once!

Syntax:

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>

The attributes xattribute and yattribute signal how the x,y values of the referenced path, at the interpolated values given by repeatCount, will be converted into changing qualities of the SVGObject across the interpolation. The reader will note that we suggest allowing simple transformations of these values since the scale of things like stroke-width may not always correspond conveniently to x-y values of curves drawn visually in the plane. The forthcoming examples should serve to make this more clear.

This example syntax is meant to imply that we may have one or more <replicatePath> tags as children of the parent <replicate> and that the attributes affected by them should be distinct and should be distinct from those attributes adjusted by any univariate <relicateAttribute>s (if they exist) within the same <replicate>.

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

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" />
cx and ry values taken from a curve

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" />
defining three attributes of an object from a single curve

Illustration 7.3

Two of the use cases that came to mind as this proposal was being developed included

This poses less of a problem in terms of mark-up than it does for implementation, it would seem. Overall the problem is relatively clear. In the case of a linear gradient for example, we might wish to be able to modify the gradient's angle gradually across a range of possible angles. In the case of coding, though, this means discovering which referent the parent of the replicate refers to, finding it and its children (like <stop>s), cloning it repeatedly and then affiliating each new cloned gradient with the associated clone of the original SVGObject.

Syntax:

<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 >

An svgModifier is considered to be any of <linearGradient>, <radialGradient>, <animate>, <pattern>, <filter>, <clipPath>, and <mask>. Note: our JavaScript parser, thus far deals with only the first three of these.

A modifierChild is any of the specified children associated with these svgModifiers: for example <stop>s for gradients, filter primitives for filters, svgObjects for patterns, clipPaths and masks. Each modifierChild can be accessed by its position from top to bottom in the list of the svgModifier's childNodes.

An SVGObject has an attribute list associated with its type. E.g. x1, y1, and fill (among others) for <rect> and so forth.

Each SVGObject is linked by href to its SVGModifier. In the case of animate, the href is null and the children are its SVGModifiers: animate, set, animateMotion and related tags. In the case of gradients, filters and patterns, the href is specified by fill="url(#f)" , a pointer to the id of the associated modifier.

Within the replicateModifier we have the following attributes:

Yes, it is a bit complex, but it is pretty cool, powerful and necessary too! The examples may make it more clear.

Example 8.1

<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>

In this example, a basic cone shape is tilted slightly by the rotation transformation. A linearGradient runs through each of the clones. However, what we've done is to extract the stop located at offset=".6" and to change its color values as we move from bottom to top. The colors running along the right and left edges remain unaffected by these changes as shown in the Illustration.

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>
differntially animating replicates

Illustration 8.4

A similar example, animated and with source can be seen at http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2010/modifierB.svg

In order to make two dimensional "patterns" one will need to be able to replicate an object along one path, and then to replicate that new cluster of replicates in yet another direction.

The most straightforward way of doing this would be to use tags to mediate the nested replications. This has been explored in a variety of examples.

Example 9.1: Laying out several copies of an object already containing replicates. Here the basic shape is formed by interpolating (replicating) between two curvilinear paths (as shown in Example 6.2). Then that shape is replicated linearly via a "translate" transformation applied to the group in which it is contained. The parent group is then replicated with translation, scaling, and rotation applied resulting in a pattern that is distinctly non-rectangular

<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>
replicating across position then across stroke

Illustration 9.3

Illustration 9.4

Illustration 9.4

A gallery: Additional examples and curiosities

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

Additional issues and complexities

If the SVG Working Group should see fit to adopt the basic gist of this proposal, there will be numerous small issues and several large issues to be worked out. The small issues consist of the myriad number of syntactic details concerning how <replicate> and its relatives would handle interactions with the rest of the specification. We have worked out enough details of our proof of concept in enough browsers though, that we have confidence that interfacing these ideas with the rest of SVG is quite doable and will involve work that is similar in kind and spirit to much of the work that group has already accomplished. There are, nevertheless, three rather large issues for which our opinions have yet to congeal but which we think are important to the overall implementation of the concepts as presented here. These are

With many of the uses we have described here, the author of an SVG document will not be interested in the exact number of replicates that should be laid down but rather in knowing that this number is enough. By this we mean that it would be nice to be able to specify something like repeatCount="indefinite" so that the browser will either a) make a best guess as to the number required for the appearance of "smoothness" or b) solve the equations describing the asymptotic behavior of the drawing as n approaches infinity. SMIL animation has a similar problem in that we would like an object not to move in discrete steps of a fixed length, but rather to traverse a given amount of change in a given time as smoothly as possible. Implementers are required to estimate, one would suppose, just how much an object must actually move in a given screen refresh in order to render properly. The problem seems very much akin to what we are asking for here, but frankly, we admit to having little advice on how the browser implementers might solve this problem.

The examples provided to date have created all replicated content as new nodes in the DOM. If the reader examines some of these examples (particularly some involving animation) she will notice that the impact of so many DOM objects on browser performance is considerable. However, frequently, neither the SVG author nor the user will need interactive access to the content; they only need the content to be rendered. If we are generating a game board, or a richly featured environmental simulation, we want the replicated content to be active, clickable, interactive nodes. But in figure 4.2, for example, we are concerned with appearance of the final rendered object (at a given zoom and viewBox) and not with having each of the constituent nodes available for interaction. Accordingly, we think that as with the <pattern> node in which physically replicated content does not have impact on the DOM, an author using <replicate> should be able to choose whether or not it will have impact, so as to allow considerable speed up of rendering and animation. Syntax like

<replicate repeatCount="indefinite" DOMImpact="none"> 

should be considered to enable this.

Interactions between <replicate> and <animate> and between <replicate> and itself.

It is our sense that the realm of interaction between <replicate> and both <animate> and itself could be much greater than what we have had a chance to experiment with or to specify.

For example we envision several of the following capabilities:
  1. To be able to replicate along a path (as in Example 7.1) but to do so while that path is animated with changing d values. (Currently the replicates are given the values of the path statically as they are cloned.)
  2. For nested replications, to be able to change the repeatCount of the inner replication.
  3. To be able to animate the replication process, so that we might be able to view the introduction of new clones across time, rather than all concurrently.
  4. To be able to interact with "apparent surfaces" of the object as rendered. For example, in the simple figure of Illustration 4.1, it might be nice to be able to develop an application that would allow one to click and drag on a part of the "handle" of the object and have the proximities of the constituent components be recognized as parts of the three-dimensional entity that one perceives. That is, it might be possible, at least in many cases, to develop a shadow wire-frame for such an object that could be accessed through a JavaScript API, hence bringing true 3-D representations into SVG.

Source Code

The JavaScript code running our Proof of Concept has been remarkably easy to develop. The two authors spent probably two weeks apiece writing and then refining the basic code, including the development of example test cases. The JavaScipt program underlying the functionality that is seen for the examples of this paper is less than 150 lines of code. We recognize that if the proposal is favorably received by the SVG Working Group, it will be many months until the proposal is refined enough to become a part of a specification. And after that it will take time before such additions to the SVG spec are implemented by browser manufacturers.

Accordingly, we wish to work with the Working Group or other public spirited groups to make sure that the code is available, in that interim time period, to others who would like to use it and to develop it as an open source project. Recommendations on how best to do this while maintaining sufficient standardization that it could still be merged into the SVG standard are very welcome.

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;
}

Conclusions

We have found the <replicate> concept to a powerful one and one which is very "SVG-like." It bootstraps on the concepts of declarative graphics represented both in SVG and in SVG animation. It relies on and extrapolates on many extant parts of the spec. It is, once one begins to play with it, very intuitive for those already well-versed in SVG and SMIL. Developing the Proof of Concept in JavaScript was not at all a painstaking ordeal, suggesting to us that implementation in the wireless and web marketplaces should not be either.

It subtends a lot of the content of previous proposals for <contour> and <doodle>,and adds a heathy range of rich gradients, non-rectilinear textures, and quasi-3D objects to SVG all based on a part of SVG that is familiar to many already!

We are, in short, quite enamored of this technology and hope that you will a) experiment with it and b) advocate for its adoption.

Bibliography

[1] http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html

[2] http://www.inria.fr/recherche/equipes/otto.en.html

[3] http://srufaculty.sru.edu/david.dailey/svg/newstuff/gradient10.svg

[4] http://srufaculty.sru.edu/david.dailey/svg/newstuff/gradientmask1.svg

[5] http://srufaculty.sru.edu/david.dailey/svg/newstuff/gradientfilter1.svg

[6] http://srufaculty.sru.edu/david.dailey/svg/triangles7.svg

[7] http://srufaculty.sru.edu/david.dailey/svg/triangles4l.svg

[8] http://srufaculty.sru.edu/david.dailey/svg/newstuff/tilesA.svg

[9] http://srufaculty.sru.edu/david.dailey/svg/svgopen2008/tiles12.svg

[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.

[14] Israel Eisenberg, Tubefy Explained, Part I. http://owl3d.com/svg/tubefy/articles/article1.html

[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

[17] http://srufaculty.sru.edu/david.dailey/svg/svgopen2008/patternTile3.svg

[18] http://en.wikipedia.org/wiki/Turtle_graphics

[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

[20] http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#animation_varieties

[21] Opera has included much of SVG's animation module since initially supporting SVG; IE/ASV has very mature SMIL support, Firefox 4.0 beta has robust SMIL support for most attributes for most of the tags, while Safari and Chrome have fairly rudimentary SMIL implementations as of this writing.

[22] http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#timing

[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

[24] Some of the examples we use will look a bit more complex because of their use of namespaces. Since <replicate> is not a real part of SVG, we discovered that a bug in the Opera 10.6 browser prevented it from allowing some of the animated examples to start properly. This was overcome with the use of namespace information, by either declaring a namespace preface to the replicate tag: <madeupnamespace:replicate> or by adding a namespace variable within the tag itself: <replicate namespace="madeupnamespace">. One of our authors preferred to just wait until Opera fixes its bug, but the other argued, probably more knowledgeably, that using tags from a foreign name space really should reference that name space in some sense of grammatical compliance with how XML should work. The author with the better sense of humor clearly wrote this footnote

[25] The fact that the retrieved list of SVG tags is actually a node list rather than an array actually makes the code a bit more complex than what is shown here. The example here is merely by way of illustration, for those not familiar with the intricacies of manipulating SVG across browsers with JavaScript.

[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>

However it is not clear that we can use either <animateMotion> or <mpath> without the other. It therefore, made more sense to us to smush the two into a single <relicatePath> command parallel to what would be a <animatePath> command if this model were seen as adequate. Not having looked into the historical logic of how <animateMotion> and <mpath> came to be, we realize we may be missing some crucial piece of reasoning here.