From 674a1c6fd3474a8d9aa614e3ad3106732c43fa71 Mon Sep 17 00:00:00 2001 From: Miura Meng Date: Mon, 3 Aug 2026 23:20:10 -0400 Subject: [PATCH 1/3] flip the drawn geometry of h and the step types, not just the axes lines(type = 'h') always drops vertically and the step types fix which coordinate moves first, so swapping the data alone leaves the marks in the unflipped orientation. Draw flipped 'h' as horizontal segments to the baseline and exchange 's'/'S' under flip. Geometry checked from the SVG output directly so the new tests run on any OS. --- NEWS.md | 4 ++++ R/type_lines.R | 29 +++++++++++++++++++++-- inst/tinytest/test-flip.R | 49 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9281cc898..a4a3d1a2f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,6 +47,10 @@ where the formatting is also better._ ### Bug fixes +- `flip = TRUE` now flips the drawn geometry of the single-letter line types, + not just the axes: `type = "h"` draws horizontal segments to the baseline, + and the step types `"s"` and `"S"` swap which coordinate moves first. + (#675 @haomeng797-ship-it) - Fixed several bugs specific to plots with free facets (i.e., `facet.args = list(free = TRUE)`): - Single-valued discrete axes no longer trigger invalid `par(usr)` values. diff --git a/R/type_lines.R b/R/type_lines.R index a76155be6..7caa91fa4 100644 --- a/R/type_lines.R +++ b/R/type_lines.R @@ -77,12 +77,37 @@ data_lines = function(dodge = 0, fixed.dodge = FALSE) { draw_lines = function(type = "l") { - fun = function(ix, iy, icol, ipch, ibg, ilty, ilwd, icex = 1, ...) { + fun = function(ix, iy, icol, ipch, ibg, ilty, ilwd, icex = 1, flip = FALSE, ...) { + ltype = type + if (isTRUE(flip)) { + # flip_datapoints() has already swapped the coordinates, but the + # base engine still draws these types in fixed orientations: + # type = "h" always drops vertically, and the step types commit + # to which coordinate moves first. So draw "h" as explicit + # horizontal segments to the baseline, and mirror the step order. + if (ltype == "h") { + x0 = if (par("xlog")) 10^par("usr")[1] else 0 + segments( + x0 = x0, + y0 = iy, + x1 = ix, + y1 = iy, + col = icol, + lty = ilty, + lwd = ilwd + ) + return(invisible(NULL)) + } else if (ltype == "s") { + ltype = "S" + } else if (ltype == "S") { + ltype = "s" + } + } lines( x = ix, y = iy, col = icol, - type = type, + type = ltype, pch = ipch, bg = ibg, lty = ilty, diff --git a/inst/tinytest/test-flip.R b/inst/tinytest/test-flip.R index 35bb36049..885e38c1c 100644 --- a/inst/tinytest/test-flip.R +++ b/inst/tinytest/test-flip.R @@ -113,3 +113,52 @@ f = function() { tinyplot(x_dt, y, grid = TRUE, flip = TRUE) } expect_snapshot_plot(f, label = "flip_date") + + +# flipped single-letter line types keep their geometry (#675) +# These checks read the SVG output directly, so they run on every OS. + +svg_of = function(...) { + tf = tempfile(fileext = ".svg") + svglite::svglite(tf) + tinyplot(...) + dev.off() + readLines(tf) +} + +num_attr = function(el, attr) { + as.numeric(sub(sprintf(".*%s='([^']*)'.*", attr), "\\1", el)) +} + +first_step = function(svg) { + pl = grep(" 1)) + +# unflipped "h" still draws vertical drops +svg = svg_of(x, y, type = "h", axes = FALSE) +seg = grep(" 1) + +d = first_step(svg_of(x, y, type = "S", flip = TRUE, axes = FALSE)) +expect_true(abs(d[1]) > 1 && abs(d[2]) < 1e-6) + +d = first_step(svg_of(x, y, type = "s", axes = FALSE)) +expect_true(abs(d[1]) > 1 && abs(d[2]) < 1e-6) From 412799149e12d5b687ac8af35681c332134333c2 Mon Sep 17 00:00:00 2001 From: Miura Meng Date: Tue, 4 Aug 2026 18:28:55 -0400 Subject: [PATCH 2/3] add snapshot tests for the flipped line types --- inst/tinytest/_tinysnapshot/flip_type_h.svg | 76 +++++++++++ .../_tinysnapshot/flip_type_h_grouped.svg | 120 ++++++++++++++++++ .../tinytest/_tinysnapshot/flip_type_step.svg | 67 ++++++++++ .../_tinysnapshot/flip_type_step_rev.svg | 67 ++++++++++ inst/tinytest/test-flip.R | 27 ++++ 5 files changed, 357 insertions(+) create mode 100644 inst/tinytest/_tinysnapshot/flip_type_h.svg create mode 100644 inst/tinytest/_tinysnapshot/flip_type_h_grouped.svg create mode 100644 inst/tinytest/_tinysnapshot/flip_type_step.svg create mode 100644 inst/tinytest/_tinysnapshot/flip_type_step_rev.svg diff --git a/inst/tinytest/_tinysnapshot/flip_type_h.svg b/inst/tinytest/_tinysnapshot/flip_type_h.svg new file mode 100644 index 000000000..1690072b9 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/flip_type_h.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + +Flipped type = "h" +(1:10)^2 +1:10 + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +2 +4 +6 +8 +10 + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/flip_type_h_grouped.svg b/inst/tinytest/_tinysnapshot/flip_type_h_grouped.svg new file mode 100644 index 000000000..6c8101089 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/flip_type_h_grouped.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + +Tree +3 +1 +5 +2 +4 + + + + + + + +Flipped grouped type = "h" +circumference +age + + + + + + + +50 +100 +150 +200 + + + + +500 +1000 +1500 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/flip_type_step.svg b/inst/tinytest/_tinysnapshot/flip_type_step.svg new file mode 100644 index 000000000..5a19e5798 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/flip_type_step.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + +Flipped type = "s" +(1:10)^2 +1:10 + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +2 +4 +6 +8 +10 + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/flip_type_step_rev.svg b/inst/tinytest/_tinysnapshot/flip_type_step_rev.svg new file mode 100644 index 000000000..359633f73 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/flip_type_step_rev.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + +Flipped type = "S" +(1:10)^2 +1:10 + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +2 +4 +6 +8 +10 + + + + + + + + + + + + diff --git a/inst/tinytest/test-flip.R b/inst/tinytest/test-flip.R index 885e38c1c..6326937b9 100644 --- a/inst/tinytest/test-flip.R +++ b/inst/tinytest/test-flip.R @@ -115,6 +115,33 @@ f = function() { expect_snapshot_plot(f, label = "flip_date") +# flipped single-letter line types (#675) + +f = function() { + tinyplot(1:10, (1:10)^2, type = "h", flip = TRUE, main = "Flipped type = \"h\"") +} +expect_snapshot_plot(f, label = "flip_type_h") + +f = function() { + tinyplot(1:10, (1:10)^2, type = "s", flip = TRUE, main = "Flipped type = \"s\"") +} +expect_snapshot_plot(f, label = "flip_type_step") + +f = function() { + tinyplot(1:10, (1:10)^2, type = "S", flip = TRUE, main = "Flipped type = \"S\"") +} +expect_snapshot_plot(f, label = "flip_type_step_rev") + +f = function() { + tinyplot( + circumference ~ age | Tree, data = Orange, + type = "h", flip = TRUE, grid = TRUE, + main = "Flipped grouped type = \"h\"" + ) +} +expect_snapshot_plot(f, label = "flip_type_h_grouped") + + # flipped single-letter line types keep their geometry (#675) # These checks read the SVG output directly, so they run on every OS. From 4f88cd5421cb87b102e3b8fdddfa5a3ca43b1e0d Mon Sep 17 00:00:00 2001 From: Miura Meng Date: Tue, 4 Aug 2026 19:38:12 -0400 Subject: [PATCH 3/3] drop the SVG geometry assertions, now covered by the snapshots --- inst/tinytest/test-flip.R | 49 --------------------------------------- 1 file changed, 49 deletions(-) diff --git a/inst/tinytest/test-flip.R b/inst/tinytest/test-flip.R index 6326937b9..c2e9537dd 100644 --- a/inst/tinytest/test-flip.R +++ b/inst/tinytest/test-flip.R @@ -140,52 +140,3 @@ f = function() { ) } expect_snapshot_plot(f, label = "flip_type_h_grouped") - - -# flipped single-letter line types keep their geometry (#675) -# These checks read the SVG output directly, so they run on every OS. - -svg_of = function(...) { - tf = tempfile(fileext = ".svg") - svglite::svglite(tf) - tinyplot(...) - dev.off() - readLines(tf) -} - -num_attr = function(el, attr) { - as.numeric(sub(sprintf(".*%s='([^']*)'.*", attr), "\\1", el)) -} - -first_step = function(svg) { - pl = grep(" 1)) - -# unflipped "h" still draws vertical drops -svg = svg_of(x, y, type = "h", axes = FALSE) -seg = grep(" 1) - -d = first_step(svg_of(x, y, type = "S", flip = TRUE, axes = FALSE)) -expect_true(abs(d[1]) > 1 && abs(d[2]) < 1e-6) - -d = first_step(svg_of(x, y, type = "s", axes = FALSE)) -expect_true(abs(d[1]) > 1 && abs(d[2]) < 1e-6)