Abstract
We have used SVG to animate the Conference Logos at the Opening Session of each of the World Wide Web Conference Series since 2002 and as the basis of a number of student projects aimed at developing tools to ease the prduction of SVG animations. Our approach has been to construct a path toolkit, called path_ology, for manipulating SVG path descriptions using XSLT. The three main components of the toolkit are support for basic transformations of SVG paths, adding a third dimension to give 3D effects and finally support for animation scene development.
Table of Contents
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(http://www.iw3c2.org/conferences/) 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 basic elements of the path library are the conversion of the SVG path description into a canonical internal form consisting of subpaths made up of absolute cubics, arcs and lines and the conversion of the internal form back to either the original form or a more desirable form.
Once the internal form has been generated, the path definitions can be manipulated by specific transformations. The library of transformations is open-ended and can easily be enhanced by the individual user. A number of students have used the path library as the basis for their own extensions.
The conversion in and out of an XML format also gives the opportunity to avoid certain path sequences that are implemented incorrectly in specific implementations. For example Safari incorrectly animated the sequence Z followed by m and Opera the sequence m followed by m.
To achieve 3D effects, the approach taken has been to add a Z-dimension to each path primitive, provide 3-D transformations to each 3D path individually, followed by a perspective transformation back to the 2-D form.
The main animation support has been the ability to define a set of individual scenes (keyframes) and to automate the transformation of the individual paths to animation commands. For 3D animations, if the keyframes are sufficiently close, the 2D interpolations give realistic 3D effects. All the animations have soundtracks synchronised to the animation actions. Both the Adobe plugin and Safari are able to support excellent synchronisation over animations lasting several minutes.
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:
A normalized path has only four elements, moveto, line, curve and closepath
A moveto command establishes a new current point and starts a new subpath
A Z or z command closes a subpath by adding a line command back to the initial current point always
A moveto command after a Z or z establishes a new subpath
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:
The current position (cx,cy) is added to each command.
All coordinates are made absolute.
All Bezier curves are converted to C commands (cubics).
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).
All lines are converted to L commands.
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 L125,275 H150V250 M150,250 l25,25h25v-25C215,275 235,225 250,250S285,225 300,250Q325,275 350,250 T400,250A20,20,1,0,0,450,250a30,30,1,0,0,50,0 c15,25 35-25 50,0s35-25 50,0s35,25 50,0q25,25 50,0t50,0t50,0 v100h-650z l100,-100h300z m500,-100v40h50 m25,0 m25,0 v50h40z h50 "> <set attributeName="id" to="'example2'" begin="1s" /> </path>
would be transformed into:
<path style="stroke:blue" id="example">
<s>
<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"/>
</s>
<s>
<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"/>
</s>
<s>
<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" />
</s>
<s>
<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" />
</s>
<s>
<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"/>
</s>
<s>
<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"/>
</s>
<s>
<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"/>
</s>
<content>
<set attributeName="id" to="'example2'" begin="1s" />
</content>
</path>
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.
There are two transformations that change the XML internal form back to d attributes of SVG path elements.
back_to_svg generates a d attribute consisting of M, Z, A, C and L commands only. The commands can either be absolute or relative. This is achieved by a mode parameter added to the bat file (mode=abs or mode=rel). A bug in the current Chrome implementation (May 2009) means that an m command following a Z command is not animated correctly. In consequence, a third mode is provided (relMabs) that always outputs absolute M commands.
The second transformation, simplify_and_back_to_svg, is similar to back_to_svg except that it tries to simplify both L and C commands. If possible C commands are turned to S, Q or T commands while L commands are turned to H and V. A parameter toline if set to ctol will also attempt to convert C to L, H or V if appropriate. A typical bat file might be:
java -jar saxon.jar -o new.svg inner.xml simplify_and_back_to_svg.xsl mode=relMabs toline=ctol
A set of simple conversions allow the user to manipulate SVG paths into a desired form.
Frequently, path data has an accuracy more than what is required. This transformation just changes the accuracy of all x- and y-values to the number of decimal places specified. So, for example, if a single decimal place accuracy is required, setting the variable round to 10 and applying the template below is all that is required:
<xsl:template match="@x1 | @y1| @x2 | @y2 | @x3 | @y3| @cx | @cy">
<xsl:attribute name="{name()}"> <xsl:value-of select="round(. * $round) div $round"/></xsl:attribute>
</xsl:template>
The transform attribute of a path is applied to its coordinate values. The individual transforms in the attribute value are turned into matrices and multiplied together. The resulting matrix is applied to the values in the path definition. This is often of value when animating between a path defined without a transformation and one that has a transform attribute. Without apply_transform, it is difficult to achieve this in SVG.
The last command becomes the first and so on taking account of the changes in meaning of M and Z commands. The internal form of the subpaths and their commands makes this quite a simple operation. It is useful when animating along a path and the path description is the opposite direction to the one required.
![]() |
Outline of Australia and lower accuracy reversed path (SVG animates an ellipse along the path in the presentation)
Figure 3. Australia reversed
For an object made up of a series of short lines, this transformation takes any short line, which has lines before and after it, and converts it to a cubic Bezier with the tangent at the start of the curve equal to the direction of the first line and similarly the end tangent the same as the direction of the third line. The parameter short defines the maximum length of the line which is converted. The heuristic is due to Maxim Shemanery [Shemanery].
Figure 4 shows an implementation of the heuristic developed by Shemanery to transform SVG paths made up of lines into equivalent spline curves where a certain amount of smoothing has taken place.
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.
Converts the basic line drawing elements (rect, circle, ellipse, polyline and polygon) to path elements. This conversion is an SVG to SVG transformation that should be applied before d attributes are converted to XML.
Transforms all line commands to the equivalent cubic. The two control points are positioned 1/3 and 2/3 of the way along the line.
Breaks each cubic at the midpoint into two cubics. Figure 6 shows a duck made from a number of cubics having each cubic divided into two five times. The transformation can be used when it is necessary to have effects applied to small sections of a curve. Also useful when adjusting the number of curves in two paths to allow them to be animated. A version that only breaks curves above a defined length would be relatively easy to implement.
Merges two short cubics into a single cubic where the variable smallc is the maximum length of the two curves. Figure 7 shows the set of ducks in Figure 6 after the merge transformation.
Takes two short drawing commands (either cubics or lines) and generates a single line between start and endpoints. Normally used in association with smooth_short_lines to reduce the size of paths made up of too many segments. Figure 8 shows the UK coastline before and after the shorten operation.
There is a global variable, shorten, that defines the maximum length for the two commands for the transformation to be applied. The transformation may well be performed several times, if there is too much detail in the original description.
The transformation curve_length calculates the length of each line or cubic adding this as an attribute l to each command together with an attribute er which indicates the estimated maximum error in the length value. The transformation path_length sums the individual lengths and errors, adding the length and error to the path as a whole and the individual subpaths. These two transformations are normally used together to calculate the length of a path.
Given the last random number in a sequence, the transformation calculates the next random number. A useful utility function when random effects are required.
Once the basic library is in place, it is relatively easy to develop special templates for one-off functions that would be much more time consuming if you had to start each from scratch. Two examples are given here of effects that were required for the New York and Tokyo Logos.
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
<metadata>
<scenes>
<timing>6s</timing>
<objbg transform="translate(0,0,-300)">
<obj class="w18mt01" href="w18mt01"/>
.....
</objbg>
<objbg transform="rotx(90)">
<obj class="w18fg01" href="w18fg01"/>
....
</objbg>
<scene id="w18st1">
<perspective>
<over>-20</over>
<up>-11</up>
<rad>1600</rad>
<pp>1800</pp>
<newx>0</newx>
<newy>0</newy>
<scale>1</scale>
<spline>constant</spline>
</perspective>
<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)" />
</objfg>
</scene>
<scene id="w18st2">
......
</scene>
......
</scenes>
</metadata>
<defs>
<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..."/>
</defs>
More SVG
</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.
Any 3D scene can be enhanced by adding shadows of objects on the Y=0 plane. The transformation add_drop_shadow.xsl is performed before def_perspective_scenes.xsl. The scene definition is enhanced by an additional shadow element:
<shadow><over>50</over><up>-40</up></shadow> <scene id="..">
The shadow element defines the position of the sun. Unless otherwise specified, every object defined in the scene has an additional object added which is the shadow. The class of this object is the same as the original object but with drop added before. The user needs to add another style rule indicating how the shadow is to be rendered. For example:
path.dropxxx {fill:#333333;stroke:none;fill-opacity:0.5}
Making the shadow semi-transparent allows any objects underneath it to be visible.
For complex objects where only some contribute to the shadow, it is possible to improve the efficiency by not rendering the shadow of those objects. An additional attribute is added to the object:
<obj class="xxx" drop="no" href="yyy" />
Figures 27 and 28 show the enhancement given to the Edinburgh dance floor and the La Mancha plain.
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.
The Library has evolved over the years mainly in response to our own needs or the interests of our students. In addition to the transformations defined in this paper, we have a number of presentation tools for managing a specific animation. In particular, a timeline presentation that lists all the animation steps and their dependencies. Also, we have a number of tools to aid the morphing of objects by manipulating control points.
We may add some more tools aimed at the definition of 3D objects. For man-made objects, a representation where the points defining the object are separated from the surface definitions tends to be more convenient and was used by Peter Gould [Gould05].
[Hopgood03] Using XSLT and SVG in Teaching: 3D, Sound and Nostalgia. 2003. SVG Open. http://www.svgopen.org/2003/papers/SoundNostalgiaSexPaepos/index.html.
[Gould05] 3D Animation Using SVG and XSLT. 2005. SVG Open. http://www.svgopen.org/2005/papers/3d_animation_xslt_svg/index.html.
[Shemanery] A very simple method of smoothing polygons with Bezier curves. http://www.antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION. [Visited 22 July 2009].
[Hoffman] Bezier curves. http://www.fho-emden.de/~hoffmann/bezier18122002.pdf. [Visited 22 July 2009].