How to fill geometric figures created by lines and curves?
The short answer: It's a pretty evil hack.
Now let's elaborate: As discussed in especially in this GitHub thread, it is not possible to access the coordinates resulting from geom_curve
(it uses CurveGrob
for plotting and "These values are all calculated at draw time" [@thomasp85]). One effect of its 'calculation at draw time behaviour' can be seen below - it makes a difference if you add coord_plot
or not. This is different with geom_spline
: Adding coord_fixed
does not change the coordinates.
See below in plot one and two: The red curve is created with geom_curve
- it loses touch with the geom_segment lines...
@thomasp85 suggested in the GitHub thread that one could use his package ggforce
instead. Now, to have real control over the curvature, one needs to use geom_bspline and play around with the curvature.
Once the curvature is found, one can use the coordinates in the ggplot_build object. We can calculate the polygons based on those coordinates (this is also not quite trivial, because one needs to create cuts and add points for the correct 'edges'). See below.
library(tidyverse)
library(ggforce)
mydata = data.frame(x = 128, xend = c(33, 223, 159.67, 96.33), y = -208, yend = c(-100,-100,-45,-45))
#for spline control points.
my_spline <- data.frame(x = c(33, 128, 223), y = c(-100, 24,-100))
Next I demonstrate the difference between 'calculation at draw time (red curve) and 'direct calculation':
With coord_fixed
Both red and black curve touch the segments
ggplot(mydata) +
geom_curve(aes(x = 33, xend = 223, y = -100, yend = -100), curvature = -.65, color = 'red') +
geom_segment(aes(x = x, xend = xend, y = y, yend = yend)) +
geom_bspline(data = my_spline, aes(x, y )) +
coord_fixed()
Without coord_fixed
The red curve does not touch the segments, but the black curve still does
ggplot(mydata) +
geom_curve(aes(x = 33, xend = 223, y = -100, yend = -100), curvature = -.65, color = 'red') +
geom_segment(aes(x = x, xend = xend, y = y, yend = yend)) +
geom_bspline(data = my_spline, aes(x, y ))
# Final hack
# Get x/y coordinates from ggplot_build
p <- ggplot(mydata) +
geom_bspline(data = my_spline, aes(x, y ))
pb <- ggplot_build(p)$data[[1]]
#create groups for fill
data_polygon <- data.frame(x = pb[['x']], y = pb[['y']]) %>%
mutate(cut_poly = cut(x, c(-Inf, 96.33, 159.67, Inf), labels = letters[1:3]))
#add corner points - repeat extremes from b, otherwise there will be a gap
data_add <- data_polygon %>%
filter(cut_poly == 'b') %>%
slice(which.min(x), which.max(x)) %>%
mutate(cut_poly = letters[c(1,3)]) %>%
bind_rows(data.frame(x = 128, y = -208, cut_poly = letters[1:3], stringsAsFactors = FALSE)) %>%
arrange(x) #important to arrange, otherwise you get irregular polygons
data_plot <- rbind(data_polygon,data_add)
ggplot(data_plot) +
geom_polygon(aes(x, y, fill = cut_poly), color = 'black')
Created on 2019-12-05 by the reprex package (v0.3.0)
You can access the curve data for the geoms generated in ggforce package, which makes the job of creating polygons from curves much easier.
You can then use geom_polygon to draw individual polygons and fill them with different colors
library(ggforce)
p1 <- ggplot() + geom_arc(aes(x0 = 125, y0 = -200, r = 100, start = -pi/3, end = -pi/9))
p2 <- ggplot() + geom_arc(aes(x0 = 125, y0 = -200, r = 100, start = -pi/9, end = pi/9))
p3 <- ggplot() + geom_arc(aes(x0 = 125, y0 = -200, r = 100, start = pi/9, end = pi/3))
df_poly1 <- rbind(c(125,-200),data.frame(x = ggplot_build(p1)$data[[1]]$x,y = ggplot_build(p1)$data[[1]]$y),c(125,-200))
df_poly2 <- rbind(c(125,-200),data.frame(x = ggplot_build(p2)$data[[1]]$x,y = ggplot_build(p2)$data[[1]]$y),c(125,-200))
df_poly3 <- rbind(c(125,-200),data.frame(x = ggplot_build(p3)$data[[1]]$x,y = ggplot_build(p3)$data[[1]]$y),c(125,-200))
ggplot() +
geom_polygon(data = df_poly1, aes(x,y), fill = 'red') +
geom_polygon(data = df_poly2, aes(x,y), fill = 'blue') +
geom_polygon(data = df_poly3, aes(x,y), fill = 'green')
This will produce an image like this.