Setup
Packages
Base Plot
To avoid repetitive code, we make a base plot:
my_font <- "Roboto Condensed"
my_font_size <- 20
my_point_size <- 2
my_arrowhead <- arrowheadr::arrow_head_deltoid(d = 2.3, n = 100)
# 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))
Points
Points have x and y coordinates.
Polar Coordinates
A point’s x and y coordinates can be specified in polar coordinates
-
@r
: The distance from the origin to the point (i.e., the vector’s magnitude) -
@theta
: The angle (in radians) from the line on the x-axis to the line containing the vector.
p2
#> <ob_point>
#> @ x: num 3
#> @ y: num 4
#> Other props: alpha, color, fill, shape, size, stroke, auto_label,
#> bounding_box, length, r, theta, style, tibble, xy,
#> geom, label, place, aesthetics
p2@r
#> [1] 5
p2@theta
#> 0.3π
Code
bp +
coord_equal(xlim = c(-p2@r, p2@r),
ylim = c(-p2@r, p2@r)) +
scale_x_continuous(breaks = -10:10,
minor_breaks = NULL,
labels = signs_centered) +
scale_y_continuous(breaks = -10:10,
minor_breaks = NULL,
labels = signs::signs) +
ob_circle(radius = p2@r) +
p2@label(plot_point = TRUE,
size = 16,
polar_just = ob_polar(p2@theta, r = 1.5)) +
ob_segment(p1 = ob_point(),
p2 = p2,
label = ob_label(paste0("*r* = ", round(p2@r, 2)),
size = 16,
vjust = 0)) +
ob_arc(
end = p2@theta,
color = "royalblue3",
label = ob_label(
paste0("θ = ",
degree(p2@theta)),
size = 16,
color = "royalblue3"))
A point can be created with polar coordinates
If the angle is numeric instead of an angle, it is assumed to be in radians.
ob_polar(r = 1, theta = pi)@theta
#> π
Convert to tibble
This will extract any styles that have been set.
get_tibble(ob_point(1,2,
color = "red",
shape = 16))
#> # A tibble: 1 × 4
#> x y color shape
#> <dbl> <dbl> <chr> <dbl>
#> 1 1 2 red 16
As a convenience, the tibble associated with the point object can be accessed with the @tibble
property.
ob_point(1:5,2,
color = "blue",
shape = 1:5)@tibble
#> # A tibble: 5 × 4
#> x y color shape
#> <int> <dbl> <chr> <int>
#> 1 1 2 blue 1
#> 2 2 2 blue 2
#> 3 3 2 blue 3
#> 4 4 2 blue 4
#> 5 5 2 blue 5
Methods
Arithmetic
Points can be added and subtracted:
p1 <- ob_point(2, 3)
p2 <- ob_point(2, 1)
p3 <- p1 + p2
p3
#> <ob_point>
#> @ x: num 4
#> @ y: num 4
#> Other props: alpha, color, fill, shape, size, stroke, auto_label,
#> bounding_box, length, r, theta, style, tibble, xy,
#> geom, label, place, aesthetics
p3 - p2
#> <ob_point>
#> @ x: num 2
#> @ y: num 3
#> Other props: alpha, color, fill, shape, size, stroke, auto_label,
#> bounding_box, length, r, theta, style, tibble, xy,
#> geom, label, place, aesthetics
Points can be scaled with constants
p2 * 2
#> <ob_point>
#> @ x: num 4
#> @ y: num 2
#> Other props: alpha, color, fill, shape, size, stroke, auto_label,
#> bounding_box, length, r, theta, style, tibble, xy,
#> geom, label, place, aesthetics
p3 / 4
#> <ob_point>
#> @ x: num 1
#> @ y: num 1
#> Other props: alpha, color, fill, shape, size, stroke, auto_label,
#> bounding_box, length, r, theta, style, tibble, xy,
#> geom, label, place, aesthetics
The x and y coordinates can be scaled separately with other points:
p1 / p3
#> <ob_point>
#> @ x: num 0.5
#> @ y: num 0.75
#> Other props: alpha, color, fill, shape, size, stroke, auto_label,
#> bounding_box, length, r, theta, style, tibble, xy,
#> geom, label, place, aesthetics
p1 * p3
#> <ob_point>
#> @ x: num 8
#> @ y: num 12
#> Other props: alpha, color, fill, shape, size, stroke, auto_label,
#> bounding_box, length, r, theta, style, tibble, xy,
#> geom, label, place, aesthetics
Distance
The distance between two points:
distance(p1, p2)
#> [1] 2
The shortest distance from a point to a line:
The shortest distance from a point to a circle’s edge:
c1 <- ob_circle(center = ob_point(2, 2), radius = 2)
p1 <- c1@center + ob_polar(
r = c1@radius * 1,
theta = degree(115),
color = "royalblue3")
p2 <- c1@center + ob_polar(
r = c1@radius * 2,
theta = degree(45),
color = "firebrick4")
p3 <- c1@center + ob_polar(
r = c1@radius * .3,
theta = degree(190),
color = "orchid4")
# p1 is on circle, so its distance is 0
distance(p1, c1)
#> [1] 0
# p2 is outside the circle
distance(p2, c1)
#> [1] 2
# p3 is inside the circle
distance(p3, c1)
#> [1] 1.4
Code
intersect_c1_p2 <- c1@point_at((p2 - c1@center)@theta)
seg_style <- ob_style(
arrowhead_length = 7,
arrow_head = my_arrowhead,
arrow_fins = my_arrowhead,
resect = unit(5, "pt")
)
seg_c1_p2 <- ob_segment(
intersect_c1_p2,
p2,
style = seg_style,
label = scales::number(distance(intersect_c1_p2, p2), .1))
intersect_c1_p3 <- c1@point_at((p3 - c1@center)@theta)
seg_c1_p3 <- ob_segment(
intersect_c1_p3,
p3,
color = p3@color,
label = scales::number(distance(intersect_c1_p3, p3), .1),
style = seg_style)
p_labels <- subscript("P", 1:3)
bp +
c1 +
p1@label(label = p_labels[1],
plot_point = T,
polar_just = ob_polar(
theta = (p1 - c1@center)@theta,
r = 1.3)) +
seg_c1_p2 +
seg_c1_p2@midpoint(c(0,1)) +
seg_c1_p2@midpoint(1)@label(
label = p_labels[2],
polar_just = ob_polar(theta = seg_c1_p3@line@angle, 1.5)) +
seg_c1_p3 +
seg_c1_p3@midpoint(c(0,1)) +
seg_c1_p3@midpoint(c(1))@label(
label = p_labels[3],
polar_just = ob_polar(theta = seg_c1_p3@line@angle, 1.5))
#> Warning: Removed 1 rows containingmissingvalues (`geom_arrow_segment()`).
#> Warning: Removed 1 row containing missing values or values outside the scale range
#> (`geom_rich_text()`).
#> Warning: Removed 1 row containing missing values or values outside the scale range
#> (`geom_point()`).
#> Warning: Removed 1 row containing missing values or values outside the scale range
#> (`geom_rich_text()`).
Convert points to geoms
The as.geom
function is called implicitly whenever a point object is added to a ggplot.
This is equivalent to
And this is equivalent to
ggplot() +
geom_point(
aes(
x,
y,
color = I(color),
size = I(size)),
data = get_tibble_defaults(pts))
That is, any style information that can be mapped will be handled via the I
(identity) function in the mapping statement (aes
).
Calling the as.geom
function directly is useful for overriding any style information in the points.
This is equivalent to
ggplot() +
geom_point(
aes(x = x,
y = y,
size = I(size),
color = I(color)),
stroke = 1.5,
fill = "yellow",
shape = "triangle filled",
data = pts@tibble
)
Bounding box
It is possible to find the rectangle that bounds all the points in an ob_point
object