-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Add points and edges through a proxy #122
base: main
Are you sure you want to change the base?
Feature: Add points and edges through a proxy #122
Conversation
39d4ca4
to
2a3c177
Compare
2a3c177
to
fe64a40
Compare
Hi @casperhart, This PR provides the functionality to add points and edges to an existing detour instance through a proxy object (similar to the approach taken by leaflet and other Javascript libraries). The Shiny app in the library(shiny)
library(detourr)
dataset <- tourr::flea |>
dplyr::mutate(id = dplyr::row_number())
create_fake_box <- function(datum) {
# expected tibble needs to have two rows with ncols(datum) + 1 columns
box_dist <- rnorm(1, mean = 3)
bounds_list <- rbind(datum + box_dist, datum - box_dist) |>
as.list()
do.call(tidyr::expand_grid, bounds_list)
}
main_ui <- function(id) {
ns <- NS(id)
fluidPage(
column(6,
displayScatter2dOutput(
ns("detourr_out"), width = "100%", height = "400px"
),
textOutput(ns("detour_click_output"))
),
column(6,
h1("Adding points and edges to detourr through Shiny"),
p(
"In this demonstration,",
"clicking on points on the detourr instance to the left",
"adds a box around the point.",
"Click on the play button to view the box in different projections"
)
)
)
}
main_server <- function(id) {
moduleServer(id, function(input, output, session){
output$detourr_out <- shinyRenderDisplayScatter2d({
detour(dataset,
tour_aes(projection = -c(id, species), colour = species, label = id)
) |>
tour_path(grand_tour(2), fps = 60) |>
show_scatter(
alpha = 0.7,
axes = TRUE
)
})
output$detour_click_output <- renderText({
input$detourr_out_detour_click
})
observeEvent(input$detourr_out_detour_click, {
req(
!is.null(input$detourr_out_detour_click),
input$detourr_out_detour_click != -1
)
data_to_send <- dataset |>
dplyr::select(-species) |>
dplyr::filter(id == input$detourr_out_detour_click) |>
dplyr::select(-id)
box_to_send <- data_to_send |> create_fake_box()
cube_box <- geozoo::cube.iterate(p = ncol(data_to_send))
display_scatter_proxy(session$ns("detourr_out")) |>
add_points(
box_to_send,
.data = dataset |> dplyr::select(-c(id, species))
) |>
add_edges(
edge_list = cube_box$edges
)
})
})
}
ui <- function() {
main_ui("main")
}
server <- function(input, output, session) {
main_server("main")
}
shinyApp(ui, server, options = list(port = 5534)) |
@@ -18,7 +18,8 @@ Imports: | |||
rlang, | |||
purrr, | |||
viridisLite, | |||
grDevices | |||
grDevices, | |||
cli |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cli
package to provide alerts and errors
.data = NULL, | ||
.col_means = NULL, | ||
.scale_factor = NULL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach had to be implemented because the tourr package does scaling internally and therefore we need to scale the points that are given to make sure that they fit in the exact required location.
} catch (error) { | ||
console.error(`Could not find detour widget ${x.id}`) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be catching other errors apart from the widget not being found but for the time being I'm leaving it as it is, because that is the most likely candidate of throwing errors here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what scenarios would the widget not be found?
protected auxPoint: THREE.Points; | ||
protected auxData: tf.Tensor2D; | ||
protected auxEdgeData: number[]; | ||
protected auxEdge: THREE.LineSegments; | ||
protected auxEdgeBuffer: THREE.BufferGeometry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented it such that only one set of additional points can be added at a given time. @casperhart, if you have suggestions for a better implementation of this, I'm open to implementing it.
@@ -386,18 +486,19 @@ export abstract class DisplayScatter { | |||
this.scene.add(this.axisSegments); | |||
} | |||
|
|||
private addEdgeSegments(pointsBuffer: THREE.BufferAttribute) { | |||
private addEdgeSegments(pointsBuffer: THREE.BufferAttribute, edges: number[]): THREE.LineSegments { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extended addEdgeSegments
function to enable arrays outside of the internal class variable.
describe("add_edges", { | ||
it("returns a proxy class object", { | ||
# Arrange | ||
proxy <- list( | ||
id = "id", | ||
session = list( | ||
sendCustomMessage = \(x, y) "stubbed function" | ||
) | ||
) |> structure(class = "detour_proxy") | ||
points <- data.frame(list( | ||
from = c(1, 2, 3), | ||
to = c(2, 3, 4) | ||
)) | ||
|
||
# Act | ||
out <- add_edges(proxy, points) | ||
|
||
# Assert | ||
expect_s3_class(out, "detour_proxy") | ||
expect_vector(out$id, ptype = character(), size = 1) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simple tests to begin with at the moment. But can be extended to more extensive tests once the single widget setup is complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something isn't working properly for me. I have run the demo, but the box is much bigger than in the screen capture you added:
Also, if I zoom right in and then click a point, the new points that are added are much bigger than the existing points:
And do you have any other examples of how this might be used? Not code examples, just so I can better understand what the feature is for
} catch (error) { | ||
console.error(`Could not find detour widget ${x.id}`) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what scenarios would the widget not be found?
Depends on: #121
Changes:
display_scatter_proxy
add_points
andadd_edges
to add additional points and edges using the proxy returned by the above function