datastorm-open / visNetwork

R package, using vis.js library for network visualization
Other
545 stars 125 forks source link

Preserving node locations during visUpdateNodes() #381

Closed julianstanley closed 4 years ago

julianstanley commented 4 years ago

Hi @bthieurmel, thanks as always for this great tool and your commitment to keeping it up-to-date.

Is it possible to record the X/Y coordinates of all nodes on the canvas so that, when something like visUpdateNodes refreshes the graph, all of the nodes stay in the same place (even if they've been moved/dragged by the user)?

If I could somehow keep track of user drag events, and also use the vis.js's fixed x and y coordinates, I feel like this might be feasible? Is there an easier way?


More details

With my use of visNetwork, I often need to make automatic changes to the graph. For example, when a user creates a new node, I would like to automatically add a recursive edge to that node.

So, I find myself often needing to refresh the visNetwork plot. For this, visNetworkProxy is really handy. However, visNetworkProxy will re-arrange all of the nodes on the canvas, which can be really disorienting with lots of nodes.

For example, take this contrived example where a user can use a text box to edit the name of a node:

library(shiny)
library(visNetwork)

ui <- fluidPage(
  textInput("node_name", "Choose a node name"),
  actionButton("submit", "Make Changes"),
  visNetworkOutput("my_graph")
)

server <- function(input, output, session) {
  my_data <- reactiveValues(
    nodes = data.frame(id = "A", label = "A")
  )

  output$my_graph <- renderVisNetwork({
    visNetwork(
      nodes = my_data$nodes
    )
  })

  observeEvent(input$submit, {
    my_data$nodes$label <- input$node_name
  })

  observe({
    visNetworkProxy("my_graph") %>%
      visUpdateNodes(nodes = my_data$nodes)
  })
}

shinyApp(ui, server)

So, here, the node starts off in the middle and is named 'A':

image

Then, let's say I drag it to the edge of the canvas and then try to re-name it to "Robert":

image

When I hit "Make Changes", the canvas refreshes and the node snaps back to the middle.

image

This isn't terribly inconvenient in this example but, if I had a few dozen nodes and had them aligned in a meaningful pattern, this constant refreshing would be very disorienting.

bthieurmel commented 4 years ago

Hi,

It's already the case : position are kept during update. Your example is wrong because using reactiveValues, you're also update main visNetwork each time you change the label...!

  output$my_graph <- renderVisNetwork({
    visNetwork(
      nodes = data.frame(id = "A", label = "A")
    )
  })

You have to init the main network once using visNetwork, and then update using visNetworkProxy. Each time you call visNetwork, the network is initialized...!

julianstanley commented 4 years ago

Ahh, brilliant--always great when the issue is already fixed :laughing:. Thanks much, @bthieurmel

For anyone who has happened upon this issue, an alternate solution is to isolate the reactiveValues:

output$my_graph <- renderVisNetwork({
    visNetwork(
      nodes = isolate(my_data$nodes)
    )
  })

In case that data structure is important. Or, as mentioned above, not use that data structure in the first place :smile: