6 min read

Week01 - Easy Flowcharts and Diagrams

Week01 - Easy Flowcharts and Diagrams

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:

  1. This widget was started last week, and
  2. 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 DiagrammeRs 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
)

graph LR; tw[htmltools tag] --> diagram

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.