New Upstream Release - r-bioc-scater

Ready changes

Summary

Merged new upstream version: 1.28.0+ds (was: 1.26.1+ds).

Diff

diff --git a/DESCRIPTION b/DESCRIPTION
index a23473b..a87b0b3 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -7,22 +7,26 @@ Authors@R: c(
         person("Quin", "Wills", role=c("aut"), email="qilin@quinwills.net"),
         person("Vladimir", "Kiselev", role=c("ctb"), email="vk6@sanger.ac.uk"),
         person("Felix G.M.", "Ernst", role=c("ctb"), email="felix.gm.ernst@outlook.com"),
-        person("Alan", "O'Callaghan", role=c("ctb", "cre"), email="alan.ocallaghan@outlook.com"))
-Version: 1.26.1
-Date: 2022-11-13
+        person("Alan", "O'Callaghan", role=c("ctb", "cre"), email="alan.ocallaghan@outlook.com"),
+        person("Yun", "Peng", role=c("ctb"), email="yunyunp96@gmail.com"),
+        person("Leo", "Lahti", role=c("ctb"), email="leo.lahti@utu.fi",
+	             comment = c(ORCID = "0000-0001-5537-637X"))
+	)
+Version: 1.28.0
+Date: 2023-04-24
 License: GPL-3
 Title: Single-Cell Analysis Toolkit for Gene Expression Data in R
 Description: A collection of tools for doing various analyses of
         single-cell RNA-seq gene expression data, with a focus on
         quality control and visualization.
 Depends: SingleCellExperiment, scuttle, ggplot2
-Imports: stats, utils, methods, grid, gridExtra, Matrix, BiocGenerics,
-        S4Vectors, SummarizedExperiment, DelayedArray,
-        DelayedMatrixStats, beachmat, BiocNeighbors, BiocSingular,
-        BiocParallel, rlang, ggbeeswarm, viridis, Rtsne, RColorBrewer,
-        RcppML, uwot, pheatmap, ggrastr, ggrepel
+Imports: stats, utils, methods, Matrix, BiocGenerics, S4Vectors,
+        SummarizedExperiment, DelayedArray, DelayedMatrixStats,
+        beachmat, BiocNeighbors, BiocSingular, BiocParallel, rlang,
+        ggbeeswarm, viridis, Rtsne, RColorBrewer, RcppML, uwot,
+        pheatmap, ggrepel, densvis, ggrastr
 Suggests: BiocStyle, snifter, cowplot, biomaRt, knitr, scRNAseq,
-        robustbase, rmarkdown, testthat, Biobase
+        robustbase, rmarkdown, testthat, Biobase, scattermore
 VignetteBuilder: knitr
 biocViews: ImmunoOncology, SingleCell, RNASeq, QualityControl,
         Preprocessing, Normalization, Visualization,
@@ -30,21 +34,23 @@ biocViews: ImmunoOncology, SingleCell, RNASeq, QualityControl,
         Sequencing, Software, DataImport, DataRepresentation,
         Infrastructure, Coverage
 Encoding: UTF-8
-RoxygenNote: 7.2.1
+RoxygenNote: 7.2.3
 URL: http://bioconductor.org/packages/scater/
 BugReports: https://support.bioconductor.org/
 git_url: https://git.bioconductor.org/packages/scater
-git_branch: RELEASE_3_16
-git_last_commit: d73b6c0
-git_last_commit_date: 2022-11-13
-Date/Publication: 2022-11-13
+git_branch: RELEASE_3_17
+git_last_commit: e654498
+git_last_commit_date: 2023-04-25
+Date/Publication: 2023-04-25
 NeedsCompilation: no
-Packaged: 2022-11-13 23:06:23 UTC; biocbuild
+Packaged: 2023-04-25 22:39:31 UTC; biocbuild
 Author: Davis McCarthy [aut],
   Kieran Campbell [aut],
   Aaron Lun [aut, ctb],
   Quin Wills [aut],
   Vladimir Kiselev [ctb],
   Felix G.M. Ernst [ctb],
-  Alan O'Callaghan [ctb, cre]
+  Alan O'Callaghan [ctb, cre],
+  Yun Peng [ctb],
+  Leo Lahti [ctb] (<https://orcid.org/0000-0001-5537-637X>)
 Maintainer: Alan O'Callaghan <alan.ocallaghan@outlook.com>
diff --git a/NAMESPACE b/NAMESPACE
index d84ec80..876f798 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -161,8 +161,9 @@ importFrom(SummarizedExperiment,rowData)
 importFrom(beachmat,realizeFileBackedMatrix)
 importFrom(beachmat,rowBlockApply)
 importFrom(ggbeeswarm,geom_quasirandom)
+importFrom(ggplot2,.data)
 importFrom(ggplot2,aes)
-importFrom(ggplot2,aes_string)
+importFrom(ggplot2,after_stat)
 importFrom(ggplot2,coord_cartesian)
 importFrom(ggplot2,coord_flip)
 importFrom(ggplot2,element_blank)
@@ -171,7 +172,9 @@ importFrom(ggplot2,element_rect)
 importFrom(ggplot2,element_text)
 importFrom(ggplot2,facet_grid)
 importFrom(ggplot2,facet_wrap)
+importFrom(ggplot2,geom_bin2d)
 importFrom(ggplot2,geom_boxplot)
+importFrom(ggplot2,geom_hex)
 importFrom(ggplot2,geom_hline)
 importFrom(ggplot2,geom_jitter)
 importFrom(ggplot2,geom_line)
@@ -184,6 +187,7 @@ importFrom(ggplot2,ggplot)
 importFrom(ggplot2,ggtitle)
 importFrom(ggplot2,guide_legend)
 importFrom(ggplot2,guides)
+importFrom(ggplot2,labs)
 importFrom(ggplot2,position_jitter)
 importFrom(ggplot2,scale_colour_gradient)
 importFrom(ggplot2,scale_colour_gradient2)
@@ -196,6 +200,8 @@ importFrom(ggplot2,scale_y_log10)
 importFrom(ggplot2,stat_density)
 importFrom(ggplot2,stat_smooth)
 importFrom(ggplot2,stat_summary)
+importFrom(ggplot2,stat_summary_2d)
+importFrom(ggplot2,stat_summary_hex)
 importFrom(ggplot2,theme)
 importFrom(ggplot2,theme_bw)
 importFrom(ggplot2,theme_classic)
@@ -203,8 +209,6 @@ importFrom(ggplot2,unit)
 importFrom(ggplot2,xlab)
 importFrom(ggplot2,ylab)
 importFrom(ggrepel,geom_text_repel)
-importFrom(grid,grid.draw)
-importFrom(gridExtra,grid.arrange)
 importFrom(methods,as)
 importFrom(rlang,as_label)
 importFrom(scuttle,.bpNotSharedOrUp)
@@ -217,6 +221,7 @@ importFrom(stats,median)
 importFrom(stats,model.matrix)
 importFrom(stats,runif)
 importFrom(utils,head)
+importFrom(utils,modifyList)
 importFrom(viridis,scale_colour_viridis)
 importFrom(viridis,scale_fill_viridis)
 importFrom(viridis,viridis)
diff --git a/R/AllClasses.R b/R/AllClasses.R
index 4d745cd..a9c0e36 100644
--- a/R/AllClasses.R
+++ b/R/AllClasses.R
@@ -2,6 +2,7 @@
 
 ################################################################################
 ### defining the SCESet class
+## #' @inheritParams Biobase ExpressionSet
 
 #' The "Single Cell Expression Set" (SCESet)  class
 #'
@@ -36,7 +37,6 @@
 #'}
 #' @name SCESet
 #' @rdname SCESet
-#' @inheritParams Biobase ExpressionSet
 #' @aliases SCESet-class
 #' @references  Thanks to the Monocle package 
 #' (github.com/cole-trapnell-lab/monocle-release/) for their CellDataSet class, 
diff --git a/R/SingleCellExperiment-methods.R b/R/SingleCellExperiment-methods.R
index 0125aa5..db75377 100644
--- a/R/SingleCellExperiment-methods.R
+++ b/R/SingleCellExperiment-methods.R
@@ -4,20 +4,20 @@
 ################################################################################
 ### accessors
 
-GET_FUN <- function(exprs_values) {
-    (exprs_values) # To get evaluated.
+GET_FUN <- function(assay.type) {
+    (assay.type) # To get evaluated.
     function(object) {
-        if (exprs_values %in% assayNames(object))
-            return(assay(object, i = exprs_values))
+        if (assay.type %in% assayNames(object))
+            return(assay(object, i = assay.type))
         else
             return(NULL)
     }
 }
 
-SET_FUN <- function(exprs_values) {
-    (exprs_values) # To get evaluated.
+SET_FUN <- function(assay.type) {
+    (assay.type) # To get evaluated.
     function(object, value) {
-        assay(object, i = exprs_values) <- value
+        assay(object, i = assay.type) <- value
         object
     }
 }
@@ -252,9 +252,9 @@ setReplaceMethod("bootstraps", c("SingleCellExperiment", "array"),
 #'     featureControlInfo(merged_sceset) <- featureControlInfo(x)[!discard,]
 #'     
 #'     ## add remaining assayData to merged SCESet
-#'     assay_names <- intersect(names(Biobase::assayData(x)),
+#'     assay.types <- intersect(names(Biobase::assayData(x)),
 #'                              names(Biobase::assayData(y)))
-#'     for (assaydat in assay_names) {
+#'     for (assaydat in assay.types) {
 #'         new_dat <- Biobase::combine(get_exprs(x, assaydat), get_exprs(y, assaydat))
 #'         set_exprs(merged_sceset, assaydat) <- new_dat
 #'     }
diff --git a/R/defunct.R b/R/defunct.R
index 512ef7f..7b25d93 100644
--- a/R/defunct.R
+++ b/R/defunct.R
@@ -63,3 +63,8 @@ runDiffusionMap <- function(...) {
     .Defunct()
 }
 
+#' @export
+#' @rdname defunct
+multiplot <- function(...) {
+    .Defunct("scater::multiplot is defunct.\nUse 'gridExtra::grid.arrange' instead")
+}
diff --git a/R/getExplanatoryPCs.R b/R/getExplanatoryPCs.R
index eb2012a..bf25929 100644
--- a/R/getExplanatoryPCs.R
+++ b/R/getExplanatoryPCs.R
@@ -44,4 +44,5 @@ getExplanatoryPCs <- function(x, dimred="PCA", n_dimred=10, ...) {
     # Using getVarianceExplained to handle variable selection.
     dummy <- SingleCellExperiment(list(pc_space=t(reddims)), colData=colData(x))
     getVarianceExplained(dummy, exprs_values="pc_space", ...)
+    
 }
diff --git a/R/getVarianceExplained.R b/R/getVarianceExplained.R
index 4a673aa..7ae9466 100644
--- a/R/getVarianceExplained.R
+++ b/R/getVarianceExplained.R
@@ -5,7 +5,8 @@
 #' @param x A numeric matrix of expression values, usually log-transformed and normalized.
 #' 
 #' Alternatively, a \linkS4class{SummarizedExperiment} containing such a matrix.
-#' @param exprs_values String or integer scalar specifying the expression values for which to compute the variance.
+#' @param assay.type String or integer scalar specifying the expression values for which to compute the variance (also an alias \code{exprs_value} is accepted).
+#' @param exprs_values Alias for \code{assay.type}.
 #' @param variables A \linkS4class{DataFrame} or data.frame containing one or more variables of interest.
 #' This should have number of rows equal to the number of columns in \code{x}.
 #'
@@ -105,12 +106,12 @@ setMethod("getVarianceExplained", "ANY", .get_variance_explained)
 #' @rdname getVarianceExplained
 #' @importFrom SummarizedExperiment colData assay
 #' @importClassesFrom SummarizedExperiment SummarizedExperiment
-setMethod("getVarianceExplained", "SummarizedExperiment", function(x, variables=NULL, ..., exprs_values="logcounts")
+setMethod("getVarianceExplained", "SummarizedExperiment", function(x, variables=NULL, ..., exprs_values="logcounts", assay.type=exprs_values)
 {
     if (is.null(variables)) {
         variables <- colData(x)
     } else if (is.character(variables)) {
         variables <- colData(x)[,variables,drop=FALSE]
     }
-    .get_variance_explained(assay(x, exprs_values), variables=variables, ...)
+    .get_variance_explained(assay(x, assay.type), variables=variables, ...)
 })
diff --git a/R/ggsce.R b/R/ggsce.R
index 84069c9..398aaca 100644
--- a/R/ggsce.R
+++ b/R/ggsce.R
@@ -1,10 +1,11 @@
-4#' Create a ggplot from a SingleCellExperiment
+#' Create a ggplot from a SingleCellExperiment
 #'
 #' Create a base \link{ggplot} object from a \linkS4class{SingleCellExperiment},
 #' the contents of which can be directly referenced in subsequent layers without prior specification.
 #'
 #' @param x A \linkS4class{SingleCellExperiment} object.
 #' This is expected to have row names for \code{ggcells} and column names for \code{ggfeatures}.
+#' @param assay.type String or integer scalar specifying the expression values for which to compute the variance (also an alias \code{exprs_value} is accepted).
 #' @param mapping A list containing aesthetic mappings, usually the output of \code{\link{aes}} or related functions.
 #' @inheritParams scuttle::makePerCellDF
 #' @inheritParams scuttle::makePerFeatureDF
@@ -54,10 +55,10 @@
 #' @rdname ggsce
 ggcells <- function(x, mapping=aes(), features=NULL, exprs_values="logcounts", 
     use_dimred=TRUE, use_altexps=FALSE, prefix_altexps=FALSE, check_names=TRUE, 
-    extract_mapping=TRUE, ...) 
+    extract_mapping=TRUE, assay.type=exprs_values, ...) 
 {
     features <- c(features, .aes_in_use(mapping, extract_mapping)) 
-    df <- makePerCellDF(x, features=features, exprs_values=exprs_values, use_altexps=use_altexps, 
+    df <- makePerCellDF(x, features=features, exprs_values=assay.type, use_altexps=use_altexps, 
         use_dimred=use_dimred, prefix_altexps=prefix_altexps, check_names=check_names)
     ggplot(df, mapping=mapping, ...)
 }
@@ -65,10 +66,10 @@ ggcells <- function(x, mapping=aes(), features=NULL, exprs_values="logcounts",
 #' @export
 #' @rdname ggsce
 ggfeatures <- function(x, mapping=aes(), cells=NULL, exprs_values="logcounts", 
-    check_names=TRUE, extract_mapping=TRUE, ...)
+    check_names=TRUE, extract_mapping=TRUE, assay.type=exprs_values, ...)
 {
     cells <- c(cells, .aes_in_use(mapping, extract_mapping))
-    df <- makePerFeatureDF(x, cells=cells, exprs_values=exprs_values, check_names=check_names)
+    df <- makePerFeatureDF(x, cells=cells, exprs_values=assay.type, check_names=check_names)
     ggplot(df, mapping=mapping, ...)
 }
 
diff --git a/R/multiplot.R b/R/multiplot.R
deleted file mode 100644
index fe31f91..0000000
--- a/R/multiplot.R
+++ /dev/null
@@ -1,88 +0,0 @@
-#' Multiple plot function for ggplot2 plots
-#'
-#' Place multiple \code{\link[ggplot2]{ggplot}} plots on one page.
-#' This function is deprecated in favour of \code{\link{grid.arrange}}.
-#' It will be defunct in the next release.
-#'
-#' @param ... One or more ggplot objects.
-#' @param plotlist A list of ggplot objects, as an alternative to \code{...}.
-#' @param cols A numeric scalar giving the number of columns in the layout.
-#' @param layout A matrix specifying the layout. 
-#' If present, \code{cols} is ignored.
-#'
-#' @details 
-#' 
-#' If the layout is something like  \code{matrix(c(1,2,3,3), nrow=2, byrow=TRUE)}, then:
-#' \itemize{
-#' \item plot 1 will go in the upper left;
-#' \item plot 2 will go in the upper right;
-#' \item and plot 3 will go all the way across the bottom.
-#' }
-#' There is no way to tweak the relative heights or widths of the plots with this simple function. 
-#' It was adapted from \url{http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2)/}
-#'
-#' @return A ggplot object if one plot is supplied, otherwise an object of class
-#' "gtable" returned by \code{\link{grid.arrange}}.
-#'
-#' @importFrom gridExtra grid.arrange
-#' @importFrom grid grid.draw
-#' @export
-#' @examples
-#' library(ggplot2)
-#'
-#' ## This example uses the ChickWeight dataset, which comes with ggplot2
-#' ## First plot
-#' p1 <- ggplot(ChickWeight, aes(x = Time, y = weight, colour = Diet, group = Chick)) +
-#'    geom_line() +
-#'    ggtitle("Growth curve for individual chicks")
-#"
-#' ## Second plot
-#' p2 <- ggplot(ChickWeight, aes(x = Time, y = weight, colour = Diet)) +
-#'    geom_point(alpha = .3) +
-#'    geom_smooth(alpha = .2, size = 1) +
-#'    ggtitle("Fitted growth curve per diet")
-#'
-#' ## Third plot
-#' p3 <- ggplot(subset(ChickWeight, Time == 21), aes(x = weight, colour = Diet)) +
-#'    geom_density() +
-#'    ggtitle("Final weight, by diet")
-#"
-#' ## Fourth plot
-#' p4 <- ggplot(subset(ChickWeight, Time == 21), aes(x = weight, fill = Diet)) +
-#'     geom_histogram(colour = "black", binwidth = 50) +
-#'    facet_grid(Diet ~ .) +
-#'    ggtitle("Final weight, by diet") +
-#'    theme(legend.position = "none")        # No legend (redundant in this graph)
-#'
-#' \dontrun{
-#'   ## Combine plots and display
-#'   multiplot(p1, p2, p3, p4, cols = 2)
-#'   g <- multiplot(p1, p2, p3, p4, cols = 2)
-#'   grid::grid.draw(g)
-#' }
-#'
-multiplot <- function(..., plotlist = NULL, cols = 1, layout = NULL) {
-    ## a wrapper for grid.arrange is a bit pointless and there already exist
-    ## more comprehensive solutions (cowplot, egg, patchwork)
-    .Deprecated("gridExtra::grid.arrange")
-
-    ## Make a list from the ... arguments and plotlist
-    plots <- c(list(...), plotlist)
-
-    num_plots <- length(plots)
-
-    ## If layout is NULL, then use 'cols' to determine layout
-    if (is.null(layout)) {
-        ## Make the panel
-        ## ncol: Number of columns of plots
-        ## nrow: Number of rows needed, calculated from # of cols
-        layout <- matrix(seq(1, cols * ceiling(num_plots / cols)),
-                         ncol = cols, nrow = ceiling(num_plots / cols))
-    }
-
-    if (num_plots == 1) {
-        print(plots[[1]])
-    } else {
-        gridExtra::grid.arrange(grobs = plots, layout_matrix = layout)
-    }
-}
diff --git a/R/nexprs.R b/R/nexprs.R
index 4bf38c1..ca3b7fd 100644
--- a/R/nexprs.R
+++ b/R/nexprs.R
@@ -6,7 +6,8 @@
 #'
 #' Alternatively, a \linkS4class{SummarizedExperiment} containing such counts.
 #' @param detection_limit Numeric scalar providing the value above which  observations are deemed to be expressed. 
-#' @param exprs_values String or integer specifying the assay of \code{x} to obtain the count matrix from.
+#' @param assay.type String or integer specifying the assay of \code{x} to obtain the count matrix from (also the alias \code{exprs_values} is accepted for this argument).
+#' @param exprs_values Alias for \code{assay.type}.
 #' @param byrow Logical scalar indicating whether to count the number of detected cells per feature.
 #' If \code{FALSE}, the function will count the number of detected features per cell.
 #' @param subset_row Logical, integer or character vector indicating which rows (i.e. features) to use.
@@ -70,6 +71,6 @@ setMethod("nexprs", "ANY", .nexprs)
 #' @rdname nexprs
 #' @importFrom SummarizedExperiment assay
 #' @importClassesFrom SummarizedExperiment SummarizedExperiment
-setMethod("nexprs", "SummarizedExperiment", function(x, ..., exprs_values="counts") {
-    .nexprs(assay(x, exprs_values), ...)    
+setMethod("nexprs", "SummarizedExperiment", function(x, ..., exprs_values="counts", assay.type=exprs_values) {
+    .nexprs(assay(x, assay.type), ...)    
 })
diff --git a/R/plotColData.R b/R/plotColData.R
index 5fd2a09..c929b45 100644
--- a/R/plotColData.R
+++ b/R/plotColData.R
@@ -2,32 +2,60 @@
 #'
 #' Plot column-level (i.e., cell) metadata in an SingleCellExperiment object.
 #'
-#' @param object A \linkS4class{SingleCellExperiment} object containing expression values and experimental information.
-#' @param y String specifying the column-level metadata field to show on the y-axis.
-#' Alternatively, an \link{AsIs} vector or data.frame, see \code{?\link{retrieveCellInfo}}.
+#' @param object A \linkS4class{SingleCellExperiment} object containing
+#'   expression values and experimental information.
+#' @param y String specifying the column-level metadata field to show on the
+#'   y-axis. Alternatively, an \link{AsIs} vector or data.frame, see
+#'   \code{?\link{retrieveCellInfo}}.
 #' @param x String specifying the column-level metadata to show on the x-axis.
 #' Alternatively, an \link{AsIs} vector or data.frame, see \code{?\link{retrieveCellInfo}}.
 #' If \code{NULL}, nothing is shown on the x-axis.
-#' @param colour_by Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param shape_by Specification of a column metadata field or a feature to shape by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param size_by Specification of a column metadata field or a feature to size by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param order_by Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, 
-#' for use in point aesthetics - see \code{?\link{retrieveCellInfo}} for details.
+#' @param colour_by Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param shape_by Specification of a column metadata field or a feature to shape by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param size_by Specification of a column metadata field or a feature to size by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param order_by Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from,
+#' for use in point aesthetics - see \code{?\link{retrieveCellInfo}} for
+#' details (also alias \code{by_exprs_values} is accepted for this argument).
+#' @param by_exprs_values Alias for \code{by.assay.type}.
 #' @param other_fields Additional cell-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.
-#' @param swap_rownames Column name of \code{rowData(object)} to be used to 
-#'  identify features instead of \code{rownames(object)} when labelling plot 
-#'  elements.
+#' @param swap_rownames Column name of \code{rowData(object)} to be used to
+#'   identify features instead of \code{rownames(object)} when labelling plot
+#'   elements.
 #' @param color_by Alias to \code{colour_by}.
-#' @param ... Additional arguments for visualization, see \code{?"\link{scater-plot-args}"} for details.
+#' @param point_fun Function used to create a geom that shows individual cells.
+#'   Should take \code{...} args and return a ggplot2 geom. For example,
+#'   \code{point_fun=function(...) geom_quasirandom(...)}.
+#' @param scattermore Logical, whether to use the \code{scattermore} package to
+#'   greatly speed up plotting a large number of cells. Use \code{point_size =
+#'   0} for the most performance gain.
+#' @param bins Number of bins, can be different in x and y, to bin and summarize
+#'   the points and their values, to avoid overplotting. If \code{NULL}
+#'   (default), then the points are plotted without binning. Only used when both
+#'   x and y are numeric.
+#' @param summary_fun Function to summarize the feature value of each point
+#'   (e.g. gene expression of each cell) when the points binned, defaults to
+#'   \code{sum}. Can be either the name of the function or the function itself.
+#' @param hex Logical, whether to use \code{\link{geom_hex}}. Note that
+#'   \code{geom_hex} is broken in \code{ggplot2} version 3.4.0.
+#' @param ... Additional arguments for visualization, see
+#'   \code{?"\link{scater-plot-args}"} for details.
 #'
-#' @details 
-#' If \code{y} is continuous and \code{x=NULL}, a violin plot is generated.
-#' If \code{x} is categorical, a grouped violin plot will be generated, with one violin for each level of \code{x}.
-#' If \code{x} is continuous, a scatter plot will be generated.
+#' @details If \code{y} is continuous and \code{x=NULL}, a violin plot is
+#'   generated. If \code{x} is categorical, a grouped violin plot will be
+#'   generated, with one violin for each level of \code{x}. If \code{x} is
+#'   continuous, a scatter plot will be generated.
 #'
-#' If \code{y} is categorical and \code{x} is continuous, horizontal violin plots will be generated.
-#' If \code{x} is missing or categorical, rectangule plots will be generated where the area of a rectangle is proportional to the number of points for a combination of factors.
+#'   If \code{y} is categorical and \code{x} is continuous, horizontal violin
+#'   plots will be generated. If \code{x} is missing or categorical, rectangule
+#'   plots will be generated where the area of a rectangle is proportional to
+#'   the number of points for a combination of factors.
+#' @note Arguments \code{shape_by} and \code{size_by} are ignored when
+#' \code{scattermore = TRUE}. Using \code{scattermore} is only recommended for
+#' very large datasets to speed up plotting. Small point size is also
+#' recommended. For larger point size, the point shape may be distorted. Also,
+#' when \code{scattermore = TRUE}, the \code{point_size} argument works
+#' differently.
 #'
 #' @return A \link{ggplot} object.
 #'
@@ -36,28 +64,36 @@
 #' @examples
 #' example_sce <- mockSCE()
 #' example_sce <- logNormCounts(example_sce)
-#' colData(example_sce) <- cbind(colData(example_sce), 
+#' colData(example_sce) <- cbind(colData(example_sce),
 #'     perCellQCMetrics(example_sce))
 #'
-#' plotColData(example_sce, y = "detected", x = "sum", 
+#' plotColData(example_sce, y = "detected", x = "sum",
 #'    colour_by = "Mutation_Status") + scale_x_log10()
 #'
-#' plotColData(example_sce, y = "detected", x = "sum", 
-#'    colour_by = "Mutation_Status", size_by = "Gene_0001", 
+#' plotColData(example_sce, y = "detected", x = "sum",
+#'    colour_by = "Mutation_Status", size_by = "Gene_0001",
 #'    shape_by = "Treatment") + scale_x_log10()
 #'
-#' plotColData(example_sce, y = "Treatment", x = "sum", 
+#' plotColData(example_sce, y = "Treatment", x = "sum",
 #'    colour_by = "Mutation_Status") + scale_y_log10() # flipped violin.
 #'
-#' plotColData(example_sce, y = "detected", 
+#' plotColData(example_sce, y = "detected",
 #'    x = "Cell_Cycle", colour_by = "Mutation_Status")
-#'
+#' # With scattermore
+#' plotColData(example_sce, x = "sum", y = "detected", scattermore = TRUE,
+#'    point_size = 2)
+#' # Bin to show point density
+#' plotColData(example_sce, x = "sum", y = "detected", bins = 10)
+#' # Bin to summarize value (default is sum)
+#' plotColData(example_sce, x = "sum", y = "detected", bins = 10, colour_by = "total")
 #' @export
-plotColData <- function(object, y, x = NULL, 
+plotColData <- function(object, y, x = NULL,
     colour_by = color_by, shape_by = NULL, size_by = NULL, order_by = NULL,
-    by_exprs_values = "logcounts", other_fields=list(),
-    swap_rownames = NULL, color_by = NULL, ...)
-{
+    by_exprs_values = "logcounts", other_fields = list(),
+    swap_rownames = NULL, color_by = NULL, point_fun = NULL,
+    scattermore = FALSE,
+    bins = NULL, summary_fun = "sum", hex = FALSE,
+    by.assay.type=by_exprs_values, ...) {
     if (!is(object, "SingleCellExperiment")) {
         stop("object must be an SingleCellExperiment object.")
     }
@@ -68,7 +104,7 @@ plotColData <- function(object, y, x = NULL,
     if (is.null(y_lab)) {
         stop(sprintf("could not find '%s' in 'colData(object)'", y))
     }
-    df_to_plot <- data.frame(Y=y_by_out$val)
+    df_to_plot <- data.frame(Y = y_by_out$value)
 
     if (!is.null(x)) {
         x_by_out <- retrieveCellInfo(object, x, search = "colData")
@@ -76,16 +112,16 @@ plotColData <- function(object, y, x = NULL,
         if (is.null(x_lab)) {
             stop(sprintf("could not find '%s' in 'rowData(object)'", x))
         }
-        df_to_plot$X <- x_by_out$val
+        df_to_plot$X <- x_by_out$value
     } else {
         x_lab <- NULL
         df_to_plot$X <- factor(character(ncol(object)))
     }
 
     ## checking visualization arguments
-    vis_out <- .incorporate_common_vis_col(df_to_plot, se = object, 
-        colour_by = colour_by, shape_by = shape_by, size_by = size_by, 
-        by_exprs_values = by_exprs_values, other_fields = other_fields,
+    vis_out <- .incorporate_common_vis_col(df_to_plot, se = object,
+        colour_by = colour_by, shape_by = shape_by, size_by = size_by,
+        by.assay.type = by.assay.type, other_fields = other_fields,
         order_by = order_by,
         swap_rownames = swap_rownames)
 
@@ -96,6 +132,8 @@ plotColData <- function(object, y, x = NULL,
 
     # Creating the plot object:
     .central_plotter(df_to_plot, xlab = x_lab, ylab = y_lab,
-        colour_by = colour_by, size_by = size_by, shape_by = shape_by, 
-        ..., point_FUN=NULL)
+        colour_by = colour_by, size_by = size_by, shape_by = shape_by,
+        scattermore = scattermore,
+        bins = bins, summary_fun = summary_fun, hex = hex,
+        ..., point_FUN = point_fun)
 }
diff --git a/R/plotDots.R b/R/plotDots.R
index c699465..b57ffa2 100644
--- a/R/plotDots.R
+++ b/R/plotDots.R
@@ -10,8 +10,8 @@
 #' detected expression values.
 #' @param other_fields Additional feature-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.
 #' Note that any \link{AsIs} vectors or data.frames must be of length equal to \code{nrow(object)}, not \code{features}.
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, for entries of \code{other_fields}. 
-#' @param low_colour,high_colour,max_ave Deprecated arguments.
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from, for entries of \code{other_fields}. Also alias \code{by_exprs_values} is accepted as argument name.
+#' @param by_exprs_values Alias for \code{by.assay.type}.
 #'
 #' @return 
 #' A \link{ggplot} object containing a dot plot.
@@ -19,8 +19,8 @@
 #' @details
 #' This implements a \pkg{Seurat}-style \dQuote{dot plot} that creates a dot for each feature (row) in each group of cells (column).
 #' The proportion of detected expression values and the average expression for each feature in each group of cells is visualized efficiently using the size and colour, respectively, of each dot.
-#' If \code{block} is specified, batch-corrected averages for each group are computed with \code{\link{correctGroupSummary}}.
-#' 
+#' If \code{block} is specified, batch-corrected averages and proportions for each group are computed with \code{\link{correctGroupSummary}}.
+#'
 #' Some caution is required during interpretation due to the difficulty of simultaneously interpreting both size and colour.
 #' For example, if we coloured by z-score on a conventional blue-white-red colour axis, a gene that is downregulated in a group of cells would show up as a small blue dot.
 #' If the background colour was also white, this could be easily mistaken for a gene that is not downregulated at all.
@@ -48,7 +48,7 @@
 #' for alternatives to visualizing group-level expression values.
 #'
 #' @export
-#' @importFrom ggplot2 ggplot aes_string geom_point
+#' @importFrom ggplot2 ggplot geom_point
 #' scale_size scale_colour_gradient theme element_line element_rect 
 #' scale_colour_gradient2
 #' @importFrom SummarizedExperiment assay
@@ -56,21 +56,14 @@
 plotDots <- function(object, features, group = NULL, block=NULL,
     exprs_values = "logcounts", detection_limit = 0, zlim = NULL, 
     colour = color, color = NULL,
-    max_detected = NULL, other_fields = list(), by_exprs_values = exprs_values,
-    swap_rownames = NULL, center = FALSE, scale = FALSE,
-    low_colour = NULL, high_colour = NULL, max_ave = NULL)
+    max_detected = NULL, other_fields = list(),
+    by_exprs_values = exprs_values,
+    swap_rownames = NULL,
+    center = FALSE,
+    scale = FALSE,
+    assay.type=exprs_values,
+    by.assay.type=by_exprs_values)
 {
-    # Handling all the deprecation.
-    if (!is.null(low_colour)) {
-        .Deprecated(msg="'low_colour=' is deprecated, use 'colour=' instead")
-    }
-    if (!is.null(high_colour)) {
-        .Deprecated(msg="'high_colour=' is deprecated, use 'colour=' instead")
-    }
-    if (!is.null(max_ave)) {
-        .Deprecated(msg="'max_ave=' is deprecated, use 'zlim=' instead")
-        zlim <- c(detection_limit, max_ave)
-    }
 
     if (is.null(group)) {
         group <- rep("all", ncol(object))
@@ -89,7 +82,7 @@ plotDots <- function(object, features, group = NULL, block=NULL,
     }
 
     summarized <- summarizeAssayByGroup(
-        assay(object, exprs_values)[as.character(features), , drop = FALSE],
+        assay(object, assay.type)[as.character(features), , drop = FALSE],
         ids=ids, statistics=c("mean", "prop.detected"),
         threshold=detection_limit)
     
@@ -100,7 +93,7 @@ plotDots <- function(object, features, group = NULL, block=NULL,
     if (!is.null(block)) {
         ave <- correctGroupSummary(ave, group=summarized$group, block=summarized$block)
         num <- correctGroupSummary(num, group=summarized$group, block=summarized$block, transform="logit")
-        group.names <- colnames(ave)
+        group.names <- factor(colnames(ave), levels = levels(summarized$group))
     }
     heatmap_scale <- .heatmap_scale(ave, center=center, scale=scale, colour=colour, zlim=zlim)
 
@@ -116,17 +109,17 @@ plotDots <- function(object, features, group = NULL, block=NULL,
     }
 
     # Adding other fields, if requested.
-    vis_out <- .incorporate_common_vis_row(evals_long, se = object, 
+    vis_out <- .incorporate_common_vis_row(evals_long, se = object,
         colour_by = NULL, shape_by = NULL, size_by = NULL, 
-        by_exprs_values = by_exprs_values, other_fields = other_fields,
+        by.assay.type = by.assay.type, other_fields = other_fields,
         multiplier = rep(.subset2index(features, object), ncol(num)))
     evals_long <- vis_out$df
     ggplot(evals_long) + 
-        geom_point(aes_string(x="Group", y="Feature", size="NumDetected", col="Average")) +
-        scale_size(limits=c(0, max(evals_long$NumDetected))) + 
+        geom_point(aes(x=.data$Group, y=.data$Feature, size=.data$NumDetected, col=.data$Average)) +
+        scale_size(limits=c(0, max(evals_long$NumDetected))) +
         heatmap_scale$colour_scale +
         theme(
             panel.background = element_rect(fill = "white"),
-            panel.grid.major = element_line(size=0.5, colour = "grey80"),
-            panel.grid.minor = element_line(size=0.25, colour = "grey80"))
+            panel.grid.major = element_line(linewidth=0.5, colour = "grey80"),
+            panel.grid.minor = element_line(linewidth=0.25, colour = "grey80"))
 }
diff --git a/R/plotExplanatoryPCs.R b/R/plotExplanatoryPCs.R
index 42cf01d..cdb98a7 100644
--- a/R/plotExplanatoryPCs.R
+++ b/R/plotExplanatoryPCs.R
@@ -50,15 +50,15 @@ plotExplanatoryPCs <- function(object, nvars_to_plot = 10, npcs_to_plot=50, them
         Pct_Var_Explained=as.numeric(chosen_rsquared) * 100 # column major collapse.
     )
     
-    plot_out <- ggplot(df_to_plot, aes_string(x = "PC", y = "Pct_Var_Explained", colour = "Expl_Var")) +
+    plot_out <- ggplot(df_to_plot, aes(x = .data$PC, y = .data$"Pct_Var_Explained", colour = .data$"Expl_Var")) +
         geom_point(alpha= 1, shape = 16, size = 3) +
-        geom_line(alpha = 0.7, size = 2) +
+        geom_line(alpha = 0.7, linewidth = 2) +
         scale_y_log10(breaks = 10 ^ (-3:2), labels = c(0.001, 0.01, 0.1, 1, 10, 100)) +
         xlab("PC") +
         ylab("% variance explained") +
         coord_cartesian(ylim = c(10 ^ (-3), 100))
 
-    plot_out <- .resolve_plot_colours(plot_out, df_to_plot$Expl_Var, "")
+    plot_out <- .resolve_plot_colours(plot_out, df_to_plot$Expl_Var, "", fill = FALSE, colour = TRUE)
 
     if ( requireNamespace("cowplot", quietly = TRUE) ) {
         plot_out <- plot_out + cowplot::theme_cowplot(theme_size)
diff --git a/R/plotExplanatoryVariables.R b/R/plotExplanatoryVariables.R
index 8cd3080..ae9dd5a 100644
--- a/R/plotExplanatoryVariables.R
+++ b/R/plotExplanatoryVariables.R
@@ -48,15 +48,15 @@ plotExplanatoryVariables <- function(object, nvars_to_plot = 10, min_marginal_r2
         Pct_Var_Explained=as.numeric(chosen_rsquared) # column major collapse.
     )
 
-    plot_out <- ggplot(df_to_plot, aes_string(x = "Pct_Var_Explained", colour = "Expl_Var")) +
-        geom_line(stat = "density", alpha = 0.7, size = 2) +
+    plot_out <- ggplot(df_to_plot, aes(x = .data$Pct_Var_Explained, colour = .data$Expl_Var)) +
+        geom_line(stat = "density", alpha = 0.7, linewidth = 2) +
         geom_vline(xintercept = 1, linetype = 2) +
         scale_x_log10(breaks = 10 ^ (-3:2), labels = c(0.001, 0.01, 0.1, 1, 10, 100)) +
         xlab("% variance explained") +
         ylab("Density") +
         coord_cartesian(xlim = c(10 ^ (-3), 100))
 
-    plot_out <- .resolve_plot_colours(plot_out, df_to_plot$Expl_Var, "")
+    plot_out <- .resolve_plot_colours(plot_out, df_to_plot$Expl_Var, "", colour = TRUE)
 
     if ( requireNamespace("cowplot", quietly = TRUE) ) {
         plot_out <- plot_out + cowplot::theme_cowplot(theme_size)
diff --git a/R/plotExpression.R b/R/plotExpression.R
index dd394de..a2f3a8c 100644
--- a/R/plotExpression.R
+++ b/R/plotExpression.R
@@ -2,18 +2,19 @@
 #'
 #' Plot expression values for a set of features (e.g. genes or transcripts) in a SingleExperiment object, against a continuous or categorical covariate for all cells.
 #'
+#' @inheritParams plotColData
 #' @param object A SingleCellExperiment object containing expression values and other metadata.
 #' @param features A character vector or a list specifying the features to plot.
 #' If a list is supplied, each entry of the list can be a string, an AsIs-wrapped vector or a data.frame - see \code{?\link{retrieveCellInfo}}.
-#' @param x Specification of a column metadata field or a feature to show on the x-axis, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param exprs_values A string or integer scalar specifying which assay in \code{assays(object)} to obtain expression values from.
+#' @param x Specification of a column metadata field or a feature to show on the x-axis, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param assay.type A string or integer scalar specifying which assay in \code{assays(object)} to obtain expression values from. Also the alias \code{assay.type} is accepted.
 #' @param log2_values Logical scalar, specifying whether the expression values be transformed to the log2-scale for plotting (with an offset of 1 to avoid logging zeroes).
-#' @param colour_by Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param shape_by Specification of a column metadata field or a feature to shape by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param size_by Specification of a column metadata field or a feature to size by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param order_by Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, 
-#' for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.
+#' @param colour_by Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param shape_by Specification of a column metadata field or a feature to shape by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param size_by Specification of a column metadata field or a feature to size by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param order_by Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from,
+#' for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}. Also the alias \code{by.assay.type} is accepted.
 #' @param xlab String specifying the label for x-axis.
 #' If \code{NULL} (default), \code{x} will be used as the x-axis label.
 #' @param feature_colours Logical scalar indicating whether violins should be coloured by feature when \code{x} and \code{colour_by} are not specified and \code{one_facet=TRUE}.
@@ -23,14 +24,17 @@
 #' @param scales String indicating whether should multi-facet scales be fixed (\code{"fixed"}), free (\code{"free"}), or free in one dimension (\code{"free_x"}, \code{"free_y"}).
 #' Passed to the \code{scales} argument in the \code{\link[ggplot2]{facet_wrap}} when multiple facets are generated.
 #' @param other_fields Additional cell-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.
-#' @param swap_rownames Column name of \code{rowData(object)} to be used to 
-#'  identify features instead of \code{rownames(object)} when labelling plot 
+#' @param swap_rownames Column name of \code{rowData(object)} to be used to
+#'  identify features instead of \code{rownames(object)} when labelling plot
 #'  elements.
 #' @param color_by Alias to \code{colour_by}.
 #' @param feature_colors Alias to \code{feature_colours}.
+#' @param point_fun Function used to create a geom that shows individual cells. Should take \code{...} args and return a ggplot2 geom. For example, \code{point_fun=function(...) geom_quasirandom(...)}.
+#' @param exprs_values Alias to \code{assay.type}.
+#' @param by_exprs_values Alias to \code{by.assay.type}.
 #' @param ... Additional arguments for visualization, see \code{?"\link{scater-plot-args}"} for details.
 #'
-#' @details 
+#' @details
 #' This function plots expression values for one or more features.
 #' If \code{x} is not specified, a violin plot will be generated of expression values.
 #' If \code{x} is categorical, a grouped violin plot will be generated, with one violin for each level of \code{x}.
@@ -55,6 +59,13 @@
 #' @importFrom SummarizedExperiment assay assayNames
 #' @importClassesFrom SingleCellExperiment SingleCellExperiment
 #'
+#' @note Arguments \code{shape_by} and \code{size_by} are ignored when
+#' \code{scattermore = TRUE}. Using \code{scattermore} is only recommended for
+#' very large datasets to speed up plotting. Small point size is also
+#' recommended. For larger point size, the point shape may be distorted. Also,
+#' when \code{scattermore = TRUE}, the \code{point_size} argument works
+#' differently.
+#'
 #' @export
 #'
 #' @examples
@@ -65,44 +76,58 @@
 #' plotExpression(example_sce, rownames(example_sce)[1:15])
 #'
 #' ## plot expression against an x-axis value
-#' plotExpression(example_sce, c("Gene_0001", "Gene_0004"), 
+#' plotExpression(example_sce, c("Gene_0001", "Gene_0004"),
 #'     x="Mutation_Status")
-#' plotExpression(example_sce, c("Gene_0001", "Gene_0004"), 
+#' plotExpression(example_sce, c("Gene_0001", "Gene_0004"),
 #'     x="Gene_0002")
 #'
 #' ## add visual options
-#' plotExpression(example_sce, rownames(example_sce)[1:6], 
+#' plotExpression(example_sce, rownames(example_sce)[1:6],
 #'     colour_by = "Mutation_Status")
-#' plotExpression(example_sce, rownames(example_sce)[1:6], 
-#'     colour_by = "Mutation_Status", shape_by = "Treatment", 
+#' plotExpression(example_sce, rownames(example_sce)[1:6],
+#'     colour_by = "Mutation_Status", shape_by = "Treatment",
 #'     size_by = "Gene_0010")
 #'
 #' ## plot expression against expression values for Gene_0004
 #' plotExpression(example_sce, rownames(example_sce)[1:4],
 #'     "Gene_0004", show_smooth = TRUE)
 #'
+#' # Use scattermore
+#' plotExpression(example_sce, "Gene_0001", x = "Gene_0100", scattermore = TRUE,
+#'     point_size = 2)
+#' # Bin to show point density
+#' plotExpression(example_sce, "Gene_0001", x = "Gene_0100", bins = 10)
+#' # Bin to summarize values (default is sum but can be changed with summary_fun)
+#' plotExpression(example_sce, "Gene_0001", x = "Gene_0100", bins = 10,
+#'     colour_by = "Gene_0002", summary_fun = "mean")
+#'
 plotExpression <- function(object, features, x = NULL,
     exprs_values = "logcounts", log2_values = FALSE,
     colour_by = color_by, shape_by = NULL, size_by = NULL, order_by = NULL,
-    by_exprs_values = exprs_values, xlab = NULL, 
-    feature_colours = feature_colors, one_facet = TRUE, ncol = 2, 
+    by_exprs_values = exprs_values, xlab = NULL,
+    feature_colours = feature_colors, one_facet = TRUE, ncol = 2,
     scales = "fixed", other_fields = list(),
-    swap_rownames = NULL, 
-    color_by = NULL, feature_colors = TRUE, ...)
+    swap_rownames = NULL,
+    color_by = NULL, feature_colors = TRUE, point_fun = NULL,
+    assay.type=exprs_values,
+    scattermore = FALSE,
+    bins = NULL, summary_fun = "sum", hex = FALSE,
+    by.assay.type=by_exprs_values,
+    ...)
 {
     if (!is(object, "SingleCellExperiment")) {
         stop("object must be an SingleCellExperiment object.")
     }
 
     ## Define features to plot
-    if (exprs_values == "exprs" && !(exprs_values %in% assayNames(object))) {
-        exprs_values <- "logcounts"
+    if (assay.type == "exprs" && !(assay.type %in% assayNames(object))) {
+        assay.type <- "logcounts"
     }
 
     exprs_vals <- vector("list", length(features))
     for (i in seq_along(features)) {
-        current <- retrieveCellInfo(object, features[i], 
-            search = c("assays", "altExps"), exprs_values = exprs_values,
+        current <- retrieveCellInfo(object, features[i],
+            search = c("assays", "altExps"), assay.type = assay.type,
             swap_rownames = swap_rownames)
         features[[i]] <- current$name
         if (is.null(current$value)) {
@@ -114,30 +139,30 @@ plotExpression <- function(object, features, x = NULL,
 
     if (log2_values) {
         exprs_val <- lapply(exprs_vals, function(x) log2(x + 1))
-        ylab <- paste0("Expression (", exprs_values, "; log2-scale)")
+        ylab <- paste0("Expression (", assay.type, "; log2-scale)")
     } else {
-        ylab <- paste0("Expression (", exprs_values, ")")
+        ylab <- paste0("Expression (", assay.type, ")")
     }
 
     ## melt the expression data.
     evals_long <- data.frame(
         Feature=rep(factor(features, features), lengths(exprs_vals)),
-        Y=unlist(exprs_vals) 
+        Y=unlist(exprs_vals)
     )
 
     ## check x-coordinates are valid
-    x_by_out <- retrieveCellInfo(object, x, exprs_values = exprs_values)
-    xcoord <- x_by_out$val
+    x_by_out <- retrieveCellInfo(object, x, assay.type = assay.type)
+    xcoord <- x_by_out$value
     if (is.null(xlab)) {
         xlab <- x_by_out$name
     }
     evals_long$X <- rep(xcoord, nfeatures)
 
     ## checking visualization arguments
-    vis_out <- .incorporate_common_vis_col(evals_long, se = object, 
+    vis_out <- .incorporate_common_vis_col(evals_long, se = object,
         colour_by = colour_by, shape_by = shape_by, size_by = size_by,
         order_by = order_by,
-        by_exprs_values = by_exprs_values, other_fields = other_fields,
+        by.assay.type = by.assay.type, other_fields = other_fields,
         multiplier = rep(seq_len(ncol(object)), nfeatures),
         swap_rownames = swap_rownames)
 
@@ -147,31 +172,35 @@ plotExpression <- function(object, features, x = NULL,
     size_by <- vis_out$size_by
 
     ## Set up the faceting.
-    if ( is.null(evals_long$X) ) { 
+    if (is.null(evals_long$X)) {
         evals_long$X <- evals_long$Feature
-    } else { 
-        one_facet <- FALSE 
+    } else {
+        one_facet <- FALSE
     }
 
     # Setting up feature colours, for aesthetic appeal:
     feature_colours <- (feature_colours && one_facet && is.null(colour_by))
-    if (feature_colours) { 
+    if (feature_colours) {
         evals_long$fill_by <- evals_long$colour_by <- evals_long$Feature
         fill_by <- colour_by <- "Feature"
     } else {
         fill_by <- NULL
-    } 
+    }
 
-    # Creating the plot with faceting.        
-    plot_out <- .central_plotter(evals_long, xlab = xlab, ylab = ylab,
-                                 shape_by = shape_by, colour_by = colour_by, size_by = size_by, fill_by = fill_by,
-                                 ..., point_FUN = NULL)
+    # Creating the plot with faceting.
+    plot_out <- .central_plotter(
+        evals_long, xlab = xlab, ylab = ylab,
+        shape_by = shape_by, colour_by = colour_by, size_by = size_by,
+        fill_by = fill_by, ..., point_FUN = point_fun,
+        scattermore = scattermore, bins = bins, summary_fun = summary_fun,
+        hex = hex
+    )
     if (!one_facet) {
         plot_out <- plot_out + facet_wrap(~Feature, ncol = ncol, scales = scales)
     }
-        
+
     # Do not show x-axis ticks or labels if there is no X.
-    if ( is.null(x) ) { 
+    if (is.null(x)) {
         plot_out <- plot_out + theme(
             axis.text.x = element_text(angle = 60, vjust = 1, hjust = 1),
             axis.ticks.x = element_blank(),
@@ -180,7 +209,7 @@ plotExpression <- function(object, features, x = NULL,
     }
 
     # Destroying colour legend if feature_colours was used.
-    if (feature_colours) { 
+    if (feature_colours) {
         plot_out <- plot_out + guides(fill = "none", colour = "none")
     }
 
diff --git a/R/plotGroupedHeatmap.R b/R/plotGroupedHeatmap.R
index 11c2aa7..49d6015 100644
--- a/R/plotGroupedHeatmap.R
+++ b/R/plotGroupedHeatmap.R
@@ -53,10 +53,9 @@
 #' @importFrom Matrix rowMeans
 #' @importFrom scuttle summarizeAssayByGroup
 plotGroupedHeatmap <- function(object, features, group, block = NULL, 
-    columns=NULL, exprs_values = "logcounts", center = FALSE, scale = FALSE, 
-    zlim = NULL, colour = color, swap_rownames=NULL, symmetric=NULL, 
-    color = NULL, ...) 
-{
+    columns=NULL, exprs_values = "logcounts", center = FALSE, scale = FALSE,
+    zlim = NULL, colour = color, swap_rownames = NULL, color = NULL, assay.type=exprs_values, ...) {
+
     # Setting names, otherwise the downstream colouring fails.
     if (is.null(colnames(object))) { 
         colnames(object) <- seq_len(ncol(object)) 
@@ -66,7 +65,7 @@ plotGroupedHeatmap <- function(object, features, group, block = NULL,
     object <- .swap_rownames(object, swap_rownames)
     # in case of numeric or logical features, converts to character or factor
     features <- .handle_features(features, object)
-    heat.vals <- assay(object, exprs_values)[as.character(features), , drop=FALSE]
+    heat.vals <- assay(object, assay.type)[as.character(features), , drop=FALSE]
     if (is.factor(features)) {
         heat.vals <- heat.vals[levels(features), , drop = FALSE]
     }
@@ -83,9 +82,14 @@ plotGroupedHeatmap <- function(object, features, group, block = NULL,
     if (!is.null(columns)) {
         ids <- ids[columns,,drop=FALSE]
     }
-    heat.se <- summarizeAssayByGroup(heat.vals, ids, statistic="mean")
-    heat.vals <- correctGroupSummary(assay(heat.se), group=heat.se$group, block=heat.se$group)
-    heatmap_scale <- .heatmap_scale(heat.vals, center=center, scale=scale, colour=colour, zlim=zlim, symmetric=symmetric)
+    heat.se <- summarizeAssayByGroup(heat.vals, ids, statistics="mean")
+    if (!is.null(block)) {
+        heat.se <- correctGroupSummary(assay(heat.se), group=heat.se$group, block=heat.se$block)
+    }
+    if (inherits(heat.se, "SummarizedExperiment")) {
+        heat.se <- assay(heat.se)
+    }
+    heatmap_scale <- .heatmap_scale(heat.se, center=center, scale=scale, colour=colour, zlim=zlim)
 
     # Creating the heatmap as specified.
     pheatmap::pheatmap(heatmap_scale$x, color=heatmap_scale$colour, breaks=heatmap_scale$colour_breaks, ...) 
diff --git a/R/plotHeatmap.R b/R/plotHeatmap.R
index 55f10df..112f120 100644
--- a/R/plotHeatmap.R
+++ b/R/plotHeatmap.R
@@ -7,7 +7,7 @@
 #' @param columns A vector specifying the subset of columns in \code{object} to show as columns in the heatmap. 
 #' Also specifies the column order if \code{cluster_cols=FALSE} and \code{order_columns_by=NULL}.
 #' By default, all columns are used.
-#' @param exprs_values A string or integer scalar indicating which assay of \code{object} should be used as expression values. 
+#' @param assay.type A string or integer scalar indicating which assay of \code{object} should be used as expression values. 
 #' @param center A logical scalar indicating whether each feature should have its mean expression centered at zero prior to plotting. 
 #' @param scale A logical scalar specifying whether each feature should have its expression values scaled to have unit variance prior to plotting.
 #' @param zlim A numeric vector of length 2, specifying the upper and lower bounds for colour mapping of expression values.
@@ -36,17 +36,18 @@
 #' Each entry of the list can be any acceptable input to the \code{by} argument in \code{?\link{retrieveCellInfo}}.
 #' A character vector can also be supplied and will be treated as a list of strings.
 #' This argument is automatically appended to \code{colour_columns_by}.
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, 
-#' for colouring of column-level data - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from, 
+#' for colouring of column-level data - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.
 #' @param show_colnames,cluster_cols,... Additional arguments to pass to \code{\link[pheatmap]{pheatmap}}.
 #' @param swap_rownames Column name of \code{rowData(object)} to be used to 
 #'  identify features instead of \code{rownames(object)} when labelling plot 
 #'  elements.
-#' @param symmetric Deprecated and ignored.
 #' @param color,color_columns_by,column_annotation_colors,color_rows_by,row_annotation_colors 
 #' Aliases to \code{color}, \code{color_columns_by},
 #' \code{column_annotation_colors}, \code{color_rows_by}, 
 #' \code{row_annotation_colors}.
+#' @param exprs_values Alias to \code{assay.type}.
+#' @param by_exprs_values Alias to {by.assay.type}.
 #'
 #' @details 
 #' Setting \code{center=TRUE} is useful for examining log-fold changes of each cell's expression profile from the average across all cells.
@@ -84,29 +85,32 @@
 #' @importFrom SummarizedExperiment assay assayNames
 plotHeatmap <- function(object, features, columns = NULL,
     exprs_values = "logcounts", center = FALSE, scale = FALSE, zlim = NULL,
-    colour = color, colour_columns_by = color_columns_by,
+    colour = color, color = NULL,
+    colour_columns_by = color_columns_by,
+    color_columns_by = NULL,
     column_annotation_colours = column_annotation_colors,
-    colour_rows_by = color_rows_by,
-    row_annotation_colours = row_annotation_colors,
-    order_columns_by = NULL, by_exprs_values = exprs_values, 
-    show_colnames = FALSE, cluster_cols = is.null(order_columns_by),
-    swap_rownames = NULL, symmetric=NULL, 
-    color = NULL,
-    color_columns_by = NULL, color_rows_by = NULL, 
     column_annotation_colors = list(),
+    row_annotation_colours = row_annotation_colors,
     row_annotation_colors = list(),
-    ...) 
-{
+    colour_rows_by = color_rows_by,
+    color_rows_by = NULL,
+    order_columns_by = NULL, by_exprs_values = exprs_values,
+    show_colnames = FALSE, cluster_cols = is.null(order_columns_by),
+    swap_rownames = NULL,
+    assay.type=exprs_values,
+    by.assay.type=by_exprs_values,    
+    ...) {
+
     # Setting names, otherwise the downstream colouring fails.
-    if (is.null(colnames(object))) { 
-        colnames(object) <- seq_len(ncol(object)) 
+    if (is.null(colnames(object))) {
+        colnames(object) <- seq_len(ncol(object))
     }
 
     # Pulling out the features. swap_rownames swaps out for alt genenames
     object <- .swap_rownames(object, swap_rownames)
     # in case of numeric or logical features, converts to character or factor
     features <- .handle_features(features, object)
-    heat.vals <- assay(object, exprs_values)[as.character(features), , drop=FALSE]
+    heat.vals <- assay(object, assay.type)[as.character(features), , drop=FALSE]
     if (is.factor(features)) {
         heat.vals <- heat.vals[levels(features), , drop = FALSE]
     }
@@ -118,7 +122,7 @@ plotHeatmap <- function(object, features, columns = NULL,
     if (!is.null(order_columns_by)) {
         ordering <- list()
         for (i in seq_along(order_columns_by)) {
-            vals <- retrieveCellInfo(object, order_columns_by[[i]], exprs_values = by_exprs_values)$val
+            vals <- retrieveCellInfo(object, order_columns_by[[i]], assay.type = by.assay.type)$value
             if (!is.null(columns)) {
                 vals <- vals[columns]
             }
@@ -128,32 +132,32 @@ plotHeatmap <- function(object, features, columns = NULL,
         cluster_cols <- FALSE
         colour_columns_by <- c(colour_columns_by, order_columns_by)
     }
-    heatmap_scale <- .heatmap_scale(heat.vals, center=center, scale=scale, colour=colour, zlim=zlim, symmetric=symmetric)
+    heatmap_scale <- .heatmap_scale(heat.vals, center=center, scale=scale, colour=colour, zlim=zlim)
 
     # Collecting variables to colour_by.
     if (length(colour_columns_by)) {
         column_variables <- list()
 
-        for (i in seq_along(colour_columns_by)) { 
+        for (i in seq_along(colour_columns_by)) {
             field <- colour_columns_by[[i]]
             colour_by_out <- retrieveCellInfo(object, field,
-                exprs_values = by_exprs_values, swap_rownames = swap_rownames)
+                assay.type = by.assay.type, swap_rownames = swap_rownames)
 
-            if (is.null(colour_by_out$val)) { 
+            if (is.null(colour_by_out$value)) {
                 next
-            } else if (is.numeric(colour_by_out$val)) { 
-                colour_fac <- colour_by_out$val
+            } else if (is.numeric(colour_by_out$value)) {
+                colour_fac <- colour_by_out$value
                 col_scale <- viridis(25)
             } else {
-                colour_fac <- as.factor(colour_by_out$val)
+                colour_fac <- as.factor(colour_by_out$value)
 
                 nlevs_colour_by <- nlevels(colour_fac)
                 if (nlevs_colour_by <= 10) {
                     col_scale <- .get_palette("tableau10medium")
                 } else if (nlevs_colour_by > 10 && nlevs_colour_by <= 20) {
-                    col_scale <- .get_palette("tableau20") 
+                    col_scale <- .get_palette("tableau20")
                 } else {
-                    col_scale <- viridis(nlevs_colour_by)                    
+                    col_scale <- viridis(nlevs_colour_by)
                 }
 
                 col_scale <- col_scale[seq_len(nlevs_colour_by)]
@@ -161,7 +165,7 @@ plotHeatmap <- function(object, features, columns = NULL,
             }
 
             col_name <- colour_by_out$name
-            if (col_name=="") {
+            if (col_name == "") {
                 col_name <- paste0("unnamed", i)
             }
             column_variables[[col_name]] <- colour_fac
@@ -173,7 +177,7 @@ plotHeatmap <- function(object, features, columns = NULL,
         # No need to subset for 'columns' or 'order_columns_by',
         # as pheatmap::pheatmap uses the rownames to handle this for us.
         column_variables <- do.call(data.frame,
-            c(column_variables, list(row.names=colnames(object))))
+            c(column_variables, list(row.names = colnames(object))))
         column_annotation_colours <- column_annotation_colours[colnames(column_variables)]
     } else {
         column_variables <- column_annotation_colours <- NULL
@@ -182,26 +186,26 @@ plotHeatmap <- function(object, features, columns = NULL,
     if (length(colour_rows_by)) {
         row_variables <- list()
 
-        for (i in seq_along(colour_rows_by)) { 
+        for (i in seq_along(colour_rows_by)) {
             field <- colour_rows_by[[i]]
             colour_by_out <- retrieveFeatureInfo(object, field,
-                exprs_values = by_exprs_values)
+                assay.type = by.assay.type)
 
-            if (is.null(colour_by_out$val)) { 
+            if (is.null(colour_by_out$value)) {
                 next
-            } else if (is.numeric(colour_by_out$val)) { 
-                colour_fac <- colour_by_out$val
+            } else if (is.numeric(colour_by_out$value)) {
+                colour_fac <- colour_by_out$value
                 col_scale <- viridis(25)
             } else {
-                colour_fac <- as.factor(colour_by_out$val)
+                colour_fac <- as.factor(colour_by_out$value)
 
                 nlevs_colour_by <- nlevels(colour_fac)
                 if (nlevs_colour_by <= 10) {
                     col_scale <- .get_palette("tableau10medium")
                 } else if (nlevs_colour_by > 10 && nlevs_colour_by <= 20) {
-                    col_scale <- .get_palette("tableau20") 
+                    col_scale <- .get_palette("tableau20")
                 } else {
-                    col_scale <- viridis(nlevs_colour_by)                    
+                    col_scale <- viridis(nlevs_colour_by)
                 }
 
                 col_scale <- col_scale[seq_len(nlevs_colour_by)]
@@ -234,21 +238,20 @@ plotHeatmap <- function(object, features, columns = NULL,
     # Creating the heatmap as specified.
     pheatmap::pheatmap(
         heatmap_scale$x,
-        color=heatmap_scale$colour,
-        breaks=heatmap_scale$colour_breaks, 
-        annotation_col=column_variables,
-        annotation_row=row_variables,
-        annotation_colors=annotation_colours,
-        show_colnames=show_colnames,
-        cluster_cols=cluster_cols,
-        ...) 
+        color = heatmap_scale$colour,
+        breaks = heatmap_scale$colour_breaks,
+        annotation_col = column_variables,
+        annotation_row = row_variables,
+        annotation_colors = annotation_colours,
+        show_colnames = show_colnames,
+        cluster_cols = cluster_cols,
+        ...
+    )
 }
 
 #' @importFrom ggplot2 scale_colour_gradientn
 .heatmap_scale <- function(x, center, scale, colour=NULL, zlim=NULL, symmetric=NULL) {
-    if (!is.null(symmetric)) {
-        .Deprecated(msg="'symmetric=' is deprecated and is automatically set to TRUE when 'center=TRUE'")
-    }
+
     if (center) {
         x <- x - rowMeans(x)
     }
diff --git a/R/plotHighestExprs.R b/R/plotHighestExprs.R
index 93b07b8..53bc452 100644
--- a/R/plotHighestExprs.R
+++ b/R/plotHighestExprs.R
@@ -7,18 +7,20 @@
 #' @param colour_cells_by Specification of a column metadata field or a feature to colour by, see \code{?\link{retrieveCellInfo}} for possible values. 
 #' @param drop_features A character, logical or numeric vector indicating which features (e.g. genes, transcripts) to drop when producing the plot. 
 #' For example, spike-in transcripts might be dropped to examine the contribution from endogenous genes.
-#' @param exprs_values A integer scalar or string specifying the assay to obtain expression values from.
+#' @param assay.type A integer scalar or string specifying the assay to obtain expression values from.
 #' @param feature_names_to_plot String specifying which row-level metadata column contains the feature names.
 #' Alternatively, an \link{AsIs}-wrapped vector or a data.frame, see \code{?\link{retrieveFeatureInfo}} for possible values.
 #' Default is \code{NULL}, in which case \code{rownames(object)} are used.
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, 
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from, 
 #' for use in colouring - see \code{?\link{retrieveCellInfo}} for details.
 #' @param as_percentage logical scalar indicating whether percentages should be  plotted. 
-#' If \code{FALSE}, the raw \code{exprs_values} are shown instead.
+#' If \code{FALSE}, the raw \code{assay.type} are shown instead.
 #' @param swap_rownames Column name of \code{rowData(object)} to be used to 
 #'  identify features instead of \code{rownames(object)} when labelling plot 
 #'  elements.
 #' @param color_cells_by Alias to \code{colour_cells_by}.
+#' @param exprs_values Alias to \code{assay.type}.
+#' @param by_exprs_values Alias to \code{by.assay.type}.
 #'
 #' @details 
 #' This function will plot the percentage of counts accounted for by the top \code{n} most highly expressed features across the dataset.
@@ -42,16 +44,19 @@
 #' @importMethodsFrom DelayedArray sweep
 #' @importFrom DelayedMatrixStats rowSums2 colSums2
 #' @importFrom SummarizedExperiment assay
-#' @importFrom ggplot2 ggplot geom_point ggtitle xlab ylab theme_bw theme element_text 
+#' @importFrom ggplot2 ggplot geom_point ggtitle xlab ylab theme_bw theme element_text labs
 #' scale_colour_gradient scale_fill_manual guides
 plotHighestExprs <- function(object, n = 50, colour_cells_by = color_cells_by, 
     drop_features = NULL, exprs_values = "counts",
     by_exprs_values = exprs_values, feature_names_to_plot = NULL,
     as_percentage = TRUE, swap_rownames = NULL,
-    color_cells_by = NULL)
+    color_cells_by = NULL,
+    assay.type=exprs_values,
+    by.assay.type=by_exprs_values
+    )
 {
     ## Find the most highly expressed features in this dataset
-    exprs_mat <- assay(object, exprs_values, withDimnames=FALSE)
+    exprs_mat <- assay(object, assay.type, withDimnames=FALSE)
     ave_exprs <- rowSums2(exprs_mat)
     oo <- order(ave_exprs, decreasing=TRUE)
 
@@ -74,7 +79,7 @@ plotHighestExprs <- function(object, n = 50, colour_cells_by = color_cells_by,
             feature_names <- sprintf("Feature %i", seq_len(nrow(object)))
         }
     } else {
-        feature_names <- retrieveFeatureInfo(object, feature_names_to_plot, search = "rowData")$val
+        feature_names <- retrieveFeatureInfo(object, feature_names_to_plot, search = "rowData")$value
     }
     sub_names <- feature_names[chosen]
 
@@ -94,17 +99,17 @@ plotHighestExprs <- function(object, n = 50, colour_cells_by = color_cells_by,
     
     ## Colouring the individual dashes for the cells.
     if (!is.null(colour_cells_by)) {
-        colour_out <- retrieveCellInfo(object, colour_cells_by, exprs_values = by_exprs_values)
+        colour_out <- retrieveCellInfo(object, colour_cells_by, assay.type = by.assay.type)
         colour_cells_by <- colour_out$name
-        df_exprs_by_cell_long$colour_by <- colour_out$val[df_exprs_by_cell_long$Cell]
-        aes_to_use <- aes_string(y="Tag", x="value", colour="colour_by")
+        df_exprs_by_cell_long$colour_by <- colour_out$value[df_exprs_by_cell_long$Cell]
+        aes_to_use <- aes(y=.data$Tag, x=.data$value, colour=.data$colour_by)
     } else {
-        aes_to_use <- aes_string(y="Tag", x="value")
+        aes_to_use <- aes(y=.data$Tag, x=.data$value)
     }
 
     ## Create the plot and annotations. 
     plot_most_expressed <- ggplot(df_exprs_by_cell_long, aes_to_use) + geom_point(alpha = 0.6, shape = 124)
-    plot_most_expressed <- plot_most_expressed + xlab(exprs_values) + ylab("Feature") + theme_bw(8) +
+    plot_most_expressed <- plot_most_expressed + labs(x=assay.type, y="Feature") + theme_bw(8) +
         theme(legend.position = c(1, 0), legend.justification = c(1, 0),
               axis.text.x = element_text(colour = "gray35"),
               axis.text.y = element_text(colour = "gray35"),
@@ -113,7 +118,10 @@ plotHighestExprs <- function(object, n = 50, colour_cells_by = color_cells_by,
               title = element_text(colour = "gray35"))
 
     if (!is.null(colour_cells_by)) {
-        plot_most_expressed <- .resolve_plot_colours(plot_most_expressed, df_exprs_by_cell_long$colour_by, colour_cells_by)
+        plot_most_expressed <- .resolve_plot_colours(
+            plot_most_expressed, df_exprs_by_cell_long$colour_by, colour_cells_by,
+            fill = FALSE, colour = TRUE
+        )
     }
 
     ## Adding median expression values for each gene.
@@ -122,10 +130,10 @@ plotHighestExprs <- function(object, n = 50, colour_cells_by = color_cells_by,
         df_to_plot$pct_total <- 100 * sub_ave / total_exprs
         legend_val <- "pct_total"
     } else {
-        df_to_plot[[paste0("ave_", exprs_values)]] <- sub_ave
-        legend_val <- sprintf("ave_%s", exprs_values)
+        df_to_plot[[paste0("ave_", assay.type)]] <- sub_ave
+        legend_val <- sprintf("ave_%s", assay.type)
     }
 
-    aes <- aes_string(x = legend_val, y = "Feature")
+    aes <- aes(x = .data[[legend_val]], y = .data$Feature)
     plot_most_expressed + geom_point(aes, data = df_to_plot, colour = "gray30", shape = 21) 
 }
diff --git a/R/plotPlatePosition.R b/R/plotPlatePosition.R
index ac7ea3e..324bd0f 100644
--- a/R/plotPlatePosition.R
+++ b/R/plotPlatePosition.R
@@ -10,17 +10,20 @@
 #' @param shape_by Specification of a column metadata field or a feature to shape by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
 #' @param size_by Specification of a column metadata field or a feature to size by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
 #' @param order_by Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, 
-#' for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from, 
+#' for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.
 #' @param add_legend Logical scalar specifying whether a legend should be shown.
 #' @param theme_size Numeric scalar, see \code{?"\link{scater-plot-args}"} for details.
 #' @param point_alpha Numeric scalar specifying the transparency of the points, see \code{?"\link{scater-plot-args}"} for details.
 #' @param point_size Numeric scalar specifying the size of the points, see \code{?"\link{scater-plot-args}"} for details.
+#' @param point_shape An integer, or a string specifying the shape
+#' of the points. See \code{?"\link{scater-plot-args}"} for details. 
 #' @param other_fields Additional cell-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.
 #' @param swap_rownames Column name of \code{rowData(object)} to be used to 
 #'  identify features instead of \code{rownames(object)} when labelling plot 
 #'  elements.
 #' @param color_by Alias to \code{colour_by}.
+#' @param by_exprs_values Alias for \code{by.assay.type}.
 #'
 #' @details 
 #' This function expects plate positions to be given in a charcter format where a letter indicates the row on the plate and a numeric value  indicates the column. 
@@ -57,8 +60,9 @@ plotPlatePosition <- function(object, plate_position = NULL,
     colour_by = color_by, size_by = NULL, shape_by = NULL, order_by = NULL,
     by_exprs_values = "logcounts", 
     add_legend = TRUE, theme_size = 24, point_alpha = 0.6,
-    point_size = 24, other_fields=list(),
-    swap_rownames = NULL, color_by = NULL) 
+    point_size = 24, point_shape = 19, other_fields=list(),
+    swap_rownames = NULL, color_by = NULL,
+    by.assay.type=by_exprs_values) 
 {
     ## check object is SingleCellExperiment object
     if ( !is(object, "SingleCellExperiment") ) {
@@ -89,7 +93,8 @@ plotPlatePosition <- function(object, plate_position = NULL,
     vis_out <- .incorporate_common_vis_col(df_to_plot, se = object, 
         colour_by = colour_by, shape_by = shape_by, size_by = size_by, 
         order_by = order_by,
-        by_exprs_values = by_exprs_values, other_fields = other_fields,
+        by.assay.type = by.assay.type,
+	other_fields = other_fields,
         swap_rownames = swap_rownames)
 
     df_to_plot <- vis_out$df
@@ -98,13 +103,16 @@ plotPlatePosition <- function(object, plate_position = NULL,
     size_by <- vis_out$size_by
 
     ## make the plot with appropriate colours.
-    plot_out <- ggplot(df_to_plot, aes_string(x="X", y="Y"))
+    plot_out <- ggplot(df_to_plot, aes(x=.data$X, y=.data$Y))
 
-    point_out <- .get_point_args(colour_by, shape_by, size_by, alpha = point_alpha, size = point_size)
+    point_out <- .get_point_args(colour_by, shape_by, size_by, alpha = point_alpha, size = point_size, shape = point_shape)
     plot_out <- plot_out + do.call(geom_point, point_out$args)
 
     if (!is.null(colour_by)) {
-        plot_out <- .resolve_plot_colours(plot_out, df_to_plot$colour_by, colour_by, fill = point_out$fill)
+        plot_out <- .resolve_plot_colours(
+            plot_out, df_to_plot$colour_by, colour_by, fill = point_out$fill,
+            colour = !point_out$fill
+        )
     }
 
     ## Define plotting theme
diff --git a/R/plotRLE.R b/R/plotRLE.R
index e4f7cf6..e43d348 100644
--- a/R/plotRLE.R
+++ b/R/plotRLE.R
@@ -3,7 +3,7 @@
 #' Produce a relative log expression (RLE) plot of one or more transformations of cell expression values.
 #'
 #' @param object A SingleCellExperiment object.
-#' @param exprs_values A string or integer scalar specifying the expression matrix in \code{object} to use.
+#' @param assay.type A string or integer scalar specifying the expression matrix in \code{object} to use.
 #' @param exprs_logged A logical scalar indicating whether the expression matrix is already log-transformed.
 #' If not, a log2-transformation (+1) will be performed prior to plotting.
 #' @param style String defining the boxplot style to use, either \code{"minimal"} (default) or \code{"full"}; see Details.
@@ -11,10 +11,13 @@
 #' @param ordering A vector specifying the ordering of cells in the RLE plot.
 #' This can be useful for arranging cells by experimental conditions or batches.
 #' @param colour_by Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from,
-#' for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from,
+#' for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.
 #' @param BPPARAM A \linkS4class{BiocParallelParam} object to be used to parallelise operations using \code{\link{DelayedArray}}.
 #' @param color_by Alias to \code{colour_by}.
+#' @param exprs_values Alias to \code{assay.type}.
+#' @param by_exprs_values Alias to \code{by.assay.type}.
+#' @param assay_logged Alias to \code{exprs_logged}.
 #' @param ... further arguments passed to \code{\link[ggplot2]{geom_boxplot}} when \code{style="full"}.
 #'
 #' @return A ggplot object
@@ -60,11 +63,14 @@
 #' @importFrom DelayedArray DelayedArray
 #' @importFrom DelayedMatrixStats rowMedians
 #' @importFrom SummarizedExperiment assay
-#' @importFrom ggplot2 aes_string theme
+#' @importFrom ggplot2 theme
 plotRLE <- function(object, exprs_values="logcounts", exprs_logged = TRUE, 
                     style = "minimal", legend = TRUE, ordering = NULL, 
                     colour_by = color_by, by_exprs_values = exprs_values,
                     BPPARAM = BiocParallel::bpparam(), color_by = NULL,
+                    assay.type=exprs_values,
+                    by.assay.type=by_exprs_values,
+		    assay_logged=exprs_logged,		    
                     ...) {
 
     oldbp <- getAutoBPPARAM()
@@ -72,9 +78,9 @@ plotRLE <- function(object, exprs_values="logcounts", exprs_logged = TRUE,
     on.exit(setAutoBPPARAM(oldbp))
 
     ## Check aesthetic arguments.
-    colour_by_out <- retrieveCellInfo(object, colour_by, exprs_values = by_exprs_values)
+    colour_by_out <- retrieveCellInfo(object, colour_by, assay.type = by.assay.type)
     colour_by <- colour_by_out$name
-    colour_by_vals <- colour_by_out$val
+    colour_by_vals <- colour_by_out$value
     if (!is.null(colour_by)) {
         colour_lab <- "colour_by"
     } else {
@@ -82,9 +88,9 @@ plotRLE <- function(object, exprs_values="logcounts", exprs_logged = TRUE,
     }
 
     ## Calculate RLE for each gene in each cell.
-    exprs_mat <- assay(object, i=exprs_values, withDimnames=FALSE)
+    exprs_mat <- assay(object, i=assay.type, withDimnames=FALSE)
     exprs_mat <- DelayedArray(exprs_mat)
-    if (!exprs_logged) {
+    if (!assay_logged) {
         exprs_mat <- log2(exprs_mat + 1)
     }
     features_meds <- rowMedians(exprs_mat)
@@ -105,7 +111,15 @@ plotRLE <- function(object, exprs_values="logcounts", exprs_logged = TRUE,
             rle=as.numeric(med_devs) # column-major.
         )
         df_to_plot[["colour_by"]] <- rep(colour_by_vals, each=nrow(med_devs)) # done outside, just in case it's NULL.
-        aesth <- aes_string(x = "x", group = "x", y = "rle", colour = colour_lab, fill = colour_lab)
+        aesth <- aes(
+            x = .data$x, group = .data$x, y = .data$rle,
+            colour = .data[[colour_lab]], fill = .data[[colour_lab]]
+        )
+        if (is.null(colour_by)) {
+            aesth <- aes(
+                x = .data$x, group = .data$x, y = .data$rle
+            )
+        }
         plot_out <- .plotRLE_full(df_to_plot, aesth, ncol, ...)
 
     } else if (style == "minimal") {
@@ -117,8 +131,9 @@ plotRLE <- function(object, exprs_values="logcounts", exprs_logged = TRUE,
     } 
 
     # Adding colours.
-    plot_out <- .resolve_plot_colours(plot_out, colour_by_vals, colour_by, fill = FALSE)
-    plot_out <- .resolve_plot_colours(plot_out, colour_by_vals, colour_by, fill = TRUE)
+    # plot_out <- .resolve_plot_colours(plot_out, colour_by_vals, colour_by, fill = FALSE)
+    # plot_out <- .resolve_plot_colours(plot_out, colour_by_vals, colour_by, fill = TRUE)
+    plot_out <- .resolve_plot_colours(plot_out, colour_by_vals, colour_by, fill = TRUE, colour = TRUE)
     
     if (!legend) {
         plot_out <- plot_out + theme(legend.position = "none")
@@ -139,13 +154,21 @@ plotRLE <- function(object, exprs_values="logcounts", exprs_logged = TRUE,
     boxdf
 }
 
-#' @importFrom ggplot2 ggplot aes_string geom_segment geom_point geom_hline ylab xlab theme_classic theme element_blank
+#' @importFrom ggplot2 ggplot .data geom_segment geom_point geom_hline ylab xlab theme_classic theme element_blank
 .plotRLE_minimal <- function(df, colour_by, ncol) {
-    plot_out <- ggplot(df, aes_string(x = "x", fill = colour_by)) +
-        geom_segment(aes_string(xend = "x", y = "q25", yend = "q75"), colour = "gray60") +
-        geom_segment(aes_string(xend = "x", y = "q75", yend = "whiskMax", colour = colour_by)) +
-        geom_segment(aes_string(xend = "x", y = "q25", yend = "whiskMin", colour = colour_by)) +
-        geom_point(aes_string(y = "q50"), shape = 21) +
+    plot_aes <- aes(x = .data$x)
+    useg_aes <- aes(xend = .data$x, y = .data$q75, yend = .data$whiskMax)
+    lseg_aes <- aes(xend = .data$x, y = .data$q25, yend = .data$whiskMin)
+    if (!is.null(colour_by)) {
+        plot_aes <- aes(x = .data$x, fill = .data[[colour_by]])
+        useg_aes <- aes(xend = .data$x, y = .data$q75, yend = .data$whiskMax, colour = .data[[colour_by]])
+        lseg_aes <- aes(xend = .data$x, y = .data$q25, yend = .data$whiskMin, colour = .data[[colour_by]])
+    }
+    plot_out <- ggplot(df, plot_aes) +
+        geom_segment(aes(xend = .data$x, y = .data$q25, yend = .data$q75), colour = "gray60") +
+        geom_segment(lseg_aes) +
+        geom_segment(useg_aes) +
+        geom_point(aes(y = .data$q50), shape = 21) +
         geom_hline(yintercept = 0, colour = "gray40", alpha = 0.5) +
         ylab("Relative log expression") + xlab("Sample") +
         theme_classic() +
diff --git a/R/plotReducedDim.R b/R/plotReducedDim.R
index f6b47ca..1d20d18 100644
--- a/R/plotReducedDim.R
+++ b/R/plotReducedDim.R
@@ -3,6 +3,7 @@
 #' Plot cell-level reduced dimension results stored in a SingleCellExperiment
 #' object.
 #'
+#' @inheritParams plotColData
 #' @param object A SingleCellExperiment object.
 #' @param dimred A string or integer scalar indicating the reduced dimension
 #' result in \code{reducedDims(object)} to plot.
@@ -25,9 +26,9 @@
 #' @param order_by Specification of a column metadata field or a feature to
 #' order points by, see the \code{by} argument in
 #' \code{?\link{retrieveCellInfo}} for possible values.
-#' @param by_exprs_values A string or integer scalar specifying which assay to
+#' @param by.assay.type A string or integer scalar specifying which assay to
 #' obtain expression values from,
-#' for use in point aesthetics - see the \code{exprs_values} argument in
+#' for use in point aesthetics - see the \code{assay.type} argument in
 #' \code{?\link{retrieveCellInfo}}.
 #' @param text_by String specifying the column metadata field with which to add
 #' text labels on the plot.
@@ -54,6 +55,7 @@
 #' \code{\link[ggrastr]{rasterise}}. To control the dpi, set
 #' \code{options(ggrastr.default.dpi)},
 #' for example \code{options(ggrastr.default.dpi=300)}.
+#' @param by_exprs_values Alias for \code{by.assay.type}.
 #' @param ... Additional arguments for visualization, see
 #' \code{?"\link{scater-plot-args}"} for details.
 #'
@@ -74,6 +76,13 @@
 #' when there are too many levels to distinguish by colour.
 #' It is only available for scatterplots.
 #'
+#' @note Arguments \code{shape_by} and \code{size_by} are ignored when
+#' \code{scattermore = TRUE}. Using \code{scattermore} is only recommended for
+#' very large datasets to speed up plotting. Small point size is also
+#' recommended. For larger point size, the point shape may be distorted. Also,
+#' when \code{scattermore = TRUE}, the \code{point_size} argument works
+#' differently.
+#'
 #' @return A ggplot object
 #'
 #' @author Davis McCarthy, with modifications by Aaron Lun
@@ -94,18 +103,28 @@
 #' plotReducedDim(example_sce, "PCA", ncomponents=5, colour_by="Cell_Cycle",
 #'     shape_by="Treatment")
 #'
+#' # Use scattermore
+#' plotPCA(example_sce, ncomponents = 4, scattermore = TRUE, point_size = 3)
+#'
+#' # Bin to show point density
+#' plotPCA(example_sce, bins = 10)
+#' # Bin to summarize values (default is sum)
+#' plotPCA(example_sce, bins = 10, colour_by = "Gene_0001")
+#'
 #' @export
 #' @importFrom SingleCellExperiment reducedDim
 #' @importFrom ggrepel geom_text_repel
 plotReducedDim <- function(
-        object, dimred, ncomponents = 2, percentVar = NULL, 
+        object, dimred, ncomponents = 2, percentVar = NULL,
         colour_by = color_by, shape_by = NULL, size_by = NULL,
-        order_by = NULL, by_exprs_values = "logcounts", 
+        order_by = NULL, by_exprs_values = "logcounts",
         text_by = NULL, text_size = 5, text_colour = text_color,
         label_format = c("%s %i", " (%i%%)"), other_fields = list(),
         text_color = "black", color_by = NULL,
         swap_rownames = NULL, point.padding = NA, force = 1,
-        rasterise = FALSE, ...
+        rasterise = FALSE, scattermore = FALSE,
+        bins = NULL, summary_fun = "sum", hex = FALSE,
+        by.assay.type=by_exprs_values, ...
     ) {
 
     ## Extract reduced dimension representation of cells
@@ -137,13 +156,13 @@ plotReducedDim <- function(
     vis_out <- .incorporate_common_vis_col(df_to_plot, se = object,
         colour_by = colour_by, shape_by = shape_by, size_by = size_by,
         order_by = order_by,
-        by_exprs_values = by_exprs_values, other_fields = other_fields,
+        by.assay.type = by.assay.type, other_fields = other_fields,
         swap_rownames = swap_rownames)
     df_to_plot <- vis_out$df
     colour_by <- vis_out$colour_by
     shape_by <- vis_out$shape_by
     size_by <- vis_out$size_by
-    
+
     ## Dispatching to the central plotter in the simple case of two dimensions.
     if (length(to_plot) == 2L) {
         colnames(df_to_plot)[seq_along(to_plot)] <- c("X", "Y")
@@ -155,11 +174,13 @@ plotReducedDim <- function(
                 sprintf(label_format[2], round(percentVar[to_plot]))
             )
         }
-        
+
         plot_out <- .central_plotter(df_to_plot, xlab = labs[1], ylab = labs[2],
                                      colour_by = colour_by, size_by = size_by,
                                      shape_by = shape_by, ..., point_FUN = NULL,
-                                     rasterise = rasterise)
+                                     rasterise = rasterise,
+                                     scattermore = scattermore, bins = bins,
+                                     summary_fun = summary_fun, hex = hex)
 
         # Adding text with the median locations of the 'text_by' vector.
         if (!is.null(text_by)) {
@@ -180,8 +201,7 @@ plotReducedDim <- function(
                     data = data.frame(
                         x = by_text_x, y = by_text_y, label = names(by_text_x)
                     ),
-                    mapping = aes_string(x = "x", y = "y", label = "label"),
-                    inherit.aes = FALSE,
+                    mapping = aes(x = .data$x, y = .data$y, label = .data$label),
                     size = text_size, colour = text_colour,
                     force = force, point.padding = point.padding
                 )
@@ -192,17 +212,30 @@ plotReducedDim <- function(
     ## Otherwise, creating a paired reddim plot.
     paired_reddim_plot(df_to_plot, to_plot = to_plot, percentVar = percentVar,
         colour_by = colour_by, shape_by = shape_by, size_by = size_by,
-        dimred = dimred, label_format = label_format, rasterise = rasterise, ...)
+        dimred = dimred, label_format = label_format, rasterise = rasterise,
+        scattermore = scattermore, bins = bins, summary_fun = summary_fun,
+        hex = hex, ...)
 }
 
-#' @importFrom ggplot2 ggplot facet_grid stat_density geom_point theme
+#' @importFrom ggplot2 ggplot facet_grid stat_density geom_point theme after_stat
 paired_reddim_plot <- function(df_to_plot, to_plot, dimred, percentVar = NULL,
         colour_by=NULL, shape_by=NULL, size_by=NULL,
         label_format=c("%s %i", " (%i%%)"),
-        add_legend = TRUE, theme_size = 10, point_alpha = 0.6, point_size = NULL,
-        rasterise = FALSE
+        add_legend = TRUE, theme_size = 10, point_alpha = 0.6, point_size = NULL, point_shape = NULL,
+        rasterise = FALSE, scattermore = FALSE,
+        bins = NULL, summary_fun = "sum", hex = FALSE
     ) {
-
+    if (scattermore) {
+        rasterise <- FALSE
+        if (!is.null(shape_by) || !is.null(size_by)) {
+            warning("shape_by and size_by do not work with scattermore.")
+        }
+    }
+    if (!is.null(bins) && !is.null(colour_by) &&
+        !is.numeric(df_to_plot$colour_by)) {
+        message("Binning only applies to numeric colour_by or point counts")
+        bins <- NULL
+    }
     reddim_cols <- seq_along(to_plot)
     df_to_expand <- df_to_plot[, reddim_cols]
 
@@ -219,24 +252,39 @@ paired_reddim_plot <- function(df_to_plot, to_plot, dimred, percentVar = NULL,
     df_to_plot_big <- data.frame(gg1$all, df_to_plot[, -reddim_cols])
     colnames(df_to_plot_big)[-seq_len(4)] <- colnames(df_to_plot)
 
-    plot_out <- ggplot(df_to_plot_big, aes_string(x = "x", y = "y")) +
+    plot_out <- ggplot(df_to_plot_big, aes(x = .data$x, y = .data$y)) +
         facet_grid(xvar ~ yvar, scales = "free") +
-        stat_density(aes_string(x = "x",
-                                y = "(..scaled.. * diff(range(x)) + min(x))"),
-                     data = gg1$densities, position = "identity",
-                     colour = "grey20", geom = "line") +
+        stat_density(
+            aes(
+                x = .data$x,
+                y = after_stat(.data$..scaled..) * diff(range(.data$x)) + min(.data$x)
+            ),
+            data = gg1$densities, position = "identity",
+            colour = "grey20", geom = "line"
+        ) +
         xlab("") +
         ylab("") +
         theme_bw(theme_size)
 
     ## Setting up the point addition with various aesthetics.
     point_out <- .get_point_args(
-        colour_by, shape_by, size_by, alpha = point_alpha, size = point_size
+        colour_by, shape_by, size_by, alpha = point_alpha, size = point_size,
+        shape = point_shape, scattermore = scattermore, bins = bins,
+        summary_fun = summary_fun
     )
-    plot_out <- plot_out + do.call(geom_point, point_out$args)
-    if (!is.null(colour_by)) {
+    point_FUN <- .get_point_fun(scattermore = scattermore, bins = bins,
+                                colour_by = colour_by, hex = hex)
+    plot_out <- plot_out + do.call(point_FUN, point_out$args)
+    if (!is.null(colour_by) || !is.null(bins)) {
+        if (!is.null(bins)) {
+            if (is.null(colour_by)) colour_by <- "count"
+            else if (is.character(summary_fun)) {
+                colour_by <- paste0(summary_fun, "(", colour_by, ")")
+            }
+        }
         plot_out <- .resolve_plot_colours(
-            plot_out, df_to_plot$colour_by, colour_by, fill = point_out$fill
+            plot_out, df_to_plot$colour_by, colour_by, fill = point_out$fill,
+            colour = !point_out$fill, do_bin = !is.null(bins)
         )
     }
 
diff --git a/R/plotRowData.R b/R/plotRowData.R
index 06bedf0..c6569e9 100644
--- a/R/plotRowData.R
+++ b/R/plotRowData.R
@@ -11,10 +11,11 @@
 #' @param colour_by Specification of a row metadata field or a cell to colour by, see \code{?\link{retrieveFeatureInfo}} for possible values. 
 #' @param shape_by Specification of a row metadata field or a cell to shape by, see \code{?\link{retrieveFeatureInfo}} for possible values. 
 #' @param size_by Specification of a row metadata field or a cell to size by, see \code{?\link{retrieveFeatureInfo}} for possible values. 
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, 
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from, 
 #' for use in point aesthetics - see \code{?\link{retrieveFeatureInfo}} for details.
 #' @param other_fields Additional feature-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.
 #' @param color_by Alias to \code{colour_by}.
+#' @param by_exprs_values Alias to \code{by.assay.type}.
 #' @param ... Additional arguments for visualization, see \code{?"\link{scater-plot-args}"} for details.
 #'
 #' @details 
@@ -40,6 +41,7 @@
 plotRowData <- function(object, y, x = NULL, 
     colour_by = color_by, shape_by = NULL, size_by = NULL, 
     by_exprs_values = "logcounts", other_fields = list(), color_by = NULL,
+    by.assay.type=by_exprs_values,    
     ...)
 {
     if (!is(object, "SingleCellExperiment")) {
@@ -52,7 +54,7 @@ plotRowData <- function(object, y, x = NULL,
     if (is.null(y_lab)) {
         stop(sprintf("could not find '%s' in 'rowData(object)'", y))
     }
-    df_to_plot <- data.frame(Y=y_by_out$val)
+    df_to_plot <- data.frame(Y=y_by_out$value)
 
     if (!is.null(x)) {
         x_by_out <- retrieveFeatureInfo(object, x, search = "rowData")
@@ -60,7 +62,7 @@ plotRowData <- function(object, y, x = NULL,
         if (is.null(x_lab)) {
             stop(sprintf("could not find '%s' in 'rowData(object)'", x))
         }
-        df_to_plot$X <- x_by_out$val
+        df_to_plot$X <- x_by_out$value
     } else {
         x_lab <- NULL
         df_to_plot$X <- factor(character(nrow(object)))
@@ -69,7 +71,7 @@ plotRowData <- function(object, y, x = NULL,
     ## checking visualization arguments
     vis_out <- .incorporate_common_vis_row(df_to_plot, se = object, 
         colour_by = colour_by, shape_by = shape_by, size_by = size_by, 
-        by_exprs_values = by_exprs_values, other_fields = other_fields)
+        by.assay.type = by.assay.type, other_fields = other_fields)
 
     df_to_plot <- vis_out$df
     colour_by <- vis_out$colour_by
diff --git a/R/plotScater.R b/R/plotScater.R
index ac31ced..0ffc910 100644
--- a/R/plotScater.R
+++ b/R/plotScater.R
@@ -10,14 +10,15 @@
 #' @param colour_by Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
 #' The curve for each cell will be coloured according to this specification.
 #' @param nfeatures Numeric scalar indicating the number of top-expressed features to show n the plot.
-#' @param exprs_values String or integer scalar indicating which assay of \code{object} should be used to obtain the expression values for this plot. 
-#' @param by_exprs_values A string or integer scalar specifying which assay to obtain expression values from, 
-#' for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.
+#' @param assay.type String or integer scalar indicating which assay of \code{object} should be used to obtain the expression values for this plot. 
+#' @param by.assay.type A string or integer scalar specifying which assay to obtain expression values from, 
+#' for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.
 #' @param ncol Number of columns to use for \code{\link{facet_wrap}} if only one block is defined.
 #' @param line_width Numeric scalar specifying the line width.
 #' @param theme_size Numeric scalar specifying the font size to use for the plotting theme.
 #' @param color_by Alias to \code{colour_by}.
-#'
+#' @param exprs_values Alias to \code{assay.type}.
+#' @param by_exprs_values Alias to \code{by.assay.type}.
 #' @details 
 #' For each cell, the features are ordered from most-expressed to least-expressed.
 #' The cumulative proportion of the total expression for the cell is computed across the top \code{nfeatures} features. 
@@ -34,17 +35,20 @@
 #' @examples
 #' example_sce <- mockSCE()
 #' plotScater(example_sce)
-#' plotScater(example_sce, exprs_values = "counts", colour_by = "Cell_Cycle")
+#' plotScater(example_sce, assay.type = "counts", colour_by = "Cell_Cycle")
 #' plotScater(example_sce, block1 = "Treatment", colour_by = "Cell_Cycle")
 #'
 #' @export
 #' @importClassesFrom SingleCellExperiment SingleCellExperiment
 #' @importFrom SummarizedExperiment assay
-#' @importFrom ggplot2 ggplot geom_line facet_grid facet_wrap xlab ylab theme_bw aes_string
+#' @importFrom ggplot2 ggplot geom_line facet_grid facet_wrap xlab ylab theme_bw
 plotScater <- function(x, nfeatures = 500, exprs_values = "counts", 
     colour_by = color_by, by_exprs_values = exprs_values, 
     block1 = NULL, block2 = NULL, ncol = 3,
-    line_width = 1.5, theme_size = 10, color_by = NULL)
+    line_width = 1.5, theme_size = 10, color_by = NULL,
+    assay.type=exprs_values,
+    by.assay.type=by_exprs_values    
+    )
 {
     if (!is(x, "SingleCellExperiment")) {
         stop("x must be of class SingleCellExperiment")
@@ -52,19 +56,19 @@ plotScater <- function(x, nfeatures = 500, exprs_values = "counts",
     
     block1_out <- retrieveCellInfo(x, block1, search = "colData")
     block1 <- block1_out$name
-    block1_vals <- block1_out$val
+    block1_vals <- block1_out$value
 
     block2_out <- retrieveCellInfo(x, block2, search = "colData")
     block2 <- block2_out$name
-    block2_vals <- block2_out$val
+    block2_vals <- block2_out$value
 
     ## Setting values to colour by.
-    colour_by_out <- retrieveCellInfo(x, colour_by, exprs_values = by_exprs_values)
+    colour_by_out <- retrieveCellInfo(x, colour_by, assay.type = by.assay.type)
     colour_by <- colour_by_out$name
-    colour_by_vals <- colour_by_out$val
+    colour_by_vals <- colour_by_out$value
 
     ## Define an expression matrix depending on which values we're using
-    exprs_mat <- assay(x, i = exprs_values, withDimnames=FALSE)
+    exprs_mat <- assay(x, i = assay.type, withDimnames=FALSE)
     nfeatures <- min(nfeatures, nrow(exprs_mat))
 
     ## Use C++ to get the sequencing real estate accounted for by features
@@ -80,12 +84,12 @@ plotScater <- function(x, nfeatures = 500, exprs_values = "counts",
     seq_real_estate_long$colour_by <- rep(colour_by_vals, nfeatures)
 
     ## Set up plot
-    aes <- aes_string(x = "Feature", y = "Proportion_Library", group = "Cell")
+    aes <- aes(x = .data$Feature, y = .data$Proportion_Library, group = .data$Cell)
     if ( !is.null(colour_by) ) {
         aes$colour <- as.symbol("colour_by")
     }
     plot_out <- ggplot(seq_real_estate_long, aes) +
-        geom_line(linetype = "solid", alpha = 0.3, size = line_width)
+        geom_line(linetype = "solid", alpha = 0.3, linewidth = line_width)
 
     ## Deal with blocks for grid
     if (!is.null(block1) && !is.null(block2)) {
@@ -100,7 +104,10 @@ plotScater <- function(x, nfeatures = 500, exprs_values = "counts",
 
     ## Add extra plot theme and details
     if ( !is.null(colour_by)) { 
-        plot_out <- .resolve_plot_colours(plot_out, seq_real_estate_long$colour_by, colour_by)
+        plot_out <- .resolve_plot_colours(
+            plot_out, seq_real_estate_long$colour_by, colour_by,
+            fill = FALSE, colour = TRUE
+        )
     }
 
     plot_out <- plot_out + xlab("Number of features") + ylab("Cumulative proportion of library")
diff --git a/R/plot_central.R b/R/plot_central.R
index bdd4a25..23b9001 100644
--- a/R/plot_central.R
+++ b/R/plot_central.R
@@ -1,7 +1,7 @@
 #' General visualization parameters
 #'
 #' \pkg{scater} functions that plot points share a number of visualization parameters, which are described on this page.
-#' 
+#'
 #' @section Aesthetic parameters:
 #' \describe{
 #' \item{\code{add_legend}:}{Logical scalar, specifying whether a legend should be shown.
@@ -12,6 +12,9 @@
 #' Defaults to 0.6.}
 #' \item{\code{point_size}:}{Numeric scalar, specifying the size of the points.
 #' Defaults to \code{NULL}.}
+#' \item{\code{point_shape}:}{An integer, or a string specifying the shape
+#' of the points. Details see \code{vignette("ggplot2-specs")}. Defaults to
+#' \code{19}.}
 #' \item{\code{jitter_type}:}{String to define how points are to be jittered in a violin plot.
 #' This is either with random jitter on the x-axis (\code{"jitter"}) or in a \dQuote{beeswarm} style (if \code{"swarm"}, default).
 #' The latter usually looks more attractive, but for datasets with a large number of cells, or for dense plots, the jitter option may work better.}
@@ -29,45 +32,54 @@
 #' Defaults to \code{TRUE}.}
 #' }
 #'
-#' @section Miscellaneous fields:
-#' Addititional fields can be added to the data.frame passed to \link{ggplot} by setting the \code{other_fields} argument.
-#' This allows users to easily incorporate additional metadata for use in further \pkg{ggplot} operations.
+#' @section Miscellaneous fields: Addititional fields can be added to the
+#'   data.frame passed to \link{ggplot} by setting the \code{other_fields}
+#'   argument. This allows users to easily incorporate additional metadata for
+#'   use in further \pkg{ggplot} operations.
 #'
-#' The \code{other_fields} argument should be character vector where each string is passed to \code{\link{retrieveCellInfo}} (for cell-based plots) or \code{\link{retrieveFeatureInfo}} (for feature-based plots).
-#' Alternatively, \code{other_fields} can be a named list where each element is of any type accepted by \code{\link{retrieveCellInfo}} or \code{\link{retrieveFeatureInfo}}.
-#' This includes \link{AsIs}-wrapped vectors, data.frames or \linkS4class{DataFrame}s.
+#'   The \code{other_fields} argument should be character vector where each
+#'   string is passed to \code{\link{retrieveCellInfo}} (for cell-based plots)
+#'   or \code{\link{retrieveFeatureInfo}} (for feature-based plots).
+#'   Alternatively, \code{other_fields} can be a named list where each element
+#'   is of any type accepted by \code{\link{retrieveCellInfo}} or
+#'   \code{\link{retrieveFeatureInfo}}. This includes \link{AsIs}-wrapped
+#'   vectors, data.frames or \linkS4class{DataFrame}s.
 #'
-#' Each additional column of the output data.frame will be named according to the \code{name} returned by \code{\link{retrieveCellInfo}} or \code{\link{retrieveFeatureInfo}}.
-#' If these clash with inbuilt names (e.g., \code{X}, \code{Y}, \code{colour_by}), a warning will be raised and the additional column will not be added to avoid overwriting an existing column.
+#'   Each additional column of the output data.frame will be named according to
+#'   the \code{name} returned by \code{\link{retrieveCellInfo}} or
+#'   \code{\link{retrieveFeatureInfo}}. If these clash with inbuilt names (e.g.,
+#'   \code{X}, \code{Y}, \code{colour_by}), a warning will be raised and the
+#'   additional column will not be added to avoid overwriting an existing
+#'   column.
 #'
 #' @name scater-plot-args
 #' @importFrom stats runif
 #'
-#' @seealso
-#' \code{\link{plotColData}}, 
-#' \code{\link{plotRowData}}, 
-#' \code{\link{plotReducedDim}}, 
-#' \code{\link{plotExpression}}, 
-#' \code{\link{plotPlatePosition}},
-#' and most other plotting functions.
+#' @seealso \code{\link{plotColData}}, \code{\link{plotRowData}},
+#' \code{\link{plotReducedDim}}, \code{\link{plotExpression}},
+#' \code{\link{plotPlatePosition}}, and most other plotting functions.
 NULL
 
 #' @importFrom ggbeeswarm geom_quasirandom
-#' @importFrom ggplot2 ggplot geom_violin aes_string xlab ylab stat_summary geom_jitter position_jitter coord_flip geom_point stat_smooth geom_tile theme_bw theme
+#' @importFrom ggplot2 ggplot geom_violin xlab ylab stat_summary geom_jitter
+#'   position_jitter coord_flip geom_point stat_smooth geom_tile theme_bw theme
+#'   geom_bin2d geom_hex stat_summary_2d stat_summary_hex
 .central_plotter <- function(object, xlab = NULL, ylab = NULL,
                              colour_by = NULL, shape_by = NULL, size_by = NULL, fill_by = NULL,
                              show_median = FALSE, show_violin = TRUE, show_smooth = FALSE, show_se = TRUE,
-                             theme_size = 10, point_alpha = 0.6, point_size = NULL, add_legend = TRUE,
+                             #  show_points = TRUE,
+                             theme_size = 10, point_alpha = 0.6, point_size = NULL, point_shape = 19, add_legend = TRUE,
                              point_FUN = NULL, jitter_type = "swarm",
-                             rasterise = FALSE)
+                             rasterise = FALSE, scattermore = FALSE, bins = NULL,
+                             summary_fun = "sum", hex = FALSE)
 # Internal ggplot-creating function to plot anything that involves points.
 # Creates either a scatter plot, (horizontal) violin plots, or a rectangle plot.
 {
     if (is.numeric(object$Y)!=is.numeric(object$X)) {
         ## Making a (horizontal) violin plot.
         flipped <- (is.numeric(object$X) && !is.numeric(object$Y))
-        if (flipped) { 
-            tmp <- object$X 
+        if (flipped) {
+            tmp <- object$X
             object$X <- object$Y
             object$Y <- tmp
             tmp <- xlab
@@ -76,29 +88,44 @@ NULL
         }
 
         # Adding violins.
-        plot_out <- ggplot(object, aes_string(x="X", y="Y")) + xlab(xlab) + ylab(ylab)
+        plot_out <- ggplot(object, aes(x=.data$X, y=.data$Y)) +
+            xlab(xlab) + ylab(ylab)
         if (show_violin) {
-            if (is.null(fill_by)) { 
+            if (is.null(fill_by)) {
                 viol_args <- list(fill="grey90")
             } else {
-                viol_args <- list(mapping=aes_string(fill="fill_by"))
+                viol_args <- list(mapping=aes(fill=.data[[fill_by]]))
             }
-            plot_out <- plot_out + do.call(geom_violin, c(viol_args, list(colour = "gray60", alpha = 0.2, scale = "width", width = 0.8)))
+            plot_out <- plot_out +
+                do.call(
+                    geom_violin,
+                    c(viol_args, list(colour = "gray60", alpha = 0.2, scale = "width", width = 0.8))
+                )
         }
 
         # Adding median, if requested.
         if (show_median) {
-            plot_out <- plot_out + stat_summary(fun = median, fun.min = median, fun.max = median,
-                                                geom = "crossbar", width = 0.3, alpha = 0.8)
+            plot_out <- plot_out +
+                stat_summary(
+                    fun = median, fun.min = median, fun.max = median,
+                    geom = "crossbar", width = 0.3, alpha = 0.8
+                )
         }
 
         # Adding points.
-        point_out <- .get_point_args(colour_by, shape_by, size_by, alpha = point_alpha, size = point_size)
+        point_out <- .get_point_args(
+            colour_by, shape_by, size_by,
+            alpha = point_alpha, size = point_size, shape = point_shape
+        )
         if (is.null(point_FUN)) {
             if (jitter_type=="swarm") {
-                point_FUN <- function(...) geom_quasirandom(..., width=0.4, groupOnX=TRUE, bandwidth=1)
+                point_FUN <- function(...) {
+                    geom_quasirandom(..., width=0.4, bandwidth=1)
+                }
             } else {
-                point_FUN <- function(...) geom_jitter(..., position = position_jitter(height = 0))
+                point_FUN <- function(...) {
+                    geom_jitter(..., position = position_jitter(height = 0))
+                }
             }
         }
         plot_out <- plot_out + do.call(point_FUN, point_out$args)
@@ -107,23 +134,38 @@ NULL
         if (flipped) {
             plot_out <- plot_out + coord_flip()
         }
-
-    } else if (is.numeric(object$Y) && is.numeric(object$X)) { 
+    } else if (is.numeric(object$Y) && is.numeric(object$X)) {
         # Creating a scatter plot.
-        plot_out <- ggplot(object, aes_string(x="X", y="Y")) + xlab(xlab) + ylab(ylab)
+        plot_out <- ggplot(object, aes(x=.data$X, y=.data$Y)) + xlab(xlab) + ylab(ylab)
 
+        if (scattermore) {
+            rasterise <- FALSE
+            if (!is.null(shape_by) || !is.null(size_by)) {
+                warning("shape_by and size_by do not work with scattermore.")
+            }
+        }
+        if (!is.null(bins) && !is.null(colour_by) &&
+            !is.numeric(object$colour_by)) {
+            warning("Binning only applies to numeric colour_by or point counts")
+            bins <- NULL
+        }
         # Adding points.
-        point_out <- .get_point_args(colour_by, shape_by, size_by, alpha = point_alpha, size = point_size)
+        point_out <- .get_point_args(
+            colour_by, shape_by, size_by,
+            alpha = point_alpha, size = point_size, shape = point_shape,
+            scattermore = scattermore, bins = bins, summary_fun = summary_fun
+        )
         if (is.null(point_FUN)) {
-            point_FUN <- geom_point
+            point_FUN <- .get_point_fun(scattermore = scattermore, bins = bins,
+                                        colour_by = colour_by, hex = hex)
         }
         plot_out <- plot_out + do.call(point_FUN, point_out$args)
 
         # Adding smoothing, if requested.
         if (show_smooth) {
-            plot_out <- plot_out + stat_smooth(colour = "firebrick", linetype = 2, se = show_se)
+            plot_out <- plot_out +
+                stat_smooth(colour = "firebrick", linetype = 2, se = show_se)
         }
-
     } else {
         # Creating a rectangle area plot.
         object$X <- as.factor(object$X)
@@ -153,25 +195,44 @@ NULL
         object$Y <- as.integer(object$Y) + combined$YWidth*runif(nrow(object), -1, 1)
 
         # Creating the plot:
-        plot_out <- ggplot(object, aes_string(x="X", y="Y")) + xlab(xlab) + ylab(ylab)
-        plot_out <- plot_out + geom_tile(aes_string(x = "X", y = "Y", height = "2*YWidth", width = "2*XWidth"),
-                                         data=summary.data, colour = 'grey60', size = 0.5, fill='grey90')
+        plot_out <- ggplot(object, aes(x=.data$X, y=.data$Y)) + xlab(xlab) + ylab(ylab)
+        plot_out <- plot_out +
+            geom_tile(
+                aes(
+                    x = .data$X, y = .data$Y,
+                    height = 2 * .data$YWidth, width = 2 * .data$XWidth
+                ),
+                data = summary.data, colour = 'grey60',
+                linewidth = 0.5, fill = 'grey90'
+            )
 
         # Adding points.
-        point_out <- .get_point_args(colour_by, shape_by, size_by, alpha = point_alpha, size = point_size)
+        point_out <- .get_point_args(
+            colour_by, shape_by, size_by,
+            alpha = point_alpha, size = point_size, shape = point_shape
+        )
         if (is.null(point_FUN)) {
             point_FUN <- geom_point
         }
         plot_out <- plot_out + do.call(point_FUN, point_out$args)
     }
 
-    # Adding colour.       
-    if ( !is.null(colour_by) ) {
-        plot_out <- .resolve_plot_colours(plot_out, object$colour_by, colour_by, fill = point_out$fill)
+    # Adding colour.
+    if (!is.null(colour_by) || !is.null(bins)) {
+        if (!is.null(bins)) {
+            if (is.null(colour_by)) colour_by <- "count"
+            else if (is.character(summary_fun)) {
+                colour_by <- paste0(summary_fun, "(", colour_by, ")")
+            }
+        }
+        plot_out <- .resolve_plot_colours(
+            plot_out, object$colour_by, colour_by, fill = point_out$fill,
+            colour = !point_out$fill, do_bin = !is.null(bins)
+        )
     }
 
     ## Define plotting theme
-    if ( requireNamespace("cowplot", quietly = TRUE) ) {
+    if (requireNamespace("cowplot", quietly = TRUE)) {
         plot_out <- plot_out + cowplot::theme_cowplot(theme_size)
     } else {
         plot_out <- plot_out + theme_bw(theme_size)
@@ -188,58 +249,97 @@ NULL
     plot_out
 }
 
-#' @importFrom ggplot2 aes_string
-.get_point_args <- function(colour_by, shape_by, size_by, alpha=0.65, size=NULL) 
+#' @importFrom utils modifyList
+.get_point_args <- function(colour_by, shape_by, size_by, alpha=0.65, size=NULL,
+                            shape = NULL, scattermore = FALSE, bins = NULL,
+                            summary_fun = sum)
 ## Note the use of colour instead of fill when shape_by is set, as not all shapes have fill.
-## (Fill is still the default as it looks nicer.)
 {
-    aes_args <- list()
     fill_colour <- FALSE
+    ## used to be able to use aes_string but this is now duplicated
+    ## adding a list to a ggplot adds all the list elements
+    ## this means we need to be careful about what geoms inherit the global aes
+    aes <- list()
+    if (!is.null(bins)) {
+        shape_by <- size_by <- size <- shape <- NULL
+        fill_colour <- TRUE
+    }
     if (!is.null(shape_by)) {
-        aes_args$shape <- "shape_by"
+        aes <- modifyList(aes, aes(shape = shape_by))
         fill_colour <- FALSE
     }
     if (!is.null(colour_by)) {
-        if (fill_colour) {
-            aes_args$fill <- "colour_by"
+        if (is.null(bins)) {
+            if (fill_colour) {
+                aes <- modifyList(aes, aes(fill = colour_by))
+            } else {
+                aes <- modifyList(aes, aes(colour = colour_by))
+            }
         } else {
-            aes_args$colour <- "colour_by"
+            aes <- modifyList(aes, aes(z = colour_by))
         }
     }
     if (!is.null(size_by)) {
-        aes_args$size <- "size_by"
-    }
-    new_aes <- do.call(aes_string, aes_args)
-    
-    geom_args <- list(mapping=new_aes, alpha=alpha)
-    if (is.null(colour_by) || fill_colour) {
-        geom_args$colour <- "grey70"
+        aes <- modifyList(aes, aes(size = size_by))
     }
-    if (is.null(colour_by) || !fill_colour) { # set fill when there is no fill colour, to distinguish between e.g., pch=16 and pch=21.
-        geom_args$fill <- "grey20"
-    }
-    if (is.null(shape_by)) {
-        geom_args$shape <- 19
-    }
-    if (is.null(size_by)) {
-        geom_args$size <- size
+
+    geom_args <- list(alpha=alpha)
+    if (is.null(bins)) {
+        if (is.null(colour_by) || fill_colour) {
+            geom_args$colour <- "grey70"
+        }
+        if (is.null(colour_by) || !fill_colour) { # set fill when there is no fill colour, to distinguish between e.g., pch=16 and pch=21.
+            geom_args$fill <- "grey20"
+        }
+        if (is.null(shape_by)) {
+            geom_args$shape <- shape
+        }
+        if (is.null(size_by)) {
+            if (scattermore) geom_args$pointsize <- size
+            else geom_args$size <- size
+        }
+    } else {
+        if (!is.null(colour_by)) {
+            if (is.character(summary_fun)) summary_fun <- match.fun(summary_fun)
+            geom_args$fun <- summary_fun
+        }
+        geom_args$bins <- bins
+        geom_args$alpha <- NULL
     }
-    return(list(args=geom_args, fill=fill_colour))
+
+    class(aes) <- "uneval"
+    return(list(aes = aes, args = c(geom_args, list(mapping = aes)), fill=fill_colour))
 }
 
 #' @importFrom ggplot2 guide_legend guides
-.add_extra_guide <- function(plot_out, shape_by, size_by) 
+.add_extra_guide <- function(plot_out, shape_by, size_by)
 # Adding extra legend information on the shape and size.
 {
     guide_args <- list()
     if (!is.null(shape_by)) {
         guide_args$shape <- guide_legend(title = shape_by)
     }
-    if (!is.null(size_by)) { 
+    if (!is.null(size_by)) {
         guide_args$size <- guide_legend(title = size_by)
     }
-    if (length(guide_args)) { 
+    if (length(guide_args)) {
         plot_out <- plot_out + do.call(guides, guide_args)
     }
     return(plot_out)
 }
+
+# Get function plotting points
+.get_point_fun <- function(scattermore, bins, colour_by = NULL,
+                           hex = FALSE) {
+    if (!is.null(bins)) {
+        if (is.null(colour_by))
+            point_FUN <- if (hex) geom_hex else geom_bin2d
+        else
+            point_FUN <- if (hex) stat_summary_hex else stat_summary_2d
+    } else if (scattermore) {
+        rlang::check_installed("scattermore")
+        point_FUN <- scattermore::geom_scattermore
+    } else
+        point_FUN <- geom_point
+    point_FUN
+}
diff --git a/R/plot_colours.R b/R/plot_colours.R
index 30d1bcc..25c7655 100644
--- a/R/plot_colours.R
+++ b/R/plot_colours.R
@@ -1,4 +1,4 @@
-.get_palette <- function(palette_name) 
+.get_palette <- function(palette_name)
 # Function to define colour palettes.
 {
     switch(palette_name,
@@ -36,39 +36,47 @@
 
 #' @importFrom ggplot2 scale_fill_manual scale_colour_manual
 #' @importFrom viridis scale_fill_viridis scale_colour_viridis
-.resolve_plot_colours <- function(plot_out, colour_by, colour_by_name, fill = FALSE) 
+.resolve_plot_colours <- function(plot_out, colour_by, colour_by_name,
+                                  fill = FALSE, colour = FALSE, do_bin = FALSE)
 # Get nice plotting colour schemes for very general colour variables
 {
-    if ( is.null(colour_by) ) {
+    if (is.null(colour_by) && !do_bin) {
         return(plot_out)
     }
-
     # Picking whether to fill or not.
-    if ( fill ) {
+    aesthetics <- c("fill", "colour")[c(fill, colour)]
+
+    if (fill) {
         VIRIDFUN <- scale_fill_viridis
         SCALEFUN <- scale_fill_manual
-    } else {
+    } else if (colour) {
         VIRIDFUN <- scale_colour_viridis
         SCALEFUN <- scale_colour_manual
     }
 
     # Set a sensible colour scheme and return the plot_out object
-    if ( is.numeric(colour_by) ) {
-        plot_out <- plot_out + VIRIDFUN(name = colour_by_name)
+    if (is.numeric(colour_by) || do_bin) {
+        plot_out <- plot_out + VIRIDFUN(
+            name = colour_by_name, aesthetics = aesthetics
+        )
     } else {
         nlevs_colour_by <- nlevels(as.factor(colour_by))
         if (nlevs_colour_by <= 10) {
             plot_out <- plot_out + SCALEFUN(
                 values = .get_palette("tableau10medium"),
-                name = colour_by_name)
+                name = colour_by_name,
+                aesthetics = aesthetics)
         } else {
             if (nlevs_colour_by > 10 && nlevs_colour_by <= 20) {
                 plot_out <- plot_out + SCALEFUN(
                     values = .get_palette("tableau20"),
-                    name = colour_by_name)
+                    name = colour_by_name,
+                    aesthetics = aesthetics)
             } else {
                 plot_out <- plot_out + VIRIDFUN(
-                    name = colour_by_name, discrete = TRUE)
+                    name = colour_by_name, discrete = TRUE
+                    # , aesthetics = aesthetics
+                    )
             }
         }
     }
diff --git a/R/retrieveCellInfo.R b/R/retrieveCellInfo.R
index 8fa55a7..b23f312 100644
--- a/R/retrieveCellInfo.R
+++ b/R/retrieveCellInfo.R
@@ -2,16 +2,17 @@
 #'
 #' Retrieves a per-cell (meta)data field from a \linkS4class{SingleCellExperiment} based on a single keyword,
 #' typically for use in visualization functions.
-#' 
+#'
 #' @param x A \linkS4class{SingleCellExperiment} object.
 #' @param by A string specifying the field to extract (see Details).
 #' Alternatively, a data.frame, \linkS4class{DataFrame} or an \link{AsIs} vector.
 #' @param search Character vector specifying the types of data or metadata to use.
-#' @param exprs_values String or integer scalar specifying the assay from which expression values should be extracted.
-#' @param swap_rownames Column name of \code{rowData(object)} to be used to 
-#'  identify features instead of \code{rownames(object)} when labelling plot 
+#' @param assay.type String or integer scalar specifying the assay from which expression values should be extracted.
+#' @param swap_rownames Column name of \code{rowData(object)} to be used to
+#'  identify features instead of \code{rownames(object)} when labelling plot
 #'  elements.
-#' 
+#' @param exprs_values Alias to \code{assay.type}.
+#'
 #' @return A list containing \code{name}, a string with the name of the extracted field (usually identically to \code{by});
 #' and \code{value}, a vector of length equal to \code{ncol(x)} containing per-cell (meta)data values.
 #' If \code{by=NULL}, both \code{name} and \code{value} are set to \code{NULL}.
@@ -25,17 +26,17 @@
 #'
 #' Given a character string in \code{by}, this function will:
 #' \enumerate{
-#' \item Search \code{\link{colData}} for a column named \code{by}, 
+#' \item Search \code{\link{colData}} for a column named \code{by},
 #' and return the corresponding field as the output \code{value}.
 #' We do not consider nested elements within the \code{colData}.
-#' \item Search \code{\link{assay}(x, exprs_values)} for a row named \code{by}, 
+#' \item Search \code{\link{assay}(x, assay.type)} for a row named \code{by},
 #' and return the expression vector for this feature as the output \code{value}.
 #' \item Search each alternative experiment in \code{\link{altExps}(x)} for a row names \code{by},
-#' and return the expression vector for this feature at \code{exprs_values} as the output \code{value}.
+#' and return the expression vector for this feature at \code{assay.type} as the output \code{value}.
 #' }
 #' Any match will cause the function to return without considering later possibilities.
-#' The search can be modified by changing the presence and ordering of elements in \code{search}. 
-#' 
+#' The search can be modified by changing the presence and ordering of elements in \code{search}.
+#'
 #' If there is a name clash that results in retrieval of an unintended field,
 #' users should explicitly set \code{by} to a data.frame, DataFrame or AsIs-wrapped vector containing the desired values.
 #' Developers can also consider setting \code{search} to control the fields that are returned.
@@ -43,13 +44,13 @@
 #' @author Aaron Lun
 #' @seealso
 #' \code{\link{makePerCellDF}}, which provides a more user-friendly interface to this function.
-#' 
-#' \code{\link{plotColData}}, 
-#' \code{\link{plotReducedDim}}, 
-#' \code{\link{plotExpression}}, 
+#'
+#' \code{\link{plotColData}},
+#' \code{\link{plotReducedDim}},
+#' \code{\link{plotExpression}},
 #' \code{\link{plotPlatePosition}},
 #' and most other cell-based plotting functions.
-#' 
+#'
 #' @examples
 #' example_sce <- mockSCE()
 #' example_sce <- logNormCounts(example_sce)
@@ -65,11 +66,11 @@
 #' @importFrom SingleCellExperiment altExp altExpNames
 #' @importFrom SummarizedExperiment colData assay
 retrieveCellInfo <- function(x, by, search = c("colData", "assays", "altExps"),
-        exprs_values = "logcounts", swap_rownames = NULL)
+        exprs_values = "logcounts", swap_rownames = NULL, assay.type=exprs_values)
 {
     .mopUp <- function(name, value) {
         list(name=name, value=value)
-    } 
+    }
     if (is.null(by)) {
         return(.mopUp(NULL, NULL))
     }
@@ -86,7 +87,7 @@ retrieveCellInfo <- function(x, by, search = c("colData", "assays", "altExps"),
     } else if (is.data.frame(by) || is(by, "DataFrame")) {
         if (ncol(by) != 1L) {
             stop("input data frame should only have one column")
-        } 
+        }
         if (nrow(by) != ncol(x)) {
             stop("number of rows of input data frame should be equal to 'ncol(x)'")
         }
@@ -111,14 +112,15 @@ retrieveCellInfo <- function(x, by, search = c("colData", "assays", "altExps"),
             x <- .swap_rownames(x, swap_rownames)
             m <- match(by, rownames(x))
             if (!is.na(m)) {
-                return(.mopUp(by, assay(x, exprs_values, withDimnames = FALSE)[m, ]))
+                return(.mopUp(by, assay(x, assay.type, withDimnames = FALSE)[m, ]))
             }
         } else if (s=="altExps") {
             for (i in seq_along(altExpNames(x))) {
                 current <- altExp(x, i)
+                current <- .swap_rownames(current, swap_rownames)
                 m <- match(by, rownames(current))
                 if (!is.na(m)) {
-                    return(.mopUp(by, assay(current, exprs_values, withDimnames = FALSE)[m, ]))
+                    return(.mopUp(by, assay(current, assay.type, withDimnames = FALSE)[m, ]))
                 }
             }
         }
diff --git a/R/retrieveFeatureInfo.R b/R/retrieveFeatureInfo.R
index 137468c..8a8d443 100644
--- a/R/retrieveFeatureInfo.R
+++ b/R/retrieveFeatureInfo.R
@@ -7,7 +7,8 @@
 #' @param by A string specifying the field to extract (see Details).
 #' Alternatively, a data.frame, \linkS4class{DataFrame} or an \link{AsIs} vector.
 #' @param search Character vector specifying the types of data or metadata to use.
-#' @param exprs_values String or integer scalar specifying the assay from which expression values should be extracted.
+#' @param assay.type String or integer scalar specifying the assay from which expression values should be extracted.
+#' @param exprs_values Alias to \code{assay.type}.
 #' 
 #' @return A list containing \code{name}, a string with the name of the extracted field (usually identically to \code{by});
 #' and \code{value}, a vector of length equal to \code{ncol(x)} containing per-feature (meta)data values.
@@ -25,7 +26,7 @@
 #' \item Search \code{\link{rowData}} for a column named \code{by}, 
 #' and return the corresponding field as the output \code{value}.
 #' We do not consider nested elements within the \code{rowData}.
-#' \item Search \code{\link{assay}(x, exprs_values)} for a column named \code{by}, 
+#' \item Search \code{\link{assay}(x, assay.type)} for a column named \code{by}, 
 #' and return the expression vector for this feature as the output \code{value}.
 #' }
 #' Any match will cause the function to return without considering later possibilities.
@@ -56,7 +57,7 @@
 #'
 #' @export
 #' @importFrom SummarizedExperiment rowData assay
-retrieveFeatureInfo <- function(x, by, search=c("rowData", "assays"), exprs_values="logcounts")
+retrieveFeatureInfo <- function(x, by, search=c("rowData", "assays"), exprs_values="logcounts", assay.type=exprs_values)
 {
     .mopUp <- function(name, value) {
         list(name=name, value=value)
@@ -103,7 +104,7 @@ retrieveFeatureInfo <- function(x, by, search=c("rowData", "assays"), exprs_valu
         } else if (s=="assays") {
             m <- match(by, colnames(x))
             if (!is.na(m)) {
-                return(.mopUp(by, assay(x, exprs_values, withDimnames=FALSE)[,m]))
+                return(.mopUp(by, assay(x, assay.type, withDimnames=FALSE)[,m]))
             }
         }
     }
diff --git a/R/runMDS.R b/R/runMDS.R
index 5d7097e..485f584 100644
--- a/R/runMDS.R
+++ b/R/runMDS.R
@@ -91,16 +91,16 @@ setMethod("calculateMDS", "ANY", .calculate_mds)
 #' @export
 #' @rdname runMDS
 #' @importFrom SummarizedExperiment assay
-setMethod("calculateMDS", "SummarizedExperiment", function(x, ..., exprs_values="logcounts") {
-    .calculate_mds(assay(x, exprs_values), ...)
+setMethod("calculateMDS", "SummarizedExperiment", function(x, ..., exprs_values="logcounts", assay.type=exprs_values) {
+    .calculate_mds(assay(x, assay.type), ...)
 })
 
 #' @export
 #' @rdname runMDS
 #' @importFrom SummarizedExperiment assay
-setMethod("calculateMDS", "SingleCellExperiment", function(x, ..., exprs_values="logcounts", dimred=NULL, n_dimred=NULL)
+setMethod("calculateMDS", "SingleCellExperiment", function(x, ..., exprs_values="logcounts", dimred=NULL, n_dimred=NULL, assay.type=exprs_values)
 {
-    mat <- .get_mat_from_sce(x, exprs_values=exprs_values, dimred=dimred, n_dimred=n_dimred)
+    mat <- .get_mat_from_sce(x, assay.type=assay.type, dimred=dimred, n_dimred=n_dimred)
     .calculate_mds(mat, transposed=!is.null(dimred), ...)
 })
 
diff --git a/R/runMultiUMAP.R b/R/runMultiUMAP.R
index 2a50fbe..ba12a7d 100644
--- a/R/runMultiUMAP.R
+++ b/R/runMultiUMAP.R
@@ -10,14 +10,16 @@
 #'
 #' Alternatively, a \linkS4class{SingleCellExperiment} containing relevant matrices in its assays, \code{\link{reducedDims}} or \code{\link{altExps}}.
 #' This is also the only permissible argument for \code{runMultiUMAP}.
-#' @param exprs_values A character or integer vector of assays to extract and transpose for use in the UMAP.
+#' @param assay.type A character or integer vector of assays to extract and transpose for use in the UMAP.
 #' For the SingleCellExperiment, this argument can be missing, in which case no assays are used.
 #' @param dimred A character or integer vector of \code{\link{reducedDims}} to extract for use in the UMAP.
 #' This argument can be missing, in which case no assays are used.
 #' @param altexp A character or integer vector of \code{\link{altExps}} to extract and transpose for use in the UMAP.
 #' This argument can be missing, in which case no alternative experiments are used.
-#' @param altexp_exprs_values A character or integer vector specifying the assay to extract from alternative experiments, when \code{altexp} is specified.
+#' @param altexp.assay.type A character or integer vector specifying the assay to extract from alternative experiments, when \code{altexp} is specified.
 #' This is recycled to the same length as \code{altexp}.
+#' @param exprs_values Alias to \code{assay.type}.
+#' @param altexp_exprs_values Alias to \code{altexp.assay.type}.
 #' @param ... For the generic, further arguments to pass to specific methods.
 #'
 #' For the ANY method, further arguments to pass to \code{\link[uwot]{umap}}.
@@ -102,8 +104,8 @@ setMethod("calculateMultiUMAP", "ANY", function(x, ..., metric="euclidean") {
 #' @rdname runMultiUMAP
 #' @importFrom Matrix t
 #' @importFrom SummarizedExperiment assay
-setMethod("calculateMultiUMAP", "SummarizedExperiment", function(x, exprs_values, metric="euclidean", ...) {
-    targets <- lapply(exprs_values, FUN=assay, x=x)
+setMethod("calculateMultiUMAP", "SummarizedExperiment", function(x, exprs_values, metric="euclidean", assay.type=exprs_values, ...) {
+    targets <- lapply(assay.type, FUN=assay, x=x)
     targets <- lapply(targets, t)
     callGeneric(targets, ...) 
 }) 
@@ -113,11 +115,12 @@ setMethod("calculateMultiUMAP", "SummarizedExperiment", function(x, exprs_values
 #' @importFrom Matrix t
 #' @importFrom SummarizedExperiment assay
 #' @importFrom SingleCellExperiment reducedDim altExp
-setMethod("calculateMultiUMAP", "SingleCellExperiment", function(x, exprs_values, dimred, altexp, altexp_exprs_values="logcounts", ...) {
+setMethod("calculateMultiUMAP", "SingleCellExperiment", function(x, exprs_values, dimred, altexp, altexp_exprs_values="logcounts",
+          assay.type=exprs_values, altexp.assay.type=altexp_exprs_values, ...) {
     targets1 <- targets2 <- targets3 <- list()
 
     if (!missing(exprs_values)) {
-        targets1 <- lapply(exprs_values, FUN=assay, x=x)
+        targets1 <- lapply(assay.type, FUN=assay, x=x)
         targets1 <- lapply(targets1, t)
     }
 
@@ -127,8 +130,8 @@ setMethod("calculateMultiUMAP", "SingleCellExperiment", function(x, exprs_values
 
     if (!missing(altexp)) {
         targets3 <- lapply(altexp, FUN=altExp, x=x)
-        altexp_exprs_values <- rep(altexp_exprs_values, length.out=length(targets3))
-        targets3 <- mapply(FUN=assay, x=targets3, i=altexp_exprs_values, SIMPLIFY=FALSE)
+        altexp.assay.type <- rep(altexp.assay.type, length.out=length(targets3))
+        targets3 <- mapply(FUN=assay, x=targets3, i=altexp.assay.type, SIMPLIFY=FALSE)
         targets3 <- lapply(targets3, t)
     }
 
diff --git a/R/runNMF.R b/R/runNMF.R
index 12acb7e..5f2bcfd 100644
--- a/R/runNMF.R
+++ b/R/runNMF.R
@@ -18,7 +18,6 @@
 #' @inheritSection calculatePCA Feature selection
 #' @inheritSection calculatePCA Using reduced dimensions
 #' @inheritSection calculatePCA Using alternative Experiments
-#' 
 #' @return 
 #' For \code{calculateNMF}, a numeric matrix is returned containing the NMF coordinates for each cell (row) and dimension (column).
 #' 
@@ -64,7 +63,12 @@ NULL
 
     # RcppML doesn't use transposed data
     nmf_x <- t(nmf_out$h)
-    attr(nmf_x, "basis") <- nmf_out$w
+    rownames(nmf_x) <- colnames(x)
+    colnames(nmf_x) <- paste0("NMF", seq_len(ncol(nmf_x)))
+    nmf_basis <- nmf_out$w
+    rownames(nmf_basis) <- rownames(x)
+    colnames(nmf_basis) <- paste0("NMF", seq_len(ncol(nmf_basis)))
+    attr(nmf_x, "basis") <- nmf_basis
 
     nmf_x
 }
@@ -76,15 +80,15 @@ setMethod("calculateNMF", "ANY", .calculate_nmf)
 #' @export
 #' @rdname runNMF
 #' @importFrom SummarizedExperiment assay
-setMethod("calculateNMF", "SummarizedExperiment", function(x, ..., exprs_values="logcounts") {
-    .calculate_nmf(assay(x, exprs_values), ...)
+setMethod("calculateNMF", "SummarizedExperiment", function(x, ..., exprs_values="logcounts", assay.type=exprs_values) {
+    .calculate_nmf(assay(x, assay.type), ...)
 })
 
 #' @export
 #' @rdname runNMF
 setMethod("calculateNMF", "SingleCellExperiment",
-    function(x, ..., exprs_values="logcounts", dimred=NULL, n_dimred=NULL) {
-    mat <- .get_mat_from_sce(x, exprs_values=exprs_values, dimred=dimred, n_dimred=n_dimred)
+    function(x, ..., exprs_values="logcounts", dimred=NULL, n_dimred=NULL, assay.type=exprs_values) {
+    mat <- .get_mat_from_sce(x, assay.type=assay.type, dimred=dimred, n_dimred=n_dimred)
     .calculate_nmf(mat, transposed=!is.null(dimred), ...)
 })
 
diff --git a/R/runPCA.R b/R/runPCA.R
index f8d0112..0ed02c8 100644
--- a/R/runPCA.R
+++ b/R/runPCA.R
@@ -11,13 +11,14 @@
 #' @param ntop Numeric scalar specifying the number of features with the highest variances to use for dimensionality reduction.
 #' @param subset_row Vector specifying the subset of features to use for dimensionality reduction.
 #' This can be a character vector of row names, an integer vector of row indices or a logical vector.
-#' @param exprs_values Integer scalar or string indicating which assay of \code{x} contains the expression values.
+#' @param assay.type Integer scalar or string indicating which assay of \code{x} contains the expression values.
 #' @param scale Logical scalar, should the expression values be standardized? 
 #' @param BSPARAM A \linkS4class{BiocSingularParam} object specifying which algorithm should be used to perform the PCA.
 #' @param BPPARAM A \linkS4class{BiocParallelParam} object specifying whether the PCA should be parallelized.
 #' @param altexp String or integer scalar specifying an alternative experiment containing the input data.
 #' @param dimred String or integer scalar specifying the existing dimensionality reduction results to use.
 #' @param n_dimred Integer scalar or vector specifying the dimensions to use if \code{dimred} is specified.
+#' @param exprs_values Alias to \code{assay.type}.
 #' @param ... For the \code{calculatePCA} generic, additional arguments to pass to specific methods.
 #' For the SummarizedExperiment and SingleCellExperiment methods, additional arguments to pass to the ANY method.
 #'
@@ -34,7 +35,7 @@
 #' @section Feature selection:
 #' This section is relevant if \code{x} is a numeric matrix of (log-)expression values with features in rows and cells in columns;
 #' or if \code{x} is a \linkS4class{SingleCellExperiment} and \code{dimred=NULL}.
-#' In the latter, the expression values are obtained from the assay specified by \code{exprs_values}.
+#' In the latter, the expression values are obtained from the assay specified by \code{assay.type}.
 #'
 #' The \code{subset_row} argument specifies the features to use for dimensionality reduction.
 #' The aim is to allow users to specify highly variable features to improve the signal/noise ratio,
@@ -70,7 +71,7 @@
 #' In such cases, the method is run on data from an alternative \linkS4class{SummarizedExperiment} nested within \code{x}.
 #' This is useful for performing dimensionality reduction on other features stored in \code{\link{altExp}(x, altexp)}, e.g., antibody tags. 
 #' 
-#' Setting \code{altexp} with \code{exprs_values} will use the specified assay from the alternative SummarizedExperiment.
+#' Setting \code{altexp} with \code{assay.type} will use the specified assay from the alternative SummarizedExperiment.
 #' If the alternative is a SingleCellExperiment, setting \code{dimred} will use the specified dimensionality reduction results from the alternative. 
 #' This option will also interact as expected with \code{n_dimred}.
 #'
@@ -155,15 +156,15 @@ setMethod("calculatePCA", "ANY", .calculate_pca)
 #' @export
 #' @rdname runPCA
 #' @importFrom SummarizedExperiment assay
-setMethod("calculatePCA", "SummarizedExperiment", function(x, ..., exprs_values="logcounts") {
-    .calculate_pca(assay(x, exprs_values), ...)
+setMethod("calculatePCA", "SummarizedExperiment", function(x, ..., exprs_values="logcounts", assay.type=exprs_values) {
+    .calculate_pca(assay(x, assay.type), ...)
 })
 
 #' @export
 #' @rdname runPCA
-setMethod("calculatePCA", "SingleCellExperiment", function(x, ..., exprs_values="logcounts", dimred=NULL, n_dimred=NULL) 
+setMethod("calculatePCA", "SingleCellExperiment", function(x, ..., exprs_values="logcounts", dimred=NULL, n_dimred=NULL, assay.type=exprs_values) 
 {
-    mat <- .get_mat_from_sce(x, exprs_values=exprs_values, dimred=dimred, n_dimred=n_dimred)
+    mat <- .get_mat_from_sce(x, assay.type=assay.type, dimred=dimred, n_dimred=n_dimred)
     .calculate_pca(mat, transposed=!is.null(dimred), ...)
 })
 
diff --git a/R/runTSNE.R b/R/runTSNE.R
index 03e3f8b..b4355c0 100644
--- a/R/runTSNE.R
+++ b/R/runTSNE.R
@@ -24,6 +24,8 @@
 #' @param BPPARAM A \linkS4class{BiocParallelParam} object specifying how the neighbor search should be parallelized when \code{external_neighbors=TRUE}.
 #' @param pca Logical scalar indicating whether a PCA step should be performed inside \code{\link[Rtsne]{Rtsne}}.
 #' @param use_fitsne Logical scalar indicating whether \code{\link[snifter]{fitsne}} should be used to perform t-SNE.
+#' @param use_densvis Logical scalar indicating whether \code{\link[densvis]{densne}} should be used to perform density-preserving t-SNE.
+#' @param dens_frac,dens_lambda See \code{\link[densvis]{densne}}
 #'
 #' @inheritSection calculatePCA Feature selection
 #' @inheritSection calculatePCA Using reduced dimensions
@@ -80,7 +82,7 @@ NULL
     perplexity=NULL, normalize = TRUE, theta = 0.5, 
     num_threads=NULL, ...,
     external_neighbors=FALSE, BNPARAM = KmknnParam(), BPPARAM = SerialParam(),
-    use_fitsne=FALSE)
+    use_fitsne = FALSE, use_densvis=FALSE, dens_frac = 0.3, dens_lambda = 0.1)
 { 
     if (!transposed) {
         x <- .get_mat_for_reddim(x, subset_row=subset_row, ntop=ntop, scale=scale) 
@@ -99,6 +101,9 @@ NULL
 
     if (!external_neighbors || theta == 0) {
         if (use_fitsne) {
+            if (use_densvis) {
+                warning("Can't use_fitsne and use_densvis at the same time; using fitsne")
+            }
             if (normalize) {
                 x <- Rtsne::normalize_input(x)
             }
@@ -110,10 +115,19 @@ NULL
                 c(list(x), args, simplified = TRUE)
             )
         } else {
-            tsne_out <- do.call(
-                Rtsne::Rtsne,
-                c(list(x, check_duplicates = FALSE, normalize = normalize), args)
-            )$Y
+            if (use_densvis) {
+                args$dens_frac <- dens_frac
+                args$dens_lambda <- dens_lambda
+                tsne_out <- do.call(
+                    densvis::densne,
+                    c(list(x, check_duplicates = FALSE, normalize = normalize), args)
+                )
+            } else {
+                tsne_out <- do.call(
+                    Rtsne::Rtsne,
+                    c(list(x, check_duplicates = FALSE, normalize = normalize), args)
+                )$Y
+            }
         }
     } else {
         if (normalize) {
@@ -127,7 +141,7 @@ NULL
             c(list(nn_out$index, nn_out$distance), args)
         )$Y
     }
-
+    colnames(tsne_out) <- paste0("TSNE", seq_len(ncol(tsne_out)))
     tsne_out
 }
 
@@ -138,19 +152,19 @@ setMethod("calculateTSNE", "ANY", .calculate_tsne)
 #' @export
 #' @rdname runTSNE
 #' @importFrom SummarizedExperiment assay
-setMethod("calculateTSNE", "SummarizedExperiment", function(x, ..., exprs_values="logcounts") {
-    .calculate_tsne(assay(x, exprs_values), ...)
+setMethod("calculateTSNE", "SummarizedExperiment", function(x, ..., exprs_values="logcounts", assay.type=exprs_values) {
+    .calculate_tsne(assay(x, assay.type), ...)
 })
 
 #' @export
 #' @rdname runTSNE
 setMethod("calculateTSNE", "SingleCellExperiment", function(x, ..., pca=is.null(dimred), 
-    exprs_values="logcounts", dimred=NULL, n_dimred=NULL)
+    exprs_values="logcounts", dimred=NULL, n_dimred=NULL, assay.type=exprs_values)
 {
     if ("use_dimred" %in% names(list(...))) {
         warning("`use_dimred` is unused; use `dimred` instead.")
     }
-    mat <- .get_mat_from_sce(x, exprs_values=exprs_values, dimred=dimred, n_dimred=n_dimred)
+    mat <- .get_mat_from_sce(x, assay.type=assay.type, dimred=dimred, n_dimred=n_dimred)
     .calculate_tsne(mat, transposed=!is.null(dimred), pca=pca, ...)
 })
 
diff --git a/R/runUMAP.R b/R/runUMAP.R
index fb3ca68..caa8888 100644
--- a/R/runUMAP.R
+++ b/R/runUMAP.R
@@ -19,6 +19,8 @@
 #' @param n_threads Integer scalar specifying the number of threads to use in \code{\link[uwot]{umap}}.
 #' If \code{NULL} and \code{BPPARAM} is a \linkS4class{MulticoreParam}, it is set to the number of workers in \code{BPPARAM};
 #' otherwise, the \code{\link[uwot]{umap}} defaults are used.
+#' @param use_densvis Logical scalar indicating whether \code{\link[densvis]{densne}} should be used to perform density-preserving t-SNE.
+#' @param dens_frac,dens_lambda See \code{\link[densvis]{densne}}
 #' @inheritParams runTSNE
 #'
 #' @inheritSection calculatePCA Feature selection
@@ -66,7 +68,8 @@ NULL
 .calculate_umap <- function(x, ncomponents = 2, ntop = 500, 
     subset_row = NULL, scale=FALSE, transposed=FALSE, pca=if (transposed) NULL else 50,
     n_neighbors=15, n_threads=NULL, ..., 
-    external_neighbors=FALSE, BNPARAM = KmknnParam(), BPPARAM = SerialParam()) 
+    external_neighbors=FALSE, BNPARAM = KmknnParam(), BPPARAM = SerialParam(),
+    use_densvis=FALSE, dens_frac = 0.3, dens_lambda = 0.1)
 {
     if (!transposed) {
         x <- .get_mat_for_reddim(x, subset_row=subset_row, ntop=ntop, scale=scale) 
@@ -86,7 +89,13 @@ NULL
         args$nn_method <- list(idx=cbind(seq_len(N), nn_out$index), dist=cbind(numeric(N), nn_out$distance))
     }
 
-    do.call(uwot::umap, args)
+    if (use_densvis) {
+        umap_out <- do.call(densvis::densmap, args)
+    } else {
+        umap_out <- do.call(uwot::umap, args)
+    }
+    colnames(umap_out) <- paste0("UMAP", seq_len(ncol(umap_out)))
+    umap_out
 }
 
 #' @export
@@ -96,8 +105,8 @@ setMethod("calculateUMAP", "ANY", .calculate_umap)
 #' @export
 #' @rdname runUMAP
 #' @importFrom SummarizedExperiment assay
-setMethod("calculateUMAP", "SummarizedExperiment", function(x, ..., exprs_values="logcounts") {
-    .calculate_umap(assay(x, exprs_values), ...)
+setMethod("calculateUMAP", "SummarizedExperiment", function(x, ..., exprs_values="logcounts", assay.type=exprs_values) {
+    .calculate_umap(assay(x, assay.type), ...)
 })
 
 #' @export
@@ -105,9 +114,9 @@ setMethod("calculateUMAP", "SummarizedExperiment", function(x, ..., exprs_values
 #' @importFrom SummarizedExperiment assay
 setMethod("calculateUMAP", "SingleCellExperiment", function(x, ..., 
     pca=if (!is.null(dimred)) NULL else 50,
-    exprs_values="logcounts", dimred=NULL, n_dimred=NULL)
+    exprs_values="logcounts", dimred=NULL, n_dimred=NULL, assay.type=exprs_values)
 {
-    mat <- .get_mat_from_sce(x, exprs_values=exprs_values, dimred=dimred, n_dimred=n_dimred)
+    mat <- .get_mat_from_sce(x, assay.type=assay.type, dimred=dimred, n_dimred=n_dimred)
     .calculate_umap(mat, transposed=!is.null(dimred), pca=pca, ...)
 })
 
diff --git a/R/utils_plotting.R b/R/utils_plotting.R
index 362bf88..55c261b 100644
--- a/R/utils_plotting.R
+++ b/R/utils_plotting.R
@@ -1,21 +1,21 @@
 .incorporate_common_vis_row <- function(df, se, mode, colour_by, size_by, shape_by, 
-    by_exprs_values, by_show_single, other_fields, multiplier = NULL) 
+    by.assay.type, by_show_single, other_fields, multiplier = NULL) 
 {
-    colour_by_out <- retrieveFeatureInfo(se, colour_by, exprs_values = by_exprs_values)
+    colour_by_out <- retrieveFeatureInfo(se, colour_by, assay.type = by.assay.type)
     colour_by <- colour_by_out$name
     if (!is.null(multiplier)) {
         colour_by_out$value<- colour_by_out$value[multiplier]
     }
     df$colour_by <- colour_by_out$value
 
-    shape_by_out <- retrieveFeatureInfo(se, shape_by, exprs_values = by_exprs_values)
+    shape_by_out <- retrieveFeatureInfo(se, shape_by, assay.type = by.assay.type)
     shape_by <- shape_by_out$name
     if (!is.null(multiplier)) {
         shape_by_out$value<- shape_by_out$value[multiplier]
     }
     df$shape_by <- .coerce_to_factor(shape_by_out$value, 10, "shape_by")
 
-    size_by_out <- retrieveFeatureInfo(se, size_by, exprs_values = by_exprs_values)
+    size_by_out <- retrieveFeatureInfo(se, size_by, assay.type = by.assay.type)
     size_by <- size_by_out$name
     if (!is.null(multiplier)) {
         size_by_out$value<- size_by_out$value[multiplier]
@@ -23,7 +23,7 @@
     df$size_by <- size_by_out$value
 
     for (o in other_fields) {
-        other <- retrieveFeatureInfo(se, o, exprs_values=by_exprs_values)
+        other <- retrieveFeatureInfo(se, o, assay.type=by.assay.type)
         if (!is.null(multiplier)) {
             other$value<- other$value[multiplier]
         }
@@ -34,10 +34,10 @@
 }
 
 .incorporate_common_vis_col <- function(df, se, mode, colour_by, size_by,
-        shape_by, order_by, by_exprs_values, by_show_single, other_fields,
+        shape_by, order_by, by.assay.type, by_show_single, other_fields,
         multiplier = NULL, swap_rownames = NULL) 
 {
-    colour_by_out <- retrieveCellInfo(se, colour_by, exprs_values = by_exprs_values,
+    colour_by_out <- retrieveCellInfo(se, colour_by, assay.type = by.assay.type,
         swap_rownames=swap_rownames)
     colour_by <- colour_by_out$name
     if (!is.null(multiplier)) {
@@ -45,7 +45,7 @@
     }
     df$colour_by <- colour_by_out$value
 
-    shape_by_out <- retrieveCellInfo(se, shape_by, exprs_values = by_exprs_values,
+    shape_by_out <- retrieveCellInfo(se, shape_by, assay.type = by.assay.type,
         swap_rownames=swap_rownames)
     shape_by <- shape_by_out$name
     if (!is.null(multiplier)) {
@@ -53,7 +53,7 @@
     }
     df$shape_by <- .coerce_to_factor(shape_by_out$value, 10, "shape_by")
 
-    size_by_out <- retrieveCellInfo(se, size_by, exprs_values = by_exprs_values,
+    size_by_out <- retrieveCellInfo(se, size_by, assay.type = by.assay.type,
         swap_rownames=swap_rownames)
     size_by <- size_by_out$name
     if (!is.null(multiplier)) {
@@ -61,7 +61,7 @@
     }
     df$size_by <- size_by_out$value
 
-    order_by_out <- retrieveCellInfo(se, order_by, exprs_values = by_exprs_values,
+    order_by_out <- retrieveCellInfo(se, order_by, assay.type = by.assay.type,
         swap_rownames=swap_rownames)
     order_by <- order_by_out$name
     df$order_by <- order_by_out$value
@@ -71,7 +71,7 @@
     df <- df[order(df$order_by), ]
 
     for (o in other_fields) {
-        other <- retrieveCellInfo(se, o, exprs_values=by_exprs_values,
+        other <- retrieveCellInfo(se, o, assay.type=by.assay.type,
             swap_rownames=swap_rownames)
         if (!is.null(multiplier)) {
             other$value <- other$value[multiplier]
@@ -132,15 +132,15 @@
 .handle_features <- function(features, object) {
     if (is.logical(features)) {
         if (length(features) != nrow(object)) {
-            stop("logical features index should be the of length nrow(object).")
+            stop("logical features index should be of length nrow(object).")
         }
         features <- rownames(object)[features]
     } else if (is.numeric(features)) {
-        if (any(features > nrow(object)) | any(features <= 0) | any(features != round(features))) {
+        if (any(features > nrow(object)) || any(features <= 0) || any(features != round(features))) {
             stop("All features should be round numbers; > 0 and <= nrow(object).")
         }
         features <- rownames(object)[features]
-    } else if (is.character(features) | is.factor(features)) {
+    } else if (is.character(features) || is.factor(features)) {
         if (!all(as.character(features) %in% rownames(object))) {
             stop("Some features not in input object.")
         }
diff --git a/R/utils_reddim.R b/R/utils_reddim.R
index fce0676..382ef92 100644
--- a/R/utils_reddim.R
+++ b/R/utils_reddim.R
@@ -1,6 +1,6 @@
 #' @importFrom SummarizedExperiment assay
 #' @importFrom SingleCellExperiment reducedDim
-.get_mat_from_sce <- function(x, exprs_values, dimred, n_dimred) {
+.get_mat_from_sce <- function(x, exprs_values, dimred, n_dimred, assay.type=exprs_values) {
     if (!is.null(dimred)) {
         mat <- reducedDim(x, dimred)
         if (!is.null(n_dimred)) {
@@ -11,7 +11,7 @@
         }
         mat
     } else {
-        assay(x, exprs_values)
+        assay(x, assay.type)
     }
 }
 
diff --git a/build/vignette.rds b/build/vignette.rds
index 755f493..52a915f 100644
Binary files a/build/vignette.rds and b/build/vignette.rds differ
diff --git a/debian/changelog b/debian/changelog
index f09654d..e1ec2c2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+r-bioc-scater (1.28.0+ds-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 21 May 2023 18:52:45 -0000
+
 r-bioc-scater (1.26.1+ds-1) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/exclude_snifter_from_test.patch b/debian/patches/exclude_snifter_from_test.patch
index 1d2c1cf..f6c69a9 100644
--- a/debian/patches/exclude_snifter_from_test.patch
+++ b/debian/patches/exclude_snifter_from_test.patch
@@ -2,9 +2,11 @@ Description: Ignore test requiring not yet packaged r-bioc-snifter
 Author: Andreas Tille <tille@debian.org>
 Last-Update: Sun, 22 May 2022 15:30:26 +0200
 
---- a/tests/testthat/test-red-dim.R
-+++ b/tests/testthat/test-red-dim.R
-@@ -67,12 +67,6 @@ test_that("runPCA works as expected", {
+Index: r-bioc-scater.git/tests/testthat/test-red-dim.R
+===================================================================
+--- r-bioc-scater.git.orig/tests/testthat/test-red-dim.R
++++ r-bioc-scater.git/tests/testthat/test-red-dim.R
+@@ -66,12 +66,6 @@ test_that("runPCA works as expected", {
  })
  
  
@@ -17,7 +19,7 @@ Last-Update: Sun, 22 May 2022 15:30:26 +0200
  test_that("runPCA responds to changes to various settings", {
      # Testing that various settings give different results.
      normed2 <- runPCA(normed)
-@@ -218,52 +212,6 @@ test_that("runTSNE works as expected", {
+@@ -217,52 +211,6 @@ test_that("runTSNE works as expected", {
      normed3 <- runTSNE(normed)
      expect_equal(reducedDim(normed2), reducedDim(normed3))
  
diff --git a/inst/NEWS.Rd b/inst/NEWS.Rd
index 57dde5e..da17899 100644
--- a/inst/NEWS.Rd
+++ b/inst/NEWS.Rd
@@ -1,6 +1,25 @@
 \name{NEWS}
 \title{News for Package \pkg{scater}}
 
+\section{Changes in version 1.28.0, Bioconductor 3.17 Release}{
+  \itemize{
+    \item Change \code{exprs_values} (and similar) to \code{assay.type}.
+    \item Tweak colouring of violin plots.
+    \item Fix use of block arguments in \code{plotGroupedHeatmap}.
+    \item Add scattermore and binning support to various plots (eg \code{plotReducedDim}).
+  }
+}
+
+
+\section{Changes in version 1.28.0, Bioconductor 3.17 Release}{
+  \itemize{
+    \item \code{swap_rownames} works in \code{retrieveCellInfo} for
+    \code{altExp} now as well as in the main assay.
+    \item Add \code{point_shape} argument to \code{plotDots} and
+    \code{plotPlatePosition}.
+  }
+}
+
 \section{Changes in version 1.26.0, Bioconductor 3.16 Release}{
   \itemize{
     \item Add \code{projectReducedDim} function to project points into
@@ -9,6 +28,11 @@
     \item Add \code{order_by} argument to cellwise plot functions.
     \item Add \code{rasterise} argument to \code{plotReducedDim} using
     \code{\link[ggrastr]{rasterise}}.
+    \item Add \code{use_densvis} argument to \code{runTSNE} to add densvis support in UMAP and TSNE embeddings.
+    \item Make multiplot defunct, remove deprecated args in \code{plotHeatmap},
+    \code{plotDots}.
+    \item Add \code{point_fun} argument to \code{plotColData} and
+    \code{plotExpression} determine the geom used to draw cells in those plots.
   }
 }
 \section{Changes in version 1.24.0, Bioconductor 3.15 Release}{
diff --git a/man/defunct.Rd b/man/defunct.Rd
index e2c4c6c..2ce78e7 100644
--- a/man/defunct.Rd
+++ b/man/defunct.Rd
@@ -8,6 +8,7 @@
 \alias{calculateDiffusionMap}
 \alias{calculateDiffusionMap,ANY-method}
 \alias{runDiffusionMap}
+\alias{multiplot}
 \title{Defunct functions}
 \usage{
 calculateQCMetrics(...)
@@ -21,6 +22,8 @@ calculateDiffusionMap(x, ...)
 \S4method{calculateDiffusionMap}{ANY}(x, ...)
 
 runDiffusionMap(...)
+
+multiplot(...)
 }
 \arguments{
 \item{object, x, ...}{Ignored arguments.}
diff --git a/man/getVarianceExplained.Rd b/man/getVarianceExplained.Rd
index c5522cf..81f6bca 100644
--- a/man/getVarianceExplained.Rd
+++ b/man/getVarianceExplained.Rd
@@ -10,7 +10,13 @@ getVarianceExplained(x, ...)
 
 \S4method{getVarianceExplained}{ANY}(x, variables, subset_row = NULL, BPPARAM = SerialParam())
 
-\S4method{getVarianceExplained}{SummarizedExperiment}(x, variables = NULL, ..., exprs_values = "logcounts")
+\S4method{getVarianceExplained}{SummarizedExperiment}(
+  x,
+  variables = NULL,
+  ...,
+  exprs_values = "logcounts",
+  assay.type = exprs_values
+)
 }
 \arguments{
 \item{x}{A numeric matrix of expression values, usually log-transformed and normalized.
@@ -30,7 +36,9 @@ or \code{NULL}, in which case all columns in \code{colData(x)} are used.}
 
 \item{BPPARAM}{A \linkS4class{BiocParallelParam} object specifying whether the calculations should be parallelized.}
 
-\item{exprs_values}{String or integer scalar specifying the expression values for which to compute the variance.}
+\item{exprs_values}{Alias for \code{assay.type}.}
+
+\item{assay.type}{String or integer scalar specifying the expression values for which to compute the variance (also an alias \code{exprs_value} is accepted).}
 }
 \value{
 A numeric matrix containing the percentage of variance explained by each factor (column) and for each gene (row).
diff --git a/man/ggsce.Rd b/man/ggsce.Rd
index c52f825..e13b4f6 100644
--- a/man/ggsce.Rd
+++ b/man/ggsce.Rd
@@ -15,6 +15,7 @@ ggcells(
   prefix_altexps = FALSE,
   check_names = TRUE,
   extract_mapping = TRUE,
+  assay.type = exprs_values,
   ...
 )
 
@@ -25,6 +26,7 @@ ggfeatures(
   exprs_values = "logcounts",
   check_names = TRUE,
   extract_mapping = TRUE,
+  assay.type = exprs_values,
   ...
 )
 }
@@ -35,12 +37,14 @@ This is expected to have row names for \code{ggcells} and column names for \code
 \item{mapping}{A list containing aesthetic mappings, usually the output of \code{\link{aes}} or related functions.}
 
 \item{features}{Character vector specifying the features for which to extract expression profiles across cells.
-May also include features in alternative Experiments if permitted by \code{use_altexps}.}
+May also include features in alternative Experiments if permitted by \code{use.altexps}.}
 
 \item{exprs_values, use_dimred, use_altexps, prefix_altexps, check_names}{Soft-deprecated equivalents of the arguments described above.}
 
 \item{extract_mapping}{Logical scalar indicating whether \code{features} or \code{cells} should be automatically expanded to include variables referenced in \code{mapping}.}
 
+\item{assay.type}{String or integer scalar specifying the expression values for which to compute the variance (also an alias \code{exprs_value} is accepted).}
+
 \item{...}{Further arguments to pass to \link{ggplot}.}
 
 \item{cells}{Character vector specifying the features for which to extract expression profiles across cells.}
diff --git a/man/multiplot.Rd b/man/multiplot.Rd
deleted file mode 100644
index 763d7fd..0000000
--- a/man/multiplot.Rd
+++ /dev/null
@@ -1,70 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/multiplot.R
-\name{multiplot}
-\alias{multiplot}
-\title{Multiple plot function for ggplot2 plots}
-\usage{
-multiplot(..., plotlist = NULL, cols = 1, layout = NULL)
-}
-\arguments{
-\item{...}{One or more ggplot objects.}
-
-\item{plotlist}{A list of ggplot objects, as an alternative to \code{...}.}
-
-\item{cols}{A numeric scalar giving the number of columns in the layout.}
-
-\item{layout}{A matrix specifying the layout. 
-If present, \code{cols} is ignored.}
-}
-\value{
-A ggplot object if one plot is supplied, otherwise an object of class
-"gtable" returned by \code{\link{grid.arrange}}.
-}
-\description{
-Place multiple \code{\link[ggplot2]{ggplot}} plots on one page.
-This function is deprecated in favour of \code{\link{grid.arrange}}.
-It will be defunct in the next release.
-}
-\details{
-If the layout is something like  \code{matrix(c(1,2,3,3), nrow=2, byrow=TRUE)}, then:
-\itemize{
-\item plot 1 will go in the upper left;
-\item plot 2 will go in the upper right;
-\item and plot 3 will go all the way across the bottom.
-}
-There is no way to tweak the relative heights or widths of the plots with this simple function. 
-It was adapted from \url{http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2)/}
-}
-\examples{
-library(ggplot2)
-
-## This example uses the ChickWeight dataset, which comes with ggplot2
-## First plot
-p1 <- ggplot(ChickWeight, aes(x = Time, y = weight, colour = Diet, group = Chick)) +
-   geom_line() +
-   ggtitle("Growth curve for individual chicks")
-## Second plot
-p2 <- ggplot(ChickWeight, aes(x = Time, y = weight, colour = Diet)) +
-   geom_point(alpha = .3) +
-   geom_smooth(alpha = .2, size = 1) +
-   ggtitle("Fitted growth curve per diet")
-
-## Third plot
-p3 <- ggplot(subset(ChickWeight, Time == 21), aes(x = weight, colour = Diet)) +
-   geom_density() +
-   ggtitle("Final weight, by diet")
-## Fourth plot
-p4 <- ggplot(subset(ChickWeight, Time == 21), aes(x = weight, fill = Diet)) +
-    geom_histogram(colour = "black", binwidth = 50) +
-   facet_grid(Diet ~ .) +
-   ggtitle("Final weight, by diet") +
-   theme(legend.position = "none")        # No legend (redundant in this graph)
-
-\dontrun{
-  ## Combine plots and display
-  multiplot(p1, p2, p3, p4, cols = 2)
-  g <- multiplot(p1, p2, p3, p4, cols = 2)
-  grid::grid.draw(g)
-}
-
-}
diff --git a/man/nexprs.Rd b/man/nexprs.Rd
index 6e4f8c7..22205ef 100644
--- a/man/nexprs.Rd
+++ b/man/nexprs.Rd
@@ -17,7 +17,7 @@ nexprs(x, ...)
   BPPARAM = SerialParam()
 )
 
-\S4method{nexprs}{SummarizedExperiment}(x, ..., exprs_values = "counts")
+\S4method{nexprs}{SummarizedExperiment}(x, ..., exprs_values = "counts", assay.type = exprs_values)
 }
 \arguments{
 \item{x}{A numeric matrix of counts where features are rows and cells are columns.
@@ -40,7 +40,9 @@ If \code{FALSE}, the function will count the number of detected features per cel
 \item{BPPARAM}{A \linkS4class{BiocParallelParam} object specifying whether the calculations should be parallelized.
 Only relevant when \code{x} is a \linkS4class{DelayedMatrix}.}
 
-\item{exprs_values}{String or integer specifying the assay of \code{x} to obtain the count matrix from.}
+\item{exprs_values}{Alias for \code{assay.type}.}
+
+\item{assay.type}{String or integer specifying the assay of \code{x} to obtain the count matrix from (also the alias \code{exprs_values} is accepted for this argument).}
 }
 \value{
 An integer vector containing counts per gene or cell, depending on the provided arguments.
diff --git a/man/plotColData.Rd b/man/plotColData.Rd
index c0f8232..c8021a9 100644
--- a/man/plotColData.Rd
+++ b/man/plotColData.Rd
@@ -16,14 +16,22 @@ plotColData(
   other_fields = list(),
   swap_rownames = NULL,
   color_by = NULL,
+  point_fun = NULL,
+  scattermore = FALSE,
+  bins = NULL,
+  summary_fun = "sum",
+  hex = FALSE,
+  by.assay.type = by_exprs_values,
   ...
 )
 }
 \arguments{
-\item{object}{A \linkS4class{SingleCellExperiment} object containing expression values and experimental information.}
+\item{object}{A \linkS4class{SingleCellExperiment} object containing
+expression values and experimental information.}
 
-\item{y}{String specifying the column-level metadata field to show on the y-axis.
-Alternatively, an \link{AsIs} vector or data.frame, see \code{?\link{retrieveCellInfo}}.}
+\item{y}{String specifying the column-level metadata field to show on the
+y-axis. Alternatively, an \link{AsIs} vector or data.frame, see
+\code{?\link{retrieveCellInfo}}.}
 
 \item{x}{String specifying the column-level metadata to show on the x-axis.
 Alternatively, an \link{AsIs} vector or data.frame, see \code{?\link{retrieveCellInfo}}.
@@ -37,18 +45,42 @@ If \code{NULL}, nothing is shown on the x-axis.}
 
 \item{order_by}{Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, 
-for use in point aesthetics - see \code{?\link{retrieveCellInfo}} for details.}
+\item{by_exprs_values}{Alias for \code{by.assay.type}.}
 
 \item{other_fields}{Additional cell-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.}
 
-\item{swap_rownames}{Column name of \code{rowData(object)} to be used to 
-identify features instead of \code{rownames(object)} when labelling plot 
+\item{swap_rownames}{Column name of \code{rowData(object)} to be used to
+identify features instead of \code{rownames(object)} when labelling plot
 elements.}
 
 \item{color_by}{Alias to \code{colour_by}.}
 
-\item{...}{Additional arguments for visualization, see \code{?"\link{scater-plot-args}"} for details.}
+\item{point_fun}{Function used to create a geom that shows individual cells.
+Should take \code{...} args and return a ggplot2 geom. For example,
+\code{point_fun=function(...) geom_quasirandom(...)}.}
+
+\item{scattermore}{Logical, whether to use the \code{scattermore} package to
+greatly speed up plotting a large number of cells. Use \code{point_size =
+0} for the most performance gain.}
+
+\item{bins}{Number of bins, can be different in x and y, to bin and summarize
+the points and their values, to avoid overplotting. If \code{NULL}
+(default), then the points are plotted without binning. Only used when both
+x and y are numeric.}
+
+\item{summary_fun}{Function to summarize the feature value of each point
+(e.g. gene expression of each cell) when the points binned, defaults to
+\code{sum}. Can be either the name of the function or the function itself.}
+
+\item{hex}{Logical, whether to use \code{\link{geom_hex}}. Note that
+\code{geom_hex} is broken in \code{ggplot2} version 3.4.0.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from,
+for use in point aesthetics - see \code{?\link{retrieveCellInfo}} for
+details (also alias \code{by_exprs_values} is accepted for this argument).}
+
+\item{...}{Additional arguments for visualization, see
+\code{?"\link{scater-plot-args}"} for details.}
 }
 \value{
 A \link{ggplot} object.
@@ -57,32 +89,49 @@ A \link{ggplot} object.
 Plot column-level (i.e., cell) metadata in an SingleCellExperiment object.
 }
 \details{
-If \code{y} is continuous and \code{x=NULL}, a violin plot is generated.
-If \code{x} is categorical, a grouped violin plot will be generated, with one violin for each level of \code{x}.
-If \code{x} is continuous, a scatter plot will be generated.
-
-If \code{y} is categorical and \code{x} is continuous, horizontal violin plots will be generated.
-If \code{x} is missing or categorical, rectangule plots will be generated where the area of a rectangle is proportional to the number of points for a combination of factors.
+If \code{y} is continuous and \code{x=NULL}, a violin plot is
+  generated. If \code{x} is categorical, a grouped violin plot will be
+  generated, with one violin for each level of \code{x}. If \code{x} is
+  continuous, a scatter plot will be generated.
+
+  If \code{y} is categorical and \code{x} is continuous, horizontal violin
+  plots will be generated. If \code{x} is missing or categorical, rectangule
+  plots will be generated where the area of a rectangle is proportional to
+  the number of points for a combination of factors.
+}
+\note{
+Arguments \code{shape_by} and \code{size_by} are ignored when
+\code{scattermore = TRUE}. Using \code{scattermore} is only recommended for
+very large datasets to speed up plotting. Small point size is also
+recommended. For larger point size, the point shape may be distorted. Also,
+when \code{scattermore = TRUE}, the \code{point_size} argument works
+differently.
 }
 \examples{
 example_sce <- mockSCE()
 example_sce <- logNormCounts(example_sce)
-colData(example_sce) <- cbind(colData(example_sce), 
+colData(example_sce) <- cbind(colData(example_sce),
     perCellQCMetrics(example_sce))
 
-plotColData(example_sce, y = "detected", x = "sum", 
+plotColData(example_sce, y = "detected", x = "sum",
    colour_by = "Mutation_Status") + scale_x_log10()
 
-plotColData(example_sce, y = "detected", x = "sum", 
-   colour_by = "Mutation_Status", size_by = "Gene_0001", 
+plotColData(example_sce, y = "detected", x = "sum",
+   colour_by = "Mutation_Status", size_by = "Gene_0001",
    shape_by = "Treatment") + scale_x_log10()
 
-plotColData(example_sce, y = "Treatment", x = "sum", 
+plotColData(example_sce, y = "Treatment", x = "sum",
    colour_by = "Mutation_Status") + scale_y_log10() # flipped violin.
 
-plotColData(example_sce, y = "detected", 
+plotColData(example_sce, y = "detected",
    x = "Cell_Cycle", colour_by = "Mutation_Status")
-
+# With scattermore
+plotColData(example_sce, x = "sum", y = "detected", scattermore = TRUE,
+   point_size = 2)
+# Bin to show point density
+plotColData(example_sce, x = "sum", y = "detected", bins = 10)
+# Bin to summarize value (default is sum)
+plotColData(example_sce, x = "sum", y = "detected", bins = 10, colour_by = "total")
 }
 \author{
 Davis McCarthy, with modifications by Aaron Lun
diff --git a/man/plotDots.Rd b/man/plotDots.Rd
index 9919a4b..fbbae70 100644
--- a/man/plotDots.Rd
+++ b/man/plotDots.Rd
@@ -20,9 +20,8 @@ plotDots(
   swap_rownames = NULL,
   center = FALSE,
   scale = FALSE,
-  low_colour = NULL,
-  high_colour = NULL,
-  max_ave = NULL
+  assay.type = exprs_values,
+  by.assay.type = by_exprs_values
 )
 }
 \arguments{
@@ -36,7 +35,7 @@ Alternatively, any value that can be used in the \code{by} argument to \code{\li
 \item{block}{String specifying the field of \code{\link{colData}(object)} containing a blocking factor (e.g., batch of origin).
 Alternatively, any value that can be used in the \code{by} argument to \code{\link{retrieveCellInfo}}.}
 
-\item{exprs_values}{A string or integer scalar indicating which assay of \code{object} should be used as expression values.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
 \item{detection_limit}{Numeric scalar providing the value above which observations are deemed to be expressed.}
 
@@ -57,7 +56,7 @@ detected expression values.}
 \item{other_fields}{Additional feature-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.
 Note that any \link{AsIs} vectors or data.frames must be of length equal to \code{nrow(object)}, not \code{features}.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, for entries of \code{other_fields}.}
+\item{by_exprs_values}{Alias for \code{by.assay.type}.}
 
 \item{swap_rownames}{Column name of \code{rowData(object)} to be used to 
 identify features instead of \code{rownames(object)} when labelling plot 
@@ -69,7 +68,9 @@ elements.}
 \item{scale}{A logical scalar specifying whether each row should have its
 average expression values scaled to unit variance prior to plotting.}
 
-\item{low_colour, high_colour, max_ave}{Deprecated arguments.}
+\item{assay.type}{A string or integer scalar indicating which assay of \code{object} should be used as expression values.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from, for entries of \code{other_fields}. Also alias \code{by_exprs_values} is accepted as argument name.}
 }
 \value{
 A \link{ggplot} object containing a dot plot.
@@ -82,7 +83,7 @@ respectively, for each feature in each group of cells.
 \details{
 This implements a \pkg{Seurat}-style \dQuote{dot plot} that creates a dot for each feature (row) in each group of cells (column).
 The proportion of detected expression values and the average expression for each feature in each group of cells is visualized efficiently using the size and colour, respectively, of each dot.
-If \code{block} is specified, batch-corrected averages for each group are computed with \code{\link{correctGroupSummary}}.
+If \code{block} is specified, batch-corrected averages and proportions for each group are computed with \code{\link{correctGroupSummary}}.
 
 Some caution is required during interpretation due to the difficulty of simultaneously interpreting both size and colour.
 For example, if we coloured by z-score on a conventional blue-white-red colour axis, a gene that is downregulated in a group of cells would show up as a small blue dot.
diff --git a/man/plotExpression.Rd b/man/plotExpression.Rd
index 7994cac..d10d69d 100644
--- a/man/plotExpression.Rd
+++ b/man/plotExpression.Rd
@@ -24,6 +24,13 @@ plotExpression(
   swap_rownames = NULL,
   color_by = NULL,
   feature_colors = TRUE,
+  point_fun = NULL,
+  assay.type = exprs_values,
+  scattermore = FALSE,
+  bins = NULL,
+  summary_fun = "sum",
+  hex = FALSE,
+  by.assay.type = by_exprs_values,
   ...
 )
 }
@@ -35,7 +42,7 @@ If a list is supplied, each entry of the list can be a string, an AsIs-wrapped v
 
 \item{x}{Specification of a column metadata field or a feature to show on the x-axis, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.}
 
-\item{exprs_values}{A string or integer scalar specifying which assay in \code{assays(object)} to obtain expression values from.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
 \item{log2_values}{Logical scalar, specifying whether the expression values be transformed to the log2-scale for plotting (with an offset of 1 to avoid logging zeroes).}
 
@@ -47,8 +54,7 @@ If a list is supplied, each entry of the list can be a string, an AsIs-wrapped v
 
 \item{order_by}{Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, 
-for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.}
+\item{by_exprs_values}{Alias to \code{by.assay.type}.}
 
 \item{xlab}{String specifying the label for x-axis.
 If \code{NULL} (default), \code{x} will be used as the x-axis label.}
@@ -65,14 +71,37 @@ Passed to the \code{scales} argument in the \code{\link[ggplot2]{facet_wrap}} wh
 
 \item{other_fields}{Additional cell-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.}
 
-\item{swap_rownames}{Column name of \code{rowData(object)} to be used to 
-identify features instead of \code{rownames(object)} when labelling plot 
+\item{swap_rownames}{Column name of \code{rowData(object)} to be used to
+identify features instead of \code{rownames(object)} when labelling plot
 elements.}
 
 \item{color_by}{Alias to \code{colour_by}.}
 
 \item{feature_colors}{Alias to \code{feature_colours}.}
 
+\item{point_fun}{Function used to create a geom that shows individual cells. Should take \code{...} args and return a ggplot2 geom. For example, \code{point_fun=function(...) geom_quasirandom(...)}.}
+
+\item{assay.type}{A string or integer scalar specifying which assay in \code{assays(object)} to obtain expression values from. Also the alias \code{assay.type} is accepted.}
+
+\item{scattermore}{Logical, whether to use the \code{scattermore} package to
+greatly speed up plotting a large number of cells. Use \code{point_size =
+0} for the most performance gain.}
+
+\item{bins}{Number of bins, can be different in x and y, to bin and summarize
+the points and their values, to avoid overplotting. If \code{NULL}
+(default), then the points are plotted without binning. Only used when both
+x and y are numeric.}
+
+\item{summary_fun}{Function to summarize the feature value of each point
+(e.g. gene expression of each cell) when the points binned, defaults to
+\code{sum}. Can be either the name of the function or the function itself.}
+
+\item{hex}{Logical, whether to use \code{\link{geom_hex}}. Note that
+\code{geom_hex} is broken in \code{ggplot2} version 3.4.0.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from,
+for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}. Also the alias \code{by.assay.type} is accepted.}
+
 \item{...}{Additional arguments for visualization, see \code{?"\link{scater-plot-args}"} for details.}
 }
 \value{
@@ -96,6 +125,14 @@ Note that this assumes that the expression values are numeric.
 If not, and \code{x} is continuous, horizontal violin plots will be generated.
 If \code{x} is missing or categorical, rectangule plots will be generated where the area of a rectangle is proportional to the number of points for a combination of factors.
 }
+\note{
+Arguments \code{shape_by} and \code{size_by} are ignored when
+\code{scattermore = TRUE}. Using \code{scattermore} is only recommended for
+very large datasets to speed up plotting. Small point size is also
+recommended. For larger point size, the point shape may be distorted. Also,
+when \code{scattermore = TRUE}, the \code{point_size} argument works
+differently.
+}
 \examples{
 example_sce <- mockSCE()
 example_sce <- logNormCounts(example_sce)
@@ -104,22 +141,31 @@ example_sce <- logNormCounts(example_sce)
 plotExpression(example_sce, rownames(example_sce)[1:15])
 
 ## plot expression against an x-axis value
-plotExpression(example_sce, c("Gene_0001", "Gene_0004"), 
+plotExpression(example_sce, c("Gene_0001", "Gene_0004"),
     x="Mutation_Status")
-plotExpression(example_sce, c("Gene_0001", "Gene_0004"), 
+plotExpression(example_sce, c("Gene_0001", "Gene_0004"),
     x="Gene_0002")
 
 ## add visual options
-plotExpression(example_sce, rownames(example_sce)[1:6], 
+plotExpression(example_sce, rownames(example_sce)[1:6],
     colour_by = "Mutation_Status")
-plotExpression(example_sce, rownames(example_sce)[1:6], 
-    colour_by = "Mutation_Status", shape_by = "Treatment", 
+plotExpression(example_sce, rownames(example_sce)[1:6],
+    colour_by = "Mutation_Status", shape_by = "Treatment",
     size_by = "Gene_0010")
 
 ## plot expression against expression values for Gene_0004
 plotExpression(example_sce, rownames(example_sce)[1:4],
     "Gene_0004", show_smooth = TRUE)
 
+# Use scattermore
+plotExpression(example_sce, "Gene_0001", x = "Gene_0100", scattermore = TRUE,
+    point_size = 2)
+# Bin to show point density
+plotExpression(example_sce, "Gene_0001", x = "Gene_0100", bins = 10)
+# Bin to summarize values (default is sum but can be changed with summary_fun)
+plotExpression(example_sce, "Gene_0001", x = "Gene_0100", bins = 10,
+    colour_by = "Gene_0002", summary_fun = "mean")
+
 }
 \author{
 Davis McCarthy, with modifications by Aaron Lun
diff --git a/man/plotGroupedHeatmap.Rd b/man/plotGroupedHeatmap.Rd
index 6b47672..f1ce71f 100644
--- a/man/plotGroupedHeatmap.Rd
+++ b/man/plotGroupedHeatmap.Rd
@@ -16,8 +16,8 @@ plotGroupedHeatmap(
   zlim = NULL,
   colour = color,
   swap_rownames = NULL,
-  symmetric = NULL,
   color = NULL,
+  assay.type = exprs_values,
   ...
 )
 }
@@ -34,7 +34,7 @@ Alternatively, any value that can be used in the \code{by} argument to \code{\li
 
 \item{columns}{A vector specifying the subset of columns in \code{object} to use when computing averages.}
 
-\item{exprs_values}{A string or integer scalar indicating which assay of \code{object} should be used as expression values.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
 \item{center}{A logical scalar indicating whether each feature should have its mean expression 
 (specifically, the mean of averages across all groups) centered at zero prior to plotting.}
@@ -55,10 +55,10 @@ colour palette from \code{\link[RColorBrewer]{brewer.pal}} otherwise.}
 identify features instead of \code{rownames(object)} when labelling plot 
 elements.}
 
-\item{symmetric}{Deprecated and ignored.}
-
 \item{color}{Alias to \code{colour}.}
 
+\item{assay.type}{A string or integer scalar indicating which assay of \code{object} should be used as expression values.}
+
 \item{...}{Additional arguments to pass to \code{\link[pheatmap]{pheatmap}}.}
 }
 \value{
diff --git a/man/plotHeatmap.Rd b/man/plotHeatmap.Rd
index 1d246c4..609d3f9 100644
--- a/man/plotHeatmap.Rd
+++ b/man/plotHeatmap.Rd
@@ -13,21 +13,22 @@ plotHeatmap(
   scale = FALSE,
   zlim = NULL,
   colour = color,
+  color = NULL,
   colour_columns_by = color_columns_by,
+  color_columns_by = NULL,
   column_annotation_colours = column_annotation_colors,
-  colour_rows_by = color_rows_by,
+  column_annotation_colors = list(),
   row_annotation_colours = row_annotation_colors,
+  row_annotation_colors = list(),
+  colour_rows_by = color_rows_by,
+  color_rows_by = NULL,
   order_columns_by = NULL,
   by_exprs_values = exprs_values,
   show_colnames = FALSE,
   cluster_cols = is.null(order_columns_by),
   swap_rownames = NULL,
-  symmetric = NULL,
-  color = NULL,
-  color_columns_by = NULL,
-  color_rows_by = NULL,
-  column_annotation_colors = list(),
-  row_annotation_colors = list(),
+  assay.type = exprs_values,
+  by.assay.type = by_exprs_values,
   ...
 )
 }
@@ -40,7 +41,7 @@ plotHeatmap(
 Also specifies the column order if \code{cluster_cols=FALSE} and \code{order_columns_by=NULL}.
 By default, all columns are used.}
 
-\item{exprs_values}{A string or integer scalar indicating which assay of \code{object} should be used as expression values.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
 \item{center}{A logical scalar indicating whether each feature should have its mean expression centered at zero prior to plotting.}
 
@@ -55,6 +56,10 @@ If \code{center=TRUE}, this defaults to the range of the centered expression mat
 This defaults to \link[viridis]{viridis} if \code{center=FALSE}, and the the \code{"RdYlBu"}
 colour palette from \code{\link[RColorBrewer]{brewer.pal}} otherwise.}
 
+\item{color, color_columns_by, column_annotation_colors, color_rows_by, row_annotation_colors}{Aliases to \code{color}, \code{color_columns_by},
+\code{column_annotation_colors}, \code{color_rows_by}, 
+\code{row_annotation_colors}.}
+
 \item{colour_columns_by}{A list of values specifying how the columns should be annotated with colours.
 Each entry of the list can be any acceptable input to the \code{by} argument in \code{?\link{retrieveCellInfo}}.
 A character vector can also be supplied and will be treated as a list of strings.}
@@ -67,20 +72,19 @@ colour scale is chosen.
 The full list of colour maps is passed to \code{\link[pheatmap]{pheatmap}} 
 as the \code{annotation_colours} argument.}
 
+\item{row_annotation_colours}{Similar to \code{column_annotation_colours} but 
+relating to row annotation rather than column annotation.}
+
 \item{colour_rows_by}{Similar to \code{colour_columns_by} but for rows rather
 than columns. Each entry of the list can be any acceptable input to the 
 \code{by} argument in \code{?\link{retrieveFeatureInfo}}.}
 
-\item{row_annotation_colours}{Similar to \code{column_annotation_colours} but 
-relating to row annotation rather than column annotation.}
-
 \item{order_columns_by}{A list of values specifying how the columns should be ordered.
 Each entry of the list can be any acceptable input to the \code{by} argument in \code{?\link{retrieveCellInfo}}.
 A character vector can also be supplied and will be treated as a list of strings.
 This argument is automatically appended to \code{colour_columns_by}.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, 
-for colouring of column-level data - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.}
+\item{by_exprs_values}{Alias to {by.assay.type}.}
 
 \item{show_colnames, cluster_cols, ...}{Additional arguments to pass to \code{\link[pheatmap]{pheatmap}}.}
 
@@ -88,11 +92,10 @@ for colouring of column-level data - see the \code{exprs_values} argument in \co
 identify features instead of \code{rownames(object)} when labelling plot 
 elements.}
 
-\item{symmetric}{Deprecated and ignored.}
+\item{assay.type}{A string or integer scalar indicating which assay of \code{object} should be used as expression values.}
 
-\item{color, color_columns_by, column_annotation_colors, color_rows_by, row_annotation_colors}{Aliases to \code{color}, \code{color_columns_by},
-\code{column_annotation_colors}, \code{color_rows_by}, 
-\code{row_annotation_colors}.}
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from, 
+for colouring of column-level data - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.}
 }
 \value{
 A heatmap is produced on the current graphics device. 
diff --git a/man/plotHighestExprs.Rd b/man/plotHighestExprs.Rd
index 2d343c1..285328e 100644
--- a/man/plotHighestExprs.Rd
+++ b/man/plotHighestExprs.Rd
@@ -14,7 +14,9 @@ plotHighestExprs(
   feature_names_to_plot = NULL,
   as_percentage = TRUE,
   swap_rownames = NULL,
-  color_cells_by = NULL
+  color_cells_by = NULL,
+  assay.type = exprs_values,
+  by.assay.type = by_exprs_values
 )
 }
 \arguments{
@@ -27,23 +29,27 @@ plotHighestExprs(
 \item{drop_features}{A character, logical or numeric vector indicating which features (e.g. genes, transcripts) to drop when producing the plot. 
 For example, spike-in transcripts might be dropped to examine the contribution from endogenous genes.}
 
-\item{exprs_values}{A integer scalar or string specifying the assay to obtain expression values from.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, 
-for use in colouring - see \code{?\link{retrieveCellInfo}} for details.}
+\item{by_exprs_values}{Alias to \code{by.assay.type}.}
 
 \item{feature_names_to_plot}{String specifying which row-level metadata column contains the feature names.
 Alternatively, an \link{AsIs}-wrapped vector or a data.frame, see \code{?\link{retrieveFeatureInfo}} for possible values.
 Default is \code{NULL}, in which case \code{rownames(object)} are used.}
 
 \item{as_percentage}{logical scalar indicating whether percentages should be  plotted. 
-If \code{FALSE}, the raw \code{exprs_values} are shown instead.}
+If \code{FALSE}, the raw \code{assay.type} are shown instead.}
 
 \item{swap_rownames}{Column name of \code{rowData(object)} to be used to 
 identify features instead of \code{rownames(object)} when labelling plot 
 elements.}
 
 \item{color_cells_by}{Alias to \code{colour_cells_by}.}
+
+\item{assay.type}{A integer scalar or string specifying the assay to obtain expression values from.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from, 
+for use in colouring - see \code{?\link{retrieveCellInfo}} for details.}
 }
 \value{
 A \link{ggplot} object.
diff --git a/man/plotPlatePosition.Rd b/man/plotPlatePosition.Rd
index 29a4a70..9bfc7e1 100644
--- a/man/plotPlatePosition.Rd
+++ b/man/plotPlatePosition.Rd
@@ -16,9 +16,11 @@ plotPlatePosition(
   theme_size = 24,
   point_alpha = 0.6,
   point_size = 24,
+  point_shape = 19,
   other_fields = list(),
   swap_rownames = NULL,
-  color_by = NULL
+  color_by = NULL,
+  by.assay.type = by_exprs_values
 )
 }
 \arguments{
@@ -36,8 +38,7 @@ Alternatively, a list of two factors (\code{"row"} and \code{"column"}) can be s
 
 \item{order_by}{Specification of a column metadata field or a feature to order points by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, 
-for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.}
+\item{by_exprs_values}{Alias for \code{by.assay.type}.}
 
 \item{add_legend}{Logical scalar specifying whether a legend should be shown.}
 
@@ -47,6 +48,9 @@ for use in point aesthetics - see the \code{exprs_values} argument in \code{?\li
 
 \item{point_size}{Numeric scalar specifying the size of the points, see \code{?"\link{scater-plot-args}"} for details.}
 
+\item{point_shape}{An integer, or a string specifying the shape
+of the points. See \code{?"\link{scater-plot-args}"} for details.}
+
 \item{other_fields}{Additional cell-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.}
 
 \item{swap_rownames}{Column name of \code{rowData(object)} to be used to 
@@ -54,6 +58,9 @@ identify features instead of \code{rownames(object)} when labelling plot
 elements.}
 
 \item{color_by}{Alias to \code{colour_by}.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from, 
+for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.}
 }
 \value{
 A ggplot object.
diff --git a/man/plotRLE.Rd b/man/plotRLE.Rd
index 2f66167..5d28492 100644
--- a/man/plotRLE.Rd
+++ b/man/plotRLE.Rd
@@ -16,13 +16,16 @@ plotRLE(
   by_exprs_values = exprs_values,
   BPPARAM = BiocParallel::bpparam(),
   color_by = NULL,
+  assay.type = exprs_values,
+  by.assay.type = by_exprs_values,
+  assay_logged = exprs_logged,
   ...
 )
 }
 \arguments{
 \item{object}{A SingleCellExperiment object.}
 
-\item{exprs_values}{A string or integer scalar specifying the expression matrix in \code{object} to use.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
 \item{exprs_logged}{A logical scalar indicating whether the expression matrix is already log-transformed.
 If not, a log2-transformation (+1) will be performed prior to plotting.}
@@ -36,13 +39,19 @@ This can be useful for arranging cells by experimental conditions or batches.}
 
 \item{colour_by}{Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from,
-for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.}
+\item{by_exprs_values}{Alias to \code{by.assay.type}.}
 
 \item{BPPARAM}{A \linkS4class{BiocParallelParam} object to be used to parallelise operations using \code{\link{DelayedArray}}.}
 
 \item{color_by}{Alias to \code{colour_by}.}
 
+\item{assay.type}{A string or integer scalar specifying the expression matrix in \code{object} to use.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from,
+for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.}
+
+\item{assay_logged}{Alias to \code{exprs_logged}.}
+
 \item{...}{further arguments passed to \code{\link[ggplot2]{geom_boxplot}} when \code{style="full"}.}
 }
 \value{
diff --git a/man/plotReducedDim.Rd b/man/plotReducedDim.Rd
index 6de5d58..0c10725 100644
--- a/man/plotReducedDim.Rd
+++ b/man/plotReducedDim.Rd
@@ -25,6 +25,11 @@ plotReducedDim(
   point.padding = NA,
   force = 1,
   rasterise = FALSE,
+  scattermore = FALSE,
+  bins = NULL,
+  summary_fun = "sum",
+  hex = FALSE,
+  by.assay.type = by_exprs_values,
   ...
 )
 }
@@ -59,10 +64,7 @@ possible values.}
 order points by, see the \code{by} argument in
 \code{?\link{retrieveCellInfo}} for possible values.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to
-obtain expression values from,
-for use in point aesthetics - see the \code{exprs_values} argument in
-\code{?\link{retrieveCellInfo}}.}
+\item{by_exprs_values}{Alias for \code{by.assay.type}.}
 
 \item{text_by}{String specifying the column metadata field with which to add
 text labels on the plot.
@@ -99,6 +101,27 @@ elements.}
 \code{options(ggrastr.default.dpi)},
 for example \code{options(ggrastr.default.dpi=300)}.}
 
+\item{scattermore}{Logical, whether to use the \code{scattermore} package to
+greatly speed up plotting a large number of cells. Use \code{point_size =
+0} for the most performance gain.}
+
+\item{bins}{Number of bins, can be different in x and y, to bin and summarize
+the points and their values, to avoid overplotting. If \code{NULL}
+(default), then the points are plotted without binning. Only used when both
+x and y are numeric.}
+
+\item{summary_fun}{Function to summarize the feature value of each point
+(e.g. gene expression of each cell) when the points binned, defaults to
+\code{sum}. Can be either the name of the function or the function itself.}
+
+\item{hex}{Logical, whether to use \code{\link{geom_hex}}. Note that
+\code{geom_hex} is broken in \code{ggplot2} version 3.4.0.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to
+obtain expression values from,
+for use in point aesthetics - see the \code{assay.type} argument in
+\code{?\link{retrieveCellInfo}}.}
+
 \item{...}{Additional arguments for visualization, see
 \code{?"\link{scater-plot-args}"} for details.}
 }
@@ -126,6 +149,14 @@ This is useful for annotating position-related metadata (e.g., clusters)
 when there are too many levels to distinguish by colour.
 It is only available for scatterplots.
 }
+\note{
+Arguments \code{shape_by} and \code{size_by} are ignored when
+\code{scattermore = TRUE}. Using \code{scattermore} is only recommended for
+very large datasets to speed up plotting. Small point size is also
+recommended. For larger point size, the point shape may be distorted. Also,
+when \code{scattermore = TRUE}, the \code{point_size} argument works
+differently.
+}
 \examples{
 example_sce <- mockSCE()
 example_sce <- logNormCounts(example_sce)
@@ -139,6 +170,14 @@ plotReducedDim(example_sce, "PCA", ncomponents=5)
 plotReducedDim(example_sce, "PCA", ncomponents=5, colour_by="Cell_Cycle",
     shape_by="Treatment")
 
+# Use scattermore
+plotPCA(example_sce, ncomponents = 4, scattermore = TRUE, point_size = 3)
+
+# Bin to show point density
+plotPCA(example_sce, bins = 10)
+# Bin to summarize values (default is sum)
+plotPCA(example_sce, bins = 10, colour_by = "Gene_0001")
+
 }
 \author{
 Davis McCarthy, with modifications by Aaron Lun
diff --git a/man/plotRowData.Rd b/man/plotRowData.Rd
index d5f14e9..bf484c3 100644
--- a/man/plotRowData.Rd
+++ b/man/plotRowData.Rd
@@ -14,6 +14,7 @@ plotRowData(
   by_exprs_values = "logcounts",
   other_fields = list(),
   color_by = NULL,
+  by.assay.type = by_exprs_values,
   ...
 )
 }
@@ -33,13 +34,15 @@ If \code{NULL}, nothing is shown on the x-axis.}
 
 \item{size_by}{Specification of a row metadata field or a cell to size by, see \code{?\link{retrieveFeatureInfo}} for possible values.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, 
-for use in point aesthetics - see \code{?\link{retrieveFeatureInfo}} for details.}
+\item{by_exprs_values}{Alias to \code{by.assay.type}.}
 
 \item{other_fields}{Additional feature-based fields to include in the data.frame, see \code{?"\link{scater-plot-args}"} for details.}
 
 \item{color_by}{Alias to \code{colour_by}.}
 
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from, 
+for use in point aesthetics - see \code{?\link{retrieveFeatureInfo}} for details.}
+
 \item{...}{Additional arguments for visualization, see \code{?"\link{scater-plot-args}"} for details.}
 }
 \value{
diff --git a/man/plotScater.Rd b/man/plotScater.Rd
index ae1ff48..854d77e 100644
--- a/man/plotScater.Rd
+++ b/man/plotScater.Rd
@@ -15,7 +15,9 @@ plotScater(
   ncol = 3,
   line_width = 1.5,
   theme_size = 10,
-  color_by = NULL
+  color_by = NULL,
+  assay.type = exprs_values,
+  by.assay.type = by_exprs_values
 )
 }
 \arguments{
@@ -23,13 +25,12 @@ plotScater(
 
 \item{nfeatures}{Numeric scalar indicating the number of top-expressed features to show n the plot.}
 
-\item{exprs_values}{String or integer scalar indicating which assay of \code{object} should be used to obtain the expression values for this plot.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
 \item{colour_by}{Specification of a column metadata field or a feature to colour by, see the \code{by} argument in \code{?\link{retrieveCellInfo}} for possible values. 
 The curve for each cell will be coloured according to this specification.}
 
-\item{by_exprs_values}{A string or integer scalar specifying which assay to obtain expression values from, 
-for use in point aesthetics - see the \code{exprs_values} argument in \code{?\link{retrieveCellInfo}}.}
+\item{by_exprs_values}{Alias to \code{by.assay.type}.}
 
 \item{block1}{String specifying the column-level metadata field by which to separate the cells into separate panels in the plot. 
 Alternatively, an \link{AsIs} vector or data.frame, see \code{?\link{retrieveCellInfo}}.
@@ -44,6 +45,11 @@ Default is \code{NULL}, in which case there is no blocking.}
 \item{theme_size}{Numeric scalar specifying the font size to use for the plotting theme.}
 
 \item{color_by}{Alias to \code{colour_by}.}
+
+\item{assay.type}{String or integer scalar indicating which assay of \code{object} should be used to obtain the expression values for this plot.}
+
+\item{by.assay.type}{A string or integer scalar specifying which assay to obtain expression values from, 
+for use in point aesthetics - see the \code{assay.type} argument in \code{?\link{retrieveCellInfo}}.}
 }
 \value{
 A \link{ggplot} object.
@@ -63,7 +69,7 @@ If both are specified, each panel corresponds to a combination of levels.
 \examples{
 example_sce <- mockSCE()
 plotScater(example_sce)
-plotScater(example_sce, exprs_values = "counts", colour_by = "Cell_Cycle")
+plotScater(example_sce, assay.type = "counts", colour_by = "Cell_Cycle")
 plotScater(example_sce, block1 = "Treatment", colour_by = "Cell_Cycle")
 
 }
diff --git a/man/retrieveCellInfo.Rd b/man/retrieveCellInfo.Rd
index 87f814d..5453479 100644
--- a/man/retrieveCellInfo.Rd
+++ b/man/retrieveCellInfo.Rd
@@ -9,7 +9,8 @@ retrieveCellInfo(
   by,
   search = c("colData", "assays", "altExps"),
   exprs_values = "logcounts",
-  swap_rownames = NULL
+  swap_rownames = NULL,
+  assay.type = exprs_values
 )
 }
 \arguments{
@@ -20,11 +21,13 @@ Alternatively, a data.frame, \linkS4class{DataFrame} or an \link{AsIs} vector.}
 
 \item{search}{Character vector specifying the types of data or metadata to use.}
 
-\item{exprs_values}{String or integer scalar specifying the assay from which expression values should be extracted.}
+\item{exprs_values}{Alias to \code{assay.type}.}
 
-\item{swap_rownames}{Column name of \code{rowData(object)} to be used to 
-identify features instead of \code{rownames(object)} when labelling plot 
+\item{swap_rownames}{Column name of \code{rowData(object)} to be used to
+identify features instead of \code{rownames(object)} when labelling plot
 elements.}
+
+\item{assay.type}{String or integer scalar specifying the assay from which expression values should be extracted.}
 }
 \value{
 A list containing \code{name}, a string with the name of the extracted field (usually identically to \code{by});
@@ -44,16 +47,16 @@ This allows downstream visualization functions to accommodate arbitrary inputs f
 
 Given a character string in \code{by}, this function will:
 \enumerate{
-\item Search \code{\link{colData}} for a column named \code{by}, 
+\item Search \code{\link{colData}} for a column named \code{by},
 and return the corresponding field as the output \code{value}.
 We do not consider nested elements within the \code{colData}.
-\item Search \code{\link{assay}(x, exprs_values)} for a row named \code{by}, 
+\item Search \code{\link{assay}(x, assay.type)} for a row named \code{by},
 and return the expression vector for this feature as the output \code{value}.
 \item Search each alternative experiment in \code{\link{altExps}(x)} for a row names \code{by},
-and return the expression vector for this feature at \code{exprs_values} as the output \code{value}.
+and return the expression vector for this feature at \code{assay.type} as the output \code{value}.
 }
 Any match will cause the function to return without considering later possibilities.
-The search can be modified by changing the presence and ordering of elements in \code{search}. 
+The search can be modified by changing the presence and ordering of elements in \code{search}.
 
 If there is a name clash that results in retrieval of an unintended field,
 users should explicitly set \code{by} to a data.frame, DataFrame or AsIs-wrapped vector containing the desired values.
@@ -74,9 +77,9 @@ retrieveCellInfo(example_sce, data.frame(stuff=arbitrary.field))
 \seealso{
 \code{\link{makePerCellDF}}, which provides a more user-friendly interface to this function.
 
-\code{\link{plotColData}}, 
-\code{\link{plotReducedDim}}, 
-\code{\link{plotExpression}}, 
+\code{\link{plotColData}},
+\code{\link{plotReducedDim}},
+\code{\link{plotExpression}},
 \code{\link{plotPlatePosition}},
 and most other cell-based plotting functions.
 }
diff --git a/man/retrieveFeatureInfo.Rd b/man/retrieveFeatureInfo.Rd
index 8d1676b..e346adb 100644
--- a/man/retrieveFeatureInfo.Rd
+++ b/man/retrieveFeatureInfo.Rd
@@ -8,7 +8,8 @@ retrieveFeatureInfo(
   x,
   by,
   search = c("rowData", "assays"),
-  exprs_values = "logcounts"
+  exprs_values = "logcounts",
+  assay.type = exprs_values
 )
 }
 \arguments{
@@ -19,7 +20,9 @@ Alternatively, a data.frame, \linkS4class{DataFrame} or an \link{AsIs} vector.}
 
 \item{search}{Character vector specifying the types of data or metadata to use.}
 
-\item{exprs_values}{String or integer scalar specifying the assay from which expression values should be extracted.}
+\item{exprs_values}{Alias to \code{assay.type}.}
+
+\item{assay.type}{String or integer scalar specifying the assay from which expression values should be extracted.}
 }
 \value{
 A list containing \code{name}, a string with the name of the extracted field (usually identically to \code{by});
@@ -42,7 +45,7 @@ Given a character string in \code{by}, this function will:
 \item Search \code{\link{rowData}} for a column named \code{by}, 
 and return the corresponding field as the output \code{value}.
 We do not consider nested elements within the \code{rowData}.
-\item Search \code{\link{assay}(x, exprs_values)} for a column named \code{by}, 
+\item Search \code{\link{assay}(x, assay.type)} for a column named \code{by}, 
 and return the expression vector for this feature as the output \code{value}.
 }
 Any match will cause the function to return without considering later possibilities.
diff --git a/man/runMDS.Rd b/man/runMDS.Rd
index 747d8ad..b5a3fd5 100644
--- a/man/runMDS.Rd
+++ b/man/runMDS.Rd
@@ -22,14 +22,15 @@ calculateMDS(x, ...)
   ...
 )
 
-\S4method{calculateMDS}{SummarizedExperiment}(x, ..., exprs_values = "logcounts")
+\S4method{calculateMDS}{SummarizedExperiment}(x, ..., exprs_values = "logcounts", assay.type = exprs_values)
 
 \S4method{calculateMDS}{SingleCellExperiment}(
   x,
   ...,
   exprs_values = "logcounts",
   dimred = NULL,
-  n_dimred = NULL
+  n_dimred = NULL,
+  assay.type = exprs_values
 )
 
 runMDS(x, ..., altexp = NULL, name = "MDS")
@@ -68,7 +69,9 @@ This can be a character vector of row names, an integer vector of row indices or
 calculated by \code{FUN} should be stored as \sQuote{dist} attribute of the 
 matrix returned/stored by \code{calculateMDS} or \code{runMDS}.}
 
-\item{exprs_values}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
+\item{exprs_values}{Alias to \code{assay.type}.}
+
+\item{assay.type}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
 
 \item{dimred}{String or integer scalar specifying the existing dimensionality reduction results to use.}
 
@@ -99,7 +102,7 @@ of the MDS matrix calculated.
 
 This section is relevant if \code{x} is a numeric matrix of (log-)expression values with features in rows and cells in columns;
 or if \code{x} is a \linkS4class{SingleCellExperiment} and \code{dimred=NULL}.
-In the latter, the expression values are obtained from the assay specified by \code{exprs_values}.
+In the latter, the expression values are obtained from the assay specified by \code{assay.type}.
 
 The \code{subset_row} argument specifies the features to use for dimensionality reduction.
 The aim is to allow users to specify highly variable features to improve the signal/noise ratio,
@@ -139,7 +142,7 @@ This section is relevant if \code{x} is a \linkS4class{SingleCellExperiment} and
 In such cases, the method is run on data from an alternative \linkS4class{SummarizedExperiment} nested within \code{x}.
 This is useful for performing dimensionality reduction on other features stored in \code{\link{altExp}(x, altexp)}, e.g., antibody tags. 
 
-Setting \code{altexp} with \code{exprs_values} will use the specified assay from the alternative SummarizedExperiment.
+Setting \code{altexp} with \code{assay.type} will use the specified assay from the alternative SummarizedExperiment.
 If the alternative is a SingleCellExperiment, setting \code{dimred} will use the specified dimensionality reduction results from the alternative. 
 This option will also interact as expected with \code{n_dimred}.
 
diff --git a/man/runMultiUMAP.Rd b/man/runMultiUMAP.Rd
index 2cbbe9e..850e27f 100644
--- a/man/runMultiUMAP.Rd
+++ b/man/runMultiUMAP.Rd
@@ -12,7 +12,13 @@ calculateMultiUMAP(x, ...)
 
 \S4method{calculateMultiUMAP}{ANY}(x, ..., metric = "euclidean")
 
-\S4method{calculateMultiUMAP}{SummarizedExperiment}(x, exprs_values, metric = "euclidean", ...)
+\S4method{calculateMultiUMAP}{SummarizedExperiment}(
+  x,
+  exprs_values,
+  metric = "euclidean",
+  assay.type = exprs_values,
+  ...
+)
 
 \S4method{calculateMultiUMAP}{SingleCellExperiment}(
   x,
@@ -20,6 +26,8 @@ calculateMultiUMAP(x, ...)
   dimred,
   altexp,
   altexp_exprs_values = "logcounts",
+  assay.type = exprs_values,
+  altexp.assay.type = altexp_exprs_values,
   ...
 )
 
@@ -43,7 +51,9 @@ For the SummarizedExperiment and SingleCellExperiment methods, and for \code{run
 \item{metric}{Character vector specifying the type of distance to use for each matrix in \code{x}.
 This is recycled to the same number of matrices supplied in \code{x}.}
 
-\item{exprs_values}{A character or integer vector of assays to extract and transpose for use in the UMAP.
+\item{exprs_values}{Alias to \code{assay.type}.}
+
+\item{assay.type}{A character or integer vector of assays to extract and transpose for use in the UMAP.
 For the SingleCellExperiment, this argument can be missing, in which case no assays are used.}
 
 \item{dimred}{A character or integer vector of \code{\link{reducedDims}} to extract for use in the UMAP.
@@ -52,7 +62,9 @@ This argument can be missing, in which case no assays are used.}
 \item{altexp}{A character or integer vector of \code{\link{altExps}} to extract and transpose for use in the UMAP.
 This argument can be missing, in which case no alternative experiments are used.}
 
-\item{altexp_exprs_values}{A character or integer vector specifying the assay to extract from alternative experiments, when \code{altexp} is specified.
+\item{altexp_exprs_values}{Alias to \code{altexp.assay.type}.}
+
+\item{altexp.assay.type}{A character or integer vector specifying the assay to extract from alternative experiments, when \code{altexp} is specified.
 This is recycled to the same length as \code{altexp}.}
 
 \item{name}{String specifying the name of the \code{\link{reducedDims}} in which to store the UMAP.}
diff --git a/man/runNMF.Rd b/man/runNMF.Rd
index 3026ebf..d39e3db 100644
--- a/man/runNMF.Rd
+++ b/man/runNMF.Rd
@@ -21,14 +21,15 @@ calculateNMF(x, ...)
   ...
 )
 
-\S4method{calculateNMF}{SummarizedExperiment}(x, ..., exprs_values = "logcounts")
+\S4method{calculateNMF}{SummarizedExperiment}(x, ..., exprs_values = "logcounts", assay.type = exprs_values)
 
 \S4method{calculateNMF}{SingleCellExperiment}(
   x,
   ...,
   exprs_values = "logcounts",
   dimred = NULL,
-  n_dimred = NULL
+  n_dimred = NULL,
+  assay.type = exprs_values
 )
 
 runNMF(x, ..., altexp = NULL, name = "NMF")
@@ -58,7 +59,9 @@ This can be a character vector of row names, an integer vector of row indices or
 
 \item{seed}{Random number generation seed to be passed to \code{\link[RcppML]{nmf}}.}
 
-\item{exprs_values}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
+\item{exprs_values}{Alias to \code{assay.type}.}
+
+\item{assay.type}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
 
 \item{dimred}{String or integer scalar specifying the existing dimensionality reduction results to use.}
 
@@ -87,7 +90,7 @@ Users are advised to test multiple random seeds, and then use \code{\link{set.se
 
 This section is relevant if \code{x} is a numeric matrix of (log-)expression values with features in rows and cells in columns;
 or if \code{x} is a \linkS4class{SingleCellExperiment} and \code{dimred=NULL}.
-In the latter, the expression values are obtained from the assay specified by \code{exprs_values}.
+In the latter, the expression values are obtained from the assay specified by \code{assay.type}.
 
 The \code{subset_row} argument specifies the features to use for dimensionality reduction.
 The aim is to allow users to specify highly variable features to improve the signal/noise ratio,
@@ -127,7 +130,7 @@ This section is relevant if \code{x} is a \linkS4class{SingleCellExperiment} and
 In such cases, the method is run on data from an alternative \linkS4class{SummarizedExperiment} nested within \code{x}.
 This is useful for performing dimensionality reduction on other features stored in \code{\link{altExp}(x, altexp)}, e.g., antibody tags. 
 
-Setting \code{altexp} with \code{exprs_values} will use the specified assay from the alternative SummarizedExperiment.
+Setting \code{altexp} with \code{assay.type} will use the specified assay from the alternative SummarizedExperiment.
 If the alternative is a SingleCellExperiment, setting \code{dimred} will use the specified dimensionality reduction results from the alternative. 
 This option will also interact as expected with \code{n_dimred}.
 
diff --git a/man/runPCA.Rd b/man/runPCA.Rd
index 7aadc90..d5a7bee 100644
--- a/man/runPCA.Rd
+++ b/man/runPCA.Rd
@@ -22,14 +22,15 @@ calculatePCA(x, ...)
   BPPARAM = SerialParam()
 )
 
-\S4method{calculatePCA}{SummarizedExperiment}(x, ..., exprs_values = "logcounts")
+\S4method{calculatePCA}{SummarizedExperiment}(x, ..., exprs_values = "logcounts", assay.type = exprs_values)
 
 \S4method{calculatePCA}{SingleCellExperiment}(
   x,
   ...,
   exprs_values = "logcounts",
   dimred = NULL,
-  n_dimred = NULL
+  n_dimred = NULL,
+  assay.type = exprs_values
 )
 
 \S4method{runPCA}{SingleCellExperiment}(x, ..., altexp = NULL, name = "PCA")
@@ -60,7 +61,9 @@ This can be a character vector of row names, an integer vector of row indices or
 
 \item{BPPARAM}{A \linkS4class{BiocParallelParam} object specifying whether the PCA should be parallelized.}
 
-\item{exprs_values}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
+\item{exprs_values}{Alias to \code{assay.type}.}
+
+\item{assay.type}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
 
 \item{dimred}{String or integer scalar specifying the existing dimensionality reduction results to use.}
 
@@ -97,7 +100,7 @@ For full reproducibility, users should call \code{\link{set.seed}} prior to runn
 
 This section is relevant if \code{x} is a numeric matrix of (log-)expression values with features in rows and cells in columns;
 or if \code{x} is a \linkS4class{SingleCellExperiment} and \code{dimred=NULL}.
-In the latter, the expression values are obtained from the assay specified by \code{exprs_values}.
+In the latter, the expression values are obtained from the assay specified by \code{assay.type}.
 
 The \code{subset_row} argument specifies the features to use for dimensionality reduction.
 The aim is to allow users to specify highly variable features to improve the signal/noise ratio,
@@ -137,7 +140,7 @@ This section is relevant if \code{x} is a \linkS4class{SingleCellExperiment} and
 In such cases, the method is run on data from an alternative \linkS4class{SummarizedExperiment} nested within \code{x}.
 This is useful for performing dimensionality reduction on other features stored in \code{\link{altExp}(x, altexp)}, e.g., antibody tags. 
 
-Setting \code{altexp} with \code{exprs_values} will use the specified assay from the alternative SummarizedExperiment.
+Setting \code{altexp} with \code{assay.type} will use the specified assay from the alternative SummarizedExperiment.
 If the alternative is a SingleCellExperiment, setting \code{dimred} will use the specified dimensionality reduction results from the alternative. 
 This option will also interact as expected with \code{n_dimred}.
 
diff --git a/man/runTSNE.Rd b/man/runTSNE.Rd
index 89e58bf..3227a06 100644
--- a/man/runTSNE.Rd
+++ b/man/runTSNE.Rd
@@ -25,10 +25,13 @@ calculateTSNE(x, ...)
   external_neighbors = FALSE,
   BNPARAM = KmknnParam(),
   BPPARAM = SerialParam(),
-  use_fitsne = FALSE
+  use_fitsne = FALSE,
+  use_densvis = FALSE,
+  dens_frac = 0.3,
+  dens_lambda = 0.1
 )
 
-\S4method{calculateTSNE}{SummarizedExperiment}(x, ..., exprs_values = "logcounts")
+\S4method{calculateTSNE}{SummarizedExperiment}(x, ..., exprs_values = "logcounts", assay.type = exprs_values)
 
 \S4method{calculateTSNE}{SingleCellExperiment}(
   x,
@@ -36,7 +39,8 @@ calculateTSNE(x, ...)
   pca = is.null(dimred),
   exprs_values = "logcounts",
   dimred = NULL,
-  n_dimred = NULL
+  n_dimred = NULL,
+  assay.type = exprs_values
 )
 
 runTSNE(x, ..., altexp = NULL, name = "TSNE")
@@ -82,7 +86,13 @@ otherwise, the \code{\link[Rtsne]{Rtsne}} defaults are used.}
 
 \item{use_fitsne}{Logical scalar indicating whether \code{\link[snifter]{fitsne}} should be used to perform t-SNE.}
 
-\item{exprs_values}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
+\item{use_densvis}{Logical scalar indicating whether \code{\link[densvis]{densne}} should be used to perform density-preserving t-SNE.}
+
+\item{dens_frac, dens_lambda}{See \code{\link[densvis]{densne}}}
+
+\item{exprs_values}{Alias to \code{assay.type}.}
+
+\item{assay.type}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
 
 \item{pca}{Logical scalar indicating whether a PCA step should be performed inside \code{\link[Rtsne]{Rtsne}}.}
 
@@ -123,7 +133,7 @@ This presumes that the existing dimensionality reduction is sufficient such that
 
 This section is relevant if \code{x} is a numeric matrix of (log-)expression values with features in rows and cells in columns;
 or if \code{x} is a \linkS4class{SingleCellExperiment} and \code{dimred=NULL}.
-In the latter, the expression values are obtained from the assay specified by \code{exprs_values}.
+In the latter, the expression values are obtained from the assay specified by \code{assay.type}.
 
 The \code{subset_row} argument specifies the features to use for dimensionality reduction.
 The aim is to allow users to specify highly variable features to improve the signal/noise ratio,
@@ -163,7 +173,7 @@ This section is relevant if \code{x} is a \linkS4class{SingleCellExperiment} and
 In such cases, the method is run on data from an alternative \linkS4class{SummarizedExperiment} nested within \code{x}.
 This is useful for performing dimensionality reduction on other features stored in \code{\link{altExp}(x, altexp)}, e.g., antibody tags. 
 
-Setting \code{altexp} with \code{exprs_values} will use the specified assay from the alternative SummarizedExperiment.
+Setting \code{altexp} with \code{assay.type} will use the specified assay from the alternative SummarizedExperiment.
 If the alternative is a SingleCellExperiment, setting \code{dimred} will use the specified dimensionality reduction results from the alternative. 
 This option will also interact as expected with \code{n_dimred}.
 
diff --git a/man/runUMAP.Rd b/man/runUMAP.Rd
index 87e4f3b..0df7547 100644
--- a/man/runUMAP.Rd
+++ b/man/runUMAP.Rd
@@ -23,10 +23,13 @@ calculateUMAP(x, ...)
   ...,
   external_neighbors = FALSE,
   BNPARAM = KmknnParam(),
-  BPPARAM = SerialParam()
+  BPPARAM = SerialParam(),
+  use_densvis = FALSE,
+  dens_frac = 0.3,
+  dens_lambda = 0.1
 )
 
-\S4method{calculateUMAP}{SummarizedExperiment}(x, ..., exprs_values = "logcounts")
+\S4method{calculateUMAP}{SummarizedExperiment}(x, ..., exprs_values = "logcounts", assay.type = exprs_values)
 
 \S4method{calculateUMAP}{SingleCellExperiment}(
   x,
@@ -34,7 +37,8 @@ calculateUMAP(x, ...)
   pca = if (!is.null(dimred)) NULL else 50,
   exprs_values = "logcounts",
   dimred = NULL,
-  n_dimred = NULL
+  n_dimred = NULL,
+  assay.type = exprs_values
 )
 
 runUMAP(x, ..., altexp = NULL, name = "UMAP")
@@ -77,7 +81,13 @@ otherwise, the \code{\link[uwot]{umap}} defaults are used.}
 
 \item{BPPARAM}{A \linkS4class{BiocParallelParam} object specifying whether the PCA should be parallelized.}
 
-\item{exprs_values}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
+\item{use_densvis}{Logical scalar indicating whether \code{\link[densvis]{densne}} should be used to perform density-preserving t-SNE.}
+
+\item{dens_frac, dens_lambda}{See \code{\link[densvis]{densne}}}
+
+\item{exprs_values}{Alias to \code{assay.type}.}
+
+\item{assay.type}{Integer scalar or string indicating which assay of \code{x} contains the expression values.}
 
 \item{dimred}{String or integer scalar specifying the existing dimensionality reduction results to use.}
 
@@ -108,7 +118,7 @@ The neighbor search results are then used directly to create the UMAP embedding.
 
 This section is relevant if \code{x} is a numeric matrix of (log-)expression values with features in rows and cells in columns;
 or if \code{x} is a \linkS4class{SingleCellExperiment} and \code{dimred=NULL}.
-In the latter, the expression values are obtained from the assay specified by \code{exprs_values}.
+In the latter, the expression values are obtained from the assay specified by \code{assay.type}.
 
 The \code{subset_row} argument specifies the features to use for dimensionality reduction.
 The aim is to allow users to specify highly variable features to improve the signal/noise ratio,
@@ -148,7 +158,7 @@ This section is relevant if \code{x} is a \linkS4class{SingleCellExperiment} and
 In such cases, the method is run on data from an alternative \linkS4class{SummarizedExperiment} nested within \code{x}.
 This is useful for performing dimensionality reduction on other features stored in \code{\link{altExp}(x, altexp)}, e.g., antibody tags. 
 
-Setting \code{altexp} with \code{exprs_values} will use the specified assay from the alternative SummarizedExperiment.
+Setting \code{altexp} with \code{assay.type} will use the specified assay from the alternative SummarizedExperiment.
 If the alternative is a SingleCellExperiment, setting \code{dimred} will use the specified dimensionality reduction results from the alternative. 
 This option will also interact as expected with \code{n_dimred}.
 
diff --git a/man/scater-plot-args.Rd b/man/scater-plot-args.Rd
index 6f63e9d..a007d16 100644
--- a/man/scater-plot-args.Rd
+++ b/man/scater-plot-args.Rd
@@ -17,6 +17,9 @@ Defaults to 10.}
 Defaults to 0.6.}
 \item{\code{point_size}:}{Numeric scalar, specifying the size of the points.
 Defaults to \code{NULL}.}
+\item{\code{point_shape}:}{An integer, or a string specifying the shape
+of the points. Details see \code{vignette("ggplot2-specs")}. Defaults to
+\code{19}.}
 \item{\code{jitter_type}:}{String to define how points are to be jittered in a violin plot.
 This is either with random jitter on the x-axis (\code{"jitter"}) or in a \dQuote{beeswarm} style (if \code{"swarm"}, default).
 The latter usually looks more attractive, but for datasets with a large number of cells, or for dense plots, the jitter option may work better.}
@@ -38,23 +41,29 @@ Defaults to \code{TRUE}.}
 }
 
 \section{Miscellaneous fields}{
+ Addititional fields can be added to the
+  data.frame passed to \link{ggplot} by setting the \code{other_fields}
+  argument. This allows users to easily incorporate additional metadata for
+  use in further \pkg{ggplot} operations.
 
-Addititional fields can be added to the data.frame passed to \link{ggplot} by setting the \code{other_fields} argument.
-This allows users to easily incorporate additional metadata for use in further \pkg{ggplot} operations.
+  The \code{other_fields} argument should be character vector where each
+  string is passed to \code{\link{retrieveCellInfo}} (for cell-based plots)
+  or \code{\link{retrieveFeatureInfo}} (for feature-based plots).
+  Alternatively, \code{other_fields} can be a named list where each element
+  is of any type accepted by \code{\link{retrieveCellInfo}} or
+  \code{\link{retrieveFeatureInfo}}. This includes \link{AsIs}-wrapped
+  vectors, data.frames or \linkS4class{DataFrame}s.
 
-The \code{other_fields} argument should be character vector where each string is passed to \code{\link{retrieveCellInfo}} (for cell-based plots) or \code{\link{retrieveFeatureInfo}} (for feature-based plots).
-Alternatively, \code{other_fields} can be a named list where each element is of any type accepted by \code{\link{retrieveCellInfo}} or \code{\link{retrieveFeatureInfo}}.
-This includes \link{AsIs}-wrapped vectors, data.frames or \linkS4class{DataFrame}s.
-
-Each additional column of the output data.frame will be named according to the \code{name} returned by \code{\link{retrieveCellInfo}} or \code{\link{retrieveFeatureInfo}}.
-If these clash with inbuilt names (e.g., \code{X}, \code{Y}, \code{colour_by}), a warning will be raised and the additional column will not be added to avoid overwriting an existing column.
+  Each additional column of the output data.frame will be named according to
+  the \code{name} returned by \code{\link{retrieveCellInfo}} or
+  \code{\link{retrieveFeatureInfo}}. If these clash with inbuilt names (e.g.,
+  \code{X}, \code{Y}, \code{colour_by}), a warning will be raised and the
+  additional column will not be added to avoid overwriting an existing
+  column.
 }
 
 \seealso{
-\code{\link{plotColData}}, 
-\code{\link{plotRowData}}, 
-\code{\link{plotReducedDim}}, 
-\code{\link{plotExpression}}, 
-\code{\link{plotPlatePosition}},
-and most other plotting functions.
+\code{\link{plotColData}}, \code{\link{plotRowData}},
+\code{\link{plotReducedDim}}, \code{\link{plotExpression}},
+\code{\link{plotPlatePosition}}, and most other plotting functions.
 }
diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R
index 4cc3093..3430849 100644
--- a/tests/testthat/setup.R
+++ b/tests/testthat/setup.R
@@ -33,3 +33,9 @@ FAIL <- failgen()
 
 library(DelayedArray)
 setAutoBPPARAM(FAIL)
+
+
+expect_ggplot <- function(g) {
+    expect_s3_class(g, "ggplot")
+    expect_error(ggplot_build(g), NA)
+}
diff --git a/tests/testthat/test-expl-var.R b/tests/testthat/test-expl-var.R
index 4bc3ff9..e1cfb80 100644
--- a/tests/testthat/test-expl-var.R
+++ b/tests/testthat/test-expl-var.R
@@ -88,7 +88,7 @@ test_that("getVarianceExplained handles silly inputs correctly", {
 test_that("plotExplanatoryVariables works as expected", {
     out <- plotExplanatoryVariables(normed)
     ref <- plotExplanatoryVariables(varexp)
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
     medians <- apply(varexp, 2, median, na.rm=TRUE)
@@ -96,33 +96,33 @@ test_that("plotExplanatoryVariables works as expected", {
     # Responds to choice of number of variables
     out <- plotExplanatoryVariables(normed, nvars_to_plot=2)
     ref <- plotExplanatoryVariables(varexp[,order(medians, decreasing=TRUE)[1:2]])
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
     out <- plotExplanatoryVariables(normed, nvars_to_plot=Inf)
     ref <- plotExplanatoryVariables(varexp)
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
     out <- plotExplanatoryVariables(normed, nvars_to_plot=0)
     ref <- plotExplanatoryVariables(varexp[,0,drop=FALSE])
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
     # Responds to choice of minimum marginal R2.
     out <- plotExplanatoryVariables(normed, min_marginal_r2=0.5)
     ref <- plotExplanatoryVariables(varexp[,medians >= 0.5,drop=FALSE])
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
     out <- plotExplanatoryVariables(normed, min_marginal_r2=0.05)
     ref <- plotExplanatoryVariables(varexp[,medians >= 0.05,drop=FALSE])
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
     # Handles silly inputs.
-    expect_s3_class(plotExplanatoryVariables(varexp[0,,drop=FALSE]), "ggplot")
-    expect_s3_class(plotExplanatoryVariables(varexp[,0,drop=FALSE]), "ggplot")
+    expect_ggplot(plotExplanatoryVariables(varexp[0,,drop=FALSE]))
+    expect_ggplot(plotExplanatoryVariables(varexp[,0,drop=FALSE]))
 })
 
 #############################################################
@@ -180,39 +180,39 @@ test_that("getExplanatoryPCs responds to getVarianceExplained options", {
 # plotExplanatoryPCs() tests:
 
 test_that("plotExplanatoryPCs works with PC choice options", {
-    out <- plotExplanatoryPCs(normed, npcs=nrow(exppcs))
+    out <- plotExplanatoryPCs(normed, npcs_to_plot=nrow(exppcs))
     ref <- plotExplanatoryPCs(exppcs)
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
     # Handles situations where different numbers of PCs are requested.
-    out <- plotExplanatoryPCs(normed, npcs=5)
+    out <- plotExplanatoryPCs(normed, npcs_to_plot=5)
     allpcs <- runPCA(normed, ncomponents=5)
     ref <- plotExplanatoryPCs(allpcs)
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 })
 
 test_that("plotExplanatoryPCs responds to choice of number of variables", {
     maxes <- apply(exppcs, 2, max, na.rm=TRUE)
 
-    out <- plotExplanatoryPCs(normed, nvars_to_plot=2, npcs=nrow(exppcs))
+    out <- plotExplanatoryPCs(normed, nvars_to_plot=2, npcs_to_plot=nrow(exppcs))
     ref <- plotExplanatoryPCs(exppcs[,order(maxes, decreasing=TRUE)[1:2]])
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
-    out <- plotExplanatoryPCs(normed, nvars_to_plot=Inf, npcs=nrow(exppcs))
+    out <- plotExplanatoryPCs(normed, nvars_to_plot=Inf, npcs_to_plot=nrow(exppcs))
     ref <- plotExplanatoryPCs(exppcs)
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 
-    out <- plotExplanatoryPCs(normed, nvars_to_plot=0, npcs=nrow(exppcs))
+    out <- plotExplanatoryPCs(normed, nvars_to_plot=0, npcs_to_plot=nrow(exppcs))
     ref <- plotExplanatoryPCs(exppcs[,0,drop=FALSE])
-    expect_s3_class(out, "ggplot")
+    expect_ggplot(out)
     expect_identical(out$data, ref$data)
 })
 
 test_that("plotExplanatoryPCs handles silly inputs.", {
-    expect_s3_class(suppressWarnings(plotExplanatoryPCs(exppcs[0,,drop=FALSE])), "ggplot")
-    expect_s3_class(plotExplanatoryPCs(exppcs[,0,drop=FALSE]), "ggplot")
+    expect_ggplot(suppressWarnings(plotExplanatoryPCs(exppcs[0,,drop=FALSE])))
+    expect_ggplot(plotExplanatoryPCs(exppcs[,0,drop=FALSE]))
 })
diff --git a/tests/testthat/test-plot-dimred.R b/tests/testthat/test-plot-dimred.R
index cd5d6a1..a10fe1b 100644
--- a/tests/testthat/test-plot-dimred.R
+++ b/tests/testthat/test-plot-dimred.R
@@ -9,33 +9,45 @@ test_that("we can produce PCA scatterplots", {
     expect_identical(reducedDimNames(example_sce), "PCA")
 
     # Checking that visual parameters work.
-    expect_s3_class(P <- plotPCA(example_sce), "ggplot")
-    expect_s3_class(plotPCA(example_sce, colour_by = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, colour_by = "Cell_Cycle", size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, colour_by = "Cell_Cycle", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
+    expect_ggplot(P <- plotPCA(example_sce))
 
-    expect_s3_class(plotPCA(example_sce, colour_by = "ENS_0001", swap_rownames = "ENS"), "ggplot")
+    expect_ggplot(plotPCA(example_sce, colour_by = "Cell_Cycle"))
+    expect_ggplot(plotPCA(example_sce, size_by = "Gene_0001"))
+    expect_ggplot(plotPCA(example_sce, shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, colour_by = "Cell_Cycle", size_by = "Gene_0001"))
+    expect_ggplot(plotPCA(example_sce, colour_by = "Cell_Cycle", shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, size_by = "Gene_0001", shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, colour_by = "ENS_0001", swap_rownames = "ENS"))
 
     # Checking other arguments are passed successfully to plotReducedDim.
-    expect_s3_class(plotPCA(example_sce, colour_by = "Cell_Cycle", add_legend = FALSE), "ggplot")
-    expect_s3_class(plotPCA(example_sce, colour_by = "Gene_0001", by_exprs_values = "counts"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, percentVar = c(19, 5)), "ggplot")
-    expect_s3_class(plotPCA(example_sce, text_by="Cell_Cycle"), "ggplot")
-    
+    expect_ggplot(plotPCA(example_sce, colour_by = "Cell_Cycle", add_legend = FALSE))
+    expect_ggplot(plotPCA(example_sce, colour_by = "Gene_0001", by_exprs_values = "counts"))
+    expect_ggplot(plotPCA(example_sce, percentVar = c(19, 5)))
+    expect_ggplot(plotPCA(example_sce, text_by="Cell_Cycle"))
+
     # Checking that specification of multiple ncomponents works.
-    expect_s3_class(Pv <- plotPCA(example_sce, ncomponents=1:2), "ggplot")
+    expect_ggplot(Pv <- plotPCA(example_sce, ncomponents=1:2))
     expect_equal(P$data, Pv$data)
-    expect_s3_class(Pv2 <- plotPCA(example_sce, ncomponents=2:1), "ggplot")
+    expect_ggplot(Pv2 <- plotPCA(example_sce, ncomponents=2:1))
     expect_false(isTRUE(all.equal(P$data, Pv2$data)))
     expect_error(plotPCA(example_sce, ncomponents=c(51,1)), "larger than")
 
     # Check that dataframes etc are allowed
     reducedDim(example_sce, "PCA") <- DataFrame(reducedDim(example_sce, "PCA"))
     expect_error(plotReducedDim(example_sce, "PCA"), NA)
+
+    # Use scattermore
+    expect_ggplot(plotPCA(example_sce, scattermore = TRUE, point_size = 2))
+    expect_ggplot(plotPCA(example_sce, scattermore = TRUE, point_size = 2,
+                          colour_by = "Cell_Cycle"))
+
+    # Binning
+    expect_ggplot(plotPCA(example_sce, bins = 10))
+    expect_ggplot(plotPCA(example_sce, bins = 10, hex = TRUE))
+    expect_ggplot(plotPCA(example_sce, bins = 10, colour_by = "Gene_0001"))
+    expect_ggplot(plotPCA(example_sce, bins = 10, colour_by = "Gene_0001",
+                          hex = TRUE))
 })
 
 test_that("we can produce PCA pairplots", {
@@ -43,73 +55,84 @@ test_that("we can produce PCA pairplots", {
     expect_identical(reducedDimNames(example_sce), "PCA")
 
     # Checking that visual parameters work.
-    expect_s3_class(P <- plotPCA(example_sce, ncomponents=4), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, colour_by = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, colour_by = "Cell_Cycle", size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, colour_by = "Cell_Cycle", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-
-    expect_s3_class(plotPCA(example_sce, ncomponents = 4, colour_by = "ENS_0001", swap_rownames = "ENS"), "ggplot")
+    expect_ggplot(P <- plotPCA(example_sce, ncomponents = 4))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, colour_by = "Cell_Cycle"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, size_by = "Gene_0001"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, colour_by = "Cell_Cycle", size_by = "Gene_0001"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, colour_by = "Cell_Cycle", shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, size_by = "sizeFactor", shape_by = "Treatment"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, colour_by = "ENS_0001", swap_rownames = "ENS"))
 
 
     # Checking other arguments are passed successfully to plotReducedDim.
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, colour_by = "Cell_Cycle", add_legend = FALSE), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, colour_by = "Gene_0001", by_exprs_values = "counts"), "ggplot")
-    expect_s3_class(plotPCA(example_sce, ncomponents=4, percentVar=c(19, 5, 3, 2)), "ggplot")
-    
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, colour_by = "Cell_Cycle", add_legend = FALSE))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, colour_by = "Gene_0001", by_exprs_values = "counts"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, percentVar = c(19, 5, 3, 2)))
+
     # Checking that specification of multiple ncomponents works.
-    expect_s3_class(Pv <- plotPCA(example_sce, ncomponents=1:4), "ggplot")
+    expect_ggplot(Pv <- plotPCA(example_sce, ncomponents = 1:4))
     expect_equal(P$data, Pv$data)
-    expect_s3_class(Pv2 <- plotPCA(example_sce, ncomponents=4:1), "ggplot")
+    expect_ggplot(Pv2 <- plotPCA(example_sce, ncomponents = 4:1))
     expect_false(isTRUE(all.equal(P$data, Pv2$data)))
-    expect_error(plotPCA(example_sce, ncomponents=5:1), "larger than")
+    expect_error(plotPCA(example_sce, ncomponents = 5:1), "larger than")
+
+    # Use scattermore
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, scattermore = TRUE, point_size = 3))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, scattermore = TRUE, point_size = 3, colour_by = "Gene_0001"))
+
+    # Binning
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, bins = 10))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, bins = 10, hex = TRUE))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, bins = 10,
+                          colour_by = "Gene_0001"))
+    expect_ggplot(plotPCA(example_sce, ncomponents = 4, bins = 10,
+                          colour_by = "Gene_0001", hex = TRUE))
 })
 
 test_that("we can produce TSNE plots", {
     set.seed(100)
     example_sce <- runTSNE(example_sce)
     expect_identical(reducedDimNames(example_sce), "TSNE")
-    expect_s3_class(P <- plotTSNE(example_sce), "ggplot")
+    expect_ggplot(P <- plotTSNE(example_sce))
 
     set.seed(20)
     example_sce <- runTSNE(example_sce, ncomponents=3)
-    expect_s3_class(plotTSNE(example_sce, ncomponents=3), "ggplot")
+    expect_ggplot(plotTSNE(example_sce, ncomponents=3))
 })
 
 test_that("we can produce UMAP plots", {
     set.seed(100)
     example_sce <- runUMAP(example_sce, ncomponents=4)
     expect_identical(reducedDimNames(example_sce), "UMAP")
-    expect_s3_class(P <- plotUMAP(example_sce), "ggplot")
+    expect_ggplot(P <- plotUMAP(example_sce))
 
     # Handles multiple components properly.
     set.seed(20)
     P4 <- plotUMAP(example_sce, ncomponents=4)
-    expect_s3_class(P4, "ggplot")
+    expect_ggplot(P4)
 })
 
 test_that("we can produce NMF plots", {
     set.seed(100)
     example_sce <- runNMF(example_sce, ncomponents=4)
     expect_identical(reducedDimNames(example_sce), "NMF")
-    expect_s3_class(P <- plotNMF(example_sce), "ggplot")
+    expect_ggplot(P <- plotNMF(example_sce))
 
     # Handles multiple components properly.
     set.seed(20)
     P4 <- plotNMF(example_sce, ncomponents=4)
-    expect_s3_class(P4, "ggplot")
+    expect_ggplot(P4)
 })
 
 test_that("we can produce MDS plots", {
     example_sce <- runMDS(example_sce, ncomponents=4)
     expect_identical(reducedDimNames(example_sce), "MDS")
-    expect_s3_class(P <- plotMDS(example_sce), "ggplot")
+    expect_ggplot(P <- plotMDS(example_sce))
 
     # Handles multiple components properly.
-    expect_s3_class(P4 <- plotMDS(example_sce, ncomponents=4), "ggplot")
+    expect_ggplot(P4 <- plotMDS(example_sce, ncomponents=4))
 })
 
 
diff --git a/tests/testthat/test-plot-exprs.R b/tests/testthat/test-plot-exprs.R
index d8b9ac5..4c45909 100644
--- a/tests/testthat/test-plot-exprs.R
+++ b/tests/testthat/test-plot-exprs.R
@@ -7,47 +7,47 @@ rowData(example_sce)$ENS <- gsub("Gene", "ENS", rownames(example_sce))
 test_that("plotExpression works for various 'x'", {
     for (gene_set in list("Gene_0001", rownames(example_sce)[1:5])) { # different numbers of genes, types of specification.
         for (x in list(NULL, "Cell_Cycle", "Gene_0100")) { # nothing, categorical, or continuous.
-            expect_s3_class(plotExpression(example_sce, gene_set, x = x), "ggplot")
-            expect_s3_class(plotExpression(example_sce, gene_set, x = x, colour_by = "Cell_Cycle"), "ggplot")
-            expect_s3_class(plotExpression(example_sce, gene_set, x = x, size_by = "Gene_0001"), "ggplot")
-            expect_s3_class(plotExpression(example_sce, gene_set, x = x, shape_by = "Treatment"), "ggplot")
+            expect_ggplot(plotExpression(example_sce, gene_set, x = x))
+            expect_ggplot(plotExpression(example_sce, gene_set, x = x, colour_by = "Cell_Cycle"))
+            expect_ggplot(plotExpression(example_sce, gene_set, x = x, size_by = "Gene_0001"))
+            expect_ggplot(plotExpression(example_sce, gene_set, x = x, shape_by = "Treatment"))
         }
     }
 })
 
 test_that("plotExpression works for various aesthetics", {
     gene_set <- rownames(example_sce)[1:20]
-    expect_s3_class(plotExpression(example_sce, gene_set, colour_by = "Cell_Cycle", size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, colour_by = "Cell_Cycle", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-    
-    expect_s3_class(plotExpression(example_sce, gene_set, size_by = "Gene_0001", shape_by = "Treatment", by_exprs_values = "counts"), "ggplot")
+    expect_ggplot(plotExpression(example_sce, gene_set, colour_by = "Cell_Cycle", size_by = "Gene_0001"))
+    expect_ggplot(plotExpression(example_sce, gene_set, colour_by = "Cell_Cycle", shape_by = "Treatment"))
+    expect_ggplot(plotExpression(example_sce, gene_set, size_by = "Gene_0001", shape_by = "Treatment"))
+    expect_ggplot(plotExpression(example_sce, gene_set, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"))
 
-    expect_s3_class(plotExpression(example_sce, rowData(example_sce)[1:10, "ENS"], colour_by = "ENS_0001", swap_rownames="ENS"), "ggplot")
+    expect_ggplot(plotExpression(example_sce, gene_set, size_by = "Gene_0001", shape_by = "Treatment", by_exprs_values = "counts"))
+
+    expect_ggplot(plotExpression(example_sce, rowData(example_sce)[1:10, "ENS"], colour_by = "ENS_0001", swap_rownames="ENS"))
 
     # Testing options when dealing with many genes and no 'x' specified.
-    expect_s3_class(plotExpression(example_sce, gene_set, one_facet=FALSE), "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, feature_colours=FALSE), "ggplot")
+    expect_ggplot(plotExpression(example_sce, gene_set, one_facet=FALSE))
+    expect_ggplot(plotExpression(example_sce, gene_set, feature_colours=FALSE))
 })
 
 test_that("plotExpression works with semi-analysis options", {
     gene_set <- sample(rownames(example_sce)[5:15])
-    expect_s3_class(plotExpression(example_sce, gene_set, show_violin=FALSE), "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, show_median=TRUE), "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, jitter_type="jitter"), "ggplot")
+    expect_ggplot(plotExpression(example_sce, gene_set, show_violin=FALSE))
+    expect_ggplot(plotExpression(example_sce, gene_set, show_median=TRUE))
+    expect_ggplot(plotExpression(example_sce, gene_set, jitter_type="jitter"))
 
-    expect_s3_class(plotExpression(example_sce, gene_set, x="Gene_0001", show_smooth=TRUE), "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, x="Gene_0001", show_smooth=TRUE, show_se=FALSE), "ggplot")
+    expect_ggplot(plotExpression(example_sce, gene_set, x="Gene_0001", show_smooth=TRUE))
+    expect_ggplot(plotExpression(example_sce, gene_set, x="Gene_0001", show_smooth=TRUE, show_se=FALSE))
 })
 
 test_that("plotExpression works for different exprs_values", {
     gene_set <- tail(rownames(example_sce))
-    expect_s3_class(plotExpression(example_sce, gene_set, x = "Mutation_Status", exprs_values = "counts"),  "ggplot")
-    expect_s3_class(plotExpression(example_sce, gene_set, x = "Mutation_Status", exprs_values = "counts", log2_values = TRUE),  "ggplot")
+    expect_ggplot(plotExpression(example_sce, gene_set, x = "Mutation_Status", exprs_values = "counts"))
+    expect_ggplot(plotExpression(example_sce, gene_set, x = "Mutation_Status", exprs_values = "counts", log2_values = TRUE))
     expect_error(plotExpression(example_sce, rownames(example_sce)[1:6], exprs_values = "silly"), "not in names")
 
-    # And on sparse matrices.        
+    # And on sparse matrices.
     sparsified <- example_sce
     logcounts(sparsified) <- as(logcounts(sparsified), "dgCMatrix")
     sparse <- plotExpression(sparsified, "Gene_0001")
@@ -56,7 +56,7 @@ test_that("plotExpression works for different exprs_values", {
 })
 
 test_that("plotExpression works for other fields", {
-    gg <- plotExpression(example_sce, head(rownames(example_sce)), 
+    gg <- plotExpression(example_sce, head(rownames(example_sce)),
         other_fields=c("Cell_Cycle", "Mutation_Status"))
 
     expect_true("Cell_Cycle" %in% colnames(gg$data))
@@ -64,9 +64,23 @@ test_that("plotExpression works for other fields", {
 
     expect_identical(gg$data$Cell_Cycle, rep(example_sce$Cell_Cycle, 6)) # default 'n' in head().
 
-    # This should throw a warning. 
+    # This should throw a warning.
     example_sce$colour_by <- example_sce$Cell_Cycle
-    expect_warning(gg <- plotExpression(example_sce, head(rownames(example_sce)), 
+    expect_warning(gg <- plotExpression(example_sce, head(rownames(example_sce)),
         colour_by="Cell_Cycle", other_fields=c("colour_by")), "duplicated")
 })
 
+test_that("plotExpression with scattermore", {
+    expect_ggplot(plotExpression(example_sce, "Gene_0001", x = "Gene_0100", scattermore = TRUE, point_size = 2))
+    expect_ggplot(plotExpression(example_sce, "Gene_0001", x = "Gene_0100", scattermore = TRUE, point_size = 2, color_by = "Cell_Cycle"))
+})
+
+test_that("plotExpression with binning", {
+    expect_ggplot(plotExpression(example_sce, "Gene_0001", x = "Gene_0100", bins = 10))
+    expect_ggplot(plotExpression(example_sce, "Gene_0001", x = "Gene_0100", bins = 10, hex = TRUE))
+    expect_ggplot(plotExpression(example_sce, "Gene_0001", x = "Gene_0100",
+                                 bins = 10, colour_by = "Gene_0002", summary_fun = "mean"))
+    expect_ggplot(plotExpression(example_sce, "Gene_0001", x = "Gene_0100",
+                                 bins = 10, colour_by = "Gene_0002", hex = TRUE,
+                                 summary_fun = "mean"))
+})
diff --git a/tests/testthat/test-plot-heat.R b/tests/testthat/test-plot-heat.R
index efbc4f4..4541972 100644
--- a/tests/testthat/test-plot-heat.R
+++ b/tests/testthat/test-plot-heat.R
@@ -23,9 +23,6 @@ test_that("we can produce heatmaps", {
     # Colour parameters for the expression values.
     plotHeatmap(example_sce, features=rownames(example_sce)[1:10], zlim=c(0, 2))
     plotHeatmap(example_sce, features=rownames(example_sce)[1:10], colour=viridis::viridis(20))
-    expect_warning(
-        plotHeatmap(example_sce, features=rownames(example_sce)[1:10], center=TRUE, symmetric=TRUE)
-    )
          
     # Testing out the column colouring. 
     plotHeatmap(example_sce, features=rownames(example_sce)[1:10],
@@ -115,18 +112,9 @@ test_that("we can produce grouped heatmaps", {
     plotGroupedHeatmap(example_sce, features=1:10, group="Group", block="Cell_Cycle", columns=20:30)
 
     # Works with the various colour options.
-    expect_warning(
-        plotGroupedHeatmap(example_sce, features=rownames(example_sce)[1:10],
-            group="Group", center=TRUE, symmetric=TRUE)
-    )
-
-    expect_warning(
-        plotGroupedHeatmap(example_sce, features=rownames(example_sce)[1:10],
-            group="Group", center=TRUE, symmetric=TRUE)
-    )
-
     plotGroupedHeatmap(example_sce, features=rownames(example_sce)[1:10], group="Group", colour=viridis::viridis(20))
     plotGroupedHeatmap(example_sce, features=rownames(example_sce)[1:10], group="Group", zlim=c(0, 2))
+    plotGroupedHeatmap(example_sce, features=rownames(example_sce)[1:10], group="Group", block="Cell_Cycle", center=TRUE)
 
     # Works with rownames swapping.
     plotGroupedHeatmap(example_sce, features = rowData(example_sce)[1:10, "ENS"], 
@@ -135,19 +123,19 @@ test_that("we can produce grouped heatmaps", {
 
 
 test_that("plotHeatmap indexing is consistent", {
-    p1 <- plotHeatmap(example_sce, features=rownames(example_sce)[1:10], cluster_rows = FALSE, cluster_columns = FALSE)
-    p2 <- plotHeatmap(example_sce, features=1:10, cluster_rows = FALSE, cluster_columns = FALSE)
-    p3 <- plotHeatmap(example_sce, features=c(rep(TRUE, 10), rep(FALSE, 1990)), cluster_rows = FALSE, cluster_columns = FALSE)
-    p4 <- plotHeatmap(example_sce, features=factor(rownames(example_sce)[1:10]), cluster_rows = FALSE, cluster_columns = FALSE)
+    p1 <- plotHeatmap(example_sce, features=rownames(example_sce)[1:10], cluster_rows = FALSE, cluster_cols = FALSE)
+    p2 <- plotHeatmap(example_sce, features=1:10, cluster_rows = FALSE, cluster_cols = FALSE)
+    p3 <- plotHeatmap(example_sce, features=c(rep(TRUE, 10), rep(FALSE, 1990)), cluster_rows = FALSE, cluster_cols = FALSE)
+    p4 <- plotHeatmap(example_sce, features=factor(rownames(example_sce)[1:10]), cluster_rows = FALSE, cluster_cols = FALSE)
     
     ## this is apparently the ordering of genes!
     expect_equal(rownames(p1$gtable$grobs[[2]]$children[[1]]$gp$fill), rownames(p2$gtable$grobs[[2]]$children[[1]]$gp$fill))
     expect_equal(rownames(p2$gtable$grobs[[2]]$children[[1]]$gp$fill), rownames(p3$gtable$grobs[[2]]$children[[1]]$gp$fill))
     expect_equal(rownames(p3$gtable$grobs[[2]]$children[[1]]$gp$fill), rownames(p4$gtable$grobs[[2]]$children[[1]]$gp$fill))
 
-    p1 <- plotHeatmap(example_sce, features=rownames(example_sce)[10:1], cluster_rows = FALSE, cluster_columns = FALSE)
-    p2 <- plotHeatmap(example_sce, features=10:1, cluster_rows = FALSE, cluster_columns = FALSE)
-    p3 <- plotHeatmap(example_sce, features=factor(rownames(example_sce)[1:10], levels = rownames(example_sce)[10:1]), cluster_rows = FALSE, cluster_columns = FALSE)
+    p1 <- plotHeatmap(example_sce, features=rownames(example_sce)[10:1], cluster_rows = FALSE, cluster_cols = FALSE)
+    p2 <- plotHeatmap(example_sce, features=10:1, cluster_rows = FALSE, cluster_cols = FALSE)
+    p3 <- plotHeatmap(example_sce, features=factor(rownames(example_sce)[1:10], levels = rownames(example_sce)[10:1]), cluster_rows = FALSE, cluster_cols = FALSE)
     
 
     expect_equal(rownames(p1$gtable$grobs[[2]]$children[[1]]$gp$fill), rownames(p2$gtable$grobs[[2]]$children[[1]]$gp$fill))
diff --git a/tests/testthat/test-plot-other.R b/tests/testthat/test-plot-other.R
index 30ce8d7..b6a9383 100644
--- a/tests/testthat/test-plot-other.R
+++ b/tests/testthat/test-plot-other.R
@@ -1,7 +1,7 @@
 ## Tests for other plotting functions
 ## library(scater); library(testthat); source("setup.R"); source("test-plot-other.R")
 
-example_sce <- normed 
+example_sce <- normed
 colData(example_sce) <- cbind(colData(example_sce), perCellQCMetrics(example_sce))
 rowData(example_sce) <- cbind(rowData(example_sce), perFeatureQCMetrics(example_sce))
 rowData(example_sce)$ENS <- gsub("Gene", "ENS", rownames(example_sce))
@@ -18,37 +18,37 @@ test_that("we can produce plots showing cells in plate position", {
     col <- rep(1:8, 5)
     plate_position <- paste0(row, col)
 
-    # Different types of inputs are accepted.    
-    expect_s3_class(plotPlatePosition(example_sce, list(row=row, column=col)), "ggplot")
-    expect_s3_class(P <- plotPlatePosition(example_sce, plate_position), "ggplot")
+    # Different types of inputs are accepted.
+    expect_ggplot(plotPlatePosition(example_sce, list(row=row, column=col)))
+    expect_ggplot(P <- plotPlatePosition(example_sce, plate_position))
 
     alt <- example_sce
     alt$plate_position <- plate_position
-    expect_s3_class(plotPlatePosition(alt, colour_by="Cell_Cycle"), "ggplot")
+    expect_ggplot(plotPlatePosition(alt, colour_by="Cell_Cycle"))
 
     # Different types of colouring, shaping and sizing are possible.
-    expect_s3_class(plotPlatePosition(alt, colour_by = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotPlatePosition(alt, size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotPlatePosition(alt, shape_by = "Treatment"), "ggplot")
+    expect_ggplot(plotPlatePosition(alt, colour_by = "Cell_Cycle"))
+    expect_ggplot(plotPlatePosition(alt, size_by = "Gene_0001"))
+    expect_ggplot(plotPlatePosition(alt, shape_by = "Treatment"))
 
-    expect_s3_class(plotPlatePosition(alt, colour_by = "Cell_Cycle", size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotPlatePosition(alt, colour_by = "Cell_Cycle", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPlatePosition(alt, size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotPlatePosition(alt, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
+    expect_ggplot(plotPlatePosition(alt, colour_by = "Cell_Cycle", size_by = "Gene_0001"))
+    expect_ggplot(plotPlatePosition(alt, colour_by = "Cell_Cycle", shape_by = "Treatment"))
+    expect_ggplot(plotPlatePosition(alt, size_by = "Gene_0001", shape_by = "Treatment"))
+    expect_ggplot(plotPlatePosition(alt, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"))
+
+    expect_ggplot(plotPlatePosition(alt, colour_by = "ENS_0001", swap_rownames = "ENS"))
 
-    expect_s3_class(plotPlatePosition(alt, colour_by = "ENS_0001", swap_rownames = "ENS"), "ggplot")
-    
     expect_error(plotPlatePosition(alt, colour_by = "ENS_0001", swap_rownames = "asda"),
         "Cannot find column asda in rowData")
 
-    expect_s3_class(plotPlatePosition(alt, size_by = "Gene_0001", shape_by = "Treatment", by_exprs_values = "counts"), "ggplot")
+    expect_ggplot(plotPlatePosition(alt, size_by = "Gene_0001", shape_by = "Treatment", by_exprs_values = "counts"))
 
     # Checking that other arguments are passed through.
-    expect_s3_class(plotPlatePosition(alt, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment", add_legend = FALSE), "ggplot")
-    expect_s3_class(plotPlatePosition(alt, size_by = "Gene_0001", by_exprs_values = "counts"), "ggplot")
+    expect_ggplot(plotPlatePosition(alt, colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment", add_legend = FALSE))
+    expect_ggplot(plotPlatePosition(alt, size_by = "Gene_0001", by_exprs_values = "counts"))
 
     # Checking that an error is thrown,
-    expect_error(plotPlatePosition(example_sce, paste0(col, row)), "invalid format") 
+    expect_error(plotPlatePosition(example_sce, paste0(col, row)), "invalid format")
 })
 
 #################################################
@@ -58,31 +58,31 @@ test_that("we can produce plots for column metadata", {
     example_sce <- addPerCellQC(example_sce)
 
     for (y in c("detected", "Mutation_Status")) { # discrete or continuous.
-        for (x in list(NULL, "sum", "Cell_Cycle")) { # nothing, discrete or continuous. 
-              expect_s3_class(plotColData(example_sce, x = x, y = y, colour_by = "Treatment"), "ggplot")
-              expect_s3_class(plotColData(example_sce, x = x, y = y, size_by = "Gene_0001"), "ggplot")
-              expect_s3_class(plotColData(example_sce, x = x, y = y, shape_by = "Treatment"), "ggplot")
+        for (x in list(NULL, "sum", "Cell_Cycle")) { # nothing, discrete or continuous.
+              expect_ggplot(plotColData(example_sce, x = x, y = y, colour_by = "Treatment"))
+              expect_ggplot(plotColData(example_sce, x = x, y = y, size_by = "sizeFactor"))
+              expect_ggplot(plotColData(example_sce, x = x, y = y, shape_by = "Treatment"))
         }
     }
-    expect_s3_class(plotColData(example_sce, x = "sum", y = "detected", colour_by = "ENS_0001", swap_rownames = "ENS"), "ggplot")
+    expect_ggplot(plotColData(example_sce, x = "sum", y = "detected", colour_by = "ENS_0001", swap_rownames = "ENS"))
 
     # Testing more visualization schemes.
-    expect_s3_class(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", size_by = "Gene_0001"), "ggplot")
-    expect_s3_class(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotColData(example_sce, "sum", size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-    expect_s3_class(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"), "ggplot")
-    
+    expect_ggplot(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", size_by = "Gene_0001"))
+    expect_ggplot(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", shape_by = "Treatment"))
+    expect_ggplot(plotColData(example_sce, "sum", size_by = "Gene_0001", shape_by = "Treatment"))
+    expect_ggplot(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment"))
+
     # Testing that other arguments are passed through.
-    expect_s3_class(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment", add_legend = FALSE), "ggplot")
-    expect_s3_class(plotColData(example_sce, "sum", size_by = "Gene_0001", by_exprs_values = "counts"), "ggplot")
+    expect_ggplot(plotColData(example_sce, "sum", colour_by = "Cell_Cycle", size_by = "Gene_0001", shape_by = "Treatment", add_legend = FALSE))
+    expect_ggplot(plotColData(example_sce, "sum", size_by = "Gene_0001", by_exprs_values = "counts"))
 
     # Fiddling with all the semi-analysis options.
-    expect_s3_class(plotColData(example_sce, "sum", show_violin=FALSE), "ggplot")
-    expect_s3_class(plotColData(example_sce, "sum", show_median=TRUE), "ggplot")
-    expect_s3_class(plotColData(example_sce, "sum", jitter_type="jitter"), "ggplot")
+    expect_ggplot(plotColData(example_sce, "sum", show_violin=FALSE))
+    expect_ggplot(plotColData(example_sce, "sum", show_median=TRUE))
+    expect_ggplot(plotColData(example_sce, "sum", jitter_type="jitter"))
 
-    expect_s3_class(plotColData(example_sce, "sum", x="detected", show_smooth=TRUE), "ggplot")
-    expect_s3_class(plotColData(example_sce, "sum", x="detected", show_smooth=TRUE, show_se=FALSE), "ggplot")
+    expect_ggplot(plotColData(example_sce, "sum", x="detected", show_smooth=TRUE))
+    expect_ggplot(plotColData(example_sce, "sum", x="detected", show_smooth=TRUE, show_se=FALSE))
 
     # Checking that it doesn't try to retrieve expression data.
     expect_error(plotColData(example_sce, "Gene_0001", exprs_values = "counts"), "cannot find")
@@ -90,35 +90,35 @@ test_that("we can produce plots for column metadata", {
 })
 
 test_that("we can produce plots for row metadata", {
-    rowData(example_sce)$WHEE <- rep(LETTERS[1:10], length.out=nrow(example_sce))
+    rowData(example_sce)$WHEE <- rep(LETTERS[1:6], length.out=nrow(example_sce))
     rowData(example_sce)$is_feature_control <- rbinom(nrow(example_sce), 1, 0.5)
     example_sce <- addPerFeatureQC(example_sce)
 
     for (y in c("mean", "is_feature_control")) { # discrete or continuous.
-        for (x in list(NULL, "detected", "WHEE")) { # nothing, discrete or continuous. 
-              expect_s3_class(plotRowData(example_sce, x = x, y = y, colour_by = "WHEE"), "ggplot")
-              expect_s3_class(plotRowData(example_sce, x = x, y = y, size_by = "Cell_001"), "ggplot")
-              expect_s3_class(plotRowData(example_sce, x = x, y = y, shape_by = "WHEE"), "ggplot")
+        for (x in list(NULL, "detected", "WHEE")) { # nothing, discrete or continuous.
+              expect_ggplot(plotRowData(example_sce, x = x, y = y, colour_by = "WHEE"))
+              expect_ggplot(plotRowData(example_sce, x = x, y = y, size_by = "Cell_001"))
+              expect_ggplot(plotRowData(example_sce, x = x, y = y, shape_by = "WHEE"))
         }
     }
 
     # Testing more visualization schemes.
-    expect_s3_class(plotRowData(example_sce, "mean", colour_by = "is_feature_control", size_by = "Cell_002"), "ggplot")
-    expect_s3_class(plotRowData(example_sce, "mean", colour_by = "is_feature_control", shape_by = "WHEE"), "ggplot")
-    expect_s3_class(plotRowData(example_sce, "mean", size_by = "Cell_002", shape_by = "WHEE"), "ggplot")
-    expect_s3_class(plotRowData(example_sce, "mean", colour_by = "is_feature_control", size_by = "Cell_002", shape_by = "WHEE"), "ggplot")
-    
+    expect_ggplot(plotRowData(example_sce, "mean", colour_by = "is_feature_control", size_by = "Cell_002"))
+    expect_ggplot(plotRowData(example_sce, "mean", colour_by = "is_feature_control", shape_by = "WHEE"))
+    expect_ggplot(plotRowData(example_sce, "mean", size_by = "Cell_002", shape_by = "WHEE"))
+    expect_ggplot(plotRowData(example_sce, "mean", colour_by = "is_feature_control", size_by = "Cell_002", shape_by = "WHEE"))
+
     # Testing that other arguments are passed through.
-    expect_s3_class(plotRowData(example_sce, "mean", colour_by = "is_feature_control", size_by = "Cell_002", shape_by = "WHEE", add_legend = FALSE), "ggplot")
-    expect_s3_class(plotRowData(example_sce, "mean", size_by = "Cell_002", by_exprs_values = "counts"), "ggplot")
+    expect_ggplot(plotRowData(example_sce, "mean", colour_by = "is_feature_control", size_by = "Cell_002", shape_by = "WHEE", add_legend = FALSE))
+    expect_ggplot(plotRowData(example_sce, "mean", size_by = "Cell_002", by_exprs_values = "counts"))
 
     # Fiddling with all the semi-analysis options.
-    expect_s3_class(plotRowData(example_sce, "mean", show_violin=FALSE), "ggplot")
-    expect_s3_class(plotRowData(example_sce, "mean", show_median=TRUE), "ggplot")
-    expect_s3_class(plotRowData(example_sce, "mean", jitter_type="jitter"), "ggplot")
+    expect_ggplot(plotRowData(example_sce, "mean", show_violin=FALSE))
+    expect_ggplot(plotRowData(example_sce, "mean", show_median=TRUE))
+    expect_ggplot(plotRowData(example_sce, "mean", jitter_type="jitter"))
 
-    expect_s3_class(plotRowData(example_sce, "mean", x="detected", show_smooth=TRUE), "ggplot")
-    expect_s3_class(plotRowData(example_sce, "mean", x="detected", show_smooth=TRUE, show_se=FALSE), "ggplot")
+    expect_ggplot(plotRowData(example_sce, "mean", x="detected", show_smooth=TRUE))
+    expect_ggplot(plotRowData(example_sce, "mean", x="detected", show_smooth=TRUE, show_se=FALSE))
 
     # Checking that it doesn't try to retrieve expression data.
     expect_error(plotRowData(example_sce, "Cell_002", exprs_values = "counts"), "cannot find")
@@ -136,7 +136,7 @@ test_that("plotRowData works for other fields", {
 
     # This should throw a warning.
     rowData(example_sce)$colour_by <- rowData(example_sce)$STUFF
-    expect_warning(gg <- plotRowData(example_sce, "mean", 
+    expect_warning(gg <- plotRowData(example_sce, "mean",
         colour_by="STUFF", other_fields=c("colour_by")), "duplicated")
 })
 
@@ -148,52 +148,46 @@ test_that("plotRLE works as expected", {
 
     # With minimal or full.
     for (style in c("minimal", "full")) {
-        p <- plotRLE(example_sce, colour_by = "Mutation_Status", style=style)
-        expect_s3_class(p, "ggplot")
-    
         p <- plotRLE(example_sce, style=style)
-        expect_s3_class(p, "ggplot")
-        
+        expect_ggplot(p)
+
+        p <- plotRLE(example_sce, colour_by = "Mutation_Status", style=style)
+        expect_ggplot(p)
+
         p <- plotRLE(example_sce, exprs_values="cpm", exprs_logged=FALSE, style=style)
-        expect_s3_class(p, "ggplot")
-    
+        expect_ggplot(p)
+
         p <- plotRLE(example_sce, colour_by = "Gene_0004", style=style)
-        expect_s3_class(p, "ggplot")
-    
+        expect_ggplot(p)
+
         p <- plotRLE(example_sce, colour_by = "Gene_0004", by_exprs_values = "cpm", style=style)
-        expect_s3_class(p, "ggplot")
+        expect_ggplot(p)
     }
 })
 
 #################################################
-# Testing plotDots. 
+# Testing plotDots.
 
 test_that("plotDots works as expected", {
-    expect_s3_class(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10]), "ggplot")
+    expect_ggplot(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10]))
 
-    expect_s3_class(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10], colour = rainbow(7)), "ggplot")
+    expect_ggplot(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10], colour = rainbow(7)))
 
-    expect_s3_class(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10], zlim=c(-1, 1)), "ggplot")
+    expect_ggplot(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10], zlim=c(-1, 1)))
 
-    expect_s3_class(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10], max_detected=0.5), "ggplot")
+    expect_ggplot(plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10], max_detected=0.5))
 
-    expect_s3_class(plotDots(example_sce, features=rowData(example_sce)[1:10, "ENS"], swap_rownames = "ENS"), "ggplot")
+    expect_ggplot(plotDots(example_sce, features=rowData(example_sce)[1:10, "ENS"], swap_rownames = "ENS"))
 
     # Blocking works as expected.
-    expect_s3_class(plotDots(example_sce, group="Cell_Cycle", block="Mutation_Status",
-        features=rownames(example_sce)[1:10], max_detected=0.5), "ggplot")
-
-    expect_warning(
-        plotDots(example_sce, features=rownames(example_sce)[1:10], group="Cell_Cycle", max_ave=5),
-        "use 'zlim=' instead"
-    )
-
+    expect_ggplot(plotDots(example_sce, group="Cell_Cycle", block="Mutation_Status",
+        features=rownames(example_sce)[1:10], max_detected=0.5))
 
     # Checking that other_fields play nice.
     rowData(example_sce)$stuff <- runif(nrow(example_sce))
     rowData(example_sce)$otherstuff <- runif(nrow(example_sce))
-    expect_s3_class(p <- plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10], 
-        other_fields=c("stuff", "otherstuff")), "ggplot")
+    expect_ggplot(p <- plotDots(example_sce, group="Cell_Cycle", features=rownames(example_sce)[1:10],
+        other_fields=c("stuff", "otherstuff")))
 
     nuniq <- length(unique(example_sce$Cell_Cycle))
     expect_identical(p$data$stuff, rep(rowData(example_sce)$stuff[1:10], nuniq))
@@ -225,7 +219,7 @@ test_that("plotDots indexing is consistent", {
     p2 <- plotDots(example_sce, features=1:10)
     p3 <- plotDots(example_sce, features=c(rep(TRUE, 10), rep(FALSE, 1990)))
     p4 <- plotDots(example_sce, features=factor(rownames(example_sce)[1:10]))
-    
+
     expect_equal(p1, p2)
     expect_equal(p2, p3)
     expect_equal(p3, p4)
@@ -233,7 +227,7 @@ test_that("plotDots indexing is consistent", {
     p1 <- plotDots(example_sce, features=rownames(example_sce)[10:1])
     p2 <- plotDots(example_sce, features=10:1)
     p3 <- plotDots(example_sce, features=factor(rownames(example_sce)[1:10], levels = rownames(example_sce)[10:1]))
-    
+
     expect_equal(p1, p2)
     ## this has a different order but we want to check the order
     pd2 <- p2$data
@@ -245,3 +239,59 @@ test_that("plotDots indexing is consistent", {
         pd3[as.character(pd2$Feature), "Average"],
     )
 })
+
+
+test_that("plotDots factor indexing retains order", {
+    example_sce <- logNormCounts(example_sce)
+    levs <- c("G1", "G2M", "S", "G0")
+    example_sce$CycleF <- factor(example_sce$Cell_Cycle, levels = levs)
+    p1 <- plotDots(example_sce, 1:10, group="Cell_Cycle", block="Mutation_Status")
+    p2 <- plotDots(example_sce, 1:10, group = "CycleF", block = "Cell_Cycle")
+    expect_equal(levels(p1$data$Group), sort(unique(example_sce$Cell_Cycle)))
+    expect_equal(unique(as.character(p2$data$Group)), levs)
+})
+
+
+test_that("subset2index bug", {
+    example_sce <- mockSCE()
+    example_sce <- logNormCounts(example_sce)
+    feats <- rownames(example_sce)[2000:1000]
+    ffeats <- factor(feats, levels = feats)
+    p1 <- plotHighestExprs(example_sce, drop_features = feats)
+    p2 <- plotHighestExprs(example_sce, drop_features = ffeats)
+    expect_equal(sort(p1$data$Tag), sort(p2$data$Tag))
+
+    feats <- rownames(example_sce)[20:10]
+    ffeats <- factor(feats, levels = feats)
+    p1 <- plotDots(example_sce, features = feats)
+    p2 <- plotDots(example_sce, features = ffeats)
+    expect_equal(sort(p1$data$Tag), sort(p2$data$Tag))
+
+    # Checking that other_fields play nice.
+    rowData(example_sce)$stuff <- runif(nrow(example_sce))
+    rowData(example_sce)$otherstuff <- runif(nrow(example_sce))
+    p <- plotDots(example_sce, group="Cell_Cycle",
+        features=rownames(example_sce)[2:10],
+        other_fields=c("stuff", "otherstuff"))
+
+    nuniq <- length(unique(example_sce$Cell_Cycle))
+    expect_identical(p$data$stuff, rep(rowData(example_sce)$stuff[2:10], nuniq))
+    expect_identical(p$data$otherstuff, rep(rowData(example_sce)$otherstuff[2:10], nuniq))
+})
+
+test_that("plotColData with scattermore", {
+    expect_ggplot(plotColData(example_sce, x = "sum", y = "detected", scattermore = TRUE, point_size = 2))
+    expect_ggplot(plotColData(example_sce, x = "sum", y = "detected", scattermore = TRUE, point_size = 2, colour_by = "sizeFactor"))
+    expect_warning(plotColData(example_sce, x = "sum", y = "detected", scattermore = TRUE, shape_by = "Treatment"),
+                   "do not work with scattermore")
+})
+
+test_that("plotColData with bin2d", {
+    expect_ggplot(plotColData(example_sce, x = "sum", y = "detected", bins = 10))
+    expect_ggplot(plotColData(example_sce, x = "sum", y = "detected", bins = 10, hex = TRUE))
+    expect_ggplot(plotColData(example_sce, x = "sum", y = "detected", bins = 10, colour_by = "total"))
+    expect_ggplot(plotColData(example_sce, x = "sum", y = "detected", bins = 10,
+                              colour_by = "total", hex = TRUE))
+    expect_warning(plotColData(example_sce, x = "sum", y = "detected", bins = 10, colour_by = "Treatment"),
+                   "only applies to")
+})
diff --git a/tests/testthat/test-plot-scater.R b/tests/testthat/test-plot-scater.R
index cbcb661..6629e98 100644
--- a/tests/testthat/test-plot-scater.R
+++ b/tests/testthat/test-plot-scater.R
@@ -4,27 +4,27 @@
 example_sce <- sce
 
 test_that("plotScater works as expected", {
-    expect_s3_class(plotScater(example_sce), "ggplot")
-    expect_s3_class(plotScater(example_sce, colour_by = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotScater(example_sce, block1 = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotScater(example_sce, block2 = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotScater(example_sce, block1 = "Treatment", block2 = "Cell_Cycle"), "ggplot")
+    expect_ggplot(plotScater(example_sce))
+    expect_ggplot(plotScater(example_sce, colour_by = "Cell_Cycle"))
+    expect_ggplot(plotScater(example_sce, block1 = "Cell_Cycle"))
+    expect_ggplot(plotScater(example_sce, block2 = "Cell_Cycle"))
+    expect_ggplot(plotScater(example_sce, block1 = "Treatment", block2 = "Cell_Cycle"))
 
     # Different types of colouring are possible
-    expect_s3_class(plotScater(example_sce, colour_by = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotScater(example_sce, colour_by = "Gene_0001"), "ggplot")
+    expect_ggplot(plotScater(example_sce, colour_by = "Cell_Cycle"))
+    expect_ggplot(plotScater(example_sce, colour_by = "Gene_0001"))
 
-    expect_s3_class(plotScater(example_sce, block1 = "Treatment", colour_by = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotScater(example_sce, block1 = "Mutation_Status", colour_by = "Gene_0001"), "ggplot")
+    expect_ggplot(plotScater(example_sce, block1 = "Treatment", colour_by = "Cell_Cycle"))
+    expect_ggplot(plotScater(example_sce, block1 = "Mutation_Status", colour_by = "Gene_0001"))
 
-    expect_s3_class(plotScater(example_sce, block1 = "Cell_Cycle", block2 = "Treatment", colour_by = "Cell_Cycle"), "ggplot")
-    expect_s3_class(plotScater(example_sce, block1 = "Cell_Cycle", block2 = "Treatment", colour_by = "Gene_0001"), "ggplot")
+    expect_ggplot(plotScater(example_sce, block1 = "Cell_Cycle", block2 = "Treatment", colour_by = "Cell_Cycle"))
+    expect_ggplot(plotScater(example_sce, block1 = "Cell_Cycle", block2 = "Treatment", colour_by = "Gene_0001"))
     
-    expect_s3_class(plotScater(example_sce, colour_by = "Gene_0001", by_exprs_values = "counts"), "ggplot")
+    expect_ggplot(plotScater(example_sce, colour_by = "Gene_0001", by_exprs_values = "counts"))
 
     # Responds to different type of expression values.
     cpm(example_sce) <- calculateCPM(example_sce)
-    expect_s3_class(plotScater(example_sce, exprs_values="cpm"), "ggplot")
+    expect_ggplot(plotScater(example_sce, exprs_values="cpm"))
     expect_error(plotScater(example_sce, exprs_values="tpm"), "not in names")
 })
 
diff --git a/tests/testthat/test-plot-utils.R b/tests/testthat/test-plot-utils.R
index ed94d2c..d77d724 100644
--- a/tests/testthat/test-plot-utils.R
+++ b/tests/testthat/test-plot-utils.R
@@ -11,29 +11,29 @@ rownames(altExp(example_sce, 2)) <- paste0(rownames(altExp(example_sce, 2)), "-R
 
 test_that("retrieveCellInfo works in the basic case", {
     out <- retrieveCellInfo(example_sce, "Mutation_Status")
-    expect_identical(out$val, example_sce$Mutation_Status)
+    expect_identical(out$value, example_sce$Mutation_Status)
     expect_identical(out$name, "Mutation_Status")
 
     out <- retrieveCellInfo(example_sce, "Cell_Cycle")
-    expect_identical(out$val, example_sce$Cell_Cycle)
+    expect_identical(out$value, example_sce$Cell_Cycle)
     expect_identical(out$name, "Cell_Cycle")
 
     # Known gene exprs. 
     out <- retrieveCellInfo(example_sce, "Gene_0001")
-    expect_identical(out$val, logcounts(example_sce)["Gene_0001",])
+    expect_identical(out$value, logcounts(example_sce)["Gene_0001",])
     expect_identical(out$name, "Gene_0001")
 
     out <- retrieveCellInfo(example_sce, "Gene_0100")
-    expect_identical(out$val, logcounts(example_sce)["Gene_0100",])
+    expect_identical(out$value, logcounts(example_sce)["Gene_0100",])
     expect_identical(out$name, "Gene_0100")
 
     # Alternative experiments.
     out <- retrieveCellInfo(example_sce, "Gene_0005.0")
-    expect_identical(out$val, logcounts(altExp(example_sce))["Gene_0005.0",])
+    expect_identical(out$value, logcounts(altExp(example_sce))["Gene_0005.0",])
     expect_identical(out$name, "Gene_0005.0")
 
     out <- retrieveCellInfo(example_sce, "Gene_0005-R")
-    expect_identical(out$val, logcounts(altExp(example_sce, 2))["Gene_0005-R",])
+    expect_identical(out$value, logcounts(altExp(example_sce, 2))["Gene_0005-R",])
     expect_identical(out$name, "Gene_0005-R")
 
     # Known not to be either.
@@ -47,19 +47,19 @@ test_that("retrieveCellInfo works in the basic case", {
 test_that("retrieveCellInfo works with AsIs'd factors", {
     fac <- factor(example_sce$Mutation_Status)
     out <- retrieveCellInfo(example_sce, I(fac))
-    expect_identical(out$val, fac)
-    expect_false("AsIs" %in% class(out$val))
+    expect_identical(out$value, fac)
+    expect_false("AsIs" %in% class(out$value))
 })
 
 
 test_that("retrieveCellInfo handles clashes correctly", {
     example_sce$Gene_0002 <- seq_len(ncol(example_sce))
     out_m <- retrieveCellInfo(example_sce, "Gene_0002", search = "colData")
-    expect_identical(out_m$val, example_sce$Gene_0002)
+    expect_identical(out_m$value, example_sce$Gene_0002)
     expect_identical(out_m$name, "Gene_0002")
 
     out_f <- retrieveCellInfo(example_sce, "Gene_0002", search = "assays")
-    expect_identical(out_f$val, logcounts(example_sce)["Gene_0002",])
+    expect_identical(out_f$value, logcounts(example_sce)["Gene_0002",])
     expect_identical(out_f$name, "Gene_0002")
 
     # Respects ordering of inputs.
@@ -70,17 +70,17 @@ test_that("retrieveCellInfo handles clashes correctly", {
 test_that("retrieveCellInfo handles wrapped elements and columns with data.frames", {
     thing <- data.frame(B=runif(ncol(example_sce)))
     out <- retrieveCellInfo(example_sce, thing)
-    expect_identical(out$val, thing$B)
+    expect_identical(out$value, thing$B)
     expect_identical(out$name, "B")
 
     thing <- DataFrame(B=runif(ncol(example_sce)))
     out <- retrieveCellInfo(example_sce, thing)
-    expect_identical(out$val, thing$B)
+    expect_identical(out$value, thing$B)
     expect_identical(out$name, "B")
 
     thing <- I(runif(ncol(example_sce)))
     out <- retrieveCellInfo(example_sce, thing)
-    expect_identical(out$val, as.numeric(thing))
+    expect_identical(out$value, as.numeric(thing))
     expect_identical(out$name, "")
 
     # Check errors.
@@ -97,20 +97,20 @@ rowData(example_sce) <- DataFrame(HAPPY=runif(nrow(example_sce)), SAD=rbinom(nro
 test_that("retrieveFeatureInfo works for rows with strings", {
     # Known metadata.
     out <- retrieveFeatureInfo(example_sce, "HAPPY")
-    expect_identical(out$val, rowData(example_sce)$HAPPY)
+    expect_identical(out$value, rowData(example_sce)$HAPPY)
     expect_identical(out$name, "HAPPY")
 
     out <- retrieveFeatureInfo(example_sce, "SAD")
-    expect_identical(out$val, rowData(example_sce)$SAD)
+    expect_identical(out$value, rowData(example_sce)$SAD)
     expect_identical(out$name, "SAD")
 
     # Known exprs.
     out <- retrieveFeatureInfo(example_sce, "Cell_001")
-    expect_identical(out$val, logcounts(example_sce)[,"Cell_001"])
+    expect_identical(out$value, logcounts(example_sce)[,"Cell_001"])
     expect_identical(out$name, "Cell_001")
 
     out <- retrieveFeatureInfo(example_sce, "Cell_010")
-    expect_identical(out$val, logcounts(example_sce)[,"Cell_010"])
+    expect_identical(out$value, logcounts(example_sce)[,"Cell_010"])
     expect_identical(out$name, "Cell_010")
 
     # Handles errors properly.
@@ -124,11 +124,11 @@ test_that("retrieveFeatureInfo works for rows with strings", {
 test_that("retrieveFeatureInfo is responsive to search mode", {
     rowData(example_sce)$Cell_002 <- seq_len(nrow(example_sce))
     out_m <- retrieveFeatureInfo(example_sce, "Cell_002", search = "rowData")
-    expect_identical(out_m$val, rowData(example_sce)$Cell_002)
+    expect_identical(out_m$value, rowData(example_sce)$Cell_002)
     expect_identical(out_m$name, "Cell_002")
 
     out_f <- retrieveFeatureInfo(example_sce, "Cell_002", , search = "assays")
-    expect_identical(out_f$val, logcounts(example_sce)[,"Cell_002"])
+    expect_identical(out_f$value, logcounts(example_sce)[,"Cell_002"])
     expect_identical(out_f$name, "Cell_002")
 
     expect_identical(out_m, retrieveFeatureInfo(example_sce, "Cell_002", search=c("rowData", "assays")))
@@ -138,24 +138,24 @@ test_that("retrieveFeatureInfo is responsive to search mode", {
 test_that("retrieveFeatureInfo works with AsIs'd factors", {
     fac <- factor(rownames(example_sce))
     out <- retrieveFeatureInfo(example_sce, I(fac))
-    expect_identical(out$val, fac)
-    expect_false("AsIs" %in% class(out$val))
+    expect_identical(out$value, fac)
+    expect_false("AsIs" %in% class(out$value))
 })
 
 test_that("retrieveFeatureInfo works for rows with data.frames", {
     thing <- data.frame(B=runif(nrow(example_sce)))
     out <- retrieveFeatureInfo(example_sce, thing, )
-    expect_identical(out$val, thing$B)
+    expect_identical(out$value, thing$B)
     expect_identical(out$name, "B")
 
     thing <- DataFrame(B=runif(nrow(example_sce)))
     out <- retrieveFeatureInfo(example_sce, thing)
-    expect_identical(out$val, thing$B)
+    expect_identical(out$value, thing$B)
     expect_identical(out$name, "B")
 
     thing <- I(runif(nrow(example_sce)))
     out <- retrieveFeatureInfo(example_sce, thing)
-    expect_identical(out$val, as.numeric(thing))
+    expect_identical(out$value, as.numeric(thing))
     expect_identical(out$name, "")
 
     rething <- data.frame(B=runif(ncol(example_sce)), C=2)
@@ -165,10 +165,10 @@ test_that("retrieveFeatureInfo works for rows with data.frames", {
 
 test_that("gg functions work as expected", {
     gg <- ggcells(example_sce, mapping=aes(x=Gene_0001, y=Gene_0002))
-    expect_s3_class(gg, "ggplot")
+    expect_ggplot(gg)
     
     gg <- ggfeatures(example_sce, mapping=aes(x=Cell_001, y=Cell_002))
-    expect_s3_class(gg, "ggplot")
+    expect_ggplot(gg)
 })
 
 
diff --git a/tests/testthat/test-qc-plot.R b/tests/testthat/test-qc-plot.R
index 43dedde..253cf4a 100644
--- a/tests/testthat/test-qc-plot.R
+++ b/tests/testthat/test-qc-plot.R
@@ -11,28 +11,28 @@ counts(sparsified) <- as(counts(sparsified), "dgCMatrix")
 #######################################################################
 
 test_that("plotHighestExprs works on vanilla cases", {
-    expect_s3_class(plotHighestExprs(wt_qc), "ggplot")
-    expect_s3_class(plotHighestExprs(wt_qc), "ggplot")
-    expect_s3_class(plotHighestExprs(wt_qc, as_percentage = FALSE), "ggplot")
+    expect_ggplot(plotHighestExprs(wt_qc))
+    expect_ggplot(plotHighestExprs(wt_qc))
+    expect_ggplot(plotHighestExprs(wt_qc, as_percentage = FALSE))
 })
 
 test_that("plotHighestExprs' aesthetics choices work", {
-    expect_s3_class(plotHighestExprs(wt_qc, colour_cells_by = "sum"), "ggplot")
-    expect_s3_class(plotHighestExprs(wt_qc, colour_cells_by = "Mutation_Status"), "ggplot")
-    expect_s3_class(plotHighestExprs(wt_qc, colour_cells_by = NULL), "ggplot")
-    expect_s3_class(plotHighestExprs(wt_qc, colour_cells_by = "Gene_0001", by_exprs_values = "counts"), "ggplot")
+    expect_ggplot(plotHighestExprs(wt_qc, colour_cells_by = "sum"))
+    expect_ggplot(plotHighestExprs(wt_qc, colour_cells_by = "Mutation_Status"))
+    expect_ggplot(plotHighestExprs(wt_qc, colour_cells_by = NULL))
+    expect_ggplot(plotHighestExprs(wt_qc, colour_cells_by = "Gene_0001", by_exprs_values = "counts"))
 })
 
 test_that("plotHighestExprs works with different feature selections", {
-    expect_s3_class(plotHighestExprs(wt_qc, n=Inf), "ggplot")
+    expect_ggplot(plotHighestExprs(wt_qc, n=Inf))
 
     # Responds to other sources for row names.
     rowData(wt_qc)$Whee <- paste("Feature", seq_len(nrow(wt_qc)))
-    expect_s3_class(plotHighestExprs(wt_qc, feature_names_to_plot = "Whee"), "ggplot")
+    expect_ggplot(plotHighestExprs(wt_qc, feature_names_to_plot = "Whee"))
 
     dummy <- wt_qc
     rownames(dummy) <- NULL
-    expect_s3_class(plotHighestExprs(dummy), "ggplot")
+    expect_ggplot(plotHighestExprs(dummy))
     
     # Discarding and subset exclusion yield the same results.
     discard <- rbinom(nrow(wt_qc), 1, 0.5)==1
@@ -52,8 +52,8 @@ test_that("plotHighestExprs works with different feature selections", {
 test_that("plotHighestExprs works on alternative exprs", {
     alt_sce <- wt_qc 
     assayNames(alt_sce) <- "whee"
-    expect_s3_class(plotHighestExprs(alt_sce, exprs_values="whee"), "ggplot")
+    expect_ggplot(plotHighestExprs(alt_sce, exprs_values="whee"))
 
     # Works for sparse matrices.
-    expect_s3_class(plotHighestExprs(sparsified), "ggplot")
+    expect_ggplot(plotHighestExprs(sparsified))
 })
diff --git a/tests/testthat/test-red-dim.R b/tests/testthat/test-red-dim.R
index 5f96fc2..04dc966 100644
--- a/tests/testthat/test-red-dim.R
+++ b/tests/testthat/test-red-dim.R
@@ -1,5 +1,4 @@
 # Tests the options in the various reduced dimension functions
-# library(scater); library(testthat); source("setup.R"); source("test-red-dim.R")
 
 #############################################
 # Check the feature selection and scaling work.
@@ -393,60 +392,6 @@ test_that("runUMAP on existing reduced dimension results works as expected", {
 })
 
 
-## these tests no longer work because bp*apply now always changes the RNG stream
-## see https://github.com/Bioconductor/BiocParallel/pull/140#issuecomment-921627153
-
-# test_that("runUMAP works with externally computed nearest neighbor results", {
-#     skip_on_os("windows") # Use with VP-tree gives different results from internal NN search on Win32. Why? Who knows. 
-
-#     normedP <- runPCA(normed, ncomponents = 20)
-
-#     # Need to cajole the random seed to avoid different RNG states after the NN search. 
-#     seedSet <- function(...) invisible(BiocNeighbors::buildIndex(reducedDim(normedP, "PCA"), ...))
-
-#     set.seed(20)
-#     seedSet()
-#     ref <- runUMAP(normedP, dimred = "PCA")
-#     set.seed(20)
-#     alt <- runUMAP(normedP, dimred = "PCA", external_neighbors = TRUE)
-#     expect_identical(reducedDim(ref, "UMAP"), reducedDim(alt, "UMAP"))
-
-#     set.seed(21)
-#     seedSet()
-#     ref <- runUMAP(normedP, dimred = "PCA", n_neighbors = 10)
-#     set.seed(21)
-#     alt <- runUMAP(normedP, dimred = "PCA", n_neighbors = 10, external_neighbors = TRUE)
-#     expect_identical(reducedDim(ref, "UMAP"), reducedDim(alt, "UMAP"))
-
-#     set.seed(22)
-#     seedSet()
-#     ref <- runUMAP(normedP, dimred = "PCA", bandwidth = 1.5)
-#     set.seed(22) 
-#     alt <- runUMAP(normedP, dimred = "PCA", bandwidth = 1.5, external_neighbors = TRUE)
-#     expect_identical(reducedDim(ref, "UMAP"), reducedDim(alt, "UMAP"))
-
-#     # Works with alternative neighbor searching options.
-#     set.seed(23)
-#     seedSet(BNPARAM = BiocNeighbors::VptreeParam())
-#     ref <- runUMAP(normedP, dimred = "PCA")
-#     set.seed(23)
-#     alt <- runUMAP(normedP, dimred = "PCA", external_neighbors = TRUE, BNPARAM = BiocNeighbors::VptreeParam())
-#     expect_identical(reducedDim(ref, "UMAP"), reducedDim(alt, "UMAP"))
-
-#     # Works with parallelization and more seed-related cajoling.
-#     BPPARAM <- safeBPParam(2)
-#     BiocParallel::bpstart(BPPARAM)
-
-#     set.seed(24)
-#     seedSet()
-#     ref <- runUMAP(normedP, dimred = "PCA")
-#     set.seed(24) 
-#     alt <- runUMAP(normedP, dimred = "PCA", external_neighbors = TRUE, BPPARAM = BPPARAM)
-#     expect_identical(reducedDim(ref, "UMAP"), reducedDim(alt, "UMAP"))
-
-#     BiocParallel::bpstop(BPPARAM)
-# })
-
 test_that("multi-modal UMAP works as expected", {
     stuff <- matrix(rnorm(10000), ncol = 50)
     things <- list(stuff, stuff[, 1:5], stuff[, 1:20])
@@ -499,11 +444,8 @@ test_that("runNMF works as expected", {
     normed3 <- runNMF(normed, subset_row = 1:100)
     expect_false(isTRUE(all.equal(reducedDim(normed2), reducedDim(normed3))))
 
-    # set.seed(100)
-    # normed3 <- runNMF(normed, method = "Frobenius")
-    # expect_false(isTRUE(all.equal(reducedDim(normed2), reducedDim(normed3))))
-
-    # Testing out the use of existing reduced dimensions (this should not respond to any feature settings).
+    # Testing out the use of existing reduced dimensions
+    # (this should not respond to any feature settings).
     normedP <- runPCA(normed, ncomponents = 4)
     reducedDim(normedP, "PCA") <- abs(reducedDim(normedP, "PCA"))
 
@@ -548,7 +490,8 @@ test_that("runMDS works as expected", {
     normed3 <- runMDS(normed, method = "manhattan")
     expect_false(isTRUE(all.equal(reducedDim(normed2), reducedDim(normed3))))
 
-    # Testing out the use of existing reduced dimensions (this should not respond to any feature settings).
+    # Testing out the use of existing reduced dimensions
+    # (this should not respond to any feature settings).
     normedP <- runPCA(normed, ncomponents = 4)
     normed2 <- runMDS(normedP, dimred = "PCA")
 
@@ -561,7 +504,8 @@ test_that("runMDS works as expected", {
     normed3 <- runMDS(normedP, dimred = "PCA", subset_row = 1:20)
     expect_identical(reducedDim(normed2, "MDS"), reducedDim(normed3, "MDS"))
 
-    # This does, in fact, happen to be equal, due to the relationship between MDS and PCA.
+    # This does, in fact, happen to be equal,
+    # due to the relationship between MDS and PCA.
     # This is not identifiable by the sign, hence the finagling.
     normed3 <- runMDS(normedP, dimred = "PCA", n_dimred = 3)
     fold <- reducedDim(normed2, "MDS") / reducedDim(normed3, "MDS")
@@ -581,17 +525,16 @@ test_that("run* functions work with sparse matrices", {
     expect_error(runTSNE(normed), NA)
     expect_error(runNMF(normed), NA)
     expect_error(runUMAP(normed), NA)
-    # expect_error(runDiffusionMap(normed), NA)
     expect_error(runMDS(normed), NA)
 })
 
 test_that("projectReducedDim works as expected", {
-    example_sce <- mockSCE() 
+    example_sce <- mockSCE()
     example_sce <- logNormCounts(example_sce)
     example_sce <- runUMAP(example_sce)
     example_sce <- runPCA(example_sce)
 
-    example_sce_new <- mockSCE() 
+    example_sce_new <- mockSCE()
     example_sce_new <- logNormCounts(example_sce_new)
     example_sce_new <- runPCA(example_sce_new)
 
@@ -612,3 +555,15 @@ test_that("projectReducedDim works as expected", {
     )
 
 })
+
+
+test_that("plotReducedDim works with point_shape", {
+    example_sce <- mockSCE()
+    example_sce <- logNormCounts(example_sce)
+    example_sce <- runUMAP(example_sce)
+    p1 <- plotReducedDim(example_sce, "UMAP", point_shape = 15)
+    expect_equal(p1$layers[[1]]$aes_params$shape, 15)
+    p2 <- plotReducedDim(example_sce, "UMAP", point_shape = 12)
+    expect_equal(p2$layers[[1]]$aes_params$shape, 12)
+})
+

More details

Full run details

Historical runs