3
3
# ' @param ... any number of plotly objects
4
4
# ' @param nrows number of rows for laying out plots in a grid-like structure.
5
5
# ' Only used if no domain is already specified.
6
+ # ' @param widths relative width of each column on a 0-1 scale. By default all
7
+ # ' columns have an equal relative width.
8
+ # ' @param heights relative height of each row on a 0-1 scale. By default all
9
+ # ' rows have an equal relative height.
10
+ # ' @param share determines whether x/y/both axes are shared.
6
11
# ' @param which_layout adopt the layout of which plot? If the default value of
7
12
# ' "merge" is used, all plot level layout options will be included in the final
8
13
# ' layout. This argument also accepts a numeric vector which will restric
20
25
# ' subplot(p1, p2, p1, p2, nrows = 2)
21
26
# ' }
22
27
23
- subplot <- function (... , nrows = 1 , which_layout = " merge" , margin = 0.02 ) {
28
+ subplot <- function (... , nrows = 1 , widths = NULL , heights = NULL , share = NULL ,
29
+ which_layout = " merge" , margin = 0.02 ) {
24
30
# build each plot and collect relevant info
25
31
plots <- lapply(list (... ), plotly_build )
26
32
traces <- lapply(plots , " [[" , " data" )
@@ -58,7 +64,9 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.02) {
58
64
yAxisMap <- split(yAxisMap , rep(seq_along(plots ), yAxisN ))
59
65
# domains of each subplot
60
66
# TODO: allow control of column width and row height!
61
- domainInfo <- get_domains(length(plots ), nrows , margin )
67
+ domainInfo <- get_domains(
68
+ length(plots ), nrows , margin , widths = widths , heights = heights
69
+ )
62
70
# reposition shapes and annotations
63
71
annotations <- Map(reposition , annotations , split(domainInfo , seq_along(plots )))
64
72
shapes <- Map(reposition , shapes , split(domainInfo , seq_along(plots )))
@@ -122,16 +130,40 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.02) {
122
130
}
123
131
124
132
125
- get_domains <- function (nplots = 1 , nrows = 1 , margins = 0.01 ) {
133
+ get_domains <- function (nplots = 1 , nrows = 1 , margins = 0.01 ,
134
+ widths = NULL , heights = NULL ) {
126
135
if (length(margins ) == 1 ) margins <- rep(margins , 4 )
127
136
if (length(margins ) != 4 ) stop(" margins must be length 1 or 4" , call. = FALSE )
128
137
ncols <- ceiling(nplots / nrows )
138
+ widths <- widths %|| % rep(1 / ncols , ncols )
139
+ heights <- heights %|| % rep(1 / nrows , nrows )
140
+ if (length(widths ) != ncols ) {
141
+ stop(" The length of the widths argument must be equal " ,
142
+ " to the number of columns" , call. = FALSE )
143
+ }
144
+ if (length(heights ) != nrows ) {
145
+ stop(" The length of the heights argument must be equal " ,
146
+ " to the number of rows" , call. = FALSE )
147
+ }
148
+ if (any(widths < 0 | heights < 0 )) {
149
+ stop(" The widths and heights arguments must contain positive values" )
150
+ }
151
+ if (sum(widths ) > 1 | sum(heights ) > 1 ) {
152
+ stop(" The sum of the widths and heights arguments must be less than 1" )
153
+ }
154
+
155
+ widths <- cumsum(c(0 , widths ))
156
+ heights <- cumsum(c(0 , heights ))
157
+ # 'center' these values if there is still room left
158
+ widths <- widths + (1 - max(widths )) / 2
159
+ heights <- heights + (1 - max(heights )) / 2
160
+
129
161
130
162
xs <- vector(" list" , ncols )
131
163
for (i in seq_len(ncols )) {
132
164
xs [[i ]] <- c(
133
- xstart = (( i - 1 ) / ncols ) + ifelse (i == 1 , 0 , margins [1 ]) ,
134
- xend = ( i / ncols ) - ifelse (i == ncols , 0 , margins [2 ])
165
+ xstart = widths [ i ] + if (i == 1 ) 0 else margins [1 ],
166
+ xend = widths [ i + 1 ] - if (i == ncols ) 0 else margins [2 ]
135
167
)
136
168
}
137
169
xz <- rep_len(xs , nplots )
@@ -140,8 +172,8 @@ get_domains <- function(nplots = 1, nrows = 1, margins = 0.01) {
140
172
for (i in seq_len(nplots )) {
141
173
j <- ceiling(i / ncols )
142
174
ys [[i ]] <- c(
143
- ystart = 1 - (( j - 1 ) / nrows ) - ifelse (j == 1 , 0 , margins [3 ]) ,
144
- yend = 1 - (j / nrows ) + ifelse (j == nrows , 0 , margins [4 ])
175
+ ystart = 1 - (heights [ j ]) - if (j == 1 ) 0 else margins [3 ],
176
+ yend = 1 - (heights [ j + 1 ] ) + if (j == nrows ) 0 else margins [4 ]
145
177
)
146
178
}
147
179
list2df(Map(c , xz , ys ))
0 commit comments