\documentclass[article]{jss} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% declarations for jss.cls %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% almost as usual \author{Paul Murrell\\The Unversity of Auckland \And Simon Potter\\The Unversity of Auckland} \title{Dynamic and Interactive \R{} Graphics for the Web: The \pkg{gridSVG} Package} %% for pretty printing and a nice hypersummary also set: \Plainauthor{Paul Murrell, Simon Potter} %% comma-separated \Plaintitle{Dynamic and Interactive R Graphics for the Web: The gridSVG Package} %% without formatting \Shorttitle{Dynamic and Interactive R Graphics for the Web} %% a short title (if necessary) %% an abstract and keywords \Abstract{ This article describes the \pkg{gridSVG} package, which provides functions to convert \pkg{grid}-based \R{} graphics to an \svg{} format. The package also provides a function to associate hyperlinks with components of a plot, a function to animate components of a plot, a function to associate any \svg{} attribute with a component of a plot, and a function to add \js{} code to a plot. The last two of these provides a basis for adding interactivity to the \svg{} version of the plot. Together these tools provide a way to generate dynamic and interactive \R{} graphics for use in web pages. } \Keywords{world-wide web, graphics, \proglang{R}, \proglang{SVG}} \Plainkeywords{world-wide web, graphics, R, SVG} %% without formatting %% at least one keyword must be supplied %% publication information %% NOTE: Typically, this can be left commented and will be filled out by the technical editor %% \Volume{13} %% \Issue{9} %% \Month{September} %% \Year{2004} %% \Submitdate{2004-09-29} %% \Acceptdate{2004-09-29} %% The address of (at least) one author should be given %% in the following format: \Address{ Paul Murrell\\ Department of Statistics\\ The University of Auckland\\ 38 Princes Street, Auckland\\ New Zealand\\ E-mail: \email{paul@stat.auckland.ac.nz}\\ URL: \url{http://www.stat.auckland.ac.nz/~paul/} } %% It is also possible to add a telephone and fax number %% before the e-mail in the following format: %% Telephone: +43/1/31336-5053 %% Fax: +43/1/31336-734 %% for those who use Sweave please include the following line (with % symbols): %% need no \usepackage{Sweave.sty} %% end of declarations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \usepackage{boxedminipage} \newcommand{\svg}{\proglang{SVG}} \newcommand{\png}{\proglang{PNG}} \newcommand{\R}{\proglang{R}} \newcommand{\ggobi}{\proglang{GGobi}} \newcommand{\xml}{\proglang{XML}} \newcommand{\xpointer}{\proglang{XPointer}} \newcommand{\xpath}{\proglang{XPath}} \newcommand{\cairo}{\proglang{Cairo}} \newcommand{\js}{\proglang{JavaScript}} \newcommand{\mpg}{\proglang{MPEG4}} \newcommand{\java}{\proglang{Java}} \newcommand{\html}{\proglang{HTML}} \newcommand{\mac}{\proglang{MacOS X}} \newcommand{\linux}{\proglang{Linux}} \newcommand{\windows}{\proglang{Microsoft Windows}} \newcommand{\dfn}[1]{\emph{#1}} \begin{document} \SweaveOpts{keep.source = true, eps = false} <>= options(prompt="R> ", continue="R+ ", useFancyQuotes=FALSE) @ %% include your article here, just as usual %% Note that you should use the \pkg{}, \proglang{} and \code{} commands. \section{Introduction} %% Note: If there is markup in \(sub)section, then it has to be escape as above. Interactive and dynamic plots within web pages are becomingly increasingly popular, as part of a general trend towards making data sets more open and accessible on the web, for example, GapMinder \citep{bib:GapMinder} and ManyEyes \citep{manyeyes}. The \R{} language and environment for statistical computing and graphics \citep{R} has many facilities for producing plots, and it can produce graphics formats that are suitable for including in web pages, but the core graphics facilities in \R{} are largely focused on \emph{static} plots. This article describes an \R{} extension package, \pkg{gridSVG}, that is designed to embellish and transform a standard, static \R{} plot and turn it into a dynamic and interactive plot that can be embedded in a web page. \subsection[R graphics]{\R{} graphics} Several \R{} packages are being developed to produce interactive and dynamic plots for inclusion in web pages. For example, the \pkg{googleVis} package \citep{pkg:googleVis} provides an interface to the Google Visualization API and the \pkg{webvis} package \citep{pkg:webvis} produces \js{} code for the \proglang{protovis} \citep{protovis} web visualization library. In both cases, the graphics are produced by an external system rather than \R{}. This abandons the powerful graphical facilities that \R{} possesses. The \pkg{gridSVG} package provides a way to add dynamic and interactive features to plots that are produced by \R{} \citep[specifically, plots that are based on the \pkg{grid} graphics package;][]{Murrell201106}. \subsection{Vector graphics} It is straightforward to produce a \emph{static} \R{} graphic that can be included in a web page. For example, a plot that is saved as a \png{} file can be included via an \html{} \code{} element. However, raster image formats such as \png{} are only designed for a specific size and do not scale well. A better solution is possible with recent web browsers because they now have native support for the \svg{} format \citep{bib:SVGEssentials}. This allows \emph{vector} graphics to be used for a web page, which will produce better results because the plot should look good at any scale. The \svg{} file can be included in a web page via an \html{} \code{} element. Producing static \svg{} output from \R{} is a little more complicated than producing \png{} output because it depends on the operating system. On \linux{} and \mac{}, there is an \code{svg()} function, but on \windows{} the \pkg{Cairo} package \citep{pkg:cairo} must be installed to provide the \code{CairoSVG()} function. Some \R{} extension packages can be used to add some interactivity to an \R{} plot in a \png{} format. These make use of the fundamental \html{} image map technology whereby regions of a raster image can be identified and have interactive behaviour associated with them. The \pkg{iWebPlots} \citep{pkg:iwebplots} package provides functions for producing image maps with tooltips for some standard plot types and the \pkg{imagemap} package \citep{pkg:imagemap} provides similar functionality. The \pkg{gridSVG} package targets the \svg{} format instead, to allow dynamic and interactive features to be added to \R{} plots in a \emph{vector} format. \subsection{Smooth animation} The \pkg{animation} package \citep{pkg:animation} can be used to animate standard \R{} graphics. This works by producing multiple static images, in a raster format, and then either stitching the images together in a movie format, such as \mpg{}, or playing the images rapidly one after the other. A movie file can be embedded in a web page using an \code{} element and the \pkg{animation} package provides a convenience function to generate a web page with \js{} controls for playing a sequence of images. One limitation of this approach is that the result is a raster image, with the disadvantages mentioned above. It is also not possible to interact with this sort of dynamic plot. Furthermore, the animation is described in a ``brute force'' method, with every frame having to be drawn separately. By targeting the \svg{} format, the \pkg{gridSVG} package allows both dynamic and interactive features at once, in a vector graphics format, and it allows dynamic features to be described more elegantly. For example, in order to make a single point move within a plot, it is only necessary to describe the changing location of that point, and in simple cases only the start and end locations are required. \subsection{Direct interaction} A number of \R{} packages provide GUI toolkits, such as \pkg{tcltk} \citep{R}, \pkg{RGtk2} \citep{pkg:rgtk2}, and \pkg{gWidgets} \citep{pkg:gwidgets}. With regard to graphics, these provide a way to allow user interaction with a plot via menus, dialogs, buttons, and sliders. However, these sorts of interfaces do not allow direct interaction with the components of a plot, such as tooltips on data points and drill-down by clicking on a region of a plot. The \pkg{gridSVG} package is designed to allow interaction with individual components of an \R{} plot. \subsection{Extensibility} The \pkg{RSVGTipsDevice} package \citep{pkg:rsvgtips} provides an \R{} graphics device that saves \R{} plots in an \svg{} format and allows tooltips and hyperlinks to be associated with different components of the plot. The main limitation of this package is that it is hard-wired for these two sorts of interaction (tooltips and hyperlinks). The package is not designed to be extended by the user for other sorts of interaction. The \pkg{SVGAnnotation} package \citep{pkg:svgannotation} provides a much wider range of facilities for adding dynamic and interactive features to an \R{} plot (in an \svg{} format). It is possible to add tooltips, animate points, and even link plots (so that clicking on a point in one plot highlights the corresponding point in a separate plot). This package will be discussed in more detail in a later section, once we have established the structure of the \pkg{gridSVG} package. For now, it will just be stated that although the \pkg{SVGAnnotation} package provides some very powerful functions for adding interaction to some plots, it is not straightforward for a non-expert to extend the package to plots that are not currently supported. One aim of the \pkg{gridSVG} package is to provide general tools that can be easily adapted by non-experts to add interactivity to any plot or indeed any graphic in general that has been produced by \R{}'s \pkg{grid} graphics system. In summary, the \pkg{gridSVG} package is aimed at producing dynamic and interactive \R{} graphics, in a vector format, for inclusion in web pages, using a transparent mechanism that can be easily adapted and extended by the non-expert user. \section[The gridSVG package]{The \pkg{gridSVG} package} This section gives a brief introduction to the main features of the \pkg{gridSVG} package. The main function in the \pkg{gridSVG} package is \code{gridToSVG()}, which copies the current \pkg{grid} scene to an \svg{} document. An example of a simple \pkg{grid} scene, consisting of two rectangles, one above the other, is shown in Figure~\ref{figure:gridscene}. The code to produce this scene is shown below. <<>>= library("grid") @ <>= topvp <- viewport(y=1, just="top", name="topvp", height=unit(1, "lines")) botvp <- viewport(y=0, just="bottom", name="botvp", height=unit(1, "npc") - unit(1, "lines")) grid.rect(gp=gpar(fill="grey"), vp=topvp, name="toprect") grid.rect(vp=botvp, name="botrect") @ \begin{figure} \begin{center} \includegraphics[width=2in]{gridsvg-gridscene} \end{center} \caption{\label{figure:gridscene}A simple \pkg{grid} scene consisting of two rectangles, one above the other.} \end{figure} The following call to \code{gridToSVG()} creates an \svg{} document from this scene. The resulting document is called \code{"gridscene.svg"} and Figure~\ref{figure:gridsceneweb} shows this document as it appears in a web browser. <<>>= library("gridSVG") @ <>= gridToSVG("gridscene.svg") @ <>= <> <> @ \begin{figure} \begin{center} {\includegraphics[width=2in]{gridsceneweb.png}} \end{center} \caption{\label{figure:gridsceneweb}The simple \pkg{grid} scene from Figure~\ref{figure:gridscene} in an \svg{} format, viewed in Firefox.} \end{figure} The current \pkg{grid} scene could be produced from raw \pkg{grid} calls, as above, or it could be produced from calls to functions from packages that are built on top of \pkg{grid}, such as \pkg{lattice}, \pkg{ggplot2}, \pkg{vcd}, and \pkg{partykit}. Sections \ref{section:gridbased} and \ref{section:examples} provide some more complex examples. The result of calling \code{gridToSVG()} is just a static \svg{} version of the static \pkg{grid} scene, but the \pkg{gridSVG} package provides other functions to add dynamic and interactive features. \subsection{Animation} The \code{grid.animate()} function allows the components of a \pkg{grid} scene to be animated. For example, the following code animates the two rectangles in the simple \pkg{grid} scene above so that they both shrink to a width of 1 inch over a period of 3 seconds (see Figure \ref{figure:gridanimweb}). The widths begin with a value of \Sexpr{unit(1, "npc")} and end with a value of \Sexpr{unit(1, "in")}. <<>>= widthValues <- unit(c(1, 1), c("npc", "in")) widthValues @ The component of the \pkg{grid} scene that is to be animated is specified by name. For example, the first call to \code{grid.animate()} below modifies the rectangle called \code{"toprect"}. <>= grid.animate("toprect", width=widthValues, duration=3) grid.animate("botrect", width=widthValues, duration=3) @ <>= gridToSVG("gridanim.svg") @ <>= grid.newpage() <> <> <> @ \begin{figure} \begin{center} \hspace*{\fill} {\includegraphics[width=2in]{gridsceneweb.png}} \hspace*{\fill} {\includegraphics[width=2in]{gridanimweb.png}} \hspace*{\fill} \end{center} \caption{\label{figure:gridanimweb}A simple animated \pkg{grid} scene. The scene starts off as shown on the left (the same as Figure~\ref{figure:gridsceneweb}), but then the rectangles shrink over three seconds until they end up only 1 inch wide, as shown on the right.} \end{figure} \subsection{Interactivity} The \code{grid.hyperlink()} function associates a hyperlink with a component of a \pkg{grid} scene. For example, the following code adds some text to the top rectangle in the simple \pkg{grid} scene so that when the text is clicked the web browser will navigate to the \R{} home page (see Figure~\ref{figure:gridhyperweb}). Again, the component that is to be turned into a hyperlink is specified by name; in this case, it is the text component called \code{"hypertext"}. <>= grid.text("take me there", vp=topvp, name="hypertext") grid.hyperlink("hypertext", "http://www.r-project.org") @ <>= gridToSVG("gridhyper.svg") @ <>= grid.newpage() <> <> <> @ \begin{figure} \begin{center} \hspace*{\fill} {\includegraphics[width=2in]{gridhyperweb-with-cursor.png}} \hspace*{\fill} \end{center} \caption{\label{figure:gridhyperweb}A simple \pkg{grid} scene containing hyperlinked text. If the text is clicked on with the mouse, the web browser will navigate to the \R{} home page.} \end{figure} The \code{grid.garnish()} function allows general \svg{} attributes to be associated with a component of a \pkg{grid} scene. For example, the following code adds an event handler to the bottom rectangle in the simple \pkg{grid} scene above so that, when the mouse is clicked over the bottom rectangle, an alert dialog pops up (see Figure~\ref{figure:gridmouseweb}). The \code{onmousedown} attribute specifies what action to take when the mouse is clicked and the \code{pointer-events} attribute makes sure that the rectangle notices mouse clicks within its interior (not just on its drawn border). <>= grid.garnish("botrect", onmousedown="alert('ouch!')", "pointer-events"="all") @ <>= gridToSVG("gridmouse.svg") @ <>= grid.newpage() <> <> <> <> @ \begin{figure} \begin{center} \hspace*{\fill} {\includegraphics[width=2in]{gridmouseweb-with-cursor.png}} \hspace*{\fill} {\includegraphics[width=3in]{gridmousealert.png}} \hspace*{\fill} \end{center} \caption{\label{figure:gridmouseweb}A simple interactive \pkg{grid} scene. When the mouse is clicked over the bottom rectangle, an alert dialog pops up.} \end{figure} More complex interaction requires adding \js{} \citep{bib:JavaScriptGuide} to the \svg{} output. This is achieved via the \code{grid.script()} function, which can be given either \js{} code as a character vector or the name of a file that contains \js{} code. For example, the following code adds an event handler to the top rectangle in the simple \pkg{grid} scene so that, when the mouse is clicked within the top rectangle, the interior of the rectangle is filled black (see Figure~\ref{figure:gridscriptweb}). The design of the \js{} code will be described in Section \ref{section:gridsvgdesign} and Section \ref{section:examples} contains some more sophisticated examples of interaction. <>= grid.garnish("toprect", onmousedown="allblack()", "pointer-events"="all") grid.script(" allblack = function() { rect = document.getElementById('toprect.1'); rect.setAttribute('style', 'fill:black'); }") @ <>= gridToSVG("gridscript.svg") @ <>= grid.newpage() <> <> <> <> <> @ \begin{figure} \begin{center} \hspace*{\fill} {\includegraphics[width=2in]{gridmouseweb.png}} \hspace*{\fill} {\includegraphics[width=2in]{gridscriptweb-with-cursor.png}} \hspace*{\fill} \end{center} \caption{\label{figure:gridscriptweb}A slightly more complex interactive \pkg{grid} scene. When the mouse is clicked over the top rectangle, the top rectangle is filled with black.} \end{figure} \subsection[Graphics based on grid]{Graphics based on \pkg{grid}} \label{section:gridbased} The examples so far have used direct calls to \pkg{grid} functions to draw a scene, but the techniques described will also work with plots drawn by packages that are built on \pkg{grid}, such as \pkg{lattice} \citep{pkg:lattice}, \pkg{ggplot2} \citep{pkg:ggplot}, \pkg{vcd} \citep{pkg:vcd}, and \pkg{partykit} \citep{pkg:partykit}. For example, the following code generates a multipanel conditioning plot using the \pkg{lattice} package. <<>>= library("lattice") @ <>= xyplot(Sepal.Length ~ Sepal.Width | Species, iris) @ The next piece of code uses the \pkg{grid.hyperlink()} function to add hyperlinks to the strip labels in that plot. <>= grid.hyperlink("plot_01.textr.strip.1.1", "http://en.wikipedia.org/wiki/Iris_flower_data_set") grid.hyperlink("plot_01.textr.strip.1.2", "http://en.wikipedia.org/wiki/Iris_virginica") grid.hyperlink("plot_01.textr.strip.2.1", "http://en.wikipedia.org/wiki/Iris_versicolor") @ The plot can now be converted to \svg{} using \code{gridToSVG()} so that clicking on the strip labels in a web browser will navigate to the relevant pages of Wikipedia (see Figure \ref{figure:latticehyper}). <>= gridToSVG("latticehyper.svg") @ <>= <> <> <> @ \begin{figure} \begin{center} \hspace*{\fill} {\includegraphics[width=5in]{latticehyper-with-cursor.png}} \hspace*{\fill} \end{center} \caption{\label{figure:latticehyper}A \pkg{lattice} plot with hyperlinks on the strip labels.} \end{figure} \section[The design of gridSVG]{The design of \pkg{gridSVG}} \label{section:gridsvgdesign} This section describes some of the details about how the \pkg{gridSVG} package works. It is necessary to understand some of these details in order to be able to produce more complex dynamic and interactive plots with \pkg{gridSVG}. \subsection[The components of a grid scene]{The components of a \pkg{grid} scene} The \pkg{gridSVG} package works by looking at what has been drawn on the current page and converting that drawing to \svg{}. The \pkg{gridSVG} package works off the \pkg{grid} display list, which is a record of all \pkg{grid} output on the current page. This display list consists of \pkg{grid} viewports and grobs (graphical objects). The \code{grid.ls()} function can be used to list the grobs and viewports in the current scene and the following code demonstrates this for the simple scene from Figure~\ref{figure:gridscene}. There are two \code{rect} grobs, called \code{toprect} and \code{botrect}, and each rectangle is drawn in a its own viewport (\code{topvp} and \code{botvp}, respectively). <>= grid.ls(viewports=TRUE, fullNames=TRUE, print=grobPathListing) @ <>= grid.newpage() <> <> @ \subsection[Converting grobs to SVG]{Converting grobs to \svg{}} \label{section:convertinggrobs} The main function in the \pkg{gridSVG} package is called \code{gridToSVG()}. This function takes each grob in the current scene and converts it to one or more \svg{} elements. <>= gridToSVG("gridscene.svg") @ <>= <> <> @ In the simple \pkg{grid} scene shown in Figure~\ref{figure:gridscene}, each \code{rect} grob is converted to an \svg{} \code{} element, as shown below. <>= library("XML") @ <>= presentationAttributes <- c("stroke", "stroke-opacity", "fill", "fill-opacity", "opacity", "font-weight", "font-style", "font-family", "fonts-size", "stroke-dasharray", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit") removePresentation <- function(node) { removeAttributes(node, .attrs=presentationAttributes) } <>= gridscenesvg <- xmlParse("gridscene.svg") toprect <- getNodeSet(gridscenesvg, "//svg:rect[@id='toprect.1']", c(svg="http://www.w3.org/2000/svg"))[[1]] removePresentation(toprect) botrect <- getNodeSet(gridscenesvg, "//svg:rect[@id='botrect.1']", c(svg="http://www.w3.org/2000/svg"))[[1]] removePresentation(botrect) @ Most grobs have an obvious correspondence to an \code{SVG} element like this (see Table \ref{table:grobtosvg}), but there are some exceptions. For example, the many different sorts of \pkg{grid} line grobs all get converted to an \svg{} \code{} element. This includes open \code{xspline} grobs (the smooth curve is stored as a series of short straight lines). \begin{table} \caption{\label{table:grobtosvg}The \svg{} elements that basic \pkg{grid} grobs are converted into.} \begin{center} \begin{tabular}{l l} \hline {\bf \pkg{grid} grob} & {\bf \svg{} element} \\ \hline \code{rect} & \code{} \\ \code{circle} & \code{} \\ \code{lines} & \code{} \\ \code{polyline} & \code{} \\ \code{segments} & \code{} \\ \code{xspline} & \code{} or \code{} \\ \code{polygon} & \code{} \\ \code{path} & \code{} \\ \code{raster} & \code{} \\ \code{text} & \code{} \\ \hline \end{tabular} \end{center} \end{table} Another exception is the \code{text} grob, which converts to a series of nested \svg{} elements. This is necessary in order to get the size and orientation of the text correct. For example, the following \pkg{grid} code produces a text grob. <>= grid.text("take me there", name="sampleText") @ The \code{gridToSVG()} function converts this text to \svg{} elements shown below. <>= <> gridToSVG("textgrob.svg") @ <>= textgrobsvg <- xmlParse("textgrob.svg") gsvg <- getNodeSet(textgrobsvg, "//svg:g[@id='sampleText.1']", c(svg="http://www.w3.org/2000/svg"))[[1]] textsvg <- getNodeSet(gsvg, "svg:g/svg:text", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(textsvg) cat(saveXML(gsvg)) @ A \code{points} grob is also handled differently because different data symbols consist of different shapes, so the \svg{} element that is produced will depend on the choice of data symbol. Some data symbols also consist of more than one shape, for example, a circle with a plus inside it. In those cases, the \code{points} grob is converted to a \code{} element with several other elements inside it. For example, the following \pkg{grid} code draws two data symbols, one a simple circle and one a combination of a circle and a plus (a vertical line and a horizontal line; see Figure~\ref{figure:pointsgrob}). <>= pushViewport(viewport()) grid.points(1:2/3, 1:2/3, pch=c(1, 10), name="symbols") @ \begin{figure} \hspace*{\fill} \fbox{\includegraphics[width=1in]{gridsvg-pointsgrob}} \hspace*{\fill} \fbox{\includegraphics[width=1in]{gridsvg-arrowgrob}} \hspace*{\fill} \fbox{\includegraphics[width=1in]{gridsvg-circlegrob}} \hspace*{\fill} \caption{\label{figure:pointsgrob}Three simple grid scenes: on the left, two data symbols (one a simple circle and the other a combination of a circle and two lines); in the middle, a line segment with an arrow head on the end; on the right, three circles which are drawn from a single grob.} \end{figure} The \code{gridToSVG()} function converts these data symbols to the \svg{} elements shown below. <>= <> gridToSVG("pointsgrob.svg") @ <>= pointsgrobsvg <- xmlParse("pointsgrob.svg") circlesvg <- getNodeSet(pointsgrobsvg, "//svg:circle[@id='symbols.1']", c(svg="http://www.w3.org/2000/svg"))[[1]] removePresentation(circlesvg) symbolsvg <- getNodeSet(pointsgrobsvg, "//svg:g[@id='symbols.2']", c(svg="http://www.w3.org/2000/svg"))[[1]] poly1svg <- getNodeSet(pointsgrobsvg, "//svg:polyline[@id='symbols.2.1']", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(poly1svg) poly2svg <- getNodeSet(pointsgrobsvg, "//svg:polyline[@id='symbols.2.2']", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(poly2svg) circle2svg <- getNodeSet(pointsgrobsvg, "//svg:circle[@id='symbols.2.3']", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(circle2svg) cat(saveXML(symbolsvg)) @ All line grobs in \pkg{grid} can have arrow heads at either end. These are implemented in \svg{} using \code{} elements, which are then referred to using \code{marker-start} and \code{marker-end} attributes in the \code{} element. The following \pkg{grid} code draws a line segment with an arrow at the end (see Figure~\ref{figure:pointsgrob}). <>= grid.segments(0, 0, 1, 1, arrow=arrow(), name="lineWithArrow") @ <>= pushViewport(viewport(width=.8, height=.8)) <> @ This \code{segments} grob, with its arrow, is converted to the following \svg{} code. <>= <> gridToSVG("arrowgrob.svg") @ <>= # Function to break a long line of SVG code # 'text' is the SVG text (possibly containing newlines) # 'element' gives the name of the element whose line we want to break # This is used to find the line to break # 'attribs' gives one or more attribute names that want to break on # 'anchor' gives the attribute name that the new lines should line up with breakLongLine <- function(text, element, attribs, anchor, perl=FALSE) { text <- strsplit(text, "\n")[[1]] line <- grep(element, text, perl=perl) for (l in line) { newline <- text[l] indent <- regexpr(anchor, newline, perl=perl) for (i in attribs) { # Wrap attrib names in parentheses so can use \\1 in replacement text # This allows regular expression in attrib name newline <- gsub(paste("(", i, ")", sep=""), paste("\n", paste(rep(" ", indent - 1), collapse=""), "\\1", sep=""), newline, perl=perl) } text[l] <- newline } paste(text, collapse="\n") } @ <>= arrowgrobsvg <- xmlParse("arrowgrob.svg") markersvg <- getNodeSet(arrowgrobsvg, "//svg:defs", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removeChildren(markersvg, kids=list(1)) markerpathsvg <- getNodeSet(markersvg, "//svg:path", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(markerpathsvg) polylinesvg <- getNodeSet(arrowgrobsvg, "//svg:polyline", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(polylinesvg) cat(breakLongLine(saveXML(markersvg), ">= grid.circle(x=1:3/4, y=1:3/4, r=0.1, name="circles") @ When this grob is converted to \svg{}, three distinct \code{} elements are created. Because it may be useful to treat the three elements as a coherent group, the conversion nests the three \code{} elements within a \code{} element. The resulting \svg{} code for this example is shown below. <>= <> gridToSVG("circlegrob.svg") @ <>= circlegrobsvg <- xmlParse("circlegrob.svg") gsvg <- getNodeSet(circlegrobsvg, "//svg:g[@id='circles']", c(svg="http://www.w3.org/2000/svg"))[[1]] circlesvg <- getNodeSet(gsvg, "svg:circle", c(svg="http://www.w3.org/2000/svg")) devnull <- lapply(circlesvg, removePresentation) cat(saveXML(gsvg)) @ For consistency in the \svg{} code, this \code{} element is added whether a grob produces multiple shapes or not. For example, the \code{toprect} grob from Figure~\ref{figure:gridscene}, which produces only a single \code{} element, is actually recorded in \svg{} as shown below. <>= gridscenesvg <- xmlParse("gridscene.svg") toprectg <- getNodeSet(gridscenesvg, "//svg:g[@id='toprect']", c(svg="http://www.w3.org/2000/svg"))[[1]] toprect <- toprectg["rect"][[1]] devnull <- removePresentation(toprect) cat(saveXML(toprectg)) @ \subsection{Converting grob names} Each \pkg{grid} grob has a name and this name gets recorded as the \code{id} attribute of the corresponding \svg{} element. This conversion is slightly complicated by the fact that a single grob always generates multiple \svg{} elements (as described in the previous section), so some ``mangling'' of the original grob name is necessary. The general rule is that the parent \code{} element has an \code{id} attribute that is identical to the original grob name. The children of that \code{} element have \code{id} attributes based on the original grob name, but with a numeric suffix appended. The sample \svg{} code in the previous section demonstrates this idea. Some cases are more complicated, for example, determining the \code{id} attributes of \code{} elements when converting lines with arrow heads (see Section \ref{section:convertinggrobs}), but in all cases the \code{id} value is based upon the original grob name, with a sensible extension added. \subsection{Converting graphical parameters} The detailed appearance of \pkg{grid} grobs---things like colours, line widths, and fonts---is controlled by a set of graphical parameters, such as \code{col} and \code{fill} (border and fill colours), \code{lwd} (line width), and \code{fontface} and \code{fontfamily}. When a \pkg{grid} grob is converted to an \svg{} element, these graphical parameter settings are converted to \dfn{presentation attributes} in the \svg{} element. There are always default graphical parameter settings in place so that a grob will only specify explicit settings where necessary. This is reflected in \svg{}, with presentation attributes only recorded when they are explicitly set. A top level \code{} element establishes a full set of defaults. For example, in the simple \pkg{grid} scene from Figure~\ref{figure:gridscene}, the \code{toprect} grob explicitly specifies a grey fill via the \code{gp} argument. <>= grid.rect(gp=gpar(fill="grey"), vp=topvp, name="toprect") @ The top level \code{} element and the \code{} element for the \code{toprect} grob are shown below. Many default presentation attribute settings are established in the \code{} element and the grey fill for the \code{} element is recorded as \code{fill="rgb(190,190,190)"}. {\small <>= gridscenesvg <- xmlParse("gridscene.svg") gridsvg <- getNodeSet(gridscenesvg, "//svg:g[@id='gridSVG']", c(svg="http://www.w3.org/2000/svg"))[[1]] gridsvgchild <- getNodeSet(gridscenesvg, "//svg:g[@id='gridSVG']/svg:g", c(svg="http://www.w3.org/2000/svg"))[[1]] toprect <- getNodeSet(gridscenesvg, "//svg:rect[@id='toprect.1']", c(svg="http://www.w3.org/2000/svg"))[[1]] # Make the element the only child of the gridsvg element devnull <- removeChildren(gridsvg, kids=xmlChildren(gridsvg)) devnull <- addChildren(gridsvg, kids=list(toprect)) cat(gsub(", ", ",", gsub("", "\n", gsub("( +} elements, with an \code{id} attribute set from the viewport name. If a viewport sets the clipping region, a \code{} element is added to the \svg{} output and this is referenced in the \code{} element for the viewport using the \code{clip-path} attribute. We now have enough information to understand the complete \svg{} code that is produced by \code{gridToSVG()} for the simple \pkg{grid} scene in Figure~\ref{figure:gridscene}. This \svg{} code is shown in Figure~\ref{figure:gridscenesvg}. \begin{figure} \begin{boxedminipage}{\textwidth} <>= gridscenesvg <- xmlParse("gridscene.svg") removeStyle <- function(node) { removePresentation(node) children <- xmlChildren(node) if (length(children) > 0) lapply(children, removeStyle) invisible() } invisible(lapply(xmlChildren(gridscenesvg), removeStyle)) cat(breakLongLine(saveXML(gridscenesvg), ">= tg <- textGrob("this is a gTree", name="textchild") rg <- rectGrob(width=grobWidth(tg) + unit(2, "mm"), height=unit(1, "lines"), gp=gpar(col=NA, fill="grey"), name="rectchild") textWithBG <- gTree(children=gList(rg, tg), name="gtree") @ When a gTree is drawn, it draws all of its children. For example, the following code calls the above function to create a gTree and then draws it, which has the effect of drawing both the text grob and the rect grob (see Figure~\ref{figure:gtree}). <>= grid.draw(textWithBG) @ \begin{figure} \begin{center} \fbox{\includegraphics[width=2in]{gridsvg-gtreefig}} \end{center} \caption{\label{figure:gtree}A \pkg{grid} scene consisting of a gTree, with a rectangle and a piece of text as its children.} \end{figure} The output from \code{grid.ls()} shows the hierarchical nature of the scene just created. <>= grid.ls(fullNames=TRUE) @ <>= <> <> @ When a gTree is exported to \svg{}, the gTree itself creates a \code{} element, with the child grobs exported as \svg{} elements within that. The \svg{} code from the gTree created above is shown below. <>= grid.newpage() <> gridToSVG("gtree.svg") @ <>= gtreesvg <- xmlParse("gtree.svg") gtree <- getNodeSet(gtreesvg, "//svg:g[@id='gtree']", c(svg="http://www.w3.org/2000/svg"))[[1]] text <- getNodeSet(gtreesvg, "//svg:text", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(text) rect <- getNodeSet(gtreesvg, "//svg:rect", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(rect) cat(saveXML(gtree)) @ \subsection{Fonts} The \pkg{gridSVG} package does not embed fonts in the \svg{} document that it produces. It only records the name of the font that should be used to render text. Because there is no guarantee that a web browser (or the operating system of its host) has access to a particular font, \pkg{gridSVG} also records additional font names so that if the first font cannot be found, there are alternatives that can be used. The collection of font names is called a \dfn{font stack} and the \pkg{gridSVG} package maintains three font stacks: one for serif fonts, one for sans-serif fonts, and one for monospace fonts. In each case, the final font name in each stack is a generic name, like \code{"monospace"} so that the web browser should always be able to find something to use. When text is exported to \svg{}, the font stack that is recorded is the one that contains the font family used for that text. For example, the following code draws text with a Helvetica font. <>= grid.text("font test", gp=gpar(fontfamily="Helvetica"), name="font") @ The Helvetica font is part of the sans-serif font stack, so the \svg{} code records the sans-serif font stack, as shown below (see the \code{font-family} attribute within the \code{} element). <>= grid.newpage() <> gridToSVG("font.svg") @ {\small <>= fontsvg <- xmlParse("font.svg") font <- getNodeSet(fontsvg, "//svg:g[@id='font']", c(svg="http://www.w3.org/2000/svg"))[[1]] cat(gsub(", ", ",", breakLongLine(saveXML(font), ">= getSVGFonts()$mono @ Again, this does not guarantee that the Inconsolata font will be used to display the text in a browser; that will only happen if the browser has access to the Inconsolata font. This concludes the discussion of how standard \pkg{grid} objects are converted to \svg{}. The following sections describe how \pkg{gridSVG} adds dynamic and interactive features to a \pkg{grid} scene and exports those features to \svg{} code. \subsection{Hyperlinks} The \code{grid.hyperlink()} function allows hyperlinks to be associated with a component of a \pkg{grid} scene. This function works by modifying the appropriate grob to add information about the hyperlink. The modified grob is also given the class \code{linked.grob} so that it can be handled differently when it is converted to \svg{}. The conversion of a \code{linked.grob} to \svg{} creates an \code{} (anchor) element around the normal \svg{} elements that would otherwise be generated. For example, the following code draws a piece of text and associates a hyperlink with the text so that when the text is clicked with the mouse in a web browser, the browser will navigate to the \R{} home page. <>= grid.text("take me there", name="hypertext") grid.hyperlink("hypertext", href="http://www.r-project.org") @ The \code{text} grob is now a \code{linked.grob}. <>= grid.get("hypertext") @ <>= <> <> @ The \code{gridToSVG()} function converts this \code{linked.grob} to a set of \code{}, \code{}, and \code{} elements as normal, \emph{plus} an \code{} element around the outside, as shown below. <>= grid.newpage() <> gridToSVG("hypertext.svg") @ <>= hypertextsvg <- xmlParse("hypertext.svg") anchor <- getNodeSet(hypertextsvg, "//svg:a", c(svg="http://www.w3.org/2000/svg"))[[1]] text <- getNodeSet(hypertextsvg, "//svg:text", c(svg="http://www.w3.org/2000/svg"))[[1]] devnull <- removePresentation(text) cat(saveXML(anchor)) @ Placing the \svg{} anchor around the outside of the normal \svg{} output from a grob means that we should be able to associate an anchor with any component of a \pkg{grid} scene. \subsection{Animation} The \pkg{gridSVG} package provides the \code{grid.animate()} function for animating a \pkg{grid} grob. This function works by modifying the appropriate grob to add attributes that contain information about the animation. The modified grob is also given the class \code{animated.grob} so that it can be handled differently when it is converted to \svg{}. The conversion of an \code{animated.grob} to \svg{} creates an \code{} element in addition to the normal \svg{} elements that would otherwise be generated. For example, the following code draws a circle on the left of the screen and then animates it so that it moves across to the right of the screen. <>= grid.circle(x=.2, r=.1, gp=gpar(fill="black"), name="oneCircle") grid.animate("oneCircle", x=c(.2, .8)) @ The \code{circle} grob is now an \code{animated.grob}. <>= grid.get("oneCircle") @ <>= grid.newpage() <> <> @ The \code{gridToSVG()} function converts this grob to a \code{} element as normal, \emph{plus} an \code{} element, as shown below. <>= grid.newpage() <> gridToSVG("animcircle.svg") @ <>= animcirclesvg <- xmlParse("animcircle.svg") animate <- getNodeSet(animcirclesvg, "//svg:animate", c(svg="http://www.w3.org/2000/svg"))[[1]] cat(breakLongLine(saveXML(animate), ">= grid.circle(x=c(.2, .8), y=c(.2, .8), r=.1, gp=gpar(fill="black"), name="twoCircles") @ The first column of the matrix is used as animation values for the first circle and the second column is used for the second circle. The first circle will transition from an x-value of \code{.2} to an x-value of \code{.8}, while the second circle transitions from \code{.8} to \code{.2}. <>= animValues <- cbind(c(.2, .8), c(.8, .2)) animValues @ <>= grid.animate("twoCircles", x=animValues) @ The \code{gridToSVG()} function converts this grob to two \code{} elements plus two \code{} elements, as shown below. <>= grid.newpage() <> <> gridToSVG("twocircles.svg") @ <>= twocirclessvg <- xmlParse("twocircles.svg") animate <- getNodeSet(twocirclessvg, "//svg:animate", c(svg="http://www.w3.org/2000/svg")) invisible(lapply(animate, function(x) { cat(breakLongLine(saveXML(x), ">= grid.circle(x=c(.2, .8), y=c(.2, .8), r=.1, gp=gpar(fill="black"), name="groupCircles") grid.animate("groupCircles", visibility=c("visible", "hidden"), begin=1, duration=0.01, group=TRUE) @ The \svg{} code produced for this example has an \code{} element that refers to the parent \code{} element rather than the individual \code{} elements, as shown below. <>= grid.newpage() <> gridToSVG("groupcircles.svg") @ <>= groupcirclessvg <- xmlParse("groupcircles.svg") animate <- getNodeSet(groupcirclessvg, "//svg:animate", c(svg="http://www.w3.org/2000/svg")) invisible(lapply(animate, function(x) { cat(breakLongLine(saveXML(x), ">= animUnit(unit(c(.2, .8), "npc")) @ The following code generates a single value for each of two time points, for each of two shapes. The \code{id} argument to the \code{animUnit()} function is used to associate different animation values with different shapes. <<>>= animUnit(unit(c(.2, .8, .8, .2), "npc"), id=rep(1:2, each=2)) @ The following code demonstrates how to generate multiple values for each time point, by specifying the \code{timeid} argument, which associates different animation values with different time points. <<>>= animUnit(unit(c(.2, .8, .8, .2), "npc"), timeid=rep(1:2, each=2)) @ By specifying \emph{both} \code{timeid} and \code{id} arguments, it is possible to specify multiple animation values at each time point for different shapes, as shown below. <<>>= animUnit(unit(c(.2, .8, .8, .2, .1, .9, .9, .1), "npc"), timeid=rep(rep(1:2, each=2), 2), id=rep(1:2, each=4)) @ Not all features of a grob are locations or dimensions (which require \code{unit} values), so there is also an \code{animValue()} function for generating sets of non-\code{unit} animation values. The vignette \code{"animation"} in the \pkg{gridSVG} package describes these functions in more detail. \subsection{Interactivity} The general mechanism for adding interactivity to a \pkg{grid} scene involves associating \js{} code \citep{bib:JavaScriptGuide} with a component of the \pkg{grid} scene. This usually requires two steps: adding \js{} code to the scene and specifying which component of the scene will call that \js{} code (and when). The first step is straightforward. The \code{grid.script()} function adds a \code{script.grob} object to the current \pkg{grid} scene (nothing is drawn on screen, but the grob is added to the \pkg{grid} display list). The \js{} code for the \code{script.grob} object can be specified as a character vector or as the name of a file that contains \js{} code. For example, the following code adds \js{} code to a scene from the file called \code{"script.js"}. <>= grid.script(filename="script.js", name="script") @ The \code{gridToSVG()} function generates a \code{