library(tidyverse)
## ── Attaching packages ────────── tidyverse 1.2.1 ──
## ✔ ggplot2 3.1.0     ✔ purrr   0.2.5
## ✔ tibble  1.4.2     ✔ dplyr   0.7.8
## ✔ tidyr   0.8.2     ✔ stringr 1.3.1
## ✔ readr   1.1.1     ✔ forcats 0.3.0
## ── Conflicts ───────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
library(tidygraph)
## 
## Attaching package: 'tidygraph'
## The following object is masked from 'package:stats':
## 
##     filter
set.seed(20181208)

# Workaround für https://github.com/thomasp85/tidygraph/issues/83
to_all_shortest_paths <- function(graph, from, mode = "out", weights = NULL) {
  nodes <- as_tibble(graph, "nodes")
  from <- rlang::eval_tidy(enquo(from), nodes)
  from <- tidygraph:::as_ind(from, igraph:::gorder(graph))
  weights <- rlang::eval_tidy(enquo(weights), as_tibble(graph, active = "edges"))
  path <- igraph::shortest_paths(graph, from = from, mode = mode, weights = weights, output = "both")
  short_path <- slice(activate(graph, "edges"), unique(as.integer(unlist(path$epath))))
  short_path <- slice(activate(short_path, "nodes"), unique(as.integer(unlist(path$vpath))))
  list(
    shortest_path = short_path
  )
}

to_all_distances <- function(graph, from, mode = "out", weights = NULL, dist = "dist") {
  nodes <- as_tibble(graph, "nodes")
  from <- rlang::eval_tidy(enquo(from), nodes)
  from <- tidygraph:::as_ind(from, igraph:::gorder(graph))
  weights <- rlang::eval_tidy(enquo(weights), as_tibble(graph, active = "edges"))
  distances <- igraph::distances(graph, v = from, mode = mode, weights = weights)
  distance_frame <- enframe(distances[1, ], "name", dist)
  distance_attr <- left_join(activate(graph, "nodes"), distance_frame, by = "name")

  list(
    distance_attr = distance_attr
  )
}

# Zelldaten erzeugen
cells <-
  crossing(x = 1:10, y = 1:10) %>%
  mutate(cost = runif(length(x)))

# In welche Richtung dürfen wir uns aus einer Zelle heraus bewegen?
delta <-
  crossing(dx = -1:1, dy = -1:1) %>%
  filter(dx != 0 | dy != 0) %>%
  mutate(factor = sqrt(dx * dx + dy * dy))

# Graph erzeugen: Zuweisung der Kosten der Zielzelle zu der Kante
graph <-
  crossing(cells, delta) %>%
  mutate(x2 = x + dx, y2 = y + dy) %>%
  select(-dx, -dy) %>%
  filter(x2 > 0, x2 <= 10, y2 > 0, y2 <= 10) %>%
  unite("origin", x2, y2, sep = "_") %>%
  unite("dest", x, y, sep = "_") %>%
  transmute(origin, dest, cost = cost * factor) %>%
  as_tbl_graph()

# Startknoten definieren:
origin <- "6_6"

# Distanzen zu Startknoten:
distances <-
  graph %>%
  convert(to_all_distances, name == origin, weights = cost) %>%
  as_tibble("nodes")

distances
## # A tibble: 100 x 3
##    name  .tidygraph_node_index  dist
##    <chr>                 <int> <dbl>
##  1 1_2                       1 1.76 
##  2 2_1                       2 2.45 
##  3 2_2                       3 1.61 
##  4 1_1                       4 2.28 
##  5 1_3                       5 1.77 
##  6 2_3                       6 1.28 
##  7 1_4                       7 2.02 
##  8 2_4                       8 1.52 
##  9 1_5                       9 1.28 
## 10 2_5                      10 0.910
## # ... with 90 more rows
# Kürzeste Wege zu Startknoten (nur für Visualisierung):
paths <-
  graph %>%
  convert(to_all_shortest_paths, name == origin, weights = cost)

# Visualisierung:
path_nodes <-
  paths %>%
  as_tibble("nodes")

path_data <-
  paths %>%
  as_tibble("edges") %>%
  select(-cost) %>%
  gather("node", ".tidygraph_node_index", from, to, factor_key = TRUE) %>%
  left_join(path_nodes, by = ".tidygraph_node_index") %>%
  separate(name, into = c("x", "y"), convert = TRUE) %>%
  arrange(node)


distance_data <-
  cells %>%
  unite("name", x, y) %>%
  left_join(distances, by = "name") %>%
  mutate(dist = round(dist, 1)) %>%
  select(-.tidygraph_node_index) %>%
  separate(name, into = c("x", "y"), convert = TRUE)

p <-
  ggplot(distance_data, aes(x = x, y = y)) +
  geom_tile(aes(fill = cost, dist = dist)) +
  geom_path(
    data = path_data,
    aes(group = .tidygraph_edge_index),
    color = "red",
    arrow = arrow(length = unit(0.05, "inches"), type = "closed")
  ) +
  scale_fill_viridis_c(option = "plasma")
## Warning: Ignoring unknown aesthetics: dist
p +
  geom_text(aes(label = dist))

plotly::ggplotly(p)