tidyverse / ggplot2

An implementation of the Grammar of Graphics in R
https://ggplot2.tidyverse.org
Other
6.5k stars 2.02k forks source link

geom_tile borders missing notch at top left corners #3037

Closed DanielReedOcean closed 5 years ago

DanielReedOcean commented 5 years ago

This is an issue that has been discussed over at Stackoverflow. The original problem can be found here. A minimal reprex is included below.

I have the following data frame:

# Dummy data frame
df <- expand.grid(x = 1:3, y = 1:3)

I would like to plot it as a geom_tile using ggplot2 like so:

# Tile plot
ggplot(df) + 
  geom_tile(aes(x = x, y = y), 
            fill = NA, colour = "red", size = 3, width = 0.7, height = 0.7)

which gives,

rplot

Notice that in the top left corner of each tile the border has a notch missing and that the corner doesn't dovetail nicely like the other corners. The outcome I expected was a square around each tile, perhaps something like this:

rplot01

A solution offered here by Z. Lin in that Stackoverflow thread suggests that linejoin and lineend for geom_tile are switched to mitre and square, respectively. Or, that they are at least offered as parameters in the geom so that the user can choose for themselves. Hopefully, this demonstrates my issue adequately, but please let me know if I can clarify any aspect.

yutannihilation commented 5 years ago

Or, that they are at least offered as parameters in the geom so that the user can choose for themselves.

Thanks, agreed.

ptoche commented 5 years ago

Just a comment in passing. Ignore if off topic.

from https://github.com/tidyverse/ggplot2/blob/master/R/geom-tile.r

the first few lines:

' geom_rect and geom_tile do the same thing, but are

' parameterised differently: geom_rect uses the locations of the four

' corners (xmin, xmax, ymin and ymax), while

' geom_tile uses the center of the tile and its size (x,

' y, width, height).

which is precisely the problem with geom_title.

Can you achieve your goal with geom_rect instead?

yutannihilation commented 5 years ago

I don't think it matters.

library(ggplot2)

df <- expand.grid(x = 1:3, y = 1:3)
width <- 0.7
height <- 0.7

df$xmin <- df$x - width / 2
df$xmax <- df$x + width / 2
df$ymin <- df$y - height / 2
df$ymax <- df$y + height / 2

ggplot(df) + 
  geom_rect(aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax), 
            fill = NA, colour = "red", size = 3)

Created on 2018-12-16 by the reprex package (v0.2.1)

thomasp85 commented 5 years ago

The problem is that the rectangles gets converted to polygons in closed form (first point gets repeated in the end), whereas polygonGrob expect an open representation... should be fairly easy to fix here:

https://github.com/tidyverse/ggplot2/blob/9eae13b3d17bde26cf9df649887b4a6bb2ac92ce/R/geom-rect.r#L75-L80

Note that this line also need to be adapted https://github.com/tidyverse/ggplot2/blob/9eae13b3d17bde26cf9df649887b4a6bb2ac92ce/R/geom-rect.r#L42

thomasp85 commented 5 years ago

On that note I don't think it makes sense to add a lineend parameter to the geoms as they should be closed, while adding linejoin is sensible...

@yutannihilation can you update #3050 to create the correct polygon spec and remove lineend? I'm pretty sure it will break some visual tests, but it'll be more correct

yutannihilation commented 5 years ago

whereas polygonGrob expect an open representation...

Opps, I didn't notice... Thanks, it makes sense. I will update so.

thomasp85 commented 5 years ago

It's a weird bug... probably a leftover from when the other representation made sense... recall anything @hadley ?

karawoo commented 5 years ago

FYI it also seems to be inconsistent, I can't reproduce with the reprex provided

df <- expand.grid(x = 1:3, y = 1:3)
library("ggplot2")
ggplot(df) +
  geom_tile(aes(x = x, y = y),
            fill = NA, colour = "red", size = 3, width = 0.7, height = 0.7)

Created on 2019-01-15 by the reprex package (v0.2.1)

ptoche commented 5 years ago

I (too) get this:

plot

Specs summary: ggplot2_3.1.0.9000 grid_3.5.2 gtable_0.2.0 R version 3.5.2 (2018-12-20) -- "Eggshell Igloo" Platform: x86_64-apple-darwin15.6.0 (64-bit)

DanielReedOcean commented 5 years ago

In response to the original SO question, someone else commented that they couldn't reproduce the issue on macOS either. @karawoo are you running on macOS, too?

karawoo commented 5 years ago

Ah yes, I am

yutannihilation commented 5 years ago

Curious. Visual tests pass even on my Windows, so I guess this is a problem on Windows' graphic device?

yutannihilation commented 5 years ago

It seems we still need lineend even after fixing the polygon representation. Here's the result after https://github.com/tidyverse/ggplot2/pull/3050/commits/43eafa85634156fe35901e5c732ee3900c5c0449:

library("ggplot2")

ggplot(data.frame(x = 1)) +
  geom_tile(aes(x, x),
            fill = NA, colour = "red", size = 10,
            width = 1, height = 1) +
  coord_equal()

Created on 2019-01-25 by the reprex package (v0.2.1)

thomasp85 commented 5 years ago

o_O - that does not make any sense at all... maybe this is a windows issue (assuming that you're on windows...

what do you get if you run:

grid::grid.rect(width = unit(.5, 'npc'), height = unit(0.5, 'npc'), gp = grid::gpar(lwd = 20, linejoin = 'mitre'))
yutannihilation commented 5 years ago

Oh... Here's the result.

library(grid)
grid.rect(width = unit(.5, 'npc'), height = unit(0.5, 'npc'), gp = gpar(lwd = 20, linejoin = 'mitre'))

Created on 2019-01-25 by the reprex package (v0.2.1)

thomasp85 commented 5 years ago

ok, that is not what I get (i.e. I get even corners all around)

This is a grid bug (or even a windows device bug) - @pmur002 do you have any idea where this comes from..?

yutannihilation commented 5 years ago

Hmm, thanks. Sorry that I haven't noticed this is the problem on Windows earlier...

pmur002 commented 5 years ago

I see it (only on Windows). This appears to be a bug (or at least a poor design choice) in graphapp (that underlies the Windows device). graphapp's rectangle drawing draws a polyline, so what we are seeing is a line being drawn from the top-left corner around the perimeter of the rectangle. So the rounded top-left corner is round because of the rounded line start and end. You can work around the problem by specifying "square" lineend for the rectangle (which you should not have to do) - is that a possibility ? Code below shows what I mean for the simple grid.rect() demonstration

library(grid)
grid.rect(width = unit(.5, 'npc'), height = unit(0.5, 'npc'), gp = gpar(lwd = 20, linejoin = 'mitre', lineend = 'square'))
yutannihilation commented 5 years ago

Thanks!

You can work around the problem by specifying "square" lineend for the rectangle (which you should not have to do) - is that a possibility?

I think it's possible, since we already specify lineend for rect (from the very beginning), we can just change this to "square".

https://github.com/tidyverse/ggplot2/blob/256b26ab70c87d760d92ceadd67b549dae74a06a/R/geom-rect.r#L61

yutannihilation commented 5 years ago

Ah, though we can use square lineend to imitate mitre linejoin,

So, we have several choices:

  1. set linejoin = "mitre"and lineend = "square", and don't expose them as parameters
  2. expose linejoin as a parameter and set lineend = "square" fixedly
  3. expose linejoin as a parameter and set the corresponding lineend
  4. expose both lineend and linejoin as parameters
yutannihilation commented 5 years ago

The problem is that the rectangles gets converted to polygons in closed form (first point gets repeated in the end), whereas polygonGrob expect an open representation...

@thomasp85 I think I found the reason why the polygons are in closed form...

2019-01-29 13 44 02
lock[bot] commented 4 years ago

This old issue has been automatically locked. If you believe you have found a related problem, please file a new issue (with reprex) and link to this issue. https://reprex.tidyverse.org/