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-
}

R/coord-transform.r

Lines changed: 52 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,9 @@
88
#' [scales::trans_new()] for list of transformations, and instructions
99
#' on how to create your own.
1010
#'
11-
#' @param x,y transformers for x and y axes
12-
#' @param xtrans,ytrans Deprecated; use `x` and `y` instead.
13-
#' @param limx,limy limits for x and y axes. (Named so for backward
14-
#' compatibility)
15-
#' @param clip Should drawing be clipped to the extent of the plot panel? A
16-
#' setting of `"on"` (the default) means yes, and a setting of `"off"`
17-
#' means no. For details, please see [`coord_cartesian()`].
11+
#' @inheritParams coord_cartesian
12+
#' @param x,y Transformers for x and y axes or their names.
13+
#' @param limx,limy **Deprecated**: use `xlim` and `ylim` instead.
1814
#' @export
1915
#' @examples
2016
#' \donttest{
@@ -78,31 +74,25 @@
7874
#' plot + coord_trans(x = "log10")
7975
#' plot + coord_trans(x = "sqrt")
8076
#' }
81-
coord_trans <- function(x = "identity", y = "identity", limx = NULL, limy = NULL, clip = "on",
82-
xtrans, ytrans)
83-
{
84-
if (!missing(xtrans)) {
85-
gg_dep("1.0.1", "`xtrans` arguments is deprecated; please use `x` instead.")
86-
x <- xtrans
77+
coord_trans <- function(x = "identity", y = "identity", xlim = NULL, ylim = NULL,
78+
limx = "DEPRECATED", limy = "DEPRECATED", clip = "on", expand = TRUE) {
79+
if (!missing(limx)) {
80+
warning("`limx` argument is deprecated; please use `xlim` instead.", call. = FALSE)
81+
xlim <- limx
8782
}
88-
if (!missing(ytrans)) {
89-
gg_dep("1.0.1", "`ytrans` arguments is deprecated; please use `y` instead.")
90-
y <- ytrans
83+
if (!missing(limy)) {
84+
warning("`limy` argument is deprecated; please use `ylim` instead.", call. = FALSE)
85+
ylim <- limy
9186
}
9287

93-
# @kohske
94-
# Now limits are implemented.
95-
# But for backward compatibility, xlim -> limx, ylim -> ylim
96-
# Because there are many examples such as
97-
# > coord_trans(x = "log10", y = "log10")
98-
# Maybe this is changed.
88+
# resolve transformers
9989
if (is.character(x)) x <- as.trans(x)
10090
if (is.character(y)) y <- as.trans(y)
10191

102-
10392
ggproto(NULL, CoordTrans,
10493
trans = list(x = x, y = y),
105-
limits = list(x = limx, y = limy),
94+
limits = list(x = xlim, y = ylim),
95+
expand = expand,
10696
clip = clip
10797
)
10898
}
@@ -147,8 +137,8 @@ CoordTrans <- ggproto("CoordTrans", Coord,
147137

148138
setup_panel_params = function(self, scale_x, scale_y, params = list()) {
149139
c(
150-
train_trans(scale_x, self$limits$x, self$trans$x, "x"),
151-
train_trans(scale_y, self$limits$y, self$trans$y, "y")
140+
train_trans(scale_x, self$limits$x, self$trans$x, "x", self$expand),
141+
train_trans(scale_y, self$limits$y, self$trans$y, "y", self$expand)
152142
)
153143
},
154144

@@ -187,39 +177,51 @@ transform_value <- function(trans, value, range) {
187177
rescale(trans$transform(value), 0:1, range)
188178
}
189179

190-
191-
train_trans <- function(scale, limits, trans, name) {
192-
# first, calculate the range that is the numerical limits in data space
193-
194-
# expand defined by scale OR coord
195-
# @kohske
196-
# Expansion of data range sometimes go beyond domain,
197-
# so in trans, expansion takes place at the final stage.
198-
if (is.null(limits)) {
199-
range <- scale$dimension()
180+
train_trans <- function(scale, coord_limits, trans, name, expand = TRUE) {
181+
expansion <- default_expansion(scale, expand = expand)
182+
scale_trans <- scale$trans %||% identity_trans()
183+
coord_limits <- coord_limits %||% scale_trans$inverse(c(NA, NA))
184+
185+
if (scale$is_discrete()) {
186+
continuous_ranges <- expand_limits_discrete_trans(
187+
scale$get_limits(),
188+
expansion,
189+
coord_limits,
190+
trans,
191+
range_continuous = scale$range_c$range
192+
)
200193
} else {
201-
range <- range(scale$transform(limits))
194+
# transform user-specified limits to scale transformed space
195+
coord_limits <- scale$trans$transform(coord_limits)
196+
continuous_ranges <- expand_limits_continuous_trans(
197+
scale$get_limits(),
198+
expansion,
199+
coord_limits,
200+
trans
201+
)
202202
}
203203

204-
# breaks on data space
205-
out <- scale$break_info(range)
206-
207-
# trans'd range
208-
out$range <- trans$transform(out$range)
204+
# calculate break information
205+
out <- scale$break_info(continuous_ranges$continuous_range)
209206

210-
# expansion if limits are not specified
211-
if (is.null(limits)) {
212-
expand <- expand_default(scale)
213-
out$range <- expand_range(out$range, expand[1], expand[2])
214-
}
207+
# range in coord space has already been calculated
208+
# needs to be in increasing order for transform_value() to work
209+
out$range <- range(continuous_ranges$continuous_range_coord)
215210

216-
# major and minor values in plot space
211+
# major and minor values in coordinate data
217212
out$major_source <- transform_value(trans, out$major_source, out$range)
218213
out$minor_source <- transform_value(trans, out$minor_source, out$range)
214+
out$sec.major_source <- transform_value(trans, out$sec.major_source, out$range)
215+
out$sec.minor_source <- transform_value(trans, out$sec.minor_source, out$range)
219216

220217
out <- list(
221-
range = out$range, labels = out$labels,
222-
major = out$major_source, minor = out$minor_source
218+
range = out$range,
219+
labels = out$labels,
220+
major = out$major_source,
221+
minor = out$minor_source,
222+
sec.labels = out$sec.labels,
223+
sec.major = out$sec.major_source,
224+
sec.minor = out$sec.minor_source
223225
)
224226
names(out) <- paste(name, names(out), sep = ".")
225227
out

0 commit comments

Comments
 (0)