Figure 15-10: Arrows

Portfolio Categories: All Graphics and SGR Book Graphics.

Default and Custom Arrows Added to a Plot in R Graphics

Default and Custom Arrows Added to a Plot


# 15.3c Arrows =========================================================== 15.3c 

# A solid arrowhead function
# 
# This function draws an arrow from (x0,y0) to (x1,y1) with a solid
# arrowhead. 
#
# The default arrowhead length is .25 inches. 
# The default angle of the arrowhead edges is 30 degrees.
# The other parameters are standard for line and polygon formatting.
#
# The basic logic here is to use matrix rotation to translate the arrowhead
# coordinates.
#
# Note that R's trigonometric functions work with radians rather than degrees
#
myArrow = function(x0, y0, x1, y1,     # Set up arrow function ----------------+
  L = .25,                             # Default arrowhead length              |
  angle = 30,                          # Default angle of arrowhead            |
  code = 2,                            # Default arrowhead at x1,y1            |
  col = par("fg"),                     # Default color                         |
  ljoin = par("ljoin"),                # Default line joint style (0)          |
  lty = par("lty"),                    # Default line type                     |
  lwd = par("lwd"),                    # Default line width                    |
  xpd = FALSE){                        # Default stay within plot area         |
                                       # Start function code                   |
    if(code == 1){                     # Reverse arrow direction               |
      tmp = x1; x1 = x0; x0 = tmp      # Switch x values                       |
    }                                  #                                       |
    if(code == 1){                     #                                       |
      tmp = y1; y1 = y0; y0 = tmp      # Switch y values                       |
    }                                  #                                       |
#                                                                              |
# We need to control for the aspect ratio or for different x,y scales          |
#   in setting up the arrow heads. We'll do that by translating the            |
#   usr parameter setting from the original x,y units to units based           |
#   on the plot dimensions measured in inches.  This will allow us to          |
#   adjust the angles to account for different x and y scales. Note,           |
#   however, that rescaling the plot after it is drawn will distort            |
#   the arrowheads.                                                            |
#                                                                              |
    X0 = (x0 - par()$usr[1])/(par()$usr[2] - par()$usr[1]) * par()$fin[1]  #   |
    Y0 = (y0 - par()$usr[3])/(par()$usr[4] - par()$usr[3]) * par()$fin[2]  #   |
    X1 = (x1 - par()$usr[1])/(par()$usr[2] - par()$usr[1]) * par()$fin[1]  #   |
    Y1 = (y1 - par()$usr[3])/(par()$usr[4] - par()$usr[3]) * par()$fin[2]  #   |
#                                                                              |    
    oldusr = par("usr")                # Save original usr settings            |
    par(usr = c(0, par("fin")[1],      # Set up new usr settings based         |
              0, par("fin")[2]))       #  on plot dimensions in inches         |
#                                                                              |
    t = angle * pi/180                 # Convert angle degrees to radians      |
    slope = (Y1 - Y0)/(X1 - X0)        # Calculate slope of line               |
    S = atan(slope)                    # Slope angle in radians                |
                                       #                                       |
    M = ifelse(X1 < X0, -1, 1)         # Set a marker for X1 < X0              |
                                       # Set length of vector XA               |
    XA = sqrt((X1 - X0)^2 + (Y1 - Y0)^2)                                   #   |
#                                                                              |
# Get arrowhead vertices from rotated vectors                                  |
    XC = X0 + M * ((XA - L) * cos(S) + L * tan(t) * sin(S))                #   |
    YC = Y0 + M * ((XA - L) * sin(S) - L * tan(t) * cos(S))                #   |
    XB = X0 + M * ((XA - L) * cos(S) - L * tan(t) * sin(S))                #   |
    YB = Y0 + M * ((XA - L) * sin(S) + L * tan(t) * cos(S))                #   |
#                     |                                                        |
# Draw arrow line stopping at beginning of the arrowhead                       |
    lines(x = c(X0, X1 - M * L * cos(S)),                                  #   |
      y = c(Y0, Y1 - M * L * sin(S)),                                      #   |
      lty = lty, lwd = lwd,            # Apply line format options             |
      col = col, xpd = xpd,            #                                       |
      ljoin = ljoin)                   #                                       |
    polygon(x = c(X1, XB, XC),         # Draw arrow head                       |
      y = c(Y1, YB, YC),               #  at vertices                          |
      col = col,                       # Apply format options                  |
      border = col,                    #                                       |
      xpd = xpd)                       #                                       |
    par(usr = oldusr)                  # Reset to original usr settings        |
}                                      # End of myArrow function --------------+

# A plot of arrows 
png(filename = "illustrations/fig-15-10-arrows-BW.png",
  units = "in",                        # Set measurements in inches
  res = 1200,                          # Set resolution at 1200dpi
  width = 6,                           # Width at 6 inches
  height = 4)                          # Height at 4 inches

par(mai = c(.5, .5, .25, .25))         # Set margin widths in lines

plot(x = 0, y = 0, type = "n",         # Set up a blank plot
  xlim = c(0, 5), ylim = c(0, 5),      # Define x and y range
  xlab = "", ylab = "")                # Turn off axis labels

# Diverse arrows
arrows(x0 = 3, y0 = 4.25, x1 = 5, y1 = .25, 
  length = 1, angle = 15, lwd = 2)
arrows(x0 = 1.25, y0 = 1, x1 = 3.25, y1 = .75, 
  col = "darkgray", code = 3)
myArrow(x0 = 0, y0 = 1.5, x1 = 3.5, y1 = 4.5, 
  col = "black")
myArrow(x0 = 1, y0 = .5, x1 = 4.5, y1 = 0, 
  col = gray(.6)) 
myArrow(x0 = 4, y0 = 3.5, x1 = 1, y1 = 1.5, L=.5, 
  col = gray(.4), lwd = 2)
myArrow(x0 = 2.5, y0 = 1.25, x1 = 1, y1 = 4.2, 
  col = gray(.5), lwd = 4)
myArrow(x0 = 6, y0 = 5, x1 = 4, y1 = 3, 
  col = "gray", lwd = 8, angle = 20, L=.75, xpd = T)
myArrow(x0 = 0, y0 = 5, x1 = 4, y1 = 5, 
  col = "gray", lwd = 3, angle = 40, code = 1)  
myArrow(x0 = .5, y0 = .5, x1 = 4, y1 = 1.5, 
  col = "darkgray", lwd = 1, angle = 20, L=.1)

dev.off()                              # Output png file