Skip to content

Commit 66a4362

Browse files
authored
Custom inside-axis position for coord_radial() (#5908)
* Allow setting custom axis position in `r.axis.inside` * document * add test * add news bullet * fix grid bug when `direction = -1` * fix direction bug for inner axes
1 parent 744e021 commit 66a4362

File tree

5 files changed

+150
-32
lines changed

5 files changed

+150
-32
lines changed

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# ggplot2 (development version)
22

3+
* `coord_radial(r.axis.inside)` can now take a numeric value to control
4+
placement of internally placed radius axes (@teunbrand, #5805).
5+
* (internal) default labels are derived in `ggplot_build()` rather than
6+
in `ggplot_add.Layer()` (@teunbrand, #5894)
7+
* An attempt is made to use a variable's label attribute as default label
8+
(@teunbrand, #4631)
39
* Themes gain an additional `header_family` argument to easily set the font
410
for headers and titles (#5886).
511
* The `plot.subtitle`, `plot.caption` and `plot.tag` theme elements now inherit

R/coord-radial.R

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
#' @param expand If `TRUE`, the default, adds a small expansion factor the
88
#' the limits to prevent overlap between data and axes. If `FALSE`, limits
99
#' are taken directly from the scale.
10-
#' @param r.axis.inside If `TRUE`, places the radius axis inside the
11-
#' panel. If `FALSE`, places the radius axis next to the panel. The default,
12-
#' `NULL`, places the radius axis outside if the `start` and `end` arguments
13-
#' form a full circle.
10+
#' @param r.axis.inside One of the following:
11+
#' * `NULL` (default) places the axis next to the panel if `start` and
12+
#' `end` arguments form a full circle and inside the panel otherwise.
13+
#' * `TRUE` to place the radius axis inside the panel.
14+
#' * `FALSE` to place the radius axis next to the panel.
15+
#' * A numeric value, setting a theta axis value at which
16+
#' the axis should be placed inside the panel. Can be given as a length 2
17+
#' vector to control primary and secondary axis placement separately.
1418
#' @param rotate.angle If `TRUE`, transforms the `angle` aesthetic in data
1519
#' in accordance with the computed `theta` position. If `FALSE` (default),
1620
#' no such transformation is performed. Can be useful to rotate text geoms in
@@ -58,7 +62,10 @@ coord_radial <- function(theta = "x",
5862

5963
theta <- arg_match0(theta, c("x", "y"))
6064
r <- if (theta == "x") "y" else "x"
61-
check_bool(r.axis.inside, allow_null = TRUE)
65+
if (!is.numeric(r.axis.inside)) {
66+
check_bool(r.axis.inside, allow_null = TRUE)
67+
}
68+
6269
check_bool(expand)
6370
check_bool(rotate.angle)
6471
check_number_decimal(start, allow_infinite = FALSE)
@@ -130,12 +137,29 @@ CoordRadial <- ggproto("CoordRadial", Coord,
130137
},
131138

132139
setup_panel_params = function(self, scale_x, scale_y, params = list()) {
133-
c(
140+
141+
params <- c(
134142
view_scales_polar(scale_x, self$theta, expand = self$expand),
135143
view_scales_polar(scale_y, self$theta, expand = self$expand),
136144
list(bbox = polar_bbox(self$arc, inner_radius = self$inner_radius),
137145
arc = self$arc, inner_radius = self$inner_radius)
138146
)
147+
148+
axis_rotation <- self$r_axis_inside
149+
if (is.numeric(axis_rotation)) {
150+
theta_scale <- switch(self$theta, x = scale_x, y = scale_y)
151+
axis_rotation <- theta_scale$transform(axis_rotation)
152+
axis_rotation <- oob_squish(axis_rotation, params$theta.range)
153+
axis_rotation <- theta_rescale(
154+
axis_rotation, params$theta.range,
155+
params$arc, 1
156+
)
157+
params$axis_rotation <- rep_len(axis_rotation, length.out = 2)
158+
} else {
159+
params$axis_rotation <- params$arc
160+
}
161+
162+
params
139163
},
140164

141165
setup_panel_guides = function(self, panel_params, guides, params = list()) {
@@ -173,18 +197,17 @@ CoordRadial <- ggproto("CoordRadial", Coord,
173197
opposite_r <- isTRUE(scales$r$position %in% c("bottom", "left"))
174198
}
175199

176-
if (self$r_axis_inside) {
200+
if (!isFALSE(self$r_axis_inside)) {
177201

178-
arc <- rad2deg(self$arc)
179202
r_position <- c("left", "right")
180203
# If both opposite direction and opposite position, don't flip
181204
if (xor(self$direction == -1, opposite_r)) {
182-
arc <- rev(arc)
183205
r_position <- rev(r_position)
184206
}
185-
186-
guide_params[["r"]]$position <- r_position[1]
187-
guide_params[["r.sec"]]$position <- r_position[2]
207+
arc <- rad2deg(panel_params$axis_rotation) * self$direction
208+
if (opposite_r) {
209+
arc <- rev(arc)
210+
}
188211
# Set guide text angles
189212
guide_params[["r"]]$angle <- guide_params[["r"]]$angle %|W|% arc[1]
190213
guide_params[["r.sec"]]$angle <- guide_params[["r.sec"]]$angle %|W|% arc[2]
@@ -193,9 +216,9 @@ CoordRadial <- ggproto("CoordRadial", Coord,
193216
if (opposite_r) {
194217
r_position <- rev(r_position)
195218
}
196-
guide_params[["r"]]$position <- r_position[1]
197-
guide_params[["r.sec"]]$position <- r_position[2]
198219
}
220+
guide_params[["r"]]$position <- r_position[1]
221+
guide_params[["r.sec"]]$position <- r_position[2]
199222

200223
guide_params[drop_guides] <- list(NULL)
201224
guides$update_params(guide_params)
@@ -223,7 +246,7 @@ CoordRadial <- ggproto("CoordRadial", Coord,
223246
gdefs[[t]] <- guides[[t]]$get_layer_key(gdefs[[t]], layers)
224247
}
225248

226-
if (self$r_axis_inside) {
249+
if (!isFALSE(self$r_axis_inside)) {
227250
# For radial axis, we need to pretend that rotation starts at 0 and
228251
# the bounding box is for circles, otherwise tick positions will be
229252
# spaced too closely.
@@ -273,14 +296,14 @@ CoordRadial <- ggproto("CoordRadial", Coord,
273296
},
274297

275298
render_axis_v = function(self, panel_params, theme) {
276-
if (self$r_axis_inside) {
299+
if (!isFALSE(self$r_axis_inside)) {
277300
return(list(left = zeroGrob(), right = zeroGrob()))
278301
}
279302
CoordCartesian$render_axis_v(panel_params, theme)
280303
},
281304

282305
render_axis_h = function(self, panel_params, theme) {
283-
if (self$r_axis_inside) {
306+
if (!isFALSE(self$r_axis_inside)) {
284307
return(list(top = zeroGrob(), bottom = zeroGrob()))
285308
}
286309
CoordCartesian$render_axis_h(panel_params, theme)
@@ -303,8 +326,8 @@ CoordRadial <- ggproto("CoordRadial", Coord,
303326
if (length(theta_min) > 0) {
304327
theta_min <- theta_rescale(theta_min, theta_lim, arc, dir)
305328
}
306-
theta_fine <- seq(self$arc[1], self$arc[2], length.out = 100)
307329

330+
theta_fine <- theta_rescale(seq(0, 1, length.out = 100), c(0, 1), arc, dir)
308331
r_fine <- r_rescale(panel_params$r.major, panel_params$r.range,
309332
panel_params$inner_radius)
310333

@@ -345,10 +368,8 @@ CoordRadial <- ggproto("CoordRadial", Coord,
345368
theta_grid(theta_min, grid_elems[[2]], inner_radius, bbox),
346369
element_render(
347370
theme, majorr, name = "radius",
348-
x = rescale(rep(r_fine, each = length(theta_fine)) *
349-
rep(sin(theta_fine), length(r_fine)) + 0.5, from = bbox$x),
350-
y = rescale(rep(r_fine, each = length(theta_fine)) *
351-
rep(cos(theta_fine), length(r_fine)) + 0.5, from = bbox$y),
371+
x = rescale(outer(sin(theta_fine), r_fine) + 0.5, from = bbox$x),
372+
y = rescale(outer(cos(theta_fine), r_fine) + 0.5, from = bbox$y),
352373
id.lengths = rep(length(theta_fine), length(r_fine)),
353374
default.units = "native"
354375
)
@@ -359,7 +380,7 @@ CoordRadial <- ggproto("CoordRadial", Coord,
359380

360381
border <- element_render(theme, "panel.border", fill = NA)
361382

362-
if (!self$r_axis_inside) {
383+
if (isFALSE(self$r_axis_inside)) {
363384
out <- grobTree(
364385
panel_guides_grob(panel_params$guides, "theta", theme),
365386
panel_guides_grob(panel_params$guides, "theta.sec", theme),
@@ -370,14 +391,15 @@ CoordRadial <- ggproto("CoordRadial", Coord,
370391

371392
bbox <- panel_params$bbox
372393
dir <- self$direction
373-
arc <- if (dir == 1) self$arc else rev(self$arc)
374-
arc <- dir * rad2deg(-arc)
394+
rot <- panel_params$axis_rotation
395+
rot <- if (dir == 1) rot else rev(rot)
396+
rot <- dir * rad2deg(-rot)
375397

376398
left <- panel_guides_grob(panel_params$guides, position = "left", theme)
377-
left <- rotate_r_axis(left, arc[1], bbox, "left")
399+
left <- rotate_r_axis(left, rot[1], bbox, "left")
378400

379401
right <- panel_guides_grob(panel_params$guides, position = "right", theme)
380-
right <- rotate_r_axis(right, arc[2], bbox, "right")
402+
right <- rotate_r_axis(right, rot[2], bbox, "right")
381403

382404
grobTree(
383405
panel_guides_grob(panel_params$guides, "theta", theme),
@@ -426,7 +448,7 @@ CoordRadial <- ggproto("CoordRadial", Coord,
426448
},
427449

428450
setup_params = function(self, data) {
429-
if (!self$r_axis_inside) {
451+
if (isFALSE(self$r_axis_inside)) {
430452
place <- in_arc(c(0, 0.5, 1, 1.5) * pi, self$arc)
431453
if (place[1]) {
432454
return(list(r_axis = "left", fake_arc = c(0, 2) * pi))

man/coord_polar.Rd

Lines changed: 10 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)