From 3c23e31c66bbeeae31974a74a7abb371172e228a Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 19:38:07 +0100 Subject: [PATCH 1/9] Add polar bounding box calculation --- R/coord-polar.r | 27 +++++++++++++++++++++++++++ tests/testthat/test-coord-polar.r | 24 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/R/coord-polar.r b/R/coord-polar.r index 61d74e31ab..89072049ed 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -312,3 +312,30 @@ r_rescale <- function(coord, x, range) { x <- squish_infinite(x, range) rescale(x, c(0, 0.4), range) } + +# Calculate bounding box for the sector of the circle +# Takes `theta_range` as a vector of two angles in radians +polar_bbox <- function(theta_range) { + + # X and Y positions of the sector arc ends + x <- 0.5 * sin(theta_range) + 0.5 + y <- 0.5 * cos(theta_range) + 0.5 + + # Check for top, right, bottom and left if it falls in sector + pos_theta <- seq(0, 1.5 * pi, length.out = 4) + theta_range <- theta_range %% (2 * pi) + + in_sector <- if (theta_range[1] < theta_range[2]) { + pos_theta >= theta_range[1] & pos_theta <= theta_range[2] + } else { + !(pos_theta < theta_range[1] & pos_theta > theta_range[2]) + } + + # If position is in sector, take extreme bounds + # If not, choose center (+/- 0.05 buffer) or sector arc ends + bounds <- ifelse( + in_sector, + c(1, 1, 0, 0), + c(max(y, 0.55), max(x, 0.55), min(y, 0.45), min(x, 0.45)) + ) + list(x = c(bounds[4], bounds[2]), y = c(bounds[3], bounds[1])) diff --git a/tests/testthat/test-coord-polar.r b/tests/testthat/test-coord-polar.r index f1570b6a96..45c0bdd63a 100644 --- a/tests/testthat/test-coord-polar.r +++ b/tests/testthat/test-coord-polar.r @@ -79,6 +79,30 @@ test_that("Inf is squished to range", { expect_equal(d[[3]]$theta, mapped_discrete(0)) }) +test_that("polar_bbox() gives correct bounds", { + + # Full circle + bounds <- polar_bbox(c(0, 2 * pi)) + expect_equal(bounds, list(x = c(0, 1), y = c(0, 1))) + + # Overshoot + bounds <- polar_bbox(c(-3, 3) * pi) + expect_equal(bounds, list(x = c(0, 1), y = c(0, 1))) + + # Half circle + bounds <- polar_bbox(c(0.5, 1.5) * pi) + expect_equal(bounds, list(x = c(0, 1), y = c(0, 0.55))) + + # Quarter circle + bounds <- polar_bbox(c(0, 0.5 * pi)) + expect_equal(bounds, list(x = c(0.45, 1), y = c(0.45, 1))) + + # Quarter circle at 45 degrees + bounds <- polar_bbox(c(0.25, 0.75) * pi) + expect_equal(bounds, list(x = c(0.45, 1), + y = 0.5 + cos(c(0.75, 0.25) * pi) * 0.5)) + +}) # Visual tests ------------------------------------------------------------ From aa482777527933b4b0a7fece5071d8acb3147ac2 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 19:50:49 +0100 Subject: [PATCH 2/9] Apply arc transform --- R/coord-polar.r | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/R/coord-polar.r b/R/coord-polar.r index 89072049ed..b025236f82 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -58,14 +58,15 @@ #' doh + geom_bar(width = 0.9, position = "fill") + coord_polar(theta = "y") #' } #' } -coord_polar <- function(theta = "x", start = 0, direction = 1, clip = "on") { +coord_polar <- function(theta = "x", start = 0, end = 2 * pi, + direction = 1, clip = "on") { theta <- arg_match0(theta, c("x", "y")) r <- if (theta == "x") "y" else "x" ggproto(NULL, CoordPolar, theta = theta, r = r, - start = start, + arc = c(start, end), direction = sign(direction), clip = clip ) @@ -198,7 +199,7 @@ CoordPolar <- ggproto("CoordPolar", Coord, theta_rescale(self, panel_params$theta.major, panel_params) thetamin <- if (length(panel_params$theta.minor) > 0) theta_rescale(self, panel_params$theta.minor, panel_params) - thetafine <- seq(0, 2 * pi, length.out = 100) + thetafine <- seq(self$arc[1], self$arc[2], length.out = 100) rfine <- c(r_rescale(self, panel_params$r.major, panel_params$r.range), 0.45) @@ -298,14 +299,16 @@ rename_data <- function(coord, data) { } theta_rescale_no_clip <- function(coord, x, panel_params) { - rotate <- function(x) (x + coord$start) * coord$direction - rotate(rescale(x, c(0, 2 * pi), panel_params$theta.range)) + arc <- coord$arc %||% c(0, 2 * pi) + rotate <- function(x) x * coord$direction + rotate(rescale(x, arc, panel_params$theta.range)) } theta_rescale <- function(coord, x, panel_params) { + arc <- coord$arc %||% c(0, 2 * pi) x <- squish_infinite(x, panel_params$theta.range) - rotate <- function(x) (x + coord$start) %% (2 * pi) * coord$direction - rotate(rescale(x, c(0, 2 * pi), panel_params$theta.range)) + rotate <- function(x) x %% (2 * pi) * coord$direction + rotate(rescale(x, arc, panel_params$theta.range)) } r_rescale <- function(coord, x, range) { @@ -339,3 +342,4 @@ polar_bbox <- function(theta_range) { c(max(y, 0.55), max(x, 0.55), min(y, 0.45), min(x, 0.45)) ) list(x = c(bounds[4], bounds[2]), y = c(bounds[3], bounds[1])) +} From cd12d6c9394aaa5ce71fc935a1296a7e7b453ea9 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 20:12:25 +0100 Subject: [PATCH 3/9] Apply bounding box rescale --- R/coord-polar.r | 54 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/R/coord-polar.r b/R/coord-polar.r index b025236f82..050fa78874 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -78,7 +78,9 @@ coord_polar <- function(theta = "x", start = 0, end = 2 * pi, #' @export CoordPolar <- ggproto("CoordPolar", Coord, - aspect = function(details) 1, + aspect = function(details) { + diff(details$bbox$y) / diff(details$bbox$x) + }, distance = function(self, x, y, details) { if (self$theta == "x") { @@ -139,7 +141,8 @@ CoordPolar <- ggproto("CoordPolar", Coord, x.sec.range = ret$x$sec.range, y.sec.range = ret$y$sec.range, x.sec.major = ret$x$sec.major, y.sec.major = ret$y$sec.major, x.sec.minor = ret$x$sec.minor, y.sec.minor = ret$y$sec.minor, - x.sec.labels = ret$x$sec.labels, y.sec.labels = ret$y$sec.labels + x.sec.labels = ret$x$sec.labels, y.sec.labels = ret$y$sec.labels, + bbox = polar_bbox(self$arc) ) if (self$theta == "y") { @@ -160,8 +163,14 @@ CoordPolar <- ggproto("CoordPolar", Coord, data$r <- r_rescale(self, data$r, panel_params$r.range) data$theta <- theta_rescale(self, data$theta, panel_params) - data$x <- data$r * sin(data$theta) + 0.5 - data$y <- data$r * cos(data$theta) + 0.5 + data$x <- rescale( + data$r * sin(data$theta) + 0.5, + from = panel_params$bbox$x + ) + data$y <- rescale( + data$r * cos(data$theta) + 0.5, + from = panel_params$bbox$y + ) data }, @@ -213,23 +222,41 @@ CoordPolar <- ggproto("CoordPolar", Coord, element_render(theme, "panel.background"), if (length(theta) > 0) element_render( theme, majortheta, name = "angle", - x = vec_interleave(0, 0.45 * sin(theta)) + 0.5, - y = vec_interleave(0, 0.45 * cos(theta)) + 0.5, + x = rescale( + vec_interleave(0, 0.45 * sin(theta)) + 0.5, + from = panel_params$bbox$x + ), + y = rescale( + vec_interleave(0, 0.45 * cos(theta)) + 0.5, + from = panel_params$bbox$y + ), id.lengths = rep(2, length(theta)), default.units = "native" ), if (length(thetamin) > 0) element_render( theme, minortheta, name = "angle", - x = vec_interleave(0, 0.45 * sin(thetamin)) + 0.5, - y = vec_interleave(0, 0.45 * cos(thetamin)) + 0.5, + x = rescale( + vec_interleave(0, 0.45 * sin(thetamin)) + 0.5, + from = panel_params$bbox$x + ), + y = rescale( + vec_interleave(0, 0.45 * cos(thetamin)) + 0.5, + from = panel_params$bbox$y + ), id.lengths = rep(2, length(thetamin)), default.units = "native" ), element_render( theme, majorr, name = "radius", - x = rep(rfine, each = length(thetafine)) * rep(sin(thetafine), length(rfine)) + 0.5, - y = rep(rfine, each = length(thetafine)) * rep(cos(thetafine), length(rfine)) + 0.5, + x = rescale( + as.vector(outer(sin(thetafine), rfine)) + 0.5, + from = panel_params$bbox$x + ), + y = rescale( + as.vector(outer(cos(thetafine), rfine)) + 0.5, + from = panel_params$bbox$y + ), id.lengths = rep(length(thetafine), length(rfine)), default.units = "native" ) @@ -260,12 +287,15 @@ CoordPolar <- ggproto("CoordPolar", Coord, theta <- theta[-1] } + x <- rescale(0.45 * sin(theta) + 0.5, from = panel_params$bbox$x) + y <- rescale(0.45 * cos(theta) + 0.5, from = panel_params$bbox$y) + grobTree( if (length(labels) > 0) element_render( theme, "axis.text.x", labels, - unit(0.45 * sin(theta) + 0.5, "native"), - unit(0.45 * cos(theta) + 0.5, "native"), + unit(x, "native"), + unit(y, "native"), hjust = 0.5, vjust = 0.5 ), element_render(theme, "panel.border") From b30e79821c8390dea3cffb999190a682c5e3cc98 Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 21:55:51 +0100 Subject: [PATCH 4/9] Fix axis placement --- R/coord-polar.r | 102 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/R/coord-polar.r b/R/coord-polar.r index 050fa78874..0814ef4469 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -142,7 +142,8 @@ CoordPolar <- ggproto("CoordPolar", Coord, x.sec.major = ret$x$sec.major, y.sec.major = ret$y$sec.major, x.sec.minor = ret$x$sec.minor, y.sec.minor = ret$y$sec.minor, x.sec.labels = ret$x$sec.labels, y.sec.labels = ret$y$sec.labels, - bbox = polar_bbox(self$arc) + bbox = polar_bbox(self$arc), + arc = self$arc ) if (self$theta == "y") { @@ -176,6 +177,16 @@ CoordPolar <- ggproto("CoordPolar", Coord, }, render_axis_v = function(self, panel_params, theme) { + + place_axis <- in_arc(c(0, 1) * pi, panel_params$arc) + if (!any(place_axis)) { + ans <- list( + left = draw_axis(NA, "", "left", theme), + right = zeroGrob() + ) + return(ans) + } + arrange <- panel_params$r.arrange %||% c("primary", "secondary") x <- r_rescale(self, panel_params$r.major, panel_params$r.range) + 0.5 @@ -188,17 +199,77 @@ CoordPolar <- ggproto("CoordPolar", Coord, ) + 0.5 } + if (!place_axis[1]) { + panel_params$r.major <- 1 - panel_params$r.major + if (!is.null(panel_params$r.sec.major)) { + panel_params$r.sec.major <- 1 - panel_params$r.sec.major + } + } + + panel_params$r.major <- rescale(panel_params$r.major, + from = panel_params$bbox$y) + panel_params$r.sec.major <- rescale(panel_params$r.sec.major, + from = panel_params$bbox$y) + list( - left = render_axis(panel_params, arrange[1], "r", "left", theme), + left = render_axis(panel_params, arrange[1], "r", "left", theme), right = render_axis(panel_params, arrange[2], "r", "right", theme) ) }, render_axis_h = function(panel_params, theme) { - list( + + no_axis <- list( top = zeroGrob(), bottom = draw_axis(NA, "", "bottom", theme) ) + + # Return no axis if there should already be a left/right axis + if (any(in_arc(c(0, 1) * pi, panel_params$arc))) { + return(no_axis) + } + + place_axis <- in_arc(c(0.5, 1.5) * pi, panel_params$arc) + if (!any(place_axis)) { + # This should in theory never happen + cli::cli_inform(c(paste0( + "Could not find appropriate placement for the {.field radius}", + " axis." + ), i = paste0( + "A {.field radius} axis requires the [{.arg start}-{.arg end}] range to ", + "include one of: {.code c(0, 0.5, 1, 1.5) * pi}." + ))) + return(no_axis) + } + + arrange <- panel_params$r.arrange %||% c("primary", "secondary") + + y <- r_rescale(self, panel_params$r.major, panel_params$r.range) + 0.5 + panel_params$r.major <- y + if (!is.null(panel_params$r.sec.major)) { + panel_params$r.sec.major <- r_rescale( + self, + panel_params$r.sec.major, + panel_params$r.sec.range + ) + 0.5 + } + + if (!place_axis[1]) { + panel_params$r.major <- 1 - panel_params$r.major + if (!is.null(panel_params$r.sec.major)) { + panel_params$r.sec.major <- 1 - panel_params$r.sec.major + } + } + + panel_params$r.major <- rescale(panel_params$r.major, + from = panel_params$bbox$x) + panel_params$r.sec.major <- rescale(panel_params$r.sec.major, + from = panel_params$bbox$x) + + list( + top = render_axis(panel_params, arrange[2], "r", "top", theme), + bottom = render_axis(panel_params, arrange[1], "r", "bottom", theme) + ) }, render_bg = function(self, panel_params, theme) { @@ -347,22 +418,16 @@ r_rescale <- function(coord, x, range) { } # Calculate bounding box for the sector of the circle -# Takes `theta_range` as a vector of two angles in radians -polar_bbox <- function(theta_range) { +# Takes `arc` as a vector of two angles in radians +polar_bbox <- function(arc) { # X and Y positions of the sector arc ends - x <- 0.5 * sin(theta_range) + 0.5 - y <- 0.5 * cos(theta_range) + 0.5 + x <- 0.5 * sin(arc) + 0.5 + y <- 0.5 * cos(arc) + 0.5 # Check for top, right, bottom and left if it falls in sector pos_theta <- seq(0, 1.5 * pi, length.out = 4) - theta_range <- theta_range %% (2 * pi) - - in_sector <- if (theta_range[1] < theta_range[2]) { - pos_theta >= theta_range[1] & pos_theta <= theta_range[2] - } else { - !(pos_theta < theta_range[1] & pos_theta > theta_range[2]) - } + in_sector <- in_arc(pos_theta, arc) # If position is in sector, take extreme bounds # If not, choose center (+/- 0.05 buffer) or sector arc ends @@ -373,3 +438,12 @@ polar_bbox <- function(theta_range) { ) list(x = c(bounds[4], bounds[2]), y = c(bounds[3], bounds[1])) } + +in_arc <- function(theta, arc) { + arc <- arc %% (2 * pi) + if (arc[1] < arc[2]) { + theta >= arc[1] & theta <= arc[2] + } else { + !(theta < arc[1] & theta > arc[2]) + } +} From 4db3391c0e115426badb1148c89a8d088a1f391d Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 22:08:14 +0100 Subject: [PATCH 5/9] Sync start/end --- R/coord-polar.r | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/coord-polar.r b/R/coord-polar.r index 0814ef4469..6e77aa69f9 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -63,6 +63,11 @@ coord_polar <- function(theta = "x", start = 0, end = 2 * pi, theta <- arg_match0(theta, c("x", "y")) r <- if (theta == "x") "y" else "x" + if (start > end) { + n_rotate <- ((start - end) %/% (2 * pi)) + 1 + start <- start - n_rotate * 2 * pi + } + ggproto(NULL, CoordPolar, theta = theta, r = r, @@ -447,3 +452,4 @@ in_arc <- function(theta, arc) { !(theta < arc[1] & theta > arc[2]) } } + From c54f41e3fb9771c11eb3caa6f1dc90443415141c Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 22:08:27 +0100 Subject: [PATCH 6/9] axis placement tests --- tests/testthat/test-coord-polar.r | 77 +++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/testthat/test-coord-polar.r b/tests/testthat/test-coord-polar.r index 45c0bdd63a..f2959f898c 100644 --- a/tests/testthat/test-coord-polar.r +++ b/tests/testthat/test-coord-polar.r @@ -104,6 +104,83 @@ test_that("polar_bbox() gives correct bounds", { }) +test_that("axis placement is appropriate", { + + p <- ggplot_build( + ggplot(data.frame(x = 1:4), aes(x, x)) + + geom_point() + + scale_y_continuous(breaks = 1:4) + + coord_polar() + + theme_test() + ) + + get_breaks <- function(axis, var = "x") { + is_gt <- which(vapply(axis$children, inherits, logical(1), "gtable")) + if (length(is_gt) == 0) { + return(NULL) + } + axis <- axis$children[[is_gt[[1]]]] + is_txt <- which(vapply(axis$grobs, inherits, logical(1), "titleGrob")) + if (length(is_txt) == 0) { + return(NULL) + } + axis <- axis$grobs[[is_txt[[1]]]] + if (length(axis$children) == 0) { + return(NULL) + } + axis <- axis$children[[1]] + as.numeric(axis[[var]]) + } + + params <- p$layout$panel_params[[1]] + breaks <- (0:3/3) * 0.4 + 0.5 + + # Full circle, should have left axis + axis_h <- p$layout$coord$render_axis_h(params, p$plot$theme) + axis_v <- p$layout$coord$render_axis_v(params, p$plot$theme) + + expect_null(get_breaks(axis_h$top, "x")) + expect_equal(get_breaks(axis_h$bottom, "x"), NA_real_) + expect_equal(get_breaks(axis_v$left, "y"), breaks) + expect_null(get_breaks(axis_v$right, "y")) + + # Bottom half-circle, should have reverse y-axis + params$arc <- c(0.5, 1.5) * pi + axis_h <- p$layout$coord$render_axis_h(params, p$plot$theme) + axis_v <- p$layout$coord$render_axis_v(params, p$plot$theme) + + expect_null(get_breaks(axis_h$top, "x")) + expect_equal(get_breaks(axis_h$bottom, "x"), NA_real_) + expect_equal(get_breaks(axis_v$left, "y"), 1 - breaks) # opposite + expect_null(get_breaks(axis_v$right, "y")) + + # Right quarter circle, should have x-axis + params$arc <- c(0.25, 0.75) * pi + axis_h <- p$layout$coord$render_axis_h(params, p$plot$theme) + axis_v <- p$layout$coord$render_axis_v(params, p$plot$theme) + + expect_null(get_breaks(axis_h$top, "x")) + expect_equal(get_breaks(axis_h$bottom, "x"), breaks) + expect_equal(get_breaks(axis_v$left, "y"), NA_real_) + expect_null(get_breaks(axis_v$right, "y")) + + # Left quarter circle, should have reverse x-axis + params$arc <- c(1.25, 1.75) * pi + axis_h <- p$layout$coord$render_axis_h(params, p$plot$theme) + axis_v <- p$layout$coord$render_axis_v(params, p$plot$theme) + + expect_null(get_breaks(axis_h$top, "x")) + expect_equal(get_breaks(axis_h$bottom, "x"), 1 - breaks) + expect_equal(get_breaks(axis_v$left, "y"), NA_real_) + expect_null(get_breaks(axis_v$right, "y")) + + params$arc <- c(0.1, 0.4) * pi + expect_message( + p$layout$coord$render_axis_h(params, p$plot$theme), + "appropriate placement" + ) +}) + # Visual tests ------------------------------------------------------------ test_that("polar coordinates draw correctly", { From 7b286038fc4838268bb0f4478c1d7127af8d19ff Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 22:28:48 +0100 Subject: [PATCH 7/9] `end` defaults to `start + 2 * pi` --- R/coord-polar.r | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/coord-polar.r b/R/coord-polar.r index 6e77aa69f9..7b7be2116b 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -58,11 +58,12 @@ #' doh + geom_bar(width = 0.9, position = "fill") + coord_polar(theta = "y") #' } #' } -coord_polar <- function(theta = "x", start = 0, end = 2 * pi, +coord_polar <- function(theta = "x", start = 0, end = NULL, direction = 1, clip = "on") { theta <- arg_match0(theta, c("x", "y")) r <- if (theta == "x") "y" else "x" + end <- end %||% (start + 2 * pi) if (start > end) { n_rotate <- ((start - end) %/% (2 * pi)) + 1 start <- start - n_rotate * 2 * pi From 2af137f970367b571ab7e2c780b6cdfdc1a85d8c Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 22:45:59 +0100 Subject: [PATCH 8/9] Fix axis title placement --- R/coord-polar.r | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/R/coord-polar.r b/R/coord-polar.r index 7b7be2116b..f910cc6553 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -381,10 +381,17 @@ CoordPolar <- ggproto("CoordPolar", Coord, labels = function(self, labels, panel_params) { if (self$theta == "y") { - list(x = labels$y, y = labels$x) + if (any(in_arc(c(0, 1) * pi, self$arc))) { + labels <- list(x = labels$y, y = labels$x) + } else { + labels <- list(x = rev(labels$x), y = rev(labels$y)) + } } else { - labels + if (!any(in_arc(c(0, 1) * pi, self$arc))) { + labels <- list(x = rev(labels$y), y = rev(labels$x)) + } } + labels }, modify_scales = function(self, scales_x, scales_y) { From d39364617d322c9f73ecd0c2801991300efba4ac Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Thu, 3 Nov 2022 22:52:24 +0100 Subject: [PATCH 9/9] Document `end` --- R/coord-polar.r | 5 +++++ man/coord_polar.Rd | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/R/coord-polar.r b/R/coord-polar.r index f910cc6553..61e524f3c8 100644 --- a/R/coord-polar.r +++ b/R/coord-polar.r @@ -6,6 +6,8 @@ #' @param theta variable to map angle to (`x` or `y`) #' @param start Offset of starting point from 12 o'clock in radians. Offset #' is applied clockwise or anticlockwise depending on value of `direction`. +#' @param end Offset of end point from 12 o'clock in radians. Can be used to +#' make partial polar plots. Defaults to `start + 2 * pi`. #' @param direction 1, clockwise; -1, anticlockwise #' @param clip Should drawing be clipped to the extent of the plot panel? A #' setting of `"on"` (the default) means yes, and a setting of `"off"` @@ -22,6 +24,9 @@ #' geom_bar(width = 1) #' pie + coord_polar(theta = "y") #' +#' # A pie chart, but half of it is already eaten +#' pie + coord_polar(theta = "y", start = -0.5 * pi, end = 0.5 * pi) +#' #' \donttest{ #' #' # A coxcomb plot = bar chart + polar coordinates diff --git a/man/coord_polar.Rd b/man/coord_polar.Rd index dd4d53f623..878c04ae0d 100644 --- a/man/coord_polar.Rd +++ b/man/coord_polar.Rd @@ -4,7 +4,7 @@ \alias{coord_polar} \title{Polar coordinates} \usage{ -coord_polar(theta = "x", start = 0, direction = 1, clip = "on") +coord_polar(theta = "x", start = 0, end = NULL, direction = 1, clip = "on") } \arguments{ \item{theta}{variable to map angle to (\code{x} or \code{y})} @@ -12,6 +12,9 @@ coord_polar(theta = "x", start = 0, direction = 1, clip = "on") \item{start}{Offset of starting point from 12 o'clock in radians. Offset is applied clockwise or anticlockwise depending on value of \code{direction}.} +\item{end}{Offset of end point from 12 o'clock in radians. Can be used to +make partial polar plots. Defaults to \code{start + 2 * pi}.} + \item{direction}{1, clockwise; -1, anticlockwise} \item{clip}{Should drawing be clipped to the extent of the plot panel? A @@ -33,6 +36,9 @@ pie <- ggplot(mtcars, aes(x = factor(1), fill = factor(cyl))) + geom_bar(width = 1) pie + coord_polar(theta = "y") +# A pie chart, but half of it is already eaten +pie + coord_polar(theta = "y", start = -0.5 * pi, end = 0.5 * pi) + \donttest{ # A coxcomb plot = bar chart + polar coordinates