Skip to contents

Bézier Curves

Setup

Base Plot

To avoid repetitive code, we set defaults and make a base plot:

#| label: baseplot

my_font <- "Roboto Condensed"
my_font_size <- 20
my_point_size <- 2
my_arrowhead <- arrowheadr::arrow_head_deltoid(2.1)

# my_colors <- viridis::viridis(2, begin = .25, end = .5)
my_colors <- c("#3B528B", "#21908C")

theme_set(
  theme_minimal(
    base_size = my_font_size,
    base_family = my_font) +
    theme(axis.title.y = element_text(angle = 0, vjust = 0.5)))

bp <- ggdiagram(
  font_family = my_font,
  font_size = my_font_size,
  point_size = my_point_size,
  linewidth = .5,
  theme_function = theme_minimal,
  axis.title.x =  element_text(face = "italic"),
  axis.title.y = element_text(
    face = "italic",
    angle = 0,
    hjust = .5,
    vjust = .5)) +
  scale_x_continuous(labels = signs_centered,
                     limits = c(-4, 4)) +
  scale_y_continuous(labels = signs::signs,
                     limits = c(-4, 4))

Bézier curves

The ob_bezier function creates an object that specifies the control points for a bézier curve. A bézier curve is an extremely useful way of making elegantly curved lines between points.

bp + 
  {control_points <- ob_point(
    x = c(-4,4,4), 
    y = c(4,-4, 0))} + 
  ob_path(control_points, linetype = "dashed", linewidth = .25) +
  ob_bezier(control_points)
Figure 1: Bézier curve with contol points

I like to make a list of control points setting the start and end points first. Then I find internal control points by offsetting from the end points—adding or subtracting a point at a specified x and y distance (or angle using the polar function).

The c function creates a list of all the points, and the bind function binds the list into a single point object containing all the points.

# start and end of control points
p_start <- ob_point(-4,-2)
p_end <- ob_point(4, 2)
# Offset ob_point from the endpoints
p_offset <- ob_point(5,6)

# Make list of points and bind them into a single ob_point
p <- c(p_start, 
       p_start + p_offset, 
       p_end - p_offset, 
       p_end) |> 
  bind() 

bp + 
  ob_path(p, 
       linetype = "dashed", 
       color = "gray",
       linewidth = .25) +
  p + 
  ob_bezier(p, 
          label = ob_label("Labels can follow the path of the bézier curve.")) 
Figure 2: Bézier curve with contol points and label

Multiple bézier paths

If multiple point objects are supplied as a list (or concatenated with the c function), a bézier curve will be created for each point object in the list.

control_point_list <- c(
      ob_point(
        x = c(-4, -3, 4), 
        y = c(.1,  4, .1), 
        color = my_colors[1]),
      ob_point(
        x = c(-4, 3, 4), 
        y = c(-.1, -4, -.1), 
        color = my_colors[2] )
      )
bp + 
  ob_bezier(control_point_list) +
  ob_path(control_point_list, linetype = "dashed", linewidth = .25) +
  bind(control_point_list)
Figure 3: Multiple bézier paths