tidyverse / ggplot2

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

Polygons with holes #2502

Closed joojala closed 6 years ago

joojala commented 6 years ago

Is stat_density2d behaving like it should? It seems to me that it is not making holes into existing polygons when the same value region is inside another. The thing is that these areas show up as denser colors when you have alpha enabled. Yet the value of each of the shapes is the same and with fully opaque they do fill completely.

rplot02

Image 1: Image with holes is probably how it should work, image on the right (without holes) is how it works.

Is this intentional or is this a limitation of the engine? Because seems to me that the engine itself can make compound paths and thus should be easy to use even odd rule to make them holes. If not one could just connect the shapes with a sliver, although i would avoid that.

The way i have fixed these images is just make every color region a compound path and then set the rule to even odd. But seems quite misleading and should be easily addressable.

batpigandme commented 6 years ago

Could you please include self-contained reprex (short for minimal reproducible example) with code? It will help us help you if we can be sure we're all working with/looking at the same stuff.

If you've never heard of a reprex before, you might want to start by reading the tidyverse.org help page.

joojala commented 6 years ago

Fair enough.

suppose we have a point cloud that is distributed in ring shape with a hole in the middle. We could do it like this:

library(ggplot2)
len <- 4000
df <-data.frame(x=runif(len, -2.5,2.5) ,y=runif(len, -2.5,2.5) )
df <- df[ sin(sqrt(df[,1]^2+df[,2]^2))>runif(len,min= 0.6, max= 1),]

ggplot(df, aes( x=x,y=y))+geom_point()+ xlim(-3, 3) + ylim(-3, 3)  + coord_fixed()

This would get you something like this (being exactly same is hardly important). Note, that the data set clearly has a hole in the middle where there is no density.

pcloud

Now if we now make a stat density plot out of this data

ggplot()+
  stat_density2d(
    data=df, aes( x=x,y=y,fill=..level..), alpha=0.2, geom="polygon"
  ) + xlim(-3, 3) + ylim(-3, 3)  + coord_fixed()  

we get:

r_bug

Notice no hole in the data. In fact there is no holed region in the polygons produced. However, the polygons are laid out in the file as if they are meant to have holes. They are very easy to identify as such: If i now fix this as seems reasonable, same color slices being same compound path, i get:

r_bug_fixed

Which indeed looks like what i expect it to look like. So my question is still:

Now, I have made a fix for myself but might be worth patching in the real code as well.
Too much of my time is sunk on this issue, no more time to share this will have to be enough for you.

hadley commented 6 years ago

See the broader problem in #2534 — the contouring algorithm we used was not designed to draw polygons.

joojala commented 6 years ago

Its not the same problem. So what your saying is that yiu arent interested in a fix?

hadley commented 6 years ago

Why do you think it’s not the same problem?

joojala commented 6 years ago

Because it isn't, holes in polygons are a separate problem from bounding the polygons. The bounding problem is just the fact that you leave the shape open. Holes problem is that you do not tell the rendering engine that the separate closed shapes are in fact one shape.

Both of these are very easy to fix the open shape just need to get edge vectors added, since you know on which side the value is higher you will know which way to turn. Determining side can be easily done by sampling value near the line on either side. So the algorithm for finding the edges is O(N) complexity, where N is number of shapes.

inside

Now the inner object algorithms do not need outer edges. Just arrangement into compound paths, ie grouping together so that the renderer does knows that all lines are the same. Depending on how you want to implement then the resulting algorithm is on average O(log(N)) since the data was already in order. Where N is number of closed shapes.

holes

Admittedly the holes problem needs the corners sewed to work for corner bits. But the fix is entirely separate. But if you do both fixes then you dont have to bother with whether the other outer shapes should be part of the shape (since the holes will take care of this.).

hadley commented 6 years ago

Sounds like two instances of the same deeper problem to me. If you have a fix, I’d be happy to review a PR.

joojala commented 6 years ago

Well every vector engine does need to tackle this problem so this one is the fundamental, you can look it up in any book on 3d or 2d graphics.

The other one is just your algorithm being a bit weak treating data outside the window as continuation, it could treat them as a padded 0 and you wouldn't end up here. Fixing the borders wont fix this one.

lock[bot] commented 6 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/