#' Update lasso information
#'
#' Update the lasso information based on the incoming click object from the application.
#'
#' @param click A Shiny click object.
#' @param previous \code{NULL} if no waypoints exist, otherwise a list produced by this function.
#' @param tol A numeric scalar specifying the tolerance to detect if the next click closes the lasso.
#'
#' @return A list containing:
#' \describe{
#' \item{\code{coord}:}{A numeric matrix of lasso waypoint coordinates, where each row is a waypoint.
#' The first column is the x-axis coordinate and the second column is the y-axis coordinate.}
#' \item{\code{closed}:}{A logical scalar specifying whether the lasso is closed.}
#' \item{\code{panelvar1}, \code{panelvar2}:}{Strings specifying the facet on which the lasso occurs.}
#' \item{\code{mapping}:}{A list containing further information about the variables used on the x- and y-axes and for faceting.}
#' }
#'
#' @details
#' A click close to the first waypoint will close the lasso.
#' The tolerance is defined as a product of \code{tol} and the x-/y-axis range.
#'
#' @author Aaron Lun
#' @seealso \code{\link{.process_selectby_choice}},
#' \code{\link{lassoPoints}}
#' @rdname INTERNAL_update_lasso
.update_lasso <- function(click, previous=NULL, tol=0.01) {
new_lasso <- list(
lasso=NULL, closed=FALSE, panelvar1=click$panelvar1,
panelvar2=click$panelvar2, mapping=click$mapping)
if (length(previous)) {
# Closing the lasso if you click close to the starting point, within the same facet.
xrange <- click$domain$right - click$domain$left
yrange <- click$domain$top - click$domain$bottom
if (abs(click$x - previous$coord[1,1]) < xrange * tol
&& abs(click$y - previous$coord[1,2]) < yrange * tol
&& identical(previous$panelvar1, click$panelvar1) # okay for both to be NULL.
&& identical(previous$panelvar2, click$panelvar2)
) {
new_lasso$coord <- rbind(previous$coord, previous$coord[1,])
new_lasso$closed <- TRUE
} else {
# Adding a waypoint, but only to an existing open lasso, otherwise using NULL.
if (!previous$closed
&& identical(previous$panelvar1, click$panelvar1)
&& identical(previous$panelvar2, click$panelvar2) ) {
new_lasso$coord <- previous$coord
}
new_lasso$coord <- rbind(new_lasso$coord, c(click$x, click$y))
}
} else {
new_lasso$coord <- cbind(click$x, click$y)
}
return(new_lasso)
}
#' Is the object a closed lasso or Shiny brush?
#'
#' Checks if an object is a lasso or Shiny brush data store.
#' Note that it may not be either if there is no brush or if the lasso is not yet closed.
#'
#' @param x A lasso or Shiny brush object.
#'
#' @return A logical scalar specifying if \code{x} is a closed lasso or Shiny brush.
#'
#' @author Aaron Lun
#' @rdname INTERNAL_is_lasso
.is_closed_lasso <- function(x) {
isTRUE(x$closed)
}
#' @rdname INTERNAL_is_lasso
.is_brush <- function(x) {
length(x) && is.null(x$closed)
}
#' Test if Shiny brushes are identical
#'
#' Check whether brush coordinates have actually changed between two Shiny brush objects.
#'
#' @param old_brush A Shiny brush object with the elements \code{xmin}, \code{xmax}, \code{ymin} and \code{ymax}.
#' @param new_brush Another Shiny brush object for the same plot.
#'
#' @details
#' This function checks whether there are any significant differences in the rectangular regions defined by two Shiny brushes.
#' If there is no change, there is no need to waste time updating the plot.
#'
#' The tolerance is defined as one millionth of the x- and y-axis range for \code{xmin}/\code{xmax} and \code{ymin}/\code{ymax}, respectively.
#' We do not use \code{all.equal(old_brush, new_brush)}, as the plot domain can sometimes change without affecting the actual brush coordinates.
#'
#' @return A logical scalar indicating whether the brushes are identical.
#'
#' @author Aaron Lun
#' @rdname INTERNAL_identical_brushes
#' @seealso
#' \code{\link{iSEE}}
.identical_brushes <- function(old_brush, new_brush) {
old_null <- length(old_brush)==0L
new_null <- length(new_brush)==0L
if (old_null || new_null) {
return(old_null==new_null)
}
if (!.is_brush(old_brush) || !.is_brush(new_brush)) {
return(FALSE)
}
xspan <- old_brush$xmax - old_brush$xmin
tol <- xspan * 1e-6
if (abs(old_brush$xmin - new_brush$xmin) > tol
|| abs(old_brush$xmax - new_brush$xmax) > tol) {
return(FALSE)
}
yspan <- old_brush$ymax - old_brush$ymin
tol <- yspan * 1e-6
if (abs(old_brush$ymin - new_brush$ymin) > tol
|| abs(old_brush$ymax - new_brush$ymax) > tol) {
return(FALSE)
}
TRUE
}
#' Get the brushed points
#'
#' Get the identities of brushed points selected by a Shiny brush or a closed lasso.
#'
#' @param contents A data.frame containing all points displayed in the affected plot with their identities in the row names.
#' @param cur_brush A Shiny brush or lasso structure in the affected plot.
#'
#' @return
#' A character vector of names of selected points in the brush/lasso.
#' If \code{cur_brush} is empty or not a closed lasso, \code{NULL} is returned instead.
#'
#' @author Aaron Lun
#'
#' @rdname INTERNAL_get_brushed_points
#' @importFrom shiny brushedPoints
.get_brushed_points <- function(contents, cur_brush) {
if (.is_brush(cur_brush)) {
rownames(brushedPoints(contents, cur_brush))
} else if (.is_closed_lasso(cur_brush)) {
rownames(lassoPoints(contents, cur_brush))
} else {
NULL
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.