Set opacity of grey values of overlapping areas relative to n

倖福魔咒の 提交于 2020-01-22 19:39:02

问题


I struggle to understand (and control) the blending of alphas - unfortunately, alpha values don't simply "add up" (0.5 + 0.5 is not 1). But how could I achieve that?

The aim is to define the (absolute) grey value of overlapping areas relative to the total number of observations. See example below.

I tried to set scale_alpha(range = c(0,1)) to no avail, maybe I did not use it correctly.

library(ggplot2)
library(ggforce)

grid_df = data.frame(x = c(1:2, 2.5), y = rep(1,3), r = 1)

ggplot()+
geom_circle(data = grid_df, mapping = aes(x0 = x,  y0 = y, r = r), alpha = 0.33, fill = 'black') + 
  coord_fixed() 


回答1:


Adding to @MKBakker's answer, one could use a function to predict the resulting alpha from any number of layers and alpha values:

alpha_out <- function(alpha, num = 1) {
  result = alpha
  if(num == 1)  return(result)
  for(i in 2:num) { result = result + alpha * (1-result) }
  return (result)
}

alpha_out(0.33, 1)
#[1] 0.33
alpha_out(0.33, 2)
#[1] 0.5511
alpha_out(0.33, 3)
#[1] 0.699237

This makes it easier to see that alpha asymptotically approaches 1 with more layers.

alpha_out(0.33, 40)
#[1] 0.9999999

If one presumes that 0.99 is "close enough," you need to use 0.8 to get there with three layers

alpha_out(0.8, 3)
#[1] 0.992

EDIT: Added chart of results

We can see what results we'd get from a range of alphas and layers:

library(tidyverse)
alpha_table <- 
  tibble(
    alpha = rep(0.01*1:99, 10),
    layers = rep(1:10, each = 99)
  )

alpha_table <- alpha_table %>%
  rowwise() %>%
  mutate(result = alpha_out(alpha, layers))

ggplot(alpha_table, aes(alpha, result, color = as_factor(layers),
                    group = layers)) +
geom_line()

And we can also see how much alpha we need to pass a threshold of combined opacity, given each number of layers. For instance, here's how much alpha you need to reach 0.99 total opacity for a given number of layers. For 5 layers, you need alpha = 0.61, for instance.

alpha_table %>%
  group_by(layers) %>%
  filter(result >= 0.99) %>%
  slice(1)
## A tibble: 10 x 3
## Groups:   layers [10]
#   alpha layers result
#   <dbl>  <int>  <dbl>
# 1  0.99      1  0.99 
# 2  0.9       2  0.99 
# 3  0.79      3  0.991
# 4  0.69      4  0.991
# 5  0.61      5  0.991
# 6  0.54      6  0.991
# 7  0.49      7  0.991
# 8  0.44      8  0.990
# 9  0.41      9  0.991
#10  0.37     10  0.990

All this to say that I don't think there is a simple implementation to get what you're looking for. If you want 100% dark in the overlapped area, you might try these approaches:

  • image manipulation after the fact (perhaps doable using imagemagick) to apply a brightness curve to make the dark areas 100% black and make the others scale to the darkness levels you expect.

  • convert the graph to an sf object and analyze the shapes to somehow count how many shapes are overlapping at any given point. You could then manually map those to the darkness levels you want.




回答2:


First off, +1 to @JonSpring—this is just an expansion of the idea at the end of their answer. If you make an sf object, you can easily get the intersections of polygons. What you end up plotting isn't the circles themselves, but the polygons that come from splitting apart the intersecting pieces.

Starting from your grid, make a point for each row, convert that to a sf data frame, then take the buffer of the points at the radius given in the column r. This turns each point into a circle centered at the point's coordinates, and is flexible for different radii. Between the 3 circles are 6 intersecting polygons, as shown in the result.

library(dplyr)
library(sf)
library(ggplot2)
library(ggforce)

grid_df <- data.frame(x = c(1:2, 2.5), y = rep(1,3), r = 1)

grid_sf <- grid_df %>%
  mutate(geometry = purrr::map2(x, y, ~st_point(c(.x, .y)))) %>%
  st_as_sf() %>%
  st_buffer(dist = .$r, nQuadSegs = 60) %>%
  st_intersection()

grid_sf
#> Simple feature collection with 6 features and 5 fields
#> geometry type:  GEOMETRY
#> dimension:      XY
#> bbox:           xmin: 0 ymin: 0 xmax: 3.5 ymax: 2
#> epsg (SRID):    NA
#> proj4string:    NA
#>       x y r n.overlaps origins                       geometry
#> 1   1.0 1 1          1       1 POLYGON ((1.5 0.1339746, 1....
#> 1.1 1.0 1 1          2    1, 2 POLYGON ((1.75 0.3386862, 1...
#> 2   2.0 1 1          1       2 MULTIPOLYGON (((2.258819 0....
#> 1.2 1.0 1 1          3 1, 2, 3 POLYGON ((2 1, 1.999657 0.9...
#> 2.1 2.0 1 1          2    2, 3 POLYGON ((3 1, 2.999657 0.9...
#> 3   2.5 1 1          1       3 MULTIPOLYGON (((3.5 1, 3.49...

Use that n.overlaps column that comes from st_intersection to assign alpha. By default, alpha will scale from 0 to 1, but I figure you don't actually want a 0 alpha for the outer, non-overlapped parts of circles, so I scale it to get a minimum alpha.

alpha_range <- range(grid_sf$n.overlaps) / max(grid_sf$n.overlaps)

grid_sf  %>%
  ggplot() +
  geom_sf(aes(alpha = n.overlaps), fill = "black") +
  scale_alpha(range = alpha_range)

Just to expand a bit further and make the different polygons a bit more clear, take a look with a discrete fill scale instead of alpha:

grid_sf  %>%
  ggplot() +
  geom_sf(aes(fill = as.factor(n.overlaps))) +
  scale_fill_brewer(palette = "YlGnBu")




回答3:


Alpha can be added using the following approach (https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending)

The alpha of two merged shapes is calculated as follows:
A(out) = A(src) + A(dst) * 1-A(src)

Hence, for A(src) = A(dst) = 0.33, we get:

x = 0.33  
y = x + x*(1-x)  
y

[1] 0.5511

And if we have three shapes, with A = 0.33, we induce:

y = x + x*(1-x) + x*(1-(x + x*(1-x)))
y

[1] 0.699237

I could go on about which values will result in 1 when adding 2 or 3 shapes together, but the most useful comment is that alphas are not combined in an additive way.



来源:https://stackoverflow.com/questions/57394946/set-opacity-of-grey-values-of-overlapping-areas-relative-to-n

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!