Week01 - Easy Flowcharts and Diagrams
January 08, 2015
Already Cheating?
It’s just week 1 of the Building Widgets widget-a-week challenge, and some might think I am already cheating for two reasons:
- This widget was started last week, and
- I’m not the sole author.
However, these “transgressions” will likely occur throughout the year, since many widgets will be multi-week projects, and I very much like collaborating on widgets.
This Week’s Widget - DiagrammeR
DiagrammeR
is authored by Air Quality Scientist Richard Iannone who has quite a record of GitHub activity. Thanks Richard for letting me play along. If you are asking why I am involved, DiagrammeR
didn’t start out as a htmlwidget
. Since I along with > 5,000 other starrers love mermaid.js
and the htmlwidgets
infrastructure is so compelling, I thought DiagrammeR
would be perfect as one of the first htmlwidgets
. For more details on the conversion, see Widgets for Christmas or for those more technically inclined inspect this pull request.
What Does it Do?
mermaid.js
offers easy diagrams through a markdown-like spec and leverages d3
and dagre-d3
. The mermaid.js
site offers quite a few examples of flowcharts, experimental sequence diagrams, and other diagrams.
Better Than Where We Found It
In many cases, development of a htmlwidget
can often impact the JavaScript library in various ways, such as bringing attention, offering new perspective, providing new examples and tests, and applying in different contexts and fields of study. Just to insure that we didn’t just take from mermaid.js
, we were able to file two issues with mermaid.js
and demonstrate one way of forcing the diagram to fit in its container div
.
Examples in R
In general for all the widgets to come, I will try to create entirely new examples and put them in R
contexts. I will also try to convert examples from documentation of popular static R
packages.
Example 1 - Stylish Nodes
As basic as they come, this example shows the options for shapes of nodes and some styling with CSS
.
library(DiagrammeR)
DiagrammeR("
graph TD;
A[rect]-- add style -->A2[rect + style];
B{rhombus}---|+ some style|B2{rhombus + style};
C(rounded); D((circle));
style A2 fill:#c12,stroke-width:5px;
style B2 fill:none, stroke-dasharray:10;
")
Example 2 - Workflow of a Building Widgets Post
As I mentioned in my first blog post, I chose to build the site on Squarespace. Getting the workflow right will require quite a bit of iteration, but for now, here is a diagram of the workflow.
library(DiagrammeR)
# workflow of a post graph
DiagrammeR("
graph TD;
hw{htmlwidget} -->experiments;
hw -->content;
experiments -->RMarkdown
content -->RMarkdown
RMarkdown -->|R/knitr|Markdown;
Markdown -->|Pandoc|HTML;
HTML -->|git push| Github;
HTML -->|copy/paste| Squarespace ;
")
Example 3 - Dependencies of Showcase Widgets
We learn about some of the first htmlwidgets
in the showcase. htmlwidgets
will often depend on many of the same JavaScript libraries. Fortunately, htmlwidgets
handles these duplicate dependencies very nicely. Let’s make a diagram of the dependencies from all of the showcase htmlwidgets
.
# htmlwidgets network of yaml dependency or package imports/suggest
library(yaml)
library(DiagrammeR)
showcaseWidgets = list(
c("leaflet","rstudio/leaflet/master/inst/htmlwidgets/leaflet.yaml")
,c("dygraphs","rstudio/dygraphs/master/inst/htmlwidgets/dygraphs.yaml")
,c("networkD3","christophergandrud/networkD3/master/inst/htmlwidgets/sankeyNetwork.yaml")
,c("DT","rstudio/DT/master/inst/htmlwidgets/datatables.yaml")
,c("rthreejs","bwlewis/rthreejs/master/inst/htmlwidgets/globe.yaml")
)
deps = lapply(
showcaseWidgets
,function(w){
y = paste0(
"https://raw.githubusercontent.com/"
,w[2]
)
deps = yaml.load_file( y )
data.frame(
widget = w[1]
,dependency = unlist(deps)[grep(x=names(unlist(deps)),pattern=".name")]
,stringsAsFactors = F
)
}
)
deps = do.call(rbind,deps)
DiagrammeR(
c(
"graph LR;"
,sapply(
1:nrow(deps)
,function(n){
paste0(
"id",which(unique(deps[,1])==deps[n,1]),"["
,deps[n,"widget"]
,"]-->"
,"id",which(unique(deps[,2])==deps[n,2])+nrow(deps),"["
, deps[n,"dependency"]
,"]"
)
}
)
,paste0(
"style id"
, 1:length(unique(deps[,1]))
, " fill:"
, RColorBrewer::brewer.pal( n = 9, "Set1")[1:length(unique(deps[,1]))]
)
)
)
Example 4 - igraph
Example 3 certainly could have benefitted from the power of igraph
to build and manipulate the network of dependencies. Here is one way of combining igraph
and DiagrammeR
.
library(igraph)
library(pipeR)
library(DiagrammeR)
# make a simple graph
iG <- graph.formula(Sam+-Mary+-Tom++Jill)
# get the edges in a form almost ready for DiagrammeR
print( E(iG) ) %>>%
unlist %>>%
unname %>>%
(
c(
"graph LR"
,paste0(gsub(x=.,pattern="(\\s)*(->)(\\s)*",replacement="-->"))
)
) %>>%
paste(collapse=";") %>>%
DiagrammeR
## Edge sequence:
##
## [1] Mary -> Sam
## [2] Tom -> Mary
## [3] Tom -> Jill
## [4] Jill -> Tom
Example 5 - Using htmltools
RStudio’s package htmltools
offers all sorts of helpful tools when constructing web content. We designed DiagrammeR
to collaborate nicely with htmltools
. In this example, we’ll use DiagrammeR()
with an empty spec just to inject the necessary dependencies. tags$div(class='mermaid',...,spec,...)
will provide the diagram spec.
With tagList
from htmltools
we could also compose web content with multiple DiagrammeR
s along with all the HTML & JavaScript that we know and love. The next example will illustrate this.
library(htmltools)
library(DiagrammeR)
tagList(
tags$div(
class="mermaid"
,"graph LR; tw[htmltools tag] --> diagram"
)
,DiagrammeR() # here just used to inject dependencies
)
Example 6 - Animated Path
I would consider this example advanced. Various examples on the web demonstrate CSS
animation to make an animated path
in SVG
. I chose this example and amended it to work with our mermaid.js
diagram. As far as I know, it is the first example of its kind.
library(htmltools)
# animated path example (advanced) and does not work in RStudio
dg = DiagrammeR("
graph LR; animA[A]; style animA stroke-dasharray: 10, animation: dash 1s linear infinite;
")
# add a call our script makeAnimated defined in tags$script after render
dg$x$tasks = htmlwidgets::JS("makeAnimated")
tagList(
dg
,tags$script(sprintf("
function makeAnimated (el){
// get the stylesheet and add a rule for an animated path
var sty = document.styleSheets[0] //el.getElementsByTagName('style')[0];
// http://davidwalsh.name/add-rules-stylesheets
// http://css-tricks.com/svg-line-animation-works/
sty.insertRule('%s')
}"
,HTML( ' @keyframes dash { to { stroke-dashoffset: 20; } } ' )
))
)
Example 7 - Sequence Diagram
I just learned about sequence diagrams as I read through the mermaid.js
documentation, so I apologize if this is not quite right. While we are on the topic of htmlwidgets
, a sequence diagram might help explain the life of a htmlwidget
.
library(DiagrammeR)
DiagrammeR("
sequenceDiagram;
participant R;
Note left of R: Get Data<br/>Do Calculations
htmlwidgets->>R: dependencies, binding
alt Static
R->>Browser: json;
Note right of Browser: User Interact
else Shiny
loop continuously in response to event
R-->>Browser: json over socket;
Browser-->>R: json over socket;
Note left of R: Get Data<br/>Do Calculations
Note right of Browser: User Interact
end
end
")
Conclude and Reproduce
I hope you enjoyed the first installment of the widget a week project. Please, please let me know your thoughts, comments, brilliant ideas, applications, and favorite JavaScript libraries.