SVG Animation in 2D and 3D

Using an XSLT path library to manage complex animations

Table of Contents

Path Library
Path Library
XML Form
Conversion to XML
Conversion back to SVG
Simple Conversions
Specialist Transformations
Three Dimensions
Basic Library
A Complete Structure Store
Enhancements to SVG
Enhancements to Path_ology

We have been interested in the use of computer animation as an educational and visualisation tool since the early 1970s. SVG, with its excellent declarative animation capabilities, has been our main tool over the last few years. A specific project, that we started in 2002, was to animate the WWW Conference Logos( as part of the Conference Opening and Closing Sessions. From Budapest (12th, 2003) until Edinburgh (15th, 2006), the complete set of animations were shown at each conference. Since Edinburgh, we have shortened it to just the four most recent conferences which has given us the ability to expand the time of each animation. In Madrid (May 2009), the Edinburgh, Calgary, Beijing and Madrid logos were presented at the start of the official opening by the Prince (and Princess) of Asturia, the heir to the Spanish throne.

Each year we have the problem of taking a logo constructed using a drawing package like Illustrator that needs to be transformed into SVG and animated efficiently. Our solution has been to transform the SVG document saved from the drawing package into an XML form that could be manipulated more easily before transforming back to SVG.

WWW6 in Santa Clara was the first conference logo to use 3D in a significant way and that presented a specific challenge. A simple 3D wire-frame animation system called PAEPOS [Hopgood03] was defined in 2002 to animate the construction of the WWW6 logo. Peter Gould [Gould05], a student on the Oxford Brookes Masters Course in Web Technologies, extended the wireframe system to a full 3D system with hidden line elimination and lighting as his dissertation in 2004. Although it demonstrated the possibility of doing full 3D animation in SVG, the cost was extremely high and required both a fast computer with a good graphics card and a high quality SVG implementation to be effective. In consequence, our later 3D animations have not included hidden line elimination and lighting effects.

Animations tend to be of the order of 6-10 Mbytes in size so automation is necessary to achieve the desired effect efficiently. Our approach has been to construct a path toolkit, called path_ology, for manipulating SVG path descriptions. The path description is converted into an XML data structure that is manipulated by a set of XSLT transformations. Each transformation performs a simple operation which can be pipelined together to produce the desired transformation. The path library has gone through a number of iterations to improve the performance and extend the functionality. A major advantage is that with a well defined XML structure it is relatively easy to add one-off transformations for a specific task (for example, simulating Japanese painting for the 2005 Tokyo Logo, a fully articulated skier for the 2007 Calgary and charcoal drawing for the 2008 Beijing Logo).

The structure of a path library, in our case, has been heavily influenced by our interest in declarative animation although the library itself may be of value in other areas.

SVG has the limitation that animating a path requires the two paths to be similar in structure. Essentially, this requires the number of commands in the two paths to be the same and to be of similar form. Curves must animate to curves, lines to lines and closed curves to closed curves. In consequence, there is often a need to manipulate the structure of paths if the original description had not anticipated animation. To maintain a reliable animation speed (particularly necessary with a synchronised sound track), there is a need to keep path sizes down. Performance is often impacted by the size of the path description. Relative paths of low accuracy will animate faster than absolute paths of high accuracy if the path size of the first is, say, a factor 2 or 3 smaller than the second.

Essentially, SVG paths are made of two types of commands: curves and arcs. However, SVG implementations tend to differentiate between lines and curves so that the basic set consists of lines, curves and arcs. The basic shapes can all be regarded as shorthands for one of these path types. Manipulating between these three path types will improve the ability to animate between paths.

Figure 2 shows the basic structure of the current library.

Starting with the SVG file, a simple transformation is applied on the SVG file to remove basic shapes if that is a requirement. Ellipses and circles are transformed into four arcs while rectangles consist of four lines and four arcs (to handle the corners).

Each transformation in the library is a separate XSLT file. We normally use the Saxon XLST implementation 6.5.5 although an MSXSL version of the library has been implemented in the past when extra speed was needed. Some transformations are more easily expressed using features in XSLT 2.0 as implemented in Saxon Version 9.

Our applications of the library have been in a Windows (XP or Vista) environment. A sequence of transformations can be invoked using a set of commands such as the following and storing the results of the intermediate stages in temporary files:

java -jar saxon.jar  -o t1.xml file.svg       break_path.xsl
java -jar saxon.jar  -o t2.xml      t1.xml    make_abs.xsl
java -jar saxon9.jar                t2.xml    add_subpath.xsl >t3.xml
java -jar saxon.jar  -o t4.xml      t3.xml    make_xml.xsl
java -jar saxon.jar  -o t5.xml      t4.xml    change_accuracy.xsl decplaces=1
java -jar saxon.jar  -o newfile.svg t5.xml    back_to_svg.xsl mode=relMabs

Clearly, this can be adapted for other operating systems. A sequence of transformations could be defined in a pipe-like notation such as:

break_path.xsl  |  make_abs | add_subpath | back_to_svg

Expressing them as a sequence of batch commands allows us to differentiate the XSLT 1.0 and XSLT 2.0 transformations and show the parameterisation.

A major decision is the structure and content of the XML canonical form to be used by the library which is influenced by the transformations required. The current structure in use is shown below.

SVG 1.2 defines an SVG path as:

  1. A normalized path has only four elements, moveto, line, curve and closepath

  2. A moveto command establishes a new current point and starts a new subpath

  3. A Z or z command closes a subpath by adding a line command back to the initial current point always

  4. A moveto command after a Z or z establishes a new subpath

  5. A command other than moveto following a Z or z command starts a new subpath with the same current point as the previous one.

This suggests a structure consisting of a path command with a sequence of subpaths as its children and each subpath consisting of a sequence of commands. To reduce file size, each path command becomes a single XML command with its parameters as attributes. Attribute and command names are kept as short as possible to keep file size down.

The aim is to make each command self contained so, on the initial conversion, a set of operations are performed:

  1. The current position (cx,cy) is added to each command.

  2. All coordinates are made absolute.

  3. All Bezier curves are converted to C commands (cubics).

  4. The original curve's parameterisation is retained by the attributes o (which specifies the original command type and whether it was relative or absolute initially).

  5. All lines are converted to L commands.

  6. Arcs are retained as absolute arcs

The initial conversions from S to C and T to Q just require the mirror image of the control point to be added. The only complication is when an S command, say, appears with no previous C command. The standard explains the action to be taken. The formula from Q to C is to have the two control points at (cx/3+2ctrlx/3,cy/3+2ctrly/3) and (2ctrlx/3+endx/3,2ctrly/3+endy/3) where Q's control point is at (ctrlx,ctrly) and the endpoint is at (endx,endy).

The reverse operation is to generate an SVG file from the XML file. Here, the user has the option of converting to relative or absolute coordinates and to generate the shorthand equivalents if appropriate. The conversions in the other direction (C to Q, say) just require testing that the relationship above applies between the control points and end points within a specified error bound.

Internal forms for the line drawing commands used are:

<c t="C" o="C" cx="" cy="" x1="" y1="" x2="" y2="" x3="" y3="" />
<c t="L" o="L" cx="" cy="" x3="" y3=""/>
<c t="A" o="a" cx="" cy="" a="" b="" d="" e="" f="" x3="" y3="" />

Adding the initial position to each command makes both reversing and changing to relative straightforward. The attribute t defines the type and o defines the original type. All abbreviations are changed to one or other of these on input (l,v,h,V,H all become L), (S,Q,T,c,s,q,t all become C), (A,a become A). The choice of names for the arc command attributes were quite arbitrary other than being single letters (a for rx, b for ry, c for x-axis rotation, d for large-arc-flag and e for sweep-flag).

The SVG document states that each path consists of a set of subpaths which are either open or closed. The Z command closes a subpath (by adding an an L command) and may start a new open or closed subpath. The meaning of open and closed subpaths is different in terms of linecap, filling etc. This leads to an internal form where:

  • The SVG M command is changed to either an M command (denoting a closed curve) or an N command (denoting an open curve)

  • Each subpath starting with an M command is concluded by an L command followed by a Z command

  • Each subpath starting with an N command is concluded by an E command

  • Each Z command not followed by an M command will have an M or N command inserted

On reversing, Z and E become M and N and vice-versa. Another complication comes about from the possible need to turn an M or N command back to an SVG m command if a relative SVG form is required. It is easier if an M or N command retains the previous current position as well as the new one. Similarly, the Z and E command retains the next current position. The forms for the four commands are then:

<c t="M" o="M" cx="xbf" cy="ybf" x3="xa" y3="ya"/>
<c t="N" o="M" cx="xbf" cy="ybf" x3="xa" y3="ya"/>
<c t="Z" o="Z" cx="xk" cy="yk" x1="xi" y1="yi"/>
<c t="E" o=""  cx="xk" cy="yk" x1="xi" y1="yi"/>

(xa,ya) is the new current position. (xbf,ybf) is the previous current position. (xk,yk) is the current position when the Z or E command appears. Note that the insertion of an L command means that this is the start position of a closed curve and the last position of an open curve. (xi,yi) is the start position of the next subpath.

Finally, the SVG path element may contain content that needs to be retained but differentiated from the internal commands. This has a content element wrapped around it. For example:

<path style="stroke:blue" id="example" d="M100,250
l25,25h25v-25C215,275 235,225 250,250S285,225 300,250Q325,275 350,250
c15,25 35-25 50,0s35-25 50,0s35,25 50,0q25,25 50,0t50,0t50,0
m25,0 v50h40z
<set attributeName="id" to="'example2'" begin="1s" />

would be transformed into:

<path style="stroke:blue" id="example">
    <c t="N" o="M" cx="0" cy="0" x3="100" y3="250"/>
    <c t="L" o="L" cx="100" cy="250" x3="125" y3="275"/>
    <c t="L" o="H" cx="125" cy="275" x3="150" y3="275"/>
    <c t="L" o="V" cx="150" cy="275" x3="150" y3="250"/>
    <c t="E" o="" cx="150" cy="250" x1="150" y1="250"/>
    <c t="M" o="M" cx="150" cy="250" x3="150" y3="250"/>
    <c t="L" o="l" cx="150" cy="250" x3="175" y3="275"/>
    <c t="L" o="h" cx="175" cy="275" x3="200" y3="275"/>
    <c t="L" o="v" cx="200" cy="275" x3="200" y3="250"/>
    <c t="C" o="C" cx="200" cy="250" x1="215" y1="275" x2="235" y2="225" x3="250" y3="250"/>
    <c t="C" o="S" cx="250" cy="250" x1="265" y1="275" x2="285" y2="225" x3="300" y3="250"/>
    <c t="C" o="Q" cx="300" cy="250" x1="316.667" y1="266.667" x2="333.333" y2="266.667" x3="350" y3="250"/>
    <c t="C" o="T" cx="350" cy="250" x1="366.667" y1="233.333" x2="383.333" y2="233.333" x3="400" y3="250"/>
    <c t="A" o="A" cx="400" cy="250" a="20" b="20" d="1" e="0" f="0" x3="450" y3="250"/>
    <c t="A" o="a" cx="450" cy="250" a="30" b="30" d="1" e="0" f="0" x3="500" y3="250"/>
    <c t="C" o="c" cx="500" cy="250" x1="515" y1="275" x2="535" y2="225" x3="550" y3="250"/>
    <c t="C" o="s" cx="550" cy="250" x1="565" y1="275" x2="585" y2="225" x3="600" y3="250"/>
    <c t="C" o="s" cx="600" cy="250" x1="615" y1="275" x2="635" y2="275" x3="650" y3="250"/>
    <c t="C" o="q" cx="650" cy="250" x1="666.667" y1="266.667" x2="683.333" y2="266.667" x3="700" y3="250"/>
    <c t="C" o="t" cx="700" cy="250" x1="716.667" y1="233.333" x2="733.333" y2="233.333" x3="750" y3="250"/>
    <c t="C" o="t" cx="750" cy="250" x1="766.667" y1="266.667" x2="783.333" y2="266.667" x3="800" y3="250"/>
    <c t="L" o="v" cx="800" cy="250" x3="800" y3="350"/>
    <c t="L" o="h" cx="800" cy="350" x3="150" y3="350"/>
    <c t="L" o="" cx="150" cy="350" x3="150" y3="250"/>
    <c t="Z" o="Z" cx="150" cy="250" x1="150" y1="250"/>
    <c t="M" o="" cx="150" cy="250" x3="150" y3="250"/>
    <c t="L" o="l" cx="150" cy="250" x3="250" y3="150"/>
    <c t="L" o="h" cx="250" cy="150" x3="550" y3="150"/>
    <c t="L" o="" cx="550" cy="150" x3="150" y3="250"/>
    <c t="Z" o="Z" cx="150" cy="250" x1="650" y1="150" />
    <c t="N" o="m" cx="150" cy="250" x3="650" y3="150"/>
    <c t="L" o="v" cx="650" cy="150" x3="650" y3="190"/>
    <c t="L" o="h" cx="650" cy="190" x3="700" y3="190"/>
    <c t="E" o="" cx="700" cy="190" x1="725" y1="190" />
    <c t="N" o="m" cx="700" cy="190" x3="725" y3="190"/>
    <c t="E" o="" cx="725" cy="190" x1="750" y1="190"/>
    <c t="M" o="m" cx="725" cy="190" x3="750" y3="190"/>
    <c t="L" o="v" cx="750" cy="190" x3="750" y3="240"/>
    <c t="L" o="h" cx="750" cy="240" x3="790" y3="240"/>
    <c t="L" o="" cx="790" cy="240" x3="750" y3="190"/>
    <c t="Z" o="Z" cx="750" cy="190" x1="750" y1="190"/>
    <c t="N" o="" cx="750" cy="190" x3="750" y3="190"/>
    <c t="L" o="h" cx="750" cy="190" x3="800" y3="190"/>
    <c t="E" o="" cx="800" cy="190" x1="0" y1="0"/>
    <set attributeName="id" to="'example2'" begin="1s" /> 

As can be seen, the size of the internal form is quite a bit larger than the original. The advantage is that it can now be easily manipulated using XSLT and many transformations can occur down at the individual command level.

The conversion from SVG to the canonical internal XML form takes place in four steps.

The first transformation, break_path, takes the full path description and turns it into an intermediate XML form, one element per command. A major problem is handling what can be a very long SVG d attribute (these can be over 1 Mbyte for map outlines, for example). To avoid this, the string being worked on is always a small part of the total string. The next command's maximum length can be estimated given that the number of arguments to the command are known. Currently numbers with exponents are not catered for but it would be relatively easy to change if the need was there. The command recogniser is a finite state table that is optimised for numbers having several digits either before or after the decimal point. It runs significantly faster than a version that was based on regular expressions.

A second transformation, make_abs, is a quite straightforward recursive template that keeps a record of the last current position and possible Q and C control points. It uses this information to transform the next command into either an absolute cubic or a line. The appearance of a Z command is always preceded by an L command back to the initial point of the closed path. Each unclosed path is terminated by an E element and each Z followed by a drawing command has an M element inserted. The internal form of the commands is established here. Two attributes t and o give the internal type and the original type of the command. Each command includes attributes cx,cy that give the current position. The attributes x3,y3 always contain the end point that will be the next current position. The cubic command has additional attributes x1,y1,x2,y2. The arc command has additional attributes a,b,d,e,f. The attribute names are either one or two characters long to keep the file size down and each arithmetic computation rounds the number to 3 decimal places (this can be changed by the user).

The third transformation, add_subpath, inserts a subpath element s around each group starting with a command of type M. This is an XSLT2 transformation using the for-each-group function.

Finally the make_xml transformation makes the individual subpaths self-sufficient by changing M commands to N commands if the command starts an open subpath. The closing E command adds the current position of the next curve as x1,y1 attributes. This facilitates a straightforward function to do path reversal.

The overall task could have been completed in less transformations but splitting it into 4 simplifies the XSLT transformations. An earlier version completed the task in two transformations.

A set of simple conversions allow the user to manipulate SVG paths into a desired form.

Conversion from arc to C is complicated by the SVG format for arcs where the arc radii effectively define the aspect ratio of the ellipse and, if the radii are too small to define a curve from the start position to the end, it is scaled until a solution is available. For arcs greater than 90 degrees, it is sensible to break the arc into sections. A maximum of 4 cubics is needed. The papers by Maisonobe [Maisonobe] and Hoffman [Hoffman] give ways in which this can be tackled. Essentially the tangent at the two end points is known and it is a matter of positioning the two control points at places that yield the minimum error between the cubic and the arc. There is not a unique solution but an optimal solution can be found which does not require iteration.

Figure 5 shows the four possible arcs between the two points and the equivalent cubic representations. The large arcs have been converted into four cubics.

Figure 9 shows the Statue of Liberty that was part of the logo for WWW2004 in New York. The logo also included a mouse wrapped around the statue. The decision was made to animate the logo by having the mouse appear and effectively draw the Stature of Liberty which was made up of 65 curved areas.

An original curve is shown and a transformed path which consisted of pairs of cubics between the points illustrated, such that the change of direction is not great between any two pairs of cubics and the next two. Once the path has been transformed (the path shown is the right top part of the head), it was possible to define an XSLT transformation that collapses all the cubics to coincide with the top two points. At the first stage, the points of the first two cubics are animated to change to their original position. Then the next two are animated and so on until the complete line is recreated. The effect is as though the line is being drawn by hand. Once the template has been defined, each of the paths can be drawn in turn giving the effect that the whole Statue of Liberty is being drawn by hand. The process would have been both time consuming and error prone without the use of the library.

This one-off animation resulted in two more general-purpose transformations being added to the library that effectively simulated the drawing of a line. Although animating stroke-dashoffset gives a reasonable approximation to line drawing, it seemed worthwhile having a transformation that took a cubic curve and expanded it to an area using offset curves and then animating the filling of the area as in the New York animation. This became the transformation thicken_cubic, which is more a template to be modified to suit a particular application when the need arises. By splitting the original curve several times, it is possible to draw curves with quite tight turns effectively.

The left-hand side of Figure 10 shows the four curves to be animated. The right-hand side shows the curves at different stages in the animation of the curve drawing.

While generating offset curves is not easy, defining a curve at a constant distance at a specified angle to the original curve is relatively simple. This resulted in a second transformation, caligraphy, which has the curve being drawn by a caligraphic pen. The user can define the angle of the pen and its width.

For WWW2005 in Tokyo, the logo was a stylised drawing of a Geisha which had a cherry blossom made up of the three Ws as a brooch. To emphasise the relationship of the logo to the cherry blossom, the animation consisted of drawing the Japanese symbol for Sakura (cherry blossom) and then to morph this first into a realistic geisha and then to the stylised form.

The initial problem was to try and simulate the Japanese caligraphy in a realistic way. In this case, each stroke of the sakura glyph consisted of just 6 cubics in the SVG path. The two chords shown in the top left of Figure 12 were used to interpolate individual brush strokes sufficient to fill the outline of the defined area. The animation started by animating the set of lines (shown bottom left) between the two chords. These were calculated from the boundary cubics. Once the animation had started, the chord at the left end was filled in as though the ink was soaking into the paper. The individual line drawn paths had their widths animated to also simulate the ink soaking into the paper. Finally the end arc was filled in and the whole area was filled in black as though the ink had finally dried. The individual lines have been shown in Figure 12 in different colours to emphasise the effect. In the actual animation, they were all a dark grey in colour in order to make the effect quite subtle. Again, this was relatively easy to write in XSLT as a transformation on the XML description of each path. Producing the 70 kbytes of SVG from scratch would have been quite time consuming.

A further transformation was added to the library, paint_cubic, which starts with a simple cubic curve (left of Figure 13) and constructs an area offset either side of the cubic. This area is initially filled with a set of thin lines (lower left in green). As the area painting proceeds the lines gradually thicken (upper left brown). Following the line drawing is a transparent filling of the area (upper right blue) and finally the area filling catches up with the line drawing and becomes opaque (lower right red).

Figure 14 shows the type of curves that were used in the Beijing logo. The main parts of the logo were produced using Illustrator and had a simulation of charcoal drawing as their basis. Trying to animate a curve of that form would not be easy without some support. In consequence, a specialist set of transformations were generated that would animate curves made up of just 3 cubics (see Figure 15).

The first stage was to thicken the original cubic into an area by defining offsets from the original control points inside and out which gives an area enclosed by six cubics as shown by Figure 16.

Splitting each cubic into two several times gave small cubics to which charcoal effects could be applied. The set of cubics after the splitting is shown in Figure 17.

For each cubic, offset curves based on the length of the cubic were defined and these were then used to modify the original curve. The set of effects applied are shown in Figure 18.

The curves in Figure 14 are the result of applying these various effects at suitable positions on the curve. In the final animation of the logo, the three main curves making up the logo were each drawn with a different selection of effects for each. An XML file was specified that listed the effects to be applied at each section of the curve.

As a result of this work, a transformation, elabourate_cubic, was added to the library. This has a parameter thick that defines the depth of the elabouration specified and a type value. Some initial type values have been defined but the transformation was designed so that users could add their own. The initial cubic is divided into 3 segments and four parallel curves are defined that are the distance thick and 2*thick inside and outside the original curve. The user can define cubics to replace the original cubic using any of these points. Figure 19 shows the original curves and some of the elabourations possible.

Our previous work [Hopgood03] [Gould05] has shown that it is possible to achieve realistic 3D drawings using SVG and, for wire frame images, to get realistic animations of perspective views of wire frame objects by interpolating between keyframes transformed into 2D SVG paths. To get realistic animations between 3D images with hidden lines eliminated using SVG, it was only possible to generate static images and then display them at a constant frame rate to achieve the desired effect. An attempt to use declarative animation with interpolation is difficult because the order of drawing of individual paths has to change during the animation and this is not possible between keyframes. In both attempts, the 3D drawing was defined in a special XML language and this was transformed into SVG.

We were interested in seeing if we could take an image defined in SVG, add a third dimension by placing it in the Z=0 plane and then, by applying 3D transformations to parts of the image, achieve realistic 3D effects. If we limited the change in viewing position, it would be possible to keep the rendering order the same throughout an animation and, therefore, extend the original wire frame declarative animation to filled areas. A good vehicle for trying out these ideas was the animation of the WWW2006 logo shown in Figure 20.

The logo consisted of two sets of Ws (one at the top of the thistle and the other as part of 'WWW2006'). The challenge was to come up with something that we could achieve in 3D and would have a Scottish flavour. The decision was made to try and get the six Ws to perform a Scottish Country Dance (The Flower of Edinburgh) in a setting created by the characters in the text. In consequence, we added to the basic path library add_perspective_scenes. The original SVG paths making up the characters were transformed into XML using the standard library and each coordinate had a z element added with its value set to 0. Transformations allow individual paths to be transformed both in size and orientation creating either a single scene or a sequence of scenes. A perspective transformation is then performed back to 2D using a defined viewpoint, orientation and view plane. This can be different for each scene in the sequence. This allowed us to have a static scene with the viewpoint changing, or a static viewpoint with the objects changing in position or both together. Once the scenes were transformed to 2D, an animate transformation generates an animation between the scenes. Figure 21 shows the overall scene created for the WWW2006 logo.

The text has been transformed to positions that created the dance floor for the Ws. The logo animation changes the view on this scene to show the 3D effect. To animate the Ws in the dance, it was necessary to create a number of 2D versions of the Ws in positions with each leg up. By transforming the position of each W from scene to scene, and selecting the appropriate W from the set, it was possible to create the Country Dance. Figure 22 shows some of the individual frames to illustrate the effect.

The frames go from left to right and top to bottom. The two nearest dancers chase each other around the other dancers arriving back in their original positions but with the man in the lady's place and vice versa (frame 8). The chase is repeated (not shown) which gets the dancers back to their original positions. They then promenade down and back between the other dancers. Finally, not shown, the two lead dancers hold hands and dance to the far end with the other dancers moving up ready to start the sequence again for the second pair. In the actual animation, the surrounding floor is shown throughout. The effect is quite realistic.

To illustrate the generation of a single perspective scene, Figure 23 shows a miscellany of some well-known SVG pictures.

The 3D extensions have changed considerably over the last few years. The current version has a file format as shown below:

<svg ...>
Any SVG commands
      <objbg transform="translate(0,0,-300)">
        <obj class="w18mt01" href="w18mt01"/>
      <objbg transform="rotx(90)">
        <obj class="w18fg01" href="w18fg01"/>
      <scene id="w18st1">
        <objfg transform="translate(-145,-64,80) roty(45)"> 
          <obj class="w18wdm1" href="w18wdm1"/>
          <obj class="w18wdm1l" href="w18wdm1l" />
          <obj class="w18wdm1r" href="w18wdm1r" />
          <obj class="w18wdm2" href="w18wdm2" />
          <obj class="w18wdm3" href="w18wdm3" transform="rotz(60)" />
          <obj class="w18wdm4" href="w18wdm4" transform="rotz(60)" />
      <scene id="w18st2">
    <path id="w18mt01"  d="M-20,10...."/>
    <path id="w18fg01"  d="M-40,30...."/>
    <path id="w18wdm1" d="M-25,60C-8.5,65,8.3,65,25,60..."/>
    <path id="w18wdm1r" d="M25,60C25,60,25,20,25,20..."/>
    <path id="w18wdm3" d="M1.3,1.3L2.5,0L32.5,30..."/>
    <path id="w18wdm4" d="M5,5L10,0..."/>
  More SVG

The pseudo SVG is transformed by a set of command as follows:

java -jar saxon.jar  -o t1.xml file.svg   break_path.xsl
java -jar saxon.jar  -o t2.xml  t1.xml    make_abs.xsl
java -jar saxon9.jar        t2.xml        add_subpath.xsl >t3.xml
java -jar saxon.jar  -o t4.xml  t3.xml    make_xml.xsl
java -jar saxon9.jar        t4.xml        def_perspective_scenes.xsl > t5.xml
java -jar saxon.jar  -o t6.xml  t5.xml    change_accuracy.xsl decplaces=1
java -jar saxon.jar  -o out.svg t6.xml    back_to_svg.xsl mode=relMabs
java -jar saxon.jar  -o anim.svg out.svg  animate_scenes.xsl 

The first four commands convert the individual path descriptions in the defs section to the internal XML form.

The individual scene definitions use these path descriptions to construct the individual key frames of an animation. Constant background material that applies to all scenes is given by a set of objbg elements. In the example there are two separate backgrounds. The first is placed at the back of the scene with a far-away Z value. The second is an X-Y scene that is changed to an X-Z scene that forms the ground layout for the animation.

Each scene consists of a set of foreground objects defined as objfg elements. Note that both the objbg and objfg elements can have a 3D transformation applied to the individual obj elements and to the background or foreground element itself. In the foreground object example, a rotation about the Y-axis followed by a translation is applied to the whole object and two of the individual paths making up the object have a transformation about the Z-axis as well.

The perspective transformation is defined by the perspective element and this also defines any post processing to be applied after the conversion back to 2D. The 3D axes have the Y-axis downwards as for SVG and the Z-axis coming towards the viewer.

The perspective transformation is defined by the parameters in Figure 24.

A viewing point of this scene is defined in spherical coordinates by the elements over, up and rad. Both over and up are given in degrees and rad is the distance from the origin. A plane can be imagined perpendicular to the line from the viewing point to the origin. This plane is called the Picture Plane. The element pp defines the distance from the viewing point to the picture plane. The scene to be viewed normally lies on the opposite side of this plane. Visual rays from the objects to the viewing point pierce the picture plane, tracing out a perspective drawing as it scans the scene. It is this projected view that is converted back to 2D. If the picture plane is moved towards the viewer, the image becomes proportionately smaller, and vice versa. The coordinates of the viewing point can be located in any of the eight octants, so that the figure can be observed from any possible position.

Once the perspective view has been calculated, the size of the view and the origin can be changed by the parameters newx,newy and size.

Figure 25 shows a frame from the opening sequence for the Madrid animation.

The current scene definitions in the Library just cater for 3D transformations applied to individual objects and to groups of objects. This is sufficient for many animations. However, for large articulated objects it is insufficient. We have experimented with a deeply nested structure store that was modelled on the ISO standard PHIGS [PHIGS]. In PHIGS, any structure can call a substructure with its own local transformations. This can continue to any depth. The transformation applied to an object is the accumulation of the transformations that apply. For a human figure, the transformations applied to the foot are the transformations applied to the foot plus the lower leg, the upper leg, the torso etc.

For the Calgary animation, a human skier was constructed and the appropriate PHIGS-like structure store. Figure 29 shows the skier. Figure 30 shows the structure store. Figure 31 shows a number of positions of the skier with different transformations applied to the various body parts.

The path library has proved useful both as a general optimiser tool for manipulating specific SVG files and for achieving specialist effects which would be quite difficult using manipulations on the SVG directly. The performance is reasonable with a set of operations being performed in seconds not minutes unless the files are very large.

The most demanding operation is the initial conversion from SVG d attribute to XML. The current implementation breaks a long d string into sections recursively translating a command at a time. This can do the transformation at the rate of about 20 secs for a 1-Mbyte d string on a modest PC using XP. Using XSLT 2.0, it is possible to tokenize the string via a regexp. This results in significantly shorter code but performance is about a factor of 2 slower. In general, this is not a problem but can be an issue for very long d strings.

Clearly, the library could be defined using a different language. The string handling in XSLT 1.0 is not extensive (somewhat improved in XSLT 2.0 but mostly syntactic sugar) and, no doubt, transforming the d attribute to an XML form might be more easily done using some other language. However, the XSLT implementations are quite fast and a good XSLT implementation of the transformation is probably not much different from using a conventional language. One question that arises whenever another language is suggested, is which one. There is no standard language. Whichever is chosen (Javascript, Perl, C, C+, Java etc) will make it inaccessible to some.

Some benefits do come from using XSLT. It is the most appropriate transformation engine for XML and there is no question that XML is the right choice for the internal form. The ability to copy large parts of an XSLT structure while manipulating specific elements makes transformations such as conversion between absolute and relative and accuracy changing very compact and, therefore, easy to modify to suit specific activities. The ability to transform XML into XSLT is also of value. XSLT is a genuine translator writing system. The finite state recogniser for numbers in the d attribute is not written directly in XSLT. The finite state definition is written in XML and a generic finite state transformer in XSLT constructs the XSLT transformation applicable for that finite state machine. In consequence, adding support for exponents (which is currently not supported due to XSLT 1.0 limitations) would be a simple change to the finite state definition.

The use of XSLT in transforming SVG paths has given us a piece of Coursework that give students some insight into both SVG and XSLT. Getting the SVG d attribute into the correct internal form is quite demanding as are some of the transformations they had to complete. Understanding the precise relationship between the various Bezier commands and the M and Z commands also was achieved. A range of XSLT functionality has to be applied to define the library. Also a big plus is that the students enjoy the coursework and realise the power of XSLT.

The work uncovers some of the aberrations in the design of SVG. For example, the difficulty in reversing a path indicates that the use of Z is overloaded. It would have been cleaner to have Z just close a path. At the moment it acts as a final line draw, a closed path declaration and a surrogate M for the next command if an M command does not follow the Z. Similarly, allowing S without an appropriate command before it adds to code complexity. On the other hand, having S following Q is quite a valid operation, with Q having an equivalent cubic form yet the control point defined by SVG is inappropriate for this sequence.

The standard documents themselves for SVG are not as clear as they might be. In consequence, implementations have made different decisions. This is particularly true in the animation area. For example, the document states that Z followed by C causes a line to be drawn from the endpoint of the C to the initial point of the subpath. Does that mean that a subpath ...C..Z can be animated to a subpath ...C..L..Z or not?

Is it sensible to have path descriptions that are a mixture of open and closed paths? Almost always, the properties associated with closed paths are different from those associated with open paths. Frequently a closed path is filled; rarely is an open path filled.

All paths in SVG can be transformed into equivalent cubic Beziers. In consequence, it would be possible to have a single basic path primitive, the cubic Bezier with all other forms being shorthands. Animating between lines and curves would then be possible. The decision to treat lines differently from curves causes unnecessary restrictions.