Skip to content

Commit 7f317d4

Browse files
authored
Consolidate coordinate scale expansion and limiting code (#3380)
* move expantion-related functions to scale-expansion.r * allow NA for xlim and/or ylim in coord functions * get second axes to work with coord_trans() * ensure that the reverse/reciprocal transformations work with coord_trans() * mention removal of `xtrans` and `ytrans` arguments in `coord_trans()` * rename `expand_scale()` to `expansion()`
1 parent fe00b5c commit 7f317d4

33 files changed

+1252
-242
lines changed

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Suggests:
5454
rpart,
5555
sf (>= 0.7-3),
5656
svglite (>= 1.2.0.9001),
57-
testthat (>= 0.11.0),
57+
testthat (>= 2.1.0),
5858
vdiffr (>= 0.3.0)
5959
Enhances: sp
6060
License: GPL-2 | file LICENSE
@@ -190,6 +190,7 @@ Collate:
190190
'scale-continuous.r'
191191
'scale-date.r'
192192
'scale-discrete-.r'
193+
'scale-expansion.r'
193194
'scale-gradient.r'
194195
'scale-grey.r'
195196
'scale-hue.r'

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ export(ensym)
290290
export(ensyms)
291291
export(expand_limits)
292292
export(expand_scale)
293+
export(expansion)
293294
export(expr)
294295
export(facet_grid)
295296
export(facet_null)

NEWS.md

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

3+
* `expand_scale()` was deprecated in favour of `expansion()` for setting
4+
the `expand` argument of `x` and `y` scales (@paleolimbot).
5+
6+
* `coord_trans()` now draws second axes and accepts `xlim`, `ylim`,
7+
and `expand` arguments to bring it up to feature parity with
8+
`coord_cartesian()`. The `xtrans` and `ytrans` arguments that were
9+
deprecated in version 1.0.1 in favour of `x` and `y`
10+
were removed (@paleolimbot, #2990).
11+
12+
* `coord_trans()` now calculates breaks using the expanded range
13+
(previously these were calculated using the unexpanded range,
14+
which resulted in differences between plots made with `coord_trans()`
15+
and those made with `coord_cartesian()`). The expansion for discrete axes
16+
in `coord_trans()` was also updated such that it behaves identically
17+
to that in `coord_cartesian()` (@paleolimbot, #3338).
18+
19+
* All `coord_*()` functions with `xlim` and `ylim` arguments now accept
20+
vectors with `NA` as a placeholder for the minimum or maximum value
21+
(e.g., `ylim = c(0, NA)` would zoom the y-axis from 0 to the
22+
maximum value observed in the data). This mimics the behaviour
23+
of the `limits` argument in continuous scale functions
24+
(@paleolimbot, #2907).
25+
326
* `geom_abline()`, `geom_hline()`, and `geom_vline()` now issue
427
more informative warnings when supplied with set aesthetics
528
(i.e., `slope`, `intercept`, `yintercept`, and/or `xintercept`)

R/axis-secondary.R

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -187,21 +187,49 @@ AxisSecondary <- ggproto("AxisSecondary", NULL,
187187

188188
# patch for date and datetime scales just to maintain functionality
189189
# works only for linear secondary transforms that respect the time or date transform
190-
if (scale$trans$name %in% c("date", "time")){
190+
if (scale$trans$name %in% c("date", "time")) {
191191
temp_scale <- self$create_scale(new_range, trans = scale$trans)
192192
range_info <- temp_scale$break_info()
193-
names(range_info) <- paste0("sec.", names(range_info))
194-
return(range_info)
195-
}
193+
old_val_trans <- rescale(range_info$major, from = c(0, 1), to = range)
194+
old_val_minor_trans <- rescale(range_info$minor, from = c(0, 1), to = range)
195+
} else {
196+
temp_scale <- self$create_scale(new_range)
197+
range_info <- temp_scale$break_info()
196198

197-
temp_scale <- self$create_scale(new_range)
198-
range_info <- temp_scale$break_info()
199+
# Map the break values back to their correct position on the primary scale
200+
old_val <- lapply(range_info$major_source, function(x) which.min(abs(full_range - x)))
201+
old_val <- old_range[unlist(old_val)]
202+
old_val_trans <- scale$trans$transform(old_val)
203+
204+
old_val_minor <- lapply(range_info$minor_source, function(x) which.min(abs(full_range - x)))
205+
old_val_minor <- old_range[unlist(old_val_minor)]
206+
old_val_minor_trans <- scale$trans$transform(old_val_minor)
207+
208+
# rescale values from 0 to 1
209+
range_info$major[] <- round(
210+
rescale(
211+
scale$map(old_val_trans, range(old_val_trans)),
212+
from = range
213+
),
214+
digits = 3
215+
)
216+
217+
range_info$minor[] <- round(
218+
rescale(
219+
scale$map(old_val_minor_trans, range(old_val_minor_trans)),
220+
from = range
221+
),
222+
digits = 3
223+
)
224+
}
199225

200-
# Map the break values back to their correct position on the primary scale
201-
old_val <- lapply(range_info$major_source, function(x) which.min(abs(full_range - x)))
202-
old_val <- old_range[unlist(old_val)]
203-
old_val_trans <- scale$trans$transform(old_val)
204-
range_info$major[] <- round(rescale(scale$map(old_val_trans, range(old_val_trans)), from = range), digits = 3)
226+
# The _source values should be in (primary) scale_transformed space,
227+
# so that the coord doesn't have to know about the secondary scale transformation
228+
# when drawing the axis. The values in user space are useful for testing.
229+
range_info$major_source_user <- range_info$major_source
230+
range_info$minor_source_user <- range_info$minor_source
231+
range_info$major_source[] <- old_val_trans
232+
range_info$minor_source[] <- old_val_minor_trans
205233

206234
names(range_info) <- paste0("sec.", names(range_info))
207235
range_info

R/bin.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ bin_breaks_bins <- function(x_range, bins = 30, center = NULL,
102102
if (bins < 1) {
103103
stop("Need at least one bin.", call. = FALSE)
104104
} else if (zero_range(x_range)) {
105-
# 0.1 is the same width as the expansion `expand_default()` gives for 0-width data
105+
# 0.1 is the same width as the expansion `default_expansion()` gives for 0-width data
106106
width <- 0.1
107107
} else if (bins == 1) {
108108
width <- diff(x_range)

R/coord-.r

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ Coord <- ggproto("Coord",
126126
#' @keywords internal
127127
is.Coord <- function(x) inherits(x, "Coord")
128128

129-
expand_default <- function(scale, discrete = c(0, 0.6, 0, 0.6), continuous = c(0.05, 0, 0.05, 0)) {
130-
scale$expand %|W|% if (scale$is_discrete()) discrete else continuous
131-
}
132-
133129
# Renders an axis with the correct orientation or zeroGrob if no axis should be
134130
# generated
135131
render_axis <- function(panel_params, axis, scale, position, theme) {

R/coord-cartesian-.r

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,9 @@ CoordCartesian <- ggproto("CoordCartesian", Coord,
137137
)
138138

139139
view_scales_from_scale <- function(scale, coord_limits = NULL, expand = TRUE) {
140-
expansion <- if (expand) expand_default(scale) else expand_scale(0, 0)
140+
expansion <- default_expansion(scale, expand = expand)
141141
limits <- scale$get_limits()
142-
143-
if (is.null(coord_limits)) {
144-
continuous_range <- scale$dimension(expansion, limits)
145-
} else {
146-
continuous_range <- range(scale$transform(coord_limits))
147-
continuous_range <- expand_range4(continuous_range, expansion)
148-
}
149-
142+
continuous_range <- expand_limits_scale(scale, expansion, limits, coord_limits = coord_limits)
150143
aesthetic <- scale$aesthetics[1]
151144

152145
view_scales <- list(

R/coord-map.r

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,7 @@ CoordMap <- ggproto("CoordMap", Coord,
181181
for (n in c("x", "y")) {
182182
scale <- get(paste0("scale_", n))
183183
limits <- self$limits[[n]]
184-
185-
if (is.null(limits)) {
186-
range <- scale$dimension(expand_default(scale))
187-
} else {
188-
range <- range(scale$transform(limits))
189-
}
184+
range <- expand_limits_scale(scale, default_expansion(scale), coord_limits = limits)
190185
ranges[[n]] <- range
191186
}
192187

R/coord-polar.r

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,25 +111,21 @@ CoordPolar <- ggproto("CoordPolar", Coord,
111111
scale <- get(paste0("scale_", n))
112112
limits <- self$limits[[n]]
113113

114-
if (is.null(limits)) {
115-
if (self$theta == n) {
116-
expand <- expand_default(scale, c(0, 0.5), c(0, 0))
117-
} else {
118-
expand <- expand_default(scale, c(0, 0), c(0, 0))
119-
}
120-
range <- scale$dimension(expand)
114+
if (self$theta == n) {
115+
expansion <- default_expansion(scale, c(0, 0.5), c(0, 0))
121116
} else {
122-
range <- range(scale_transform(scale, limits))
117+
expansion <- default_expansion(scale, c(0, 0), c(0, 0))
123118
}
119+
range <- expand_limits_scale(scale, expansion, coord_limits = limits)
124120

125121
out <- scale$break_info(range)
126122
ret[[n]]$range <- out$range
127123
ret[[n]]$major <- out$major_source
128124
ret[[n]]$minor <- out$minor_source
129125
ret[[n]]$labels <- out$labels
130126
ret[[n]]$sec.range <- out$sec.range
131-
ret[[n]]$sec.major <- out$sec.major_source
132-
ret[[n]]$sec.minor <- out$sec.minor_source
127+
ret[[n]]$sec.major <- out$sec.major_source_user
128+
ret[[n]]$sec.minor <- out$sec.minor_source_user
133129
ret[[n]]$sec.labels <- out$sec.labels
134130
}
135131

R/coord-sf.R

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
127127

128128
setup_panel_params = function(self, scale_x, scale_y, params = list()) {
129129
# Bounding box of the data
130-
x_range <- scale_range(scale_x, self$limits$x, self$expand)
131-
y_range <- scale_range(scale_y, self$limits$y, self$expand)
130+
expansion_x <- default_expansion(scale_x, expand = self$expand)
131+
x_range <- expand_limits_scale(scale_x, expansion_x, coord_limits = self$limits$x)
132+
expansion_y <- default_expansion(scale_y, expand = self$expand)
133+
y_range <- expand_limits_scale(scale_y, expansion_y, coord_limits = self$limits$y)
132134
bbox <- c(
133135
x_range[1], y_range[1],
134136
x_range[2], y_range[2]
@@ -466,14 +468,3 @@ parse_axes_labeling <- function(x) {
466468
labs = unlist(strsplit(x, ""))
467469
list(top = labs[1], right = labs[2], bottom = labs[3], left = labs[4])
468470
}
469-
470-
scale_range <- function(scale, limits = NULL, expand = TRUE) {
471-
expansion <- if (expand) expand_default(scale) else expand_scale(0, 0)
472-
473-
if (is.null(limits)) {
474-
scale$dimension(expansion)
475-
} else {
476-
continuous_range <- range(scale$transform(limits))
477-
expand_range4(continuous_range, expansion)
478-
}
479-
}

0 commit comments

Comments
 (0)