@@ -12,34 +12,31 @@ is.margin <- function(x) {
12
12
inherits(x , " margin" )
13
13
}
14
14
15
- margin_height <- function (grob , margins ) {
16
- if (is.zero(grob )) return (unit(0 , " cm" ))
17
-
18
- grobHeight(grob ) + margins [1 ] + margins [3 ]
19
- }
20
-
21
- margin_width <- function (grob , margins ) {
22
- if (is.zero(grob )) return (unit(0 , " cm" ))
23
-
24
- grobWidth(grob ) + margins [2 ] + margins [4 ]
25
- }
26
-
27
- # ' Text grob, height, and width
15
+ # ' Create a text grob with the proper location and margins
28
16
# '
29
- # ' This function returns a list containing a text grob (and, optionally ,
30
- # ' debugging grobs) and the height and width of the text grob .
17
+ # ' `titleGrob()` is called when creating titles and labels for axes, legends ,
18
+ # ' and facet strips .
31
19
# '
32
- # ' @param label Either `NULL`, a string (length 1 character vector), or
33
- # ' an expression.
34
- # ' @param x,y x and y locations where the text is to be placed. If `x` and `y`
35
- # ' are `NULL`, `hjust` and `vjust` are used to determine the location.
36
- # ' @inheritParams titleGrob
20
+ # ' @param label Text to place on the plot. These maybe axis titles, axis labels,
21
+ # ' facet strip titles, etc.
22
+ # ' @param x,y x and y locations where the text is to be placed.
23
+ # ' @param hjust,vjust Horizontal and vertical justification of the text.
24
+ # ' @param angle Angle of rotation of the text.
25
+ # ' @param gp Additional graphical parameters in a call to `gpar()`.
26
+ # ' @param margin Margins around the text. See [margin()] for more
27
+ # ' details.
28
+ # ' @param margin_x,margin_y Whether or not to add margins in the x/y direction.
29
+ # ' @param debug If `TRUE`, aids visual debugging by drawing a solid
30
+ # ' rectangle behind the complete text area, and a point where each label
31
+ # ' is anchored.
37
32
# '
38
33
# ' @noRd
39
- title_spec <- function (label , x , y , hjust , vjust , angle , gp = gpar(),
40
- debug = FALSE , check.overlap = FALSE ) {
41
-
42
- if (is.null(label )) return (zeroGrob())
34
+ titleGrob <- function (label , x , y , hjust , vjust , angle = 0 , gp = gpar(),
35
+ margin = NULL , margin_x = FALSE , margin_y = FALSE ,
36
+ debug = FALSE , check.overlap = FALSE ) {
37
+ if (is.null(label )) {
38
+ return (zeroGrob())
39
+ }
43
40
44
41
# We rotate the justifiation values to obtain the correct x and y reference point,
45
42
# since hjust and vjust are applied relative to the rotated text frame in textGrob
@@ -48,16 +45,17 @@ title_spec <- function(label, x, y, hjust, vjust, angle, gp = gpar(),
48
45
n <- max(length(x ), length(y ), 1 )
49
46
x <- x %|| % unit(rep(just $ hjust , n ), " npc" )
50
47
y <- y %|| % unit(rep(just $ vjust , n ), " npc" )
48
+ if (! is.unit(x )) {
49
+ x <- unit(x , " npc" )
50
+ }
51
+ if (! is.unit(y )) {
52
+ y <- unit(y , " npc" )
53
+ }
51
54
52
- text_grob <- textGrob(
53
- label ,
54
- x ,
55
- y ,
56
- hjust = hjust ,
57
- vjust = vjust ,
58
- rot = angle ,
59
- gp = gp ,
60
- check.overlap = check.overlap
55
+ grob <- textGrob(
56
+ label , x , y ,
57
+ hjust = hjust , vjust = vjust ,
58
+ rot = angle , gp = gp , check.overlap = check.overlap
61
59
)
62
60
63
61
# The grob dimensions don't include the text descenders, so these need to be added
@@ -71,140 +69,78 @@ title_spec <- function(label, x, y, hjust, vjust, angle, gp = gpar(),
71
69
# Use trigonometry to calculate grobheight and width for rotated grobs. This is only
72
70
# exactly correct when vjust = 1. We need to take the absolute value so we don't make
73
71
# the grob smaller when it's flipped over.
74
- text_height <- unit(1 , " grobheight" , text_grob ) + abs(cos(angle [1 ] / 180 * pi )) * descent
75
- text_width <- unit(1 , " grobwidth" , text_grob ) + abs(sin(angle [1 ] / 180 * pi )) * descent
72
+ rad <- (angle [1 ] %% 360 ) / 180 * pi
73
+ x_descent <- abs(sin(rad )) * descent
74
+ y_descent <- abs(cos(rad )) * descent
76
75
77
- if (isTRUE(debug )) {
78
- children <- gList(
79
- rectGrob(gp = gpar(fill = " cornsilk" , col = NA )),
80
- pointsGrob(x , y , pch = 20 , gp = gpar(col = " gold" )),
81
- text_grob
82
- )
83
- } else {
84
- children <- gList(text_grob )
85
- }
86
-
87
- list (
88
- text_grob = children ,
89
- text_height = text_height ,
90
- text_width = text_width
91
- )
92
- }
93
-
94
- # ' Add margins
95
- # '
96
- # ' Given a text grob, `add_margins()` adds margins around the grob in the
97
- # ' directions determined by `margin_x` and `margin_y`.
98
- # '
99
- # ' @param grob A gList containing a grob, such as a text grob
100
- # ' @param height,width Usually the height and width of the text grob. Passed as
101
- # ' separate arguments from the grob itself because in the special case of
102
- # ' facet strip labels each set of strips should share the same height and
103
- # ' width, even if the labels are of different length.
104
- # ' @inheritParams titleGrob
105
- # '
106
- # ' @noRd
107
- add_margins <- function (grob , height , width , margin = NULL ,
108
- gp = gpar(), margin_x = FALSE , margin_y = FALSE ) {
76
+ # Set text size to actual size including descenders
77
+ width <- unit(1 , " grobwidth" , grob ) + x_descent
78
+ height <- unit(1 , " grobheight" , grob ) + y_descent
109
79
80
+ # Resolve margin
110
81
if (is.null(margin )) {
111
82
margin <- margin(0 , 0 , 0 , 0 )
112
83
}
84
+ margin_x <- isTRUE(margin_x )
85
+ margin_y <- isTRUE(margin_y )
86
+
87
+ # Initialise new values for position and dimensions
88
+ new_x <- NULL
89
+ new_y <- NULL
90
+ new_width <- NULL
91
+ new_height <- NULL
92
+
93
+ # Calculate new x/width
94
+ if (margin_x ) {
95
+ new_width <- unit.c(margin [4 ], width , margin [2 ])
96
+ new_x <- x - margin [2 ] * just $ hjust + margin [4 ] * (1 - just $ hjust )
97
+ }
113
98
114
- if (margin_x && margin_y ) {
115
- widths <- unit.c(margin [4 ], width , margin [2 ])
116
- heights <- unit.c(margin [1 ], height , margin [3 ])
99
+ # Calculate new y/height
100
+ if (margin_y ) {
101
+ new_height <- unit.c(margin [1 ], height , margin [3 ])
102
+ new_y <- y - margin [1 ] * just $ vjust + margin [3 ] * (1 - just $ vjust )
103
+ }
117
104
118
- vp <- viewport(
119
- layout = grid.layout(3 , 3 , heights = heights , widths = widths ),
120
- gp = gp
121
- )
122
- child_vp <- viewport(layout.pos.row = 2 , layout.pos.col = 2 )
123
- } else if (margin_x ) {
124
- widths <- unit.c(margin [4 ], width , margin [2 ])
125
- vp <- viewport(layout = grid.layout(1 , 3 , widths = widths ), gp = gp )
126
- child_vp <- viewport(layout.pos.col = 2 )
105
+ # If only one margin is set, the other dimension is a null unit
106
+ if (xor(margin_x , margin_y )) {
107
+ new_width <- new_width %|| % unit(1 , " null" )
108
+ new_height <- new_height %|| % unit(1 , " null" )
109
+ }
127
110
128
- heights <- unit(1 , " null" )
129
- } else if (margin_y ) {
130
- heights <- unit.c(margin [1 ], height , margin [3 ])
111
+ # If we haven't touched the new positions/dimensions, use the previous ones
112
+ new_width <- new_width %|| % width
113
+ new_height <- new_height %|| % height
114
+ x <- new_x %|| % x
115
+ y <- new_y %|| % y
131
116
132
- vp <- viewport(layout = grid.layout(3 , 1 , heights = heights ), gp = gp )
133
- child_vp <- viewport(layout.pos.row = 2 )
117
+ # Adjust the grob
118
+ grob $ x <- x
119
+ grob $ y <- y
134
120
135
- widths <- unit(1 , " null" )
136
- } else {
137
- widths <- width
138
- heights <- height
139
- return (
140
- gTree(
141
- children = grob ,
142
- widths = widths ,
143
- heights = heights ,
144
- cl = " titleGrob"
145
- )
121
+ # Add debug rectangles/points if necessary
122
+ if (isTRUE(debug )) {
123
+ children <- gList(
124
+ rectGrob(
125
+ x = x , y = y , width = width , height = height ,
126
+ hjust = just $ hjust , vjust = just $ vjust ,
127
+ gp = gpar(fill = " cornsilk" , col = NA )
128
+ ),
129
+ pointsGrob(x , y , pch = 20 , gp = gpar(col = " gold" )),
130
+ grob
146
131
)
132
+ } else {
133
+ children <- gList(grob )
147
134
}
148
135
149
136
gTree(
150
- children = grob ,
151
- vp = vpTree(vp , vpList(child_vp )),
152
- widths = widths ,
153
- heights = heights ,
137
+ children = children ,
138
+ widths = new_width ,
139
+ heights = new_height ,
154
140
cl = " titleGrob"
155
141
)
156
142
}
157
143
158
- # ' Create a text grob with the proper location and margins
159
- # '
160
- # ' `titleGrob()` is called when creating titles and labels for axes, legends,
161
- # ' and facet strips.
162
- # '
163
- # ' @param label Text to place on the plot. These maybe axis titles, axis labels,
164
- # ' facet strip titles, etc.
165
- # ' @param x,y x and y locations where the text is to be placed.
166
- # ' @param hjust,vjust Horizontal and vertical justification of the text.
167
- # ' @param angle Angle of rotation of the text.
168
- # ' @param gp Additional graphical parameters in a call to `gpar()`.
169
- # ' @param margin Margins around the text. See [margin()] for more
170
- # ' details.
171
- # ' @param margin_x,margin_y Whether or not to add margins in the x/y direction.
172
- # ' @param debug If `TRUE`, aids visual debugging by drawing a solid
173
- # ' rectangle behind the complete text area, and a point where each label
174
- # ' is anchored.
175
- # '
176
- # ' @noRd
177
- titleGrob <- function (label , x , y , hjust , vjust , angle = 0 , gp = gpar(),
178
- margin = NULL , margin_x = FALSE , margin_y = FALSE ,
179
- debug = FALSE , check.overlap = FALSE ) {
180
-
181
- if (is.null(label ))
182
- return (zeroGrob())
183
-
184
- # Get text grob, text height, and text width
185
- grob_details <- title_spec(
186
- label ,
187
- x = x ,
188
- y = y ,
189
- hjust = hjust ,
190
- vjust = vjust ,
191
- angle = angle ,
192
- gp = gp ,
193
- debug = debug ,
194
- check.overlap = check.overlap
195
- )
196
-
197
- add_margins(
198
- grob = grob_details $ text_grob ,
199
- height = grob_details $ text_height ,
200
- width = grob_details $ text_width ,
201
- gp = gp ,
202
- margin = margin ,
203
- margin_x = margin_x ,
204
- margin_y = margin_y
205
- )
206
- }
207
-
208
144
# ' @export
209
145
widthDetails.titleGrob <- function (x ) {
210
146
sum(x $ widths )
0 commit comments