R: how do I draw a line with multiple arrows in it?

前端 未结 5 2044
梦如初夏
梦如初夏 2020-12-04 02:25

I was wondering if anyone could help me plot lines in R with multiple arrows in them, like this:

--->--->--->--->

Thanks in advance!

5条回答
  •  我在风中等你
    2020-12-04 02:51

    I thought I would add a base R solution here that calculates the arrow heads as segments. There are versions to create a new plot or add arrows to an existing plot. Longer segments are split into shorter ones:

    add_arrows <- function(x, y, ...)
    {
      # Ensure equal lengthed vectors
      min_len <- min(length(x), length(y))
      x  <- x[seq(min_len)]
      y  <- y[seq(min_len)]
    
      # Calculate y:x ratio
      yx_ratio <- 3*(max(y) - min(y)) / (max(x) - min(x))
    
      # Create start and end points
      x1 <- x[-length(x)]
      y1 <- y[-length(y)]
      x2 <- x[-1]
      y2 <- y[-1]
    
      # Length and angle of line segments
      theta <- atan((y2 - y1)/(x2 - x1))
      seg_lengths <- sqrt((x2 - x1)^2 + (y2 - y1)^2)
    
      # Break long segments into shorter segments
      long <- which(seg_lengths > (2 * min(seg_lengths)))
      if(length(long) > 0)
      {
        n_segs <- floor(seg_lengths[long] / (1.5 * min(seg_lengths)))
    
        for(i in seq_along(long))
        {
          xs <- x1[long[i]] + (0:n_segs[i]) * (x2[long[i]] - x1[long[i]]) / n_segs[i]
          ys <- y1[long[i]] + (0:n_segs[i]) * (y2[long[i]] - y1[long[i]]) / n_segs[i]
          x1 <- c(x1, xs[-length(xs)])
          x2 <- c(x2, xs[-1])
          y1 <- c(y1, ys[-length(ys)])
          y2 <- c(y2, ys[-1])
          theta <- c(theta, rep(theta[long[i]], length(xs) - 1))
        }
    
        x1 <- x1[-long]; x2 <- x2[-long]; y1 <- y1[-long]; y2 <- y2[-long];
        theta <- theta[-long]
      }
      # Arrow head length and width
      len <- sqrt((max(y) - min(y))^2 + (max(x) - min(x))^2)/ 20
      wid <- len * 0.1
    
    
      # Calculate arrow heads
      zx  <- x2 - len * cos(theta) * sign(x2 - x1)
      zy  <- y2 - len * sin(theta) * sign(x2 - x1)
      ax1 <- zx + wid * sin(theta) * sign(x2 - x1)
      ax2 <- zx - wid * sin(theta) * sign(x2 - x1)
      ay1 <- zy - wid * cos(theta) * sign(x2 - x1) * yx_ratio
      ay2 <- zy + wid * cos(theta) * sign(x2 - x1) * yx_ratio
    
      df <- data.frame(x1  = x1 + 0.1 * (x2 - x1),
                       y1  = y1 + 0.1 * (y2 - y1),
                       x2  = x2 - 0.5 * len * cos(theta) * sign(x2 - x1),
                       y2  = y2 - 0.5 * len * sin(theta) * sign(x2 - x1),
                       ax1 = ax1, ay1 = ay1, ax2 = ax2, ay2 = ay2)
    
      # Draw segments
      segments(df$x1, df$y1, df$x2,  df$y2,  col = "red", lwd = 2)
      segments(df$x2, df$y2, df$ax1, df$ay1, col = "red", lwd = 2)
      segments(df$x2, df$y2, df$ax2, df$ay2, col = "red", lwd = 2)
    }
    

    This allows you to plot arrows as the basis of your plot:

    
    plot_arrows <- function(x, y, ...)
    {
      plot(x, y, col = "white", ...)
      add_arrows(x, y, ...)
    }
    

    So now you can do:

    set.seed(69)
    
    x <- 1:10
    y <- sample(10)
    plot(x, y)
    add_arrows(x, y)
    

    Created on 2020-02-29 by the reprex package (v0.3.0)

提交回复
热议问题