Skip to content
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: Expose click events in Shiny #121

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
^renv$
^renv\.lock$
^.*\.Rproj$
^\.Rproj\.user$
srcts/*
Expand Down
25 changes: 25 additions & 0 deletions demo/detourr_shiny.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
library(shiny)
library(detourr)

ui <- function() {
fluidPage(
displayScatter3dOutput("detourr_out", width = "100%", height = "400px"),
textOutput("detour_click_output")
)
}

server <- function(input, output, session) {
output$detourr_out <- shinyRenderDisplayScatter2d({
detour(
tourr::flea |> dplyr::mutate(id = dplyr::row_number()), tour_aes(projection = -species, colour = species, label = id)
) |>
tour_path(grand_tour(3), fps = 60) |>
show_scatter(alpha = 0.7, axes = TRUE)
})

output$detour_click_output <- renderText({
print(input$detour_click)
})
}

shinyApp(ui, server, options = list(port = 5534))
34 changes: 34 additions & 0 deletions demo/shiny_detourr/click_events.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
library(shiny)
library(detourr)

ui <- function() {
fluidPage(
fluidRow(
column(6,displayScatter3dOutput("detourr_out", width = "100%", height = "400px")),
column(6,textOutput("detourr_id"))
)
)
}

server <- function(input, output, session) {
output$detourr_out <- shinyRenderDisplayScatter2d({
detour(
tourr::flea |>
dplyr::mutate(id = dplyr::row_number()),
tour_aes(projection = -species, colour = species, label = id)
) |>
tour_path(grand_tour(3), fps = 60) |>
show_scatter(alpha = 0.7, axes = TRUE)
})

output$detourr_id <- renderText({
req(!is.null(input$detourr_out_detour_click))
paste0("Clicked on id: ", input$detourr_out_detour_click)
})

observeEvent(input$detourr_out_detour_click, {
print(input$detourr_out_detour_click)
})
}

shinyApp(ui, server, options = list(port = 5534))
2 changes: 1 addition & 1 deletion inst/htmlwidgets/lib/show_sage_2d.bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/htmlwidgets/lib/show_sage_3d.bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/htmlwidgets/lib/show_scatter_2d.bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/htmlwidgets/lib/show_scatter_3d.bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/htmlwidgets/lib/show_slice_2d.bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/htmlwidgets/lib/show_slice_3d.bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"@stdlib/math-base-special-betainc": "^0.0.6",
"@tensorflow/tfjs-backend-wasm": "^3.15.0",
"@tensorflow/tfjs-core": "^3.15.0",
"shiny": "https://github.com/rstudio/shiny#v1.8.0",
"three": "^0.128.0",
"url-loader": "^4.1.1"
},
Expand Down
52 changes: 49 additions & 3 deletions srcts/show_scatter/show_scatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AxisLabel } from "./axis_label";
import { ScatterControls } from "./controls";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import "./style.css";
import Shiny from "shiny";

import wasmSimdPath from "../../node_modules/@tensorflow/tfjs-backend-wasm/dist/tfjs-backend-wasm-simd.wasm";
import wasmSimdThreadedPath from "../../node_modules/@tensorflow/tfjs-backend-wasm/dist/tfjs-backend-wasm-threaded-simd.wasm";
Expand Down Expand Up @@ -125,9 +126,18 @@ export abstract class DisplayScatter {

if (this.hasPointLabels) {
this.addToolTip();
this.canvas.addEventListener("mousemove", (event: MouseEvent) =>
this.setTooltipFromHover(event)
);
this.canvas.addEventListener("mousemove", (event: MouseEvent) => {
this.setTooltipFromHover(event);
});
this.canvas.addEventListener("click", (event: PointerEvent) => {
const clickId = this.getIdfromClick(event);
if(window.Shiny != null) {
window.Shiny.setInputValue(
`${this.getContainerElement().id}_detour_click`,
clickId == null ? -1: clickId
);
}
})
}

const pointsGeometry = new THREE.BufferGeometry();
Expand Down Expand Up @@ -731,6 +741,42 @@ export abstract class DisplayScatter {
this.toolTip.className = "detourrTooltip";
}
}
private getCoordsfromClick(event: PointerEvent, canvas: HTMLCanvasElement, dpr: number) {
const canvas_coords = canvas.getBoundingClientRect();
const x = (event.x - canvas_coords.left) * dpr * this.scaleX();
const y = (event.y - canvas_coords.top) * dpr * this.scaleY();
const width = 1;
const height = 1;
return {x: x, y: y, width: width, height: height};
}

private getIdfromClick(event: PointerEvent) : number {
const { pickingTexture, renderer, canvas } = this;
const dpr = renderer.getPixelRatio();
const { x, y, width, height } = this.getCoordsfromClick(event, canvas, dpr);
const pixelBuffer = new Uint8Array(12);

renderer.readRenderTargetPixels(
pickingTexture,
x,
pickingTexture.height - y,
width,
height,
pixelBuffer
);

const id = (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | pixelBuffer[2];
if (
id != 0 &&
id != this.backgroundColour &&
this.filteredPointIndices.includes(id - 1)
) {
return(id);
} else {
return(null);
}

}

// TODO: break away chunks in to separate functions
private animate() {
Expand Down
81 changes: 81 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2379,6 +2379,24 @@ __metadata:
languageName: node
linkType: hard

"@types/bootstrap-datepicker@npm:0.0.14":
version: 0.0.14
resolution: "@types/bootstrap-datepicker@npm:0.0.14"
dependencies:
"@types/jquery": "*"
checksum: 3544d5188f208894a632024c8bb9c1cb9b2d503c890157f88c8fe383b2370dc0d57df926b075a4a7456979d329128364433de13223dbdf01a1fa64f8f94be663
languageName: node
linkType: hard

"@types/bootstrap@npm:3.4.0":
version: 3.4.0
resolution: "@types/bootstrap@npm:3.4.0"
dependencies:
"@types/jquery": "*"
checksum: 33f7bb9ad4a1610baf0e7d3f8dd20eee095ae2ab6cfa9109d2b40e03684ace284b86738997f9c9e73e2da429b57a36372b162758c93d698ca4900031227cd3ae
languageName: node
linkType: hard

"@types/connect-history-api-fallback@npm:^1.3.5":
version: 1.3.5
resolution: "@types/connect-history-api-fallback@npm:1.3.5"
Expand All @@ -2398,6 +2416,15 @@ __metadata:
languageName: node
linkType: hard

"@types/datatables.net@npm:^1.10.19":
version: 1.10.28
resolution: "@types/datatables.net@npm:1.10.28"
dependencies:
"@types/jquery": "*"
checksum: 23763886a99fbca304199913ec619bd723fbcfae629e20cac7a2adcbed52e8663a0793b4a926a5564b4c233204e1ca2123fb3d2c68914d132f8931b507030aaf
languageName: node
linkType: hard

"@types/emscripten@npm:~0.0.34":
version: 0.0.34
resolution: "@types/emscripten@npm:0.0.34"
Expand Down Expand Up @@ -2464,6 +2491,31 @@ __metadata:
languageName: node
linkType: hard

"@types/ion-rangeslider@npm:2.3.0":
version: 2.3.0
resolution: "@types/ion-rangeslider@npm:2.3.0"
checksum: d84fe5714a53cd8709dcfda1396c9b1328e396a45013afcb3baaec1015c6c8cd5edc5cff4eff987cf3fe6211e63822532776206da0a1b4229304bccb577e7481
languageName: node
linkType: hard

"@types/jquery@npm:*":
version: 3.5.29
resolution: "@types/jquery@npm:3.5.29"
dependencies:
"@types/sizzle": "*"
checksum: 5e959762d6f7050b07b4387b6507a308113384566a77cfc4f8d0f54c2fb0a79f6bc8c057706c6aa4840cde56f32ad0e5814fb53c5f078c5db9e01670a1ecd535
languageName: node
linkType: hard

"@types/jquery@npm:3.5.14":
version: 3.5.14
resolution: "@types/jquery@npm:3.5.14"
dependencies:
"@types/sizzle": "*"
checksum: 159d6f804ed1a204b3f79f2d591a271d82e866bd45bd49fb6ef40561a25dbe0f47ec7815681b44cc2db5598425f72811e7e80ab0e983d980470998ac56feb375
languageName: node
linkType: hard

"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9":
version: 7.0.9
resolution: "@types/json-schema@npm:7.0.9"
Expand Down Expand Up @@ -2534,6 +2586,13 @@ __metadata:
languageName: node
linkType: hard

"@types/selectize@npm:0.12.34":
version: 0.12.34
resolution: "@types/selectize@npm:0.12.34"
checksum: 3e9b0ad37ae8fb1c4178b557b7d55dfb0319377f935e87bee8ac91d42e297b1fa72ea6d4276990b653ad8e991c7a393d2fb7f4d1d613922da2b77742813080e2
languageName: node
linkType: hard

"@types/serve-index@npm:^1.9.1":
version: 1.9.1
resolution: "@types/serve-index@npm:1.9.1"
Expand Down Expand Up @@ -2563,6 +2622,13 @@ __metadata:
languageName: node
linkType: hard

"@types/sizzle@npm:*":
version: 2.3.8
resolution: "@types/sizzle@npm:2.3.8"
checksum: 2ac62443dc917f5f903cbd9afc51c7d6cc1c6569b4e1a15faf04aea5b13b486e7f208650014c3dc4fed34653eded3e00fe5abffe0e6300cbf0e8a01beebf11a6
languageName: node
linkType: hard

"@types/sockjs@npm:^0.3.33":
version: 0.3.33
resolution: "@types/sockjs@npm:0.3.33"
Expand Down Expand Up @@ -3669,6 +3735,7 @@ __metadata:
eslint: ^8.8.0
eslint-config-prettier: ^8.3.0
prettier: ^2.5.1
shiny: "https://github.com/rstudio/shiny#v1.8.0"
style-loader: ^3.1.0
three: ^0.128.0
ts-loader: ^9.1.2
Expand Down Expand Up @@ -6121,6 +6188,20 @@ __metadata:
languageName: node
linkType: hard

"shiny@https://github.com/rstudio/shiny#v1.8.0":
version: 1.8.0
resolution: "shiny@https://github.com/rstudio/shiny.git#commit=283c71e77274588e8c6b5540c774a91f71865474"
dependencies:
"@types/bootstrap": 3.4.0
"@types/bootstrap-datepicker": 0.0.14
"@types/datatables.net": ^1.10.19
"@types/ion-rangeslider": 2.3.0
"@types/jquery": 3.5.14
"@types/selectize": 0.12.34
checksum: ce646b957864b4f76994067be1c87ab8ec8a2d38b5bf54ff98bb611f2c1ef7059deefe15dea61842fe711d4e8b1da18e75ae2e1c72db9a6976d483d64bffedf4
languageName: node
linkType: hard

"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.3":
version: 3.0.6
resolution: "signal-exit@npm:3.0.6"
Expand Down