Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ where the formatting is also better._
- Added layers, particularly from `tinyplot_add()`, should now respect the
x-axis order of the original plot layer. This should ensure that we don't end
up with misaligned layers. For example, when adding a ribbon on top of an
errorbar plot. (#517, #520 @grantmcdermott)
errorbar plot. (#517, #520, #523 @grantmcdermott)


### Documentation
Expand Down
36 changes: 36 additions & 0 deletions R/align_layer.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Ensure added layers respect the x-axis order of the original plot layer
# (e.g., when adding lines or ribbons on top of errorbars)
align_layer = function(settings) {

# Retrieve xlabs from current and original layers
xlabs_layer = settings[["xlabs"]]
xlabs_orig = get("xlabs", envir = get(".tinyplot_env", envir = parent.env(environment())))

# Only adjust if original layer has named xlabs
if (!is.null(names(xlabs_orig))) {
if (is.factor(settings$datapoints[["x"]])) {
# Case 1: relevel a factor (e.g., ribbon added to errorbars)
settings$datapoints[["x"]] = tryCatch(
factor(settings$datapoints[["x"]], levels = names(xlabs_orig)),
error = function(e) {
settings$datapoints[["x"]]
}
)
settings$datapoints = settings$datapoints[order(settings$datapoints[["x"]]), ]
} else if (!is.null(names(xlabs_layer))) {
# Case 2: match implicit integer -> label mapping (e.g., lines added to errorbars)
if (setequal(names(xlabs_layer), names(xlabs_orig))) {
orig_order = xlabs_orig[names(xlabs_layer)[settings$datapoints[["x"]]]]
x_layer = settings$datapoints[["x"]]
settings$datapoints[["x"]] = orig_order
# Adjust ancillary variables
for (v in c("rowid", "xmin", "xmax")) {
if (identical(settings$datapoints[[v]], x_layer)) {
settings$datapoints[[v]] = orig_order
}
}
settings$datapoints = settings$datapoints[order(settings$datapoints[["x"]]), ]
}
}
}
}
62 changes: 8 additions & 54 deletions R/tinyplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -868,13 +868,21 @@ tinyplot.default = function(
settings$type_data(settings, ...)
}

# ensure axis aligment of any added layers
if (!add) {
assign("xlabs", settings[["xlabs"]], envir = get(".tinyplot_env", envir = parent.env(environment())))
} else {
align_layer(settings)
}

# flip -> swap x and y after type_data, except for boxplots (which has its own bespoke flip logic)
flip_datapoints(settings)


#
## bubble plot -----
#

# catch some simple aesthetics for bubble plots before the standard "by"
# grouping sanitizers (actually: will only be used for dual_legend plots but
# easiest to assign/determine now)
Expand Down Expand Up @@ -1181,60 +1189,6 @@ tinyplot.default = function(
list2env(facet_window_args, environment())


#
## layering axis alignment -----
#

# ensure added layers respect the x-axis order of the original plot layer
# (e.g., when adding lines or ribbons on top of errorbars)
if (!add) {
# keep track of the x(y)labs from the original (1st) layer
assign("xlabs", xlabs, envir = get(".tinyplot_env", envir = parent.env(environment())))
assign("ylabs", ylabs, envir = get(".tinyplot_env", envir = parent.env(environment())))
} else {
# check whether we need to adjust any added layers
# aside: we need some extra accounting for flipped plots (x -> y)
if (!flip) {
labs_orig = "xlabs"
labs_layer = xlabs
dp_var = "x"
dp_ovars = c("rowid", "xmin", "xmax")
} else {
labs_orig = "ylabs"
labs_layer = ylabs
dp_var = "y"
dp_ovars = c("rowid", "ymin", "ymax")
}
# retrieve the relevant axis labs from the original layer (and we only care
# if it's not null)
labs_orig = get(labs_orig, envir = get(".tinyplot_env", envir = parent.env(environment())))
if (!is.null(names(labs_orig))) {
if (is.factor(datapoints[[dp_var]])) {
# case 1: relevel a factor (e.g., ribbon added to errorbars)
datapoints[[dp_var]] = tryCatch(
factor(datapoints[[dp_var]], levels = names(labs_orig)),
error = function(e) {
datapoints[[dp_var]]
}
)
datapoints = datapoints[order(datapoints[[dp_var]]), ]
} else if (!is.null(names(labs_layer))) {
# case 2: match implicit integer -> label mapping (e.g., lines added to errorbars)
if (setequal(names(labs_layer), names(labs_orig))) {
orig_order = labs_orig[names(labs_layer)[datapoints[[dp_var]]]]
dp_var_layer = datapoints[[dp_var]]
datapoints[[dp_var]] = orig_order
# it's a bit of a pain, but we also have to adjust some ancillary vars
for (dov in dp_ovars) {
if (identical(datapoints[[dov]], dp_var_layer)) datapoints[[dov]] = orig_order
}
datapoints = datapoints[order(datapoints[[dp_var]]), ]
}
}
}
}


#
## split and draw datapoints -----
#
Expand Down