## ----setup, include=FALSE-----------------------------------------------------
library(knitr)

opts_chunk$set(
  cache = TRUE,
  echo = TRUE,
  eval = TRUE,
  fig.align = "left",
  fig.height = 6,
  fig.width = 6,
  message = FALSE,
  out.extra = "",
  results = "hold",
  warning = FALSE
)

## Render a Graphviz/DOT diagram directly from its source to a vector PDF. The class
## diagrams, the algorithm flow chart, and the Engine call graphs in this vignette are
## therefore generated from code (and stay in sync with it) instead of being maintained
## as static images (#75). Uses DiagrammeRsvg + rsvg, so no headless browser is required.
renderDot <- function(dot) {
  svg <- DiagrammeRsvg::export_svg(DiagrammeR::grViz(dot))
  out <- fig_path(".pdf")
  dir.create(dirname(out), recursive = TRUE, showWarnings = FALSE)
  rsvg::rsvg_pdf(charToRaw(svg), out)
  include_graphics(out)
}

## ----flowchart, echo = FALSE, out.width = "62%", fig.cap = "Overview of the MPG algorithm."----
renderDot('
  digraph mpg_algorithm {
    graph [fontsize = 10, rankdir = TB]
    node [fontname = Courier, fontsize = 10]

    init    [shape = oval, label = "initialize()"]
    patches [shape = box, label = "findPatches(): seed the Voronoi map and\\liLinkMap cell ids, then build the initial\\lactive cells (patch cells spread at the\\luniform minimum cost)\\l"]
    loop    [shape = diamond, height = 1.1, label = "active\\ncells\\nremain?"]
    check   [shape = box, label = "activeCellSpreadChecker(): a cell whose\\lwaiting time has reached its resistance is\\lmarked settled and queued to spread (the\\lqueue is sorted by effective distance)\\l"]
    spread  [shape = box, label = "createActiveCell(): spread into the 4 adjacent\\lunclaimed cells, assign the Voronoi id, and\\lconnectCell() back to the parent\\l"]
    link    [shape = box, label = "createLinks() / findPath(): at a boundary\\lbetween two settled patches, record the\\lleast-cost link (keep the cheapest; drop\\lredundant indirect and malformed links)\\l"]
    out     [shape = oval, label = "write the Voronoi map\\land links to output"]

    init -> patches -> loop
    loop -> check [label = " yes"]
    check -> spread -> link -> loop
    loop -> out [label = " no"]
  }
')

## ----cells_diagram, echo = FALSE, out.width = "62%", fig.cap = "Schematic representation of Cell type data structures. An open triangle denotes inheritance."----
renderDot('
  digraph cells {
    graph [rankdir = BT, fontsize = 10]
    node [shape = record, fontname = Courier, fontsize = 10]

    Cell       [label = "{Cell|+ row : int\\l+ column : int\\l+ id : float\\l}"]
    ActiveCell [label = "{ActiveCell|+ time : float\\l+ distance : float\\l+ resistance : float\\l+ parentResistance : float\\l+ originCell : Cell\\l}"]
    LinkCell   [label = "{LinkCell|+ fromCell : Cell\\l+ originCell : Cell\\l+ distance : float\\l+ cost : float\\l}"]

    edge [arrowhead = onormal]
    ActiveCell -> Cell
    LinkCell -> Cell
  }
')

## ----data_struct_diagram, echo = FALSE, out.width = "98%", fig.cap = "Schematic representation of additional data structures. An open diamond denotes composition (a 'has-a' relationship); dashed boxes are the Cell types shown above."----
renderDot('
  digraph data_struct {
    graph [rankdir = TB, fontsize = 10]
    node [shape = record, fontname = Courier, fontsize = 10]

    InputData  [label = "{InputData|+ cost_vec : vector\\<float\\>\\l+ nrow : int\\l+ ncol : int\\l+ patch_vec : vector\\<float\\>\\l}"]
    OutputData [label = "{OutputData|+ voronoi_map : vector\\<float\\>\\l+ patch_map : vector\\<float\\>\\l+ link_data : vector\\<Link\\>\\l+ patch_list : vector\\<Patch\\>\\l}"]
    Link  [label = "{Link|+ start : Cell\\l+ end : Cell\\l+ connection : vector\\<Cell\\>\\l+ cost : float\\l}"]
    Patch [label = "{Patch|+ body : vector\\<Cell\\>\\l+ id : float\\l}"]
    ActiveCellHolder [label = "{ActiveCellHolder|+ value : float\\l+ list : vector\\<ActiveCell\\>\\l|+ add(ActiveCell)\\l+ size() : int\\l}"]
    ActiveCellQueue  [label = "{ActiveCellQueue|+ holder_list : vector\\<ActiveCellHolder\\>\\l|+ insertH(ActiveCellHolder)\\l+ size() : int\\l}"]

    node [shape = box, style = dashed]
    Cell; ActiveCell

    edge [arrowhead = odiamond, fontname = Courier, fontsize = 9]
    ActiveCellQueue -> ActiveCellHolder [label = " holder_list"]
    ActiveCellHolder -> ActiveCell [label = " list"]
    OutputData -> Link [label = " link_data"]
    OutputData -> Patch [label = " patch_list"]
    Link -> Cell [label = " start, end,\\l connection"]
    Patch -> Cell [label = " body"]
  }
')

## ----typedef_diagram, echo = FALSE, out.width = "82%", fig.cap = "Schematic representation of type definitions."----
renderDot('
  digraph typedefs {
    graph [rankdir = LR, fontsize = 10, nodesep = 0.4, ranksep = 0.7]
    node [shape = box, fontname = Courier, fontsize = 10]

    flCol   [label = "flCol\\n= vector<float>"]
    flMap   [label = "flMap\\n= vector<flCol>"]
    lcCol   [label = "lcCol\\n= vector<LinkCell>"]
    LinkMap [label = "LinkMap\\n= vector<lcCol>"]
    boolCol [label = "boolCol\\n= vector<bool>"]
    boolMap [label = "boolMap\\n= vector<boolCol>"]

    edge [arrowhead = vee, fontname = Courier, fontsize = 9]
    flCol -> flMap     [label = " vector of"]
    lcCol -> LinkMap   [label = " vector of"]
    boolCol -> boolMap [label = " vector of"]
  }
')

## ----engine_class_diagram, echo = FALSE, out.width = "72%", fig.cap = "Schematic representation of the Engine class. A leading + marks a public member and - a private member; related methods are grouped on a line. Full signatures are given in the tables below."----
renderDot('
  digraph engine {
    graph [fontsize = 10]
    node [shape = record, fontname = Courier, fontsize = 10]

    Engine [label = "{Engine|- in_data : InputData*\\l- out_data : OutputData*\\l- maxCost : float\\l- costRes : float\\l- active_cell_holder : ActiveCellQueue\\l- temporary_active_cell_holder : ActiveCellQueue\\l- spread_list : vector\\<ActiveCell\\>\\l- iLinkMap : LinkMap\\l- voronoi_map : flMap\\l- cost_map : flMap\\l- settled_map : boolMap\\l- initialized : bool\\l- error_message : char*\\l- error_message_size : int\\l|+ Engine() / ~Engine()\\l+ initialize() : bool\\l+ start() : void\\l+ emax() / emin() / calcDistance()  [static]\\l- findPatches() / getIndexFromList() / combinePatches()\\l- activeCellSpreadChecker() / createActiveCell()\\l- createLinks() / findPath() / parseMap() / lookForIndirectPath()\\l- connectCell() / cellIsZero() / cellsEqual() / outOfBounds()\\l- updateOutputMap() / writeErrorMessage()\\l}"]
  }
')

## ----call_graph_engine_initialize, echo = FALSE, out.width = "50%", fig.cap = "Call diagram for Engine::initialize()"----
renderDot('
  digraph initialize {
    graph [overlap = true, fontsize = 10]
    
    # nodes
    node [fillcolor = navy, fontcolor = white,
          fontname = Courier,
          shape = oval, style = filled]
    
    initialize
    
    node [fillcolor = grey50, fontcolor = white,
          fontname = Courier,
          shape = box, style = filled]
    
    cellIsZero; combinePatches; findPatches; getIndexFromList;
    outOfBounds; updateOutputMap; writeErrorMessage;
    
    # edges
    initialize->cellIsZero
    initialize->findPatches
    initialize->updateOutputMap
    initialize->writeErrorMessage
    cellIsZero->outOfBounds
    findPatches->combinePatches
    findPatches->getIndexFromList
    findPatches->outOfBounds
  }
')

## ----call_graph_engine_start, echo = FALSE, out.width = "60%", fig.cap = "Call diagram for Engine::start()"----
renderDot('
  digraph start {
    graph [overlap = true, fontsize = 10]
    
    # nodes
    node [fillcolor = navy, fontcolor = white,
          fontname = Courier,
          shape = oval, style = filled]
    
    start
    
    node [fillcolor = grey50, fontcolor = white,
          fontname = Courier,
          shape = box, style = filled]
    
    activeCellSpreadChecker; calcDistance; cellsEqual; connectCell; createActiveCell;
    createLinks; findPath; lookForIndirectPath;
    outOfBounds; parseMap;
    updateOutputMap; writeErrorMessage;
    
    # edges
    start->activeCellSpreadChecker
    start->createActiveCell
    start->createLinks
    start->updateOutputMap
    start->writeErrorMessage
    createActiveCell->calcDistance
    createActiveCell->connectCell
    createActiveCell->outOfBounds
    createLinks->findPath
    createLinks->outOfBounds
    findPath->parseMap
    findPath->lookForIndirectPath
    parseMap->cellsEqual
  }
')

