Skip to content

Commit

Permalink
Merge pull request #121 from janithwanni/feat/shiny-click-event
Browse files Browse the repository at this point in the history
Feature: Expose click events in Shiny
  • Loading branch information
casperhart authored Oct 28, 2024
2 parents e62e71c + d33054e commit 215b8b9
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 9 deletions.
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
49 changes: 49 additions & 0 deletions demo/shiny_detourr/click_events.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
library(shiny)
library(detourr)

ui <- function() {
fluidPage(
fluidRow(
column(6,
displayScatter3dOutput(
"detourr_out",
width = "100%",
height = "400px"
)
),
column(6,
h1("Click events through shiny"),
p(
"This app demonstrates how to obtain click events through Shiny.",
"In this example, zoom in and pan around to click on any point",
"and the id (the row number in this case)",
"will be shown below."
),
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

0 comments on commit 215b8b9

Please sign in to comment.