Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axes at interior panels #4064 #4467

Merged
merged 40 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
59850ac
Merge pull request #1 from tidyverse/master
teunbrand Jan 13, 2021
8d98452
Add axis drawing to fixed scale facet_wrap
teunbrand May 5, 2021
b486e7f
Add draw.axis argument to facet_grid
teunbrand May 5, 2021
f4b3ece
Switch to facet_wrap approach when drawing additional axis in facet_grid
teunbrand May 5, 2021
a0984fe
Document draw.axis argument
teunbrand May 5, 2021
16995f7
Add unit tests for draw.axes
teunbrand May 5, 2021
a7def9a
Sync with tidyverse master
teunbrand May 5, 2021
465596b
resolve conflict
teunbrand May 5, 2021
5ee44e9
Merge branch 'tidyverse-master' into facet_axes
teunbrand May 5, 2021
5006c53
Resolve merge conflict
teunbrand Mar 24, 2022
c97778e
Rename user-facing argument to 'axes'
teunbrand Mar 24, 2022
de88a18
Resolve merge
teunbrand May 10, 2023
2b25dcd
Sync latest changes
teunbrand May 14, 2023
24b7be0
Mechanism for label suppression
teunbrand May 16, 2023
f00d02d
censoring for wrap
teunbrand May 21, 2023
72d5508
Label censoring for grid
teunbrand May 21, 2023
ce77b7a
Test censoring logic
teunbrand May 21, 2023
574785c
Label censoring for wrap
teunbrand May 21, 2023
432c603
Test logic for wrap censoring
teunbrand May 21, 2023
7aa2d16
Visual test for censoring
teunbrand May 21, 2023
d6119b1
Add NEWS bullet
teunbrand May 21, 2023
3e9a0a2
Merge branch 'main' into facet_axes
teunbrand May 21, 2023
4d0c896
Fix merge conflict
teunbrand Oct 31, 2023
3970cad
Better panel spacing with empty panels
teunbrand Oct 31, 2023
3cc697d
resolve merge conflict
teunbrand Dec 12, 2023
925edf7
Only draw first in stack
teunbrand Dec 12, 2023
313bd89
Funnel radial r-axis through CoordCartesian
teunbrand Dec 12, 2023
1f19415
Fix order of theta grobs
teunbrand Dec 12, 2023
c629845
use dot.case instead of snake_case for argument
teunbrand Dec 13, 2023
214dda5
use dot.case instead of snake_case for argument
teunbrand Dec 13, 2023
441190c
Merge branch 'facet_axes' of https://github.com/teunbrand/ggplot2 int…
teunbrand Dec 13, 2023
7d950f7
Merge branch 'main' into facet_axes
teunbrand Dec 13, 2023
ed56066
More snake_case to dot.case conversions
teunbrand Dec 13, 2023
1a05cf2
Merge branch 'facet_axes' of https://github.com/teunbrand/ggplot2 int…
teunbrand Dec 13, 2023
5d2b39c
New args before deprecated args
teunbrand Dec 14, 2023
63c4368
add examples
teunbrand Dec 14, 2023
2501629
Merge branch 'main' into facet_axes
teunbrand Dec 14, 2023
74993a8
add `weave_axes` helper
teunbrand Dec 14, 2023
33a1d6b
use helper
teunbrand Dec 14, 2023
657356d
Merge branch 'main' into facet_axes
teunbrand Dec 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# ggplot2 (development version)

* The new argument `axes` in `facet_grid()` and `facet_wrap()` controls the
display of axes at interior panel positions. Additionally, the `axis_labels`
argument can be used to only draw tick marks or fully labelled axes
(@teunbrand, #4064).
* When legend titles are larger than the legend, title justification extends
to the placement of keys and labels (#1903).

Expand Down
23 changes: 18 additions & 5 deletions R/coord-cartesian-.R
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,27 @@ CoordCartesian <- ggproto("CoordCartesian", Coord,

render_axis_h = function(panel_params, theme) {
list(
top = panel_guides_grob(panel_params$guides, position = "top", theme = theme),
bottom = panel_guides_grob(panel_params$guides, position = "bottom", theme = theme)
top = panel_guides_grob(
panel_params$guides, position = "top",
theme = theme, labels = panel_params$draw_labels$top
),
bottom = panel_guides_grob(
panel_params$guides, position = "bottom",
theme = theme, labels = panel_params$draw_labels$bottom
)
)
},

render_axis_v = function(panel_params, theme) {
list(
left = panel_guides_grob(panel_params$guides, position = "left", theme = theme),
right = panel_guides_grob(panel_params$guides, position = "right", theme = theme)
left = panel_guides_grob(
panel_params$guides, position = "left",
theme = theme, labels = panel_params$draw_labels$left
),
right = panel_guides_grob(
panel_params$guides, position = "right",
theme = theme, labels = panel_params$draw_labels$right
)
)
}
)
Expand All @@ -146,10 +158,11 @@ view_scales_from_scale <- function(scale, coord_limits = NULL, expand = TRUE) {
view_scales
}

panel_guides_grob <- function(guides, position, theme) {
panel_guides_grob <- function(guides, position, theme, labels = NULL) {
if (!inherits(guides, "Guides")) {
return(zeroGrob())
}
pair <- guides$get_position(position)
pair$params$draw_label <- labels %||% NULL
pair$guide$draw(theme, params = pair$params)
}
10 changes: 2 additions & 8 deletions R/coord-radial.R
Original file line number Diff line number Diff line change
Expand Up @@ -241,20 +241,14 @@ CoordRadial <- ggproto("CoordRadial", Coord,
if (self$r_axis_inside) {
return(list(left = zeroGrob(), right = zeroGrob()))
}
list(
left = panel_guides_grob(panel_params$guides, position = "left", theme = theme),
right = panel_guides_grob(panel_params$guides, position = "right", theme = theme)
)
CoordCartesian$render_axis_v(panel_params, theme)
},

render_axis_h = function(self, panel_params, theme) {
if (self$r_axis_inside) {
return(list(top = zeroGrob(), bottom = zeroGrob()))
}
list(
top = panel_guides_grob(panel_params$guides, position = "top", theme = theme),
bottom = panel_guides_grob(panel_params$guides, position = "bottom", theme = theme)
)
CoordCartesian$render_axis_h(panel_params, theme)
},

render_bg = function(self, panel_params, theme) {
Expand Down
28 changes: 28 additions & 0 deletions R/facet-.R
Original file line number Diff line number Diff line change
Expand Up @@ -701,3 +701,31 @@ render_strips <- function(x = NULL, y = NULL, labeller, theme) {
y = build_strip(y, labeller, theme, FALSE)
)
}


censor_labels <- function(ranges, layout, labels) {
if (labels$x && labels$y) {
return(ranges)
}
draw <- matrix(
TRUE, length(ranges), 4,
dimnames = list(NULL, c("top", "bottom", "left", "right"))
)

if (!labels$x) {
xmax <- stats::ave(layout$ROW, layout$COL, FUN = max)
xmin <- stats::ave(layout$ROW, layout$COL, FUN = min)
draw[which(layout$ROW != xmax), "bottom"] <- FALSE
draw[which(layout$ROW != xmin), "top"] <- FALSE
}
if (!labels$y) {
ymax <- stats::ave(layout$COL, layout$ROW, FUN = max)
ymin <- stats::ave(layout$COL, layout$ROW, FUN = min)
draw[which(layout$COL != ymax), "right"] <- FALSE
draw[which(layout$COL != ymin), "left"] <- FALSE
}
for (i in seq_along(ranges)) {
ranges[[i]]$draw_labels <- as.list(draw[i, ])
}
ranges
}
104 changes: 90 additions & 14 deletions R/facet-grid-.R
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ NULL
#' variables for which margins are to be created.
#' @param facets `r lifecycle::badge("deprecated")` Please use `rows`
#' and `cols` instead.
#' @param axes Determines which axes will be drawn. When `"margins"`
#' (default), axes will be drawn at the exterior margins. `"all_x"` and
#' `"all_y"` will draw the respective axes at the interior panels too, whereas
#' `"all"` will draw all axes at all panels.
#' @param axis_labels Determines whether to draw labels for interior axes when
#' the `axes` argument is not `"margins"`. When `"all"` (default), all
#' interior axes get labels. When `"margins"`, only the exterior axes get
#' labels and the interior axes get none. When `"all_x"` or `"all_y"`, only
#' draws the labels at the interior axes in the x- or y-direction
#' respectively.
#' @export
#' @examples
#' p <- ggplot(mpg, aes(displ, cty)) + geom_point()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add an example showing the new functionality

Expand Down Expand Up @@ -112,7 +122,8 @@ facet_grid <- function(rows = NULL, cols = NULL, scales = "fixed",
space = "fixed", shrink = TRUE,
labeller = "label_value", as.table = TRUE,
switch = NULL, drop = TRUE, margins = FALSE,
facets = deprecated()) {
facets = deprecated(), axes = "margins",
axis_labels = "all") {
teunbrand marked this conversation as resolved.
Show resolved Hide resolved
# `facets` is deprecated and renamed to `rows`
if (lifecycle::is_present(facets)) {
deprecate_warn0("2.2.0", "facet_grid(facets)", "facet_grid(rows)")
Expand All @@ -137,6 +148,20 @@ facet_grid <- function(rows = NULL, cols = NULL, scales = "fixed",
y = any(space %in% c("free_y", "free"))
)

draw_axes <- arg_match0(axes, c("margins", "all_x", "all_y", "all"))
draw_axes <- list(
x = any(draw_axes %in% c("all_x", "all")),
y = any(draw_axes %in% c("all_y", "all"))
)

# Omitting labels is special-cased internally, so even when no internal axes
# are to be drawn, register as labelled.
axis_labels <- arg_match0(axis_labels, c("margins", "all_x", "all_y", "all"))
axis_labels <- list(
x = !draw_axes$x || any(axis_labels %in% c("all_x", "all")),
y = !draw_axes$y || any(axis_labels %in% c("all_y", "all"))
)

if (!is.null(switch)) {
arg_match0(switch, c("both", "x", "y"))
}
Expand All @@ -150,7 +175,8 @@ facet_grid <- function(rows = NULL, cols = NULL, scales = "fixed",
shrink = shrink,
params = list(rows = facets_list$rows, cols = facets_list$cols, margins = margins,
free = free, space_free = space_free, labeller = labeller,
as.table = as.table, switch = switch, drop = drop)
as.table = as.table, switch = switch, drop = drop,
draw_axes = draw_axes, axis_labels = axis_labels)
)
}

Expand Down Expand Up @@ -306,8 +332,22 @@ FacetGrid <- ggproto("FacetGrid", Facet,
cli::cli_abort("{.fn {snake_class(coord)}} doesn't support free scales.")
}

cols <- which(layout$ROW == 1)
rows <- which(layout$COL == 1)
if (!params$axis_labels$x) {
cols <- seq_len(nrow(layout))
x_axis_order <- as.integer(layout$PANEL[order(layout$ROW, layout$COL)])
} else {
cols <- which(layout$ROW == 1)
x_axis_order <- layout$COL
}
if (!params$axis_labels$y) {
rows <- seq_len(nrow(layout))
y_axis_order <- as.integer(layout$PANEL[order(layout$ROW, layout$COL)])
} else {
rows <- which(layout$COL == 1)
y_axis_order <- layout$ROW
}

ranges <- censor_labels(ranges, layout, params$axis_labels)
axes <- render_axes(ranges[cols], ranges[rows], coord, theme, transpose = TRUE)

col_vars <- unique0(layout[names(params$cols)])
Expand Down Expand Up @@ -367,17 +407,53 @@ FacetGrid <- ggproto("FacetGrid", Facet,
theme$panel.spacing.y %||% theme$panel.spacing)

# Add axes
panel_table <- gtable_add_rows(panel_table, max_height(axes$x$top), 0)
panel_table <- gtable_add_rows(panel_table, max_height(axes$x$bottom), -1)
panel_table <- gtable_add_cols(panel_table, max_width(axes$y$left), 0)
panel_table <- gtable_add_cols(panel_table, max_width(axes$y$right), -1)
panel_pos_col <- panel_cols(panel_table)
panel_pos_rows <- panel_rows(panel_table)
if (params$draw_axes$x) {
# Take facet_wrap approach to axis placement
axes$x$top <- matrix(axes$x$top[x_axis_order],
nrow = nrow, ncol = ncol, byrow = TRUE)
panel_table <- weave_tables_row(
panel_table, axes$x$top, -1,
unit(apply(axes$x$top, 1, max_height, value_only = TRUE), "cm"),
name = "axis-t", z = 3
)
axes$x$bottom <- matrix(axes$x$bottom[x_axis_order],
nrow = nrow, ncol = ncol, byrow = TRUE)
panel_table <- weave_tables_row(
panel_table, axes$x$bottom, 0,
unit(apply(axes$x$bottom, 1, max_height, value_only = TRUE), "cm"),
name = "axis-b", z = 3
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be abstracted away if we are using it multiple places in the code base (both for x and y and in facet_grid and facet_wrap?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like this too, but the actual facet_wrap code has very involved code dealing with empty panels (restoring axes for non-empty panels and getting the correct axis sizes). This is right between the matrix(axes$...) and weave_tables_*() steps. We can skip all of this because facet_grid doesn't have empty panels. So while the approach is the same, it would be tricky to unify the actual code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we at least abstract it away for x and y?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see what I can do

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured that facet_wrap() didn't need to measure the axis before dealing with empty panels, so axis measurement and adding it to the table is now wrapped in a weave_axes() helper. This helper is also used in facet_grid() when required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see... I knew you could do it

} else {
panel_table <- gtable_add_rows(panel_table, max_height(axes$x$top), 0)
panel_table <- gtable_add_rows(panel_table, max_height(axes$x$bottom), -1)
panel_pos_col <- panel_cols(panel_table)
panel_table <- gtable_add_grob(panel_table, axes$x$top, 1, panel_pos_col$l, clip = "off", name = paste0("axis-t-", seq_along(axes$x$top)), z = 3)
panel_table <- gtable_add_grob(panel_table, axes$x$bottom, -1, panel_pos_col$l, clip = "off", name = paste0("axis-b-", seq_along(axes$x$bottom)), z = 3)
}

panel_table <- gtable_add_grob(panel_table, axes$x$top, 1, panel_pos_col$l, clip = "off", name = paste0("axis-t-", seq_along(axes$x$top)), z = 3)
panel_table <- gtable_add_grob(panel_table, axes$x$bottom, -1, panel_pos_col$l, clip = "off", name = paste0("axis-b-", seq_along(axes$x$bottom)), z = 3)
panel_table <- gtable_add_grob(panel_table, axes$y$left, panel_pos_rows$t, 1, clip = "off", name = paste0("axis-l-", seq_along(axes$y$left)), z = 3)
panel_table <- gtable_add_grob(panel_table, axes$y$right, panel_pos_rows$t, -1, clip = "off", name = paste0("axis-r-", seq_along(axes$y$right)), z= 3)
if (params$draw_axes$y) {
# Take facet_wrap approach to axis placement
axes$y$left <- matrix(axes$y$left[y_axis_order],
nrow = nrow, ncol = ncol, byrow = TRUE)
panel_table <- weave_tables_col(
panel_table, axes$y$left, -1,
unit(apply(axes$y$left, 2, max_width, value_only = TRUE), "cm"),
name = "axis-l", z = 3
)
axes$y$right <- matrix(axes$y$right[y_axis_order],
nrow = nrow, ncol = ncol, byrow = TRUE)
panel_table <- weave_tables_col(
panel_table, axes$y$right, 0,
unit(apply(axes$y$right, 2, max_width, value_only = TRUE), "cm"),
name = "axis-r", z = 3
)
} else {
panel_table <- gtable_add_cols(panel_table, max_width(axes$y$left), 0)
panel_table <- gtable_add_cols(panel_table, max_width(axes$y$right), -1)
panel_pos_rows <- panel_rows(panel_table)
panel_table <- gtable_add_grob(panel_table, axes$y$left, panel_pos_rows$t, 1, clip = "off", name = paste0("axis-l-", seq_along(axes$y$left)), z = 3)
panel_table <- gtable_add_grob(panel_table, axes$y$right, panel_pos_rows$t, -1, clip = "off", name = paste0("axis-r-", seq_along(axes$y$right)), z= 3)
}

# Add strips
switch_x <- !is.null(params$switch) && params$switch %in% c("both", "x")
Expand Down
Loading
Loading