Skip to content

Commit df0e150

Browse files
authored
✨ Point and polygon theme elements (#6249)
* `translate_shape_string()` deals with non-character input * new element constructors * document new elements * add `element_grob()` methods * include `point` and `polygon` in theme * include template in default themes * 🧑‍💻 deal with `pathGrob()`'s id-logic. * add tests * add news bullet
1 parent df315af commit df0e150

14 files changed

+247
-28
lines changed

NAMESPACE

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ S3method(c,mapped_discrete)
2020
S3method(drawDetails,zeroGrob)
2121
S3method(element_grob,element_blank)
2222
S3method(element_grob,element_line)
23+
S3method(element_grob,element_point)
24+
S3method(element_grob,element_polygon)
2325
S3method(element_grob,element_rect)
2426
S3method(element_grob,element_text)
2527
S3method(format,ggproto)
@@ -346,6 +348,8 @@ export(element_blank)
346348
export(element_geom)
347349
export(element_grob)
348350
export(element_line)
351+
export(element_point)
352+
export(element_polygon)
349353
export(element_rect)
350354
export(element_render)
351355
export(element_text)

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# ggplot2 (development version)
22

3+
* New `element_point()` and `element_polygon()` that can be given to
4+
`theme(point, polygon)` as an extension point (@teunbrand, #6248).
35
* Turned off fallback for `size` to `linewidth` translation in
46
`geom_bar()`/`geom_col()` (#4848).
57
* `coord_radial()` now displays no axis instead of throwing an error when

R/geom-point.R

+6-5
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,7 @@ GeomPoint <- ggproto("GeomPoint", Geom,
148148
),
149149

150150
draw_panel = function(self, data, panel_params, coord, na.rm = FALSE) {
151-
if (is.character(data$shape)) {
152-
data$shape <- translate_shape_string(data$shape)
153-
}
154-
151+
data$shape <- translate_shape_string(data$shape)
155152
coords <- coord$transform(data, panel_params)
156153
ggname("geom_point",
157154
pointsGrob(
@@ -176,7 +173,8 @@ GeomPoint <- ggproto("GeomPoint", Geom,
176173
#' given as a character vector into integers that are interpreted by the
177174
#' grid system.
178175
#'
179-
#' @param shape_string A character vector giving point shapes.
176+
#' @param shape_string A character vector giving point shapes. Non-character
177+
#' input will be returned.
180178
#'
181179
#' @return An integer vector with translated shapes.
182180
#' @export
@@ -188,6 +186,9 @@ GeomPoint <- ggproto("GeomPoint", Geom,
188186
#' # Strings with 1 or less characters are interpreted as symbols
189187
#' translate_shape_string(c("a", "b", "?"))
190188
translate_shape_string <- function(shape_string) {
189+
if (!is.character(shape_string)) {
190+
return(shape_string)
191+
}
191192
# strings of length 0 or 1 are interpreted as symbols by grid
192193
if (nchar(shape_string[1]) <= 1) {
193194
return(shape_string)

R/geom-sf.R

+1-3
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,7 @@ GeomSf <- ggproto("GeomSf", Geom,
209209
if (!inherits(coord, "CoordSf")) {
210210
cli::cli_abort("{.fn {snake_class(self)}} can only be used with {.fn coord_sf}.")
211211
}
212-
if (is.character(data$shape)) {
213-
data$shape <- translate_shape_string(data$shape)
214-
}
212+
data$shape <- translate_shape_string(data$shape)
215213

216214
data <- coord$transform(data, panel_params)
217215

R/legend-draw.R

+1-5
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,7 @@ NULL
2424
#' @export
2525
#' @rdname draw_key
2626
draw_key_point <- function(data, params, size) {
27-
if (is.null(data$shape)) {
28-
data$shape <- 19
29-
} else if (is.character(data$shape)) {
30-
data$shape <- translate_shape_string(data$shape)
31-
}
27+
data$shape <- translate_shape_string(data$shape %||% 19)
3228

3329
# NULL means the default stroke size, and NA means no stroke.
3430
pointsGrob(0.5, 0.5,

R/theme-defaults.R

+22
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,17 @@ theme_grey <- function(base_size = 11, base_family = "",
145145
spacing = unit(half_line, "pt"),
146146
margins = margin_auto(half_line),
147147

148+
point = element_point(
149+
colour = ink, shape = 19, fill = paper,
150+
size = (base_size / 11) * 1.5,
151+
stroke = base_line_size
152+
),
153+
154+
polygon = element_polygon(
155+
fill = paper, colour = ink,
156+
linewidth = base_rect_size, linetype = 1
157+
),
158+
148159
geom = element_geom(
149160
ink = ink, paper = paper, accent = "#3366FF",
150161
linewidth = base_line_size, borderwidth = base_line_size,
@@ -549,6 +560,8 @@ theme_void <- function(base_size = 11, base_family = "",
549560
t <- theme(
550561
line = element_blank(),
551562
rect = element_blank(),
563+
polygon = element_blank(),
564+
point = element_blank(),
552565
text = element_text(
553566
family = base_family, face = "plain",
554567
colour = ink, size = base_size,
@@ -639,6 +652,15 @@ theme_test <- function(base_size = 11, base_family = "",
639652
lineheight = 0.9, hjust = 0.5, vjust = 0.5, angle = 0,
640653
margin = margin(), debug = FALSE
641654
),
655+
point = element_point(
656+
colour = ink, shape = 19, fill = paper,
657+
size = (base_size / 11) * 1.5,
658+
stroke = base_line_size
659+
),
660+
polygon = element_polygon(
661+
fill = paper, colour = ink,
662+
linewidth = base_rect_size, linetype = 1
663+
),
642664
title = element_text(family = header_family),
643665
spacing = unit(half_line, "pt"),
644666
margins = margin_auto(half_line),

R/theme-elements.R

+76-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#' - `element_rect()`: borders and backgrounds.
99
#' - `element_line()`: lines.
1010
#' - `element_text()`: text.
11+
#' - `element_polygon()`: polygons.
12+
#' - `element_point()`: points.
1113
#' - `element_geom()`: defaults for drawing layers.
1214
#'
1315
#' `rel()` is used to specify sizes relative to the parent,
@@ -18,15 +20,24 @@
1820
#' of the fill.
1921
#' @param colour,color Line/border colour. Color is an alias for colour.
2022
#' `alpha()` can be used to set the transparency of the colour.
21-
#' @param linewidth,borderwidth Line/border size in mm.
22-
#' @param size,fontsize text size in pts.
23+
#' @param linewidth,borderwidth,stroke Line/border size in mm.
24+
#' @param size,fontsize,pointsize text size in pts, point size in mm.
25+
#' @param linetype,bordertype Line type for lines and borders respectively. An
26+
#' integer (0:8), a name (blank, solid, dashed, dotted, dotdash, longdash,
27+
#' twodash), or a string with an even number (up to eight) of hexadecimal
28+
#' digits which give the lengths in consecutive positions in the string.
29+
#' @param shape,pointshape Shape for points (1-25).
2330
#' @param arrow.fill Fill colour for arrows.
2431
#' @param inherit.blank Should this element inherit the existence of an
2532
#' `element_blank` among its parents? If `TRUE` the existence of
2633
#' a blank element among its parents will cause this element to be blank as
2734
#' well. If `FALSE` any blank parent element will be ignored when
2835
#' calculating final element state.
2936
#' @return An S3 object of class `element`, `rel`, or `margin`.
37+
#' @details
38+
#' The `element_polygon()` and `element_point()` functions are not rendered
39+
#' in standard plots and just serve as extension points.
40+
#'
3041
#' @examples
3142
#' # A standard plot
3243
#' plot <- ggplot(mpg, aes(displ, hwy)) + geom_point()
@@ -97,10 +108,6 @@ element_rect <- function(fill = NULL, colour = NULL, linewidth = NULL,
97108

98109
#' @export
99110
#' @rdname element
100-
#' @param linetype,bordertype Line type for lines and borders respectively. An
101-
#' integer (0:8), a name (blank, solid, dashed, dotted, dotdash, longdash,
102-
#' twodash), or a string with an even number (up to eight) of hexadecimal
103-
#' digits which give the lengths in consecutive positions in the string.
104111
#' @param lineend Line end Line end style (round, butt, square)
105112
#' @param arrow Arrow specification, as created by [grid::arrow()]
106113
element_line <- function(colour = NULL, linewidth = NULL, linetype = NULL,
@@ -164,11 +171,36 @@ element_text <- function(family = NULL, face = NULL, colour = NULL,
164171
)
165172
}
166173

174+
#' @export
175+
#' @rdname element
176+
element_polygon <- function(fill = NULL, colour = NULL, linewidth = NULL,
177+
linetype = NULL, color = NULL,
178+
inherit.blank = FALSE) {
179+
structure(
180+
list(
181+
fill = fill, colour = color %||% colour, linewidth = linewidth,
182+
linetype = linetype, inherit.blank = inherit.blank
183+
),
184+
class = c("element_polygon", "element")
185+
)
186+
}
187+
188+
#' @export
189+
#' @rdname element
190+
element_point <- function(colour = NULL, shape = NULL, size = NULL, fill = NULL,
191+
stroke = NULL, color = NULL, inherit.blank = FALSE) {
192+
structure(
193+
list(
194+
colour = color %||% colour, fill = fill, shape = shape, size = size,
195+
stroke = stroke, inherit.blank = inherit.blank
196+
),
197+
class = c("element_point", "element")
198+
)
199+
}
200+
167201
#' @param ink Foreground colour.
168202
#' @param paper Background colour.
169203
#' @param accent Accent colour.
170-
#' @param pointsize Size for points in mm.
171-
#' @param pointshape Shape for points (1-25).
172204
#' @export
173205
#' @rdname element
174206
element_geom <- function(
@@ -357,6 +389,40 @@ element_grob.element_line <- function(element, x = 0:1, y = 0:1,
357389
)
358390
}
359391

392+
#' @export
393+
element_grob.element_polygon <- function(element, x = c(0, 0.5, 1, 0.5),
394+
y = c(0.5, 1, 0.5, 0), fill = NULL,
395+
colour = NULL, linewidth = NULL,
396+
linetype = NULL, ...,
397+
id = NULL, id.lengths = NULL,
398+
pathId = NULL, pathId.lengths = NULL) {
399+
400+
gp <- gg_par(lwd = linewidth, col = colour, fill = fill, lty = linetype)
401+
element_gp <- gg_par(lwd = element$linewidth, col = element$colour,
402+
fill = element$fill, lty = element$linetype)
403+
pathGrob(
404+
x = x, y = y, gp = modify_list(element_gp, gp), ...,
405+
# We swap the id logic so that `id` is always the (super)group id
406+
# (consistent with `polygonGrob()`) and `pathId` always the subgroup id.
407+
pathId = id, pathId.lengths = id.lengths,
408+
id = pathId, id.lengths = pathId.lengths
409+
)
410+
}
411+
412+
#' @export
413+
element_grob.element_point <- function(element, x = 0.5, y = 0.5, colour = NULL,
414+
shape = NULL, fill = NULL, size = NULL,
415+
stroke = NULL, ...,
416+
default.units = "npc") {
417+
418+
gp <- gg_par(col = colour, fill = fill, pointsize = size, stroke = stroke)
419+
element_gp <- gg_par(col = element$colour, fill = element$fill,
420+
pointsize = element$size, stroke = element$stroke)
421+
shape <- translate_shape_string(shape %||% element$shape %||% 19)
422+
pointsGrob(x = x, y = y, pch = shape, gp = modify_list(element_gp, gp),
423+
default.units = default.units, ...)
424+
}
425+
360426
#' Define and register new theme elements
361427
#'
362428
#' The underlying structure of a ggplot2 theme is defined via the element tree, which
@@ -532,6 +598,8 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
532598
line = el_def("element_line"),
533599
rect = el_def("element_rect"),
534600
text = el_def("element_text"),
601+
point = el_def("element_point"),
602+
polygon = el_def("element_polygon"),
535603
geom = el_def("element_geom"),
536604
title = el_def("element_text", "text"),
537605
spacing = el_def("unit"),

R/theme.R

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#' @param text all text elements ([element_text()])
2626
#' @param title all title elements: plot, axes, legends ([element_text()];
2727
#' inherits from `text`)
28+
#' @param point all point elements ([element_point()])
29+
#' @param polygon all polygon elements ([element_polygon()])
2830
#' @param geom defaults for geoms ([element_geom()])
2931
#' @param spacing all spacings ([`unit()`][grid::unit])
3032
#' @param margins all margins ([margin()])
@@ -323,6 +325,8 @@ theme <- function(...,
323325
rect,
324326
text,
325327
title,
328+
point,
329+
polygon,
326330
geom,
327331
spacing,
328332
margins,

man/element.Rd

+31-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/theme.Rd

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)