7

I'm trying to fill with different colors the 3 triangles in the following graph.

This is the graphic

data = data.frame(x=c(125), y=c(220)) #this data is just to be able to use gplot to draw figures

ggplot(data, aes(x = x, y = y)) + 
  xlim(0,250) +
  ylim(-250, 0) +
  geom_curve(x = 33, xend = 223, y = -100, yend = -100, curvature = -.65) +
  geom_segment(x=128, xend = 33, y=-208, yend = -100) +
  geom_segment(x=128, xend = 223, y=-208, yend = -100) +
  geom_segment(x=128, xend = 159.67, y=-208, yend = -45) +
  geom_segment(x=128, xend = 96.33, y=-208, yend = -45) +
  coord_fixed()

How can I do this?

tjebo
  • 21,977
  • 7
  • 58
  • 94
sb2002
  • 75
  • 5
  • Have you tried `geom_polygon()`? – Zhiqiang Wang Nov 30 '19 at 06:17
  • But how can I make a figure with a curve with geom_polygon? – sb2002 Nov 30 '19 at 08:14
  • 2
    You would probably need to pre-calculate the curve and include it in your data.frame – Richard Telford Nov 30 '19 at 12:13
  • related, but still unanswered: https://stackoverflow.com/questions/45734457/how-to-access-calculated-values-of-geom-curve-in-ggplot2-r – tjebo Nov 30 '19 at 13:53
  • 1
    When you plot lines using `geom_segment/curve` I'm pretty sure `ggplot` can't natively recognize that you've created a "shape" that could be filled. I agree with @sb2002 that it seems like the easiest way to get these to fill would be to create a data frame with an appropriate number of points to plot a curve at your desired resolution. – Mako212 Dec 04 '19 at 00:46

2 Answers2

7

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)

tjebo
  • 21,977
  • 7
  • 58
  • 94
5

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. enter image description here

Seshadri
  • 669
  • 3
  • 11
  • I agree this makes it much easier, however, there is then no control over the curvature. (Although I doubt this is very important to the OP) - I'd also bind all into one data frame and use fill by group, rather than hard coding – tjebo Dec 05 '19 at 00:32