https://github.com/cran/sjPlot
Raw File
Tip revision: 1def17c898ee1a07586dfa29a1d5e9605611dacc authored by Daniel Lüdecke on 12 October 2015, 13:46:33 UTC
version 1.8.4
Tip revision: 1def17c
sjPlotSetTheme.R
#' @title Set global theme options for sjp-functions
#' @name sjp.setTheme
#' 
#' @description Set global theme options for sjp-functions.
#' 
#' @param theme specify pre-set themes (see 'Details'). Valid argument for ggplot default-themes are for instance:
#'        \itemize{
#'          \item \code{theme_bw}
#'          \item \code{theme_classic}
#'          \item \code{theme_grey}
#'          \item \code{theme_light}
#'          \item \code{theme_linedraw}
#'          \item \code{theme_minimal}
#'        }
#'        Furthermore, there are some theme-presets, which can be used:
#'        \describe{
#'          \item{\code{"blank"}}{a theme with no grids and axes.}
#'          \item{\code{"forest"}}{a theme for forest plots, with no grids.}
#'          \item{\code{"forestgrey"}}{a theme for forest plots, with no grids, in "539" style.}
#'          \item{\code{"538"}}{a grey-scaled theme inspired by \href{http://fivethirtyeight.com}{538-charts}, adapted from \href{http://minimaxir.com/2015/02/ggplot-tutorial/}{minimaxir.com}.}
#'          \item{\code{"539"}}{a slight modification of the 538-theme.}
#'          \item{\code{"scatter"}}{a theme for scatter plots in 539-theme-style.}
#'          \item{\code{"538w"}, \code{"539w"}, \code{"scatterw"} and \code{"forestw"}}{for themes as described above, however all with white backgrounds.}
#'          \item{\code{"blues"}}{a blue-colored scheme based on the Blues color-brewer-palette.}
#'          \item{\code{"greens"}}{a green-colored scheme.}
#'        }
#' @param title.size size of plot title. Default is 1.3.
#' @param title.color color of plot title. Default is \code{"black"}.
#' @param title.align alignment of plot title. Must be one of \code{"left"} (default),
#'          \code{"center"} or \code{"right"}. You may use initial letter only.
#' @param geom.outline.size size of bar outlines. Default is 0.1. Use
#'          size of \code{0} to remove geom outline.
#' @param geom.outline.color color of geom outline. Only applies, if \code{geom.outline.size}
#'          is larger than 0.
#' @param geom.boxoutline.size size of outlines and median bar especially for boxplots.
#'          Default is 0.5. Use size of \code{0} to remove boxplot outline.
#' @param geom.boxoutline.color color of outlines and median bar especially for boxplots.
#'          Only applies, if \code{geom.boxoutline.size} is larger than 0.
#' @param geom.alpha specifies the transparancy (alpha value) of geoms
#' @param geom.linetype linetype of line geoms. Default is \code{1} (solid line).
#' @param geom.errorbar.size size (thickness) of error bars. Default is \code{0.8}
#' @param geom.errorbar.linetype linetype of error bars. Default is \code{1} (solid line).
#' @param geom.label.color color of geom's value and annotation labels
#' @param geom.label.size size of geom's value and annotation labels
#' @param geom.label.alpha alpha level of geom's value and annotation labels
#' @param geom.label.angle angle of geom's value and annotation labels
#' @param axis.title.color color of x- and y-axis title labels
#' @param axis.title.size size of x- and y-axis title labels
#' @param axis.angle.x angle for x-axis labels
#' @param axis.angle.y angle for y-axis labels
#' @param axis.angle angle for x- and y-axis labels. If set, overrides both \code{axis.angle.x} and \code{axis.angle.y}
#' @param axis.textcolor.x color for x-axis labels. If not specified, a default dark gray
#'          color palette will be used for the labels.
#' @param axis.textcolor.y color for y-axis labels. If not specified, a default dark gray
#'          color palette will be used for the labels.
#' @param axis.textcolor color for both x- and y-axis labels. 
#'          If set, overrides both \code{axis.textcolor.x} and \code{axis.textcolor.y}
#' @param axis.linecolor.x color of x-axis border
#' @param axis.linecolor.y color of y-axis border
#' @param axis.linecolor color for both x- and y-axis borders. 
#'          If set, overrides both \code{axis.linecolor.x} and \code{axis.linecolor.y}.
#' @param axis.line.size size (thickness) of axis lines. Only affected, if \code{axis.linecolor}
#'          is set.
#' @param axis.textsize.x size of x-axis labels
#' @param axis.textsize.y size of y-axis labels
#' @param axis.textsize size for both x- and y-axis labels. 
#'          If set, overrides both \code{axis.textsize.x} and \code{axis.textsize.y}.
#' @param axis.tickslen length of axis tick marks
#' @param axis.ticksol color of axis tick marks
#' @param axis.ticksmar margin between axis labels and tick marks
#' @param panel.bordercol color of whole diagram border (panel border)
#' @param panel.backcol color of the diagram's background
#' @param panel.col color of both diagram's border and background.
#'          If set, overrides both \code{panel.bordercol} and \code{panel.backcol}.
#' @param panel.major.gridcol color of the major grid lines of the diagram background
#' @param panel.minor.gridcol color of the minor grid lines of the diagram background
#' @param panel.gridcol color for both minor and major grid lines of the diagram background.
#'          If set, overrides both \code{panel.major.gridcol} and \code{panel.minor.gridcol}.
#' @param panel.major.linetype line type for major grid lines
#' @param panel.minor.linetype line type for minor grid lines
#' @param plot.backcol color of the plot's background
#' @param plot.bordercol color of whole plot's border (panel border)
#' @param plot.col color of both plot's region border and background.
#'          If set, overrides both \code{plot.backcol} and \code{plot.bordercol}.
#' @param legend.pos position of the legend, if a legend is drawn.
#'          \describe{
#'            \item{\emph{legend outside plot}}{
#'              Use \code{"bottom"}, \code{"top"}, \code{"left"} or \code{"right"} 
#'              to position the legend above, below, on the left or right side 
#'              of the diagram. Right positioning is default.
#'            }
#'            \item{\emph{legend inside plot}}{
#'              If \code{legend.inside = TRUE}, legend can be placed inside
#'              plot. Use \code{"top left"}, \code{"top right"}, \code{"bottom left"} 
#'              and \code{"bottom right"} to position legend in any of these corners, 
#'              or a two-element numeric vector with values from 0-1. See also 
#'              \code{legend.inside}.
#'            }
#'          }
#' @param legend.just justification of legend, relative to its position (\code{"center"} or 
#'          two-element numeric vector with values from 0-1. By default (outside legend),
#'          justification is centered. If legend is inside and justification not specified,
#'          legend justification is set according to legend position.
#' @param legend.inside logical, use \code{TRUE} to put legend inside the plotting area. See \code{legend.pos}.
#' @param legend.size text size of the legend. Default is 1. Relative size, so 
#'          recommended values are from 0.3 to 2.5
#' @param legend.color color of the legend labels
#' @param legend.title.size text size of the legend title
#' @param legend.title.color color of the legend title
#' @param legend.title.face font face of the legend title. By default, \code{"bold"} face is used.
#' @param legend.bordercol color of the legend's border. Default is \code{"white"}, so no visible border is drawn.
#' @param legend.backgroundcol fill color of the legend's background. Default is \code{"white"}, so no visible background is drawn.
#' @param legend.item.size size of legend's item (legend key), in centimetres.
#' @param legend.item.bordercol color of the legend's item-border. Default is \code{"white"}.
#' @param legend.item.backcol fill color of the legend's item-background. Default is \code{"grey90"}.
#' @param base base theme where theme is built on. By default, all 
#'          metrics from \code{theme_gray()} are used. See 'Details'.
#' 
#' @return The customized theme object, or \code{NULL}, if a ggplot-theme was used.
#' 
#' @details If the \code{theme} argument is one of the valid ggplot-themes, this theme
#'            will be used and all further arguments will be ignored. If you want to modify
#'            a ggplot-theme, use \code{base = "theme_xy"}, then further arguments to
#'            this function will be applied to the theme as well.
#'            \cr \cr
#'            If the \code{theme} argument is one of sjPlot-pre-set-themes, you
#'            can use further arguments for specific customization of the theme.
#'            \emph{sjPlot-pre-set-themes won't work with the \code{base} argument!}
#'            The \code{base} argument is only intended to select a ggplot-theme
#'            as base for further modifications (which can be triggered via the
#'            various function arguments).
#' 
#' @seealso \href{http://www.strengejacke.de/sjPlot/custplot/}{sjPlot manual: customize plot appearance}
#' 
#' @references \itemize{
#'              \item \href{http://zevross.com/blog/2014/08/04/beautiful-plotting-in-r-a-ggplot2-cheatsheet-3/}{Beautiful plotting in R: A ggplot2 cheatsheet}
#'              \item \href{http://minimaxir.com/2015/02/ggplot-tutorial/}{An Introduction on How to Make Beautiful Charts With R and ggplot2}
#'             }
#' 
#' @examples
#' \dontrun{
#' library(sjmisc)
#' data(efc)
#' # set sjPlot-defaults, a slightly modification
#' # of the ggplot base theme
#' sjp.setTheme()
#' 
#' # legends of all plots inside
#' sjp.setTheme(legend.pos = "top left", 
#'              legend.inside = TRUE)
#' sjp.xtab(efc$e42dep, efc$e16sex)
#' 
#' # Use classic-theme. you may need to
#' # load the ggplot2-library.
#' library(ggplot2)
#' sjp.setTheme(theme = theme_classic())
#' sjp.frq(efc$e42dep)
#' 
#' # adjust value labels
#' sjp.setTheme(geom.label.size = 3.5,
#'              geom.label.color = "#3366cc",
#'              geom.label.angle = 90)
#' # hjust-aes needs adjustment for this
#' update_geom_defaults('text', list(hjust = -0.1))
#' sjp.xtab(efc$e42dep, 
#'          efc$e16sex,
#'          labelPos = "center")
#' 
#' # Create own theme based on classic-theme
#' sjp.setTheme(base = theme_classic(),
#'              axis.linecolor = "grey50",
#'              axis.textcolor = "#6699cc")
#' sjp.frq(efc$e42dep)
#'
#' # use theme pre-set
#' sjp.setTheme(theme = "538",
#'              geom.alpha = 0.8)
#' library(ggplot2) # for custom base-line
#' sjp.frq(efc$e42dep, 
#'         geom.color = "#c0392b",
#'         expand.grid = TRUE,
#'         printPlot = FALSE)$plot + 
#'   geom_hline(yintercept = 0, 
#'              size = 0.5, 
#'              colour = "black")}
#' 
#' @import ggplot2
#' @importFrom grid unit
#' @importFrom scales brewer_pal grey_pal
#' @export
sjp.setTheme <- function(# base theme
                         theme = NULL,
                         # title defaults
                         title.color = "black",
                         title.size = 1.2,
                         title.align = "left",
                         # geom defaults
                         # geom.colors=NULL,
                         geom.outline.color = NULL,
                         geom.outline.size = 0.1,
                         geom.boxoutline.size = 0.5,
                         geom.boxoutline.color = "black",
                         geom.alpha = 1,
                         geom.linetype = 1,
                         geom.errorbar.size = 0.7,
                         geom.errorbar.linetype = 1,
                         # value labels
                         geom.label.color = NULL,
                         geom.label.size = 4,
                         geom.label.alpha = 1,
                         geom.label.angle = 0,
                         # axis titles
                         axis.title.color = "grey30",
                         axis.title.size = 1.1,
                         # axis text angle
                         axis.angle.x = 0,
                         axis.angle.y = 0,
                         axis.angle = NULL,
                         # axis text colors
                         axis.textcolor.x = "grey30",
                         axis.textcolor.y = "grey30",
                         axis.textcolor = NULL,
                         # axis line colors
                         axis.linecolor.x = NULL,
                         axis.linecolor.y = NULL,
                         axis.linecolor = NULL,
                         axis.line.size = 0.5,
                         # axis text size
                         axis.textsize.x = 1,
                         axis.textsize.y = 1,
                         axis.textsize = NULL,
                         # axis ticks
                         axis.tickslen = NULL,
                         axis.ticksol = NULL,
                         axis.ticksmar = NULL,
                         # panel defaults
                         panel.backcol = NULL,
                         panel.bordercol = NULL,
                         panel.col = NULL,
                         panel.major.gridcol = NULL,
                         panel.minor.gridcol = NULL,
                         panel.gridcol = NULL,
                         panel.major.linetype = 1,
                         panel.minor.linetype = 1,
                         # plot background color
                         plot.backcol = NULL,
                         plot.bordercol = NULL,
                         plot.col = NULL,
                         # legend
                         legend.pos = "right",
                         legend.just = NULL,
                         legend.inside = FALSE,
                         legend.size = 1,
                         legend.color = "black",
                         legend.title.size = 1,
                         legend.title.color = "black",
                         legend.title.face = "bold",
                         legend.backgroundcol = "white",
                         legend.bordercol = "white",
                         legend.item.size = NULL,
                         legend.item.backcol = "grey90",
                         legend.item.bordercol = "white",
                         base = theme_grey()) {
  sjtheme <- NULL
  title.vjust <- NULL
  axis.title.x.vjust <- NULL
  axis.title.y.vjust <- NULL
  plot.margins <- NULL
  panel.gridcol.x <- NULL
  panel.gridcol.y <- NULL
  # ----------------------------------------  
  # check for blank theme, i.e. if user requires special
  # theme without any grids or axis lines
  # ----------------------------------------  
  if (!is.null(theme) && theme == "blank") {
    base <- theme_classic()
    axis.linecolor <- "white"
    axis.ticksol <- "white"
    panel.gridcol <- "white"
    plot.col <- "white"
  }
  # ----------------------------------------  
  # check for forset theme. based on theme_bw,
  # this theme has no grids
  # ----------------------------------------  
  if (!is.null(theme) && theme == "forest") {
    base <- theme_bw()
    panel.gridcol <- "white"
    axis.tickslen <- 0
  }  
  # ----------------------------------------  
  # check for grey-scaled 538 theme.
  # ----------------------------------------  
  if (!is.null(theme) && (theme == "538" || theme == "538w")) {
    base <- theme_bw()
    g.palette <- scales::brewer_pal(palette = "Greys")(9)
    col.ind <- ifelse(theme == "538", 2, 1)
    panel.bordercol <- panel.backcol <- panel.col <- g.palette[col.ind]
    plot.backcol <- plot.bordercol <- plot.col <- g.palette[col.ind]
    panel.minor.gridcol <- g.palette[col.ind]
    axis.linecolor.x  <- axis.linecolor.y <- axis.linecolor <- g.palette[col.ind]
    legend.item.backcol <- legend.item.bordercol <- legend.backgroundcol <- legend.bordercol <- g.palette[col.ind]
    panel.major.gridcol <- g.palette[4]
    if (missing(title.color)) title.color <- g.palette[9]
    if (missing(axis.textcolor)) axis.textcolor <- g.palette[6]
    if (missing(axis.title.color)) axis.title.color <- g.palette[7]
    if (missing(geom.label.color) || is.null(geom.label.color)) geom.label.color <- g.palette[6]
    if (missing(legend.title.color)) legend.title.color <- g.palette[7]
    if (missing(legend.color)) legend.color <- g.palette[6]
    axis.tickslen <- 0
    # custom modifications
    title.align <- "center"
    axis.title.x.vjust <- -1
    axis.title.y.vjust <- 1.5
    title.vjust <- 1.75
    plot.margins <- grid::unit(c(1, .5, 1, 0.5), "cm")
    message("Theme '538' looks better with panel margins. You may want to use argument 'expand.grid = TRUE' in sjp-functions.")
  }  
  # ----------------------------------------  
  # check for grey-scaled 539 theme, which are
  # alternatives to 538
  # ----------------------------------------  
  if (!is.null(theme) && (theme == "539" || theme == "539w" || theme == "forestgrey" || theme == "forestw")) {
    base <- theme_bw()
    g.palette <- scales::brewer_pal(palette = "Greys")(9)
    col.ind <- ifelse(theme == "539w" || theme == "forestw", 1, 2)
    panel.bordercol <- panel.backcol <- panel.col <- g.palette[col.ind]
    plot.backcol <- plot.bordercol <- plot.col <- g.palette[col.ind]
    if (theme == "539" || theme == "539w") {
      panel.major.gridcol <- g.palette[4]
      panel.minor.gridcol <- g.palette[col.ind]
      panel.gridcol.x <- g.palette[col.ind]
    } else {
      panel.major.gridcol <- panel.minor.gridcol <- g.palette[col.ind]
    }
    axis.linecolor <- NULL
    if (missing(axis.linecolor.y) || is.null(axis.linecolor.y)) axis.linecolor.y <- g.palette[col.ind]
    if (missing(axis.linecolor.x) || is.null(axis.linecolor.x)) axis.linecolor.x <- g.palette[9]
    legend.item.backcol <- legend.item.bordercol <- legend.backgroundcol <- legend.bordercol <- g.palette[col.ind]
    if (missing(title.color)) title.color <- g.palette[9]
    if (missing(axis.textcolor)) axis.textcolor <- g.palette[6]
    if (missing(axis.title.color)) axis.title.color <- g.palette[7]
    if (missing(geom.label.color) || is.null(geom.label.color)) geom.label.color <- g.palette[6]
    if (missing(legend.title.color)) legend.title.color <- g.palette[7]
    if (missing(legend.color)) legend.color <- g.palette[6]
    axis.tickslen <- 0
    # custom modifications
    title.align <- "center"
    axis.title.x.vjust <- -1
    axis.title.y.vjust <- 1.5
    title.vjust <- 1.75
    plot.margins <- grid::unit(c(1, .5, 1, 0.5), "cm")
  }  
  # ----------------------------------------  
  # check for scatter, a theme with crossed
  # grids and based on 538
  # ----------------------------------------  
  if (!is.null(theme) && (theme == "scatter" || theme == "scatterw")) {
    base <- theme_bw()
    col.ind <- ifelse(theme == "scatterw", 1, 2)
    g.palette <- scales::brewer_pal(palette = "Greys")(9)
    panel.bordercol <- panel.backcol <- panel.col <- g.palette[col.ind]
    plot.backcol <- plot.bordercol <- plot.col <- g.palette[col.ind]
    panel.major.gridcol <- panel.minor.gridcol <- g.palette[4]
    axis.linecolor <- g.palette[5]
    if (missing(axis.linecolor.y) || is.null(axis.linecolor.y)) axis.linecolor.y <- g.palette[col.ind]
    if (missing(axis.linecolor.x) || is.null(axis.linecolor.x)) axis.linecolor.x <- g.palette[col.ind]
    legend.item.backcol <- legend.item.bordercol <- legend.backgroundcol <- legend.bordercol <- g.palette[col.ind]
    if (missing(title.color)) title.color <- g.palette[9]
    if (missing(axis.textcolor)) axis.textcolor <- g.palette[6]
    if (missing(axis.title.color)) axis.title.color <- g.palette[7]
    if (missing(geom.label.color) || is.null(geom.label.color)) geom.label.color <- g.palette[6]
    if (missing(legend.title.color)) legend.title.color <- g.palette[7]
    if (missing(legend.color)) legend.color <- g.palette[6]
    axis.tickslen <- 0
    # custom modifications
    panel.major.linetype <- panel.minor.linetype <- 2
    title.align <- "center"
    axis.title.x.vjust <- -1
    axis.title.y.vjust <- 1.5
    title.vjust <- 1.75
    plot.margins <- grid::unit(c(1, .5, 1, 0.5), "cm")
  }  
  if (!is.null(theme) && theme == "blues") {
    base <- theme_bw()
    g.palette <- scales::brewer_pal(palette = "Blues")(9)
    panel.bordercol <- panel.backcol <- panel.col <- g.palette[1]
    plot.backcol <- plot.bordercol <- plot.col <- g.palette[1]
    panel.major.gridcol <- g.palette[3]
    panel.minor.gridcol <- g.palette[1]
    axis.linecolor <- NULL
    axis.linecolor.y  <- g.palette[1]
    axis.linecolor.x <- g.palette[9]
    panel.gridcol.x <- g.palette[1]
    legend.item.backcol <- legend.item.bordercol <- legend.backgroundcol <- legend.bordercol <- g.palette[1]
    title.color <- "black"
    axis.textcolor <- g.palette[9]
    axis.title.color <- "black"
    if (is.null(geom.label.color)) geom.label.color <- g.palette[5]
    legend.title.color <- g.palette[8]
    legend.color <- g.palette[6]
    axis.tickslen <- 0
    # custom modifications
    title.align <- "center"
    axis.title.x.vjust <- -1
    axis.title.y.vjust <- 1.5
    title.vjust <- 1.75
    plot.margins <- grid::unit(c(1, .5, 1, 0.5), "cm")
  }  
  if (!is.null(theme) && theme == "greens") {
    base <- theme_bw()
    g.palette <- scales::brewer_pal(palette = "BrBG")(9)
    g.palette[5] <- "#f5faf5"
    panel.bordercol <- panel.backcol <- panel.col <- g.palette[5]
    plot.backcol <- plot.bordercol <- plot.col <- g.palette[5]
    panel.major.gridcol <- g.palette[6]
    panel.minor.gridcol <- g.palette[5]
    axis.linecolor <- NULL
    axis.linecolor.y  <- g.palette[5]
    axis.linecolor.x <- g.palette[9]
    panel.gridcol.x <- g.palette[5]
    legend.item.backcol <- legend.item.bordercol <- legend.backgroundcol <- legend.bordercol <- g.palette[5]
    title.color <- "black"
    axis.textcolor <- g.palette[9]
    axis.title.color <- "black"
    if (is.null(geom.label.color)) geom.label.color <- g.palette[8]
    legend.title.color <- g.palette[9]
    legend.color <- g.palette[8]
    axis.tickslen <- 0
    # custom modifications
    title.align <- "center"
    axis.title.x.vjust <- -1
    axis.title.y.vjust <- 1.5
    title.vjust <- 1.75
    plot.margins <- grid::unit(c(1, .5, 1, 0.5), "cm")
  }  
  # ----------------------------------------  
  # set defaults for geom label colors
  # ----------------------------------------  
  if (is.null(geom.label.color)) {
    geom.label.color <- "black"
  }
  # ----------------------------------------  
  # set defaults for axis text angle
  # ----------------------------------------  
  if (!is.null(axis.angle)) {
    axis.angle.x <- axis.angle.y <- axis.angle 
  } else {
    axis.angle <- axis.angle.x
  }
  # ----------------------------------------
  # set defaults for axis text color
  # ----------------------------------------  
  if (!is.null(axis.textcolor)) {
    axis.textcolor.x <- axis.textcolor.y <- axis.textcolor
  } else {
    if (is.null(axis.textcolor.x)) 
      axis.textcolor <- axis.textcolor.y 
    else 
      axis.textcolor <- axis.textcolor.x
  }
  # ----------------------------------------
  # set defaults for axis line color
  # ----------------------------------------  
  if (!is.null(axis.linecolor)) {
    axis.linecolor.x <- axis.linecolor.y <- axis.linecolor
  } else {
    if (is.null(axis.linecolor.x)) 
      axis.linecolor <- axis.linecolor.y 
    else 
      axis.linecolor <- axis.linecolor.x
  }
  # ----------------------------------------
  # set defaults for axis text size
  # ----------------------------------------
  if (!is.null(axis.textsize)) {
    axis.textsize.x <- axis.textsize.y <- axis.textsize
  } else {
    if (is.null(axis.textsize.x)) 
      axis.textsize <- axis.textsize.y 
    else 
      axis.textsize <- axis.textsize.x
  }
  # ----------------------------------------
  # set defaults for grid colors
  # ----------------------------------------
  if (!is.null(panel.gridcol)) {
    panel.major.gridcol <- panel.minor.gridcol <- panel.gridcol
  } else {
    if (is.null(panel.major.gridcol)) 
      panel.gridcol <- panel.minor.gridcol 
    else 
      panel.gridcol <- panel.major.gridcol
  }
  # ----------------------------------------
  # set defaults for panel colors
  # ----------------------------------------
  if (!is.null(panel.col)) {
    panel.backcol <- panel.bordercol <- panel.col
  } else {
    if (is.null(panel.backcol)) 
      panel.col <- panel.bordercol 
    else 
      panel.col <- panel.backcol
  }
  # ----------------------------------------
  # set title alignment
  # ----------------------------------------
  if (!is.null(title.align)) {
    if (title.align == "left" || title.align == "l") title.align <- 0
    if (title.align == "right" || title.align == "r") title.align <- 1
    if (title.align == "center" || title.align == "c") title.align <- 0.5
  } else {
    title.align <- 0
  }
  # ----------------------------------------
  # set defaults for plot colors
  # ----------------------------------------
  if (!is.null(plot.col)) {
    plot.backcol <- plot.bordercol <- plot.col
  } else {
    if (is.null(plot.backcol)) 
      plot.col <- plot.bordercol 
    else 
      plot.col <- plot.backcol
  }
  # ----------------------------------------
  # set defaults for legend pos
  # ----------------------------------------
  if (legend.inside) {
    # check if character constants have been used and if so,
    # convert to numeric vector
    if (is.character(legend.pos)) {
      if (legend.pos == "top right") legend.pos <- c(1,1)
      else if (legend.pos == "bottom right") legend.pos <- c(1,0)
      else if (legend.pos == "bottom left") legend.pos <- c(0,0)
      else if (legend.pos == "top left") legend.pos <- c(0,1)
      if (is.null(legend.just)) legend.just <- legend.pos
    }
  }
  # set justification default
  if (is.null(legend.just)) legend.just <- "center"
  # ----------------------------------------
  # check if theme-preset is requested
  # ----------------------------------------
  if (!is.null(theme) && any(class(theme) == "theme") && any(class(theme) == "gg")) {
    theme_set(theme)
  }
  # ----------------------------------------
  # else, customize theme
  # ----------------------------------------
  else if (!is.null(base) && any(class(base) == "theme") && any(class(base) == "gg")) {
    sjtheme <- base +
      # ----------------------------------------
      # set base elements that are always set
      # ----------------------------------------
      theme(plot.title = element_text(size = rel(title.size), 
                                      colour = title.color,
                                      hjust = title.align),
            axis.text = element_text(angle = axis.angle, 
                                     size = rel(axis.textsize), 
                                     colour = axis.textcolor),
            axis.text.x = element_text(angle = axis.angle.x, 
                                       size = rel(axis.textsize.x), 
                                       colour = axis.textcolor.x), 
            axis.text.y = element_text(angle = axis.angle.y, 
                                       size = rel(axis.textsize.y), 
                                       colour = axis.textcolor.y), 
            axis.title = element_text(size = rel(axis.title.size), 
                                      colour = axis.title.color),
            legend.position = legend.pos,
            legend.justification = legend.just,
            legend.text = element_text(size = rel(legend.size),
                                       colour = legend.color),
            legend.title = element_text(size = rel(legend.title.size),
                                        colour = legend.title.color,
                                        face = legend.title.face),
            legend.background = element_rect(colour = legend.bordercol, 
                                             fill = legend.backgroundcol))
    # ----------------------------------------
    # set legend items background-color
    # ----------------------------------------
    if (!is.null(legend.item.backcol)) {
      sjtheme <- sjtheme +
        theme(legend.key = element_rect(colour = legend.item.bordercol, 
                                        fill = legend.item.backcol))
    }
    # ----------------------------------------
    # set legend item size
    # ----------------------------------------
    if (!is.null(legend.item.size)) {
      sjtheme <- sjtheme +
        theme(legend.key.size = grid::unit(legend.item.size, "cm"))
    }
    # ----------------------------------------
    # set axis line colors, if defined
    # ----------------------------------------
    if (!is.null(axis.linecolor)) {
      sjtheme <- sjtheme +
        theme(axis.line = element_line(colour = axis.linecolor, 
                                       size = axis.line.size),
              axis.line.x = element_line(colour = axis.linecolor.x),
              axis.line.y = element_line(colour = axis.linecolor.y))
    }
    # ----------------------------------------
    # set axis ticks, if defined
    # ----------------------------------------
    if (!is.null(axis.ticksol)) {
      sjtheme <- sjtheme +
        theme(axis.ticks = element_line(colour = axis.ticksol))
    }
    if (!is.null(axis.tickslen)) {
      sjtheme <- sjtheme +
        theme(axis.ticks.length = grid::unit(axis.tickslen, "cm"))
    }
    if (!is.null(axis.ticksmar)) {
      sjtheme <- sjtheme +
        theme(axis.ticks.margin = grid::unit(axis.ticksmar, "cm"))
    }
    # ----------------------------------------
    # set plot colors, if defined
    # ----------------------------------------
    if (!is.null(plot.col)) {
      sjtheme <- sjtheme +
        theme(plot.background = element_rect(colour = plot.bordercol, 
                                             fill = plot.backcol))
    }
    # ----------------------------------------
    # set panel colors, if defined
    # ----------------------------------------
    if (!is.null(panel.col)) {
      sjtheme <- sjtheme +
        theme(panel.background = element_rect(colour = panel.bordercol, 
                                              fill = panel.backcol),
              panel.border = element_rect(colour = panel.bordercol))
    }
    # ----------------------------------------
    # set panel grids, if defined
    # ----------------------------------------
    if (!is.null(panel.gridcol)) {
      sjtheme <- sjtheme +
        theme(panel.grid.minor = element_line(colour = panel.minor.gridcol, linetype = panel.minor.linetype),
              panel.grid.major = element_line(colour = panel.major.gridcol, linetype = panel.major.linetype))
    }
    # ----------------------------------------
    # set plot margins. onyl applies to pre-set themes
    # ----------------------------------------
    if (!is.null(plot.margins)) {
      sjtheme <- sjtheme +
        theme(plot.margin = plot.margins)
    }
    # ----------------------------------------
    # set title adjustments. only applies to
    # pre-set themes
    # ----------------------------------------
    if (!is.null(plot.margins)) {
      sjtheme <- sjtheme +
        theme(plot.margin = plot.margins)
    }
    if (!is.null(title.vjust)) {
      sjtheme <- sjtheme +
        theme(plot.title = element_text(vjust = title.vjust))
    }
    if (!is.null(axis.title.x.vjust)) {
      sjtheme <- sjtheme +
        theme(axis.title.x = element_text(vjust = axis.title.x.vjust))
    }
    if (!is.null(axis.title.y.vjust)) {
      sjtheme <- sjtheme +
        theme(axis.title.y = element_text(vjust = axis.title.y.vjust))
    }
    # ----------------------------------------
    # panel grid colors
    # ----------------------------------------
    if (!is.null(panel.gridcol.x)) {
      sjtheme <- sjtheme +
        theme(panel.grid.minor.x = element_line(colour = panel.gridcol.x, linetype = panel.minor.linetype),
              panel.grid.major.x = element_line(colour = panel.gridcol.x, linetype = panel.major.linetype))
    }
    if (!is.null(panel.gridcol.y)) {
      sjtheme <- sjtheme +
        theme(panel.grid.minor.y = element_line(colour = panel.gridcol.y, linetype = panel.minor.linetype),
              panel.grid.major.y = element_line(colour = panel.gridcol.y, linetype = panel.major.linetype))
    }
    # ----------------------------------------
    # finally, set theme
    # ----------------------------------------
    theme_set(sjtheme)
  }
  else {
    warning("Either 'theme' or 'base' must be supplied as ggplot-theme-object to set global theme options for sjPlot.", call. = F)
  }
  
  # ----------------------------------------
  # set defaults for geoms
  # ----------------------------------------
  # if (is.null(geom.colors)) geom.colors <- diverge_hcl(9)
  # sj.theme_scales(geom.colors)
  sj.theme_geoms(geom.alpha,
                 geom.linetype,
                 geom.outline.size,
                 geom.outline.color,
                 geom.boxoutline.size,
                 geom.boxoutline.color,
                 geom.errorbar.size,
                 geom.errorbar.linetype,
                 geom.label.size,
                 geom.label.color,
                 geom.label.alpha,
                 geom.label.angle)
  # return custom theme object
  invisible(sjtheme)
}


sj.theme_geoms <- function(geom.alpha,
                           geom.linetype,
                           geom.outline.size,
                           geom.outline.color,
                           geom.boxoutline.size,
                           geom.boxoutline.color,
                           geom.errorbar.size,
                           geom.errorbar.linetype,
                           geom.label.size,
                           geom.label.color,
                           geom.label.alpha,
                           geom.label.angle) {
  # ----------------------------------------
  # helper function to customize geoms
  # ----------------------------------------
  updateGeoms <- function(geoms, parameters) {
    for (geom in geoms) update_geom_defaults(geom, parameters)
  }
  
  # Geoms that only require a default colour.
  updateGeoms(c('abline', 
                'jitter', 
                'point', 
                'density', 
                'errorbar', 
                'errorbarh', 
                'freqpoly', 
                'hline', 
                'line', 
                'area', 
                'tile', 
                'dotplot', 
                'bar', 
                'histogram'), list(alpha = geom.alpha))
  
  update_geom_defaults('text', list(size = geom.label.size, 
                                    colour = geom.label.color,
                                    alpha = geom.label.alpha,
                                    angle = geom.label.angle))
  
  # Special geoms.
  update_geom_defaults('boxplot', list(size = geom.boxoutline.size, colour = geom.boxoutline.color, alpha = geom.alpha, outlier.colour = NA))
  update_geom_defaults('line', list(linetype = geom.linetype))
  updateGeoms(c('errorbar', 'errorbarh'), list(size = geom.errorbar.size, linetype = geom.errorbar.linetype))
  
  if (!is.null(geom.outline.color)) 
    updateGeoms(c('histogram', 'bar', 'dotplot', 'area', 'tile'), list(size = geom.outline.size, colour = geom.outline.color))
  else
    updateGeoms(c('histogram', 'bar', 'dotplot', 'area', 'tile'), list(size = 0, colour = NA))  
}


sj.setGeomColors <- function(plot, 
                             geom.colors, 
                             pal.len, 
                             show.guide = TRUE, 
                             labels = NULL,
                             reverse.colors = FALSE) {
  # ---------------------------------------------------------
  # check for themr options
  # ---------------------------------------------------------
  if (!is.null(geom.colors) && geom.colors == "themr") {
    return(plot)
  }
  # ---------------------------------------------------------
  # dummy function for setting legend labels and geom-colors
  # ---------------------------------------------------------
  usenormalscale <- function(plot, geom.colors, labels) {
    if (!show.guide) {
      plot <- plot + 
        scale_fill_manual(values = geom.colors, guide = FALSE) +
        scale_colour_manual(values = geom.colors, guide = FALSE) +
        guides(fill = FALSE, colour = FALSE, text = FALSE)
    } else {
      plot <- plot + 
        scale_fill_manual(values = geom.colors, labels = labels) +
        scale_colour_manual(values = geom.colors, labels = labels) +
        guides(text = FALSE)
    }
    return(plot)
  }
  # ---------------------------------------------------------
  # dummy function for only setting legend labels, but no
  # geom-colors
  # ---------------------------------------------------------
  uselegendscale <- function(plot, labels) {
    if (!show.guide) {
      plot <- plot + 
        scale_fill_discrete(guide = FALSE) +
        scale_colour_discrete(guide = FALSE) +
        guides(fill = FALSE, colour = FALSE, text = FALSE)
    } else {
      plot <- plot + 
        scale_fill_discrete(labels = labels) +
        scale_colour_discrete(labels = labels) +
        guides(text = FALSE)
    }
    return(plot)
  }
  # ---------------------------------------------------------
  # set geom colors
  # ---------------------------------------------------------
  if (!is.null(geom.colors)) {
    # brewer palette?
    if (is.brewer.pal(geom.colors[1])) {
      if (length(geom.colors) > 1) {
        neutral.color <- geom.colors[2]
        pal.len <- pal.len - 1
      } else {
        neutral.color <- NULL
      }
      geom.colors <- scales::brewer_pal(palette = geom.colors[1])(pal.len)
      if (reverse.colors) geom.colors <- rev(geom.colors)
      if (!is.null(neutral.color)) geom.colors <- c(geom.colors, neutral.color)
    } else if (geom.colors[1] == "gs") {
      geom.colors <- scales::grey_pal()(pal.len)
      if (reverse.colors) geom.colors <- rev(geom.colors)
    } else if (length(geom.colors) > pal.len) {
      warning("More colors provided than needed. Shortening color palette.")
      geom.colors <- geom.colors[1:pal.len]
      if (reverse.colors) geom.colors <- rev(geom.colors)
    }
    
    if (length(geom.colors) < pal.len) {
      warning("Too less colors provided for plot. Using default color palette.")
      plot <- uselegendscale(plot, labels)
    } else {
      plot <- usenormalscale(plot, geom.colors, labels)
    }
  } else {
    plot <- uselegendscale(plot, labels)
  }
  return(plot)
}


#' @title Save ggplot-figure for print publication
#' @name save_plot
#' 
#' @description Convenient function to save the last ggplot-figure in
#'                high quality for publication.
#' 
#' @param filename the name of the output file; filename must end with one
#'          of the following acceptes file types: ".png", ".jpg" or ".tif".
#' @param fig the plot that should be saved. By default, the last plot is saved.
#' @param width the width of the figure, in centimetres
#' @param height the height of the figure, in centimetres
#' @param dpi resolution in dpi (dots per inch)
#' @param label.size fontsize of value labels inside plot area
#' @param axis.textsize fontsize of axis labels
#' @param axis.titlesize fontsize of axis titles
#' @param legend.textsize fontsize of legend labels
#' @param legend.titlesize fontsize of legend title
#' 
#' @inheritParams sjp.setTheme
#' 
#' @note This is a convenient function with some default settings that should
#'         come close to most of the needs for fontsize and scaling in figures
#'         when saving them for printing or publishing. It uses cairographics
#'         anti-aliasing (see \code{\link[grDevices]{png}}).
#' 
#' @import ggplot2
#' @importFrom grDevices png jpeg tiff dev.off
#' 
#' @export
save_plot <- function(filename,
                      fig = ggplot2::last_plot(),
                      width = 12,
                      height = 9,
                      dpi = 300,
                      theme = "forestw",
                      label.size = 2.4,
                      axis.textsize = .8,
                      axis.titlesize = .75,
                      legend.textsize = .5,
                      legend.titlesize = .6) {
  # -------------------------
  # get file extension
  # -------------------------
  ext <- tolower(substring(filename, 
                           regexpr("\\.[^\\.]*$", filename) + 1, 
                           nchar(filename)))
  # -------------------------
  # valid file ytpe?
  # -------------------------
  if (!ext %in% c("png", "jpg", "tif")) {
    stop("filetype must be one of `.png`, `.jpg` or `.tif`.", call. = F)
  }
  # -------------------------
  # set printable theme, adjust font sizes.
  # this is the most critical point...
  # -------------------------
  # catch old theme
  curtheme = ggplot2::theme_get()
  sjp.setTheme(theme = theme, 
               geom.label.color = "black",
               axis.title.color = "black",
               axis.textcolor = "black",
               legend.title.color = "black",
               legend.color = "black",
               geom.label.size = label.size,
               axis.textsize = axis.textsize,
               axis.title.size = axis.titlesize,
               legend.size = legend.textsize,
               legend.title.size = legend.titlesize,
               legend.item.size = .35)
  # -------------------------
  # prapare save
  # -------------------------
  if (ext == "png")
    grDevices::png(filename = filename,
        width = width,
        height = height,
        units = "cm",
        res = dpi,
        type = "cairo")
  else if (ext == "jpg")
    grDevices::jpeg(filename = filename,
         width = width,
         height = height,
         units = "cm",
         res = dpi,
         type = "cairo")
  else if (ext == "tif")
    grDevices::tiff(filename = filename,
                    width = width,
                    height = height,
                    units = "cm",
                    res = dpi,
                    type = "cairo")
  
  # print plot to device
  print(fig)
  # close device
  grDevices::dev.off()  
  # set back theme
  ggplot2::theme_set(curtheme)
}
back to top