Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fill geometric figures created by lines and curves?

Tags:

r

ggplot2

fill

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?

like image 255
sb2002 Avatar asked Nov 30 '19 05:11

sb2002


Video Answer


2 Answers

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

like image 22
Seshadri Avatar answered Sep 22 '22 08:09

Seshadri


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)

like image 174
tjebo Avatar answered Sep 21 '22 08:09

tjebo