diff --git a/NAMESPACE b/NAMESPACE
index 2071390f..b985d82a 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -10,6 +10,7 @@ export(plt_add)
export(tinyplot)
export(tinyplot_add)
export(tpar)
+export(type_abline)
export(type_area)
export(type_boxplot)
export(type_errorbar)
@@ -17,6 +18,7 @@ export(type_function)
export(type_glm)
export(type_hist)
export(type_histogram)
+export(type_hline)
export(type_jitter)
export(type_lines)
export(type_lm)
@@ -30,6 +32,7 @@ export(type_ribbon)
export(type_segments)
export(type_spineplot)
export(type_spline)
+export(type_vline)
importFrom(grDevices,adjustcolor)
importFrom(grDevices,as.raster)
importFrom(grDevices,col2rgb)
diff --git a/NEWS.md b/NEWS.md
index 869b1627..b998b8a5 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -12,8 +12,8 @@ Breaking changes:
plot type) should no longer be passed via `...` in the main plotting function.
Instead, these additional arguments must now be passed explicitly as part of the
corresponding `type_*()` function in the `type` argument. So, for example, you
-should now use `plt(Nile, type = type_histogram(breaks = 30))` instead of
-`plt(Nile, type = "histogram", breaks = 30)` if you wanted to adjust the number
+should now use `tinyplot(Nile, type = type_histogram(breaks = 30))` instead of
+`tinyplot(Nile, type = "histogram", breaks = 30)` if you wanted to adjust the number
of breaks. More details are provided below and also in the new dedicated
[Plot types vignette](https://grantmcdermott.com/tinyplot/vignettes/types.html).
The essential idea is that shortcut character types (here: `"histogram"`) all
@@ -23,7 +23,7 @@ behaviour by passing appropriate arguments. We're sorry to introduce a breaking
change, but this new approach should ensure that users have better control of
how their plots behave, and avoids guesswork on our side.
- `ribbon.alpha` is deprecated in `tinyplot()`. Use the `alpha` argument of the
-`type_ribbon()` function instead: `plt(..., type = type_ribbon(alpha = 0.5))`
+`type_ribbon()` function instead: `tinyplot(..., type = type_ribbon(alpha = 0.5))`
New features:
@@ -35,6 +35,12 @@ types enable a variety of additional features. (#222 @vincentarelbundock)
- `type_lm()` (shortcut: `"lm"`)
- `type_loess()` (shortcut: `"loess"`)
- `type_spline()` (shortcut: `"spline"`)
+ - New function-based types
+ - `type_abline()`: line with intercept and slope
+ - `type_hline()`: horizontal line
+ - `type_vline()`: verticla line
+ - `type_function()` arbitrary function.
+ - `type_spineplot()` (shortcut: `"spineplot"`) type for producing spine
- `type_function()` can trace arbitrary functions.
- New `type_spineplot()` (shortcut: `"spineplot"`) type for producing spine
plots and spinograms. These are modified versions of a histogram or mosaic
@@ -49,11 +55,11 @@ types enable a variety of additional features. (#222 @vincentarelbundock)
on the website.)
- The new `flip` argument allows for easily flipping (swapping) the orientation
of the x and y axes. This should work regardless of plot type, e.g.
- `plt(~Sepal.Length | Species, data = iris, type = "density", flip = TRUE)`.
+ `tinyplot(~Sepal.Length | Species, data = iris, type = "density", flip = TRUE)`.
(#216 @grantmcdermott)
- `tpar()` gains additional `grid.col`, `grid.lty`, and `grid.lwd` arguments for
fine-grained control over the appearance of the default panel grid when
- `plt(..., grid = TRUE)` is called. (#237 @grantmcdermott)
+ `tinyplot(..., grid = TRUE)` is called. (#237 @grantmcdermott)
- The new `tinyplot_add()` (alias: `plt_add()`) convenience function allows
easy layering of plots without having to specify repeat arguments. (#246
@vincentarelbundock)
@@ -229,7 +235,7 @@ adding transparency to plot elements and colours. Example use:
background fill by passing `bg` (or its alias, `fill`) a numeric in the range
`[0,1]`. This feature has the same effect as `bg = "by"` except for the added
transparency. Example use:
-`plt(lat ~ long | depth, data = quakes, pch = 21, cex = 2, bg = 0.2)`. (#129
+`tinyplot(lat ~ long | depth, data = quakes, pch = 21, cex = 2, bg = 0.2)`. (#129
@grantmcdermott)
diff --git a/R/assertions.R b/R/assertions.R
index 118637e9..5e48f97d 100644
--- a/R/assertions.R
+++ b/R/assertions.R
@@ -29,6 +29,23 @@ assert_choice = function(x, choice, null.ok = FALSE, name = as.character(substit
stop(msg, call. = FALSE)
}
+check_true = function(x, null.ok = FALSE) {
+ if (is.null(x) && isTRUE(null.ok)) {
+ return(invisible(TRUE))
+ }
+ if (isTRUE(x)) {
+ return(invisible(TRUE))
+ }
+ return(FALSE)
+}
+
+assert_true = function(x, null.ok = FALSE, name = as.character(substitute(x))) {
+ msg = sprintf("`%s` must be true.", name)
+ if (!isTRUE(check_true(x, null.ok = null.ok))) {
+ stop(msg, call. = FALSE)
+ }
+}
+
check_string = function(x, null.ok = FALSE) {
if (is.null(x) && isTRUE(null.ok)) {
return(invisible(TRUE))
diff --git a/R/type_abline.R b/R/type_abline.R
new file mode 100644
index 00000000..e7b6e146
--- /dev/null
+++ b/R/type_abline.R
@@ -0,0 +1,79 @@
+#' Add straight lines to a plot
+#'
+#' @inheritParams graphics::abline
+#' @inheritParams tinyplot
+#' @examples
+#' mod = lm(mpg ~ hp, data = mtcars)
+#' y = mtcars$mpg
+#' yhat = predict(mod)
+#' tinyplot(y, yhat, xlim = c(0, 40), ylim = c(0, 40))
+#' tinyplot_add(type = type_abline(a = 0, b = 1))
+#' @export
+type_abline = function(a = 0, b = 1, col = NULL, lty = NULL, lwd = NULL) {
+ data_abline = function(datapoints, ...) {
+ if (nrow(datapoints) == 0) {
+ msg = "`type_abline() only works on existing plots with x and y data points."
+ stop(msg, call. = FALSE)
+ }
+ return(list())
+ }
+ draw_abline = function() {
+ fun = function(ifacet, data_facet, icol, ilty, ilwd, ...) {
+ nfacets = length(data_facet)
+
+ if (length(a) == 1) {
+ a = rep(a, nfacets)
+ } else if (length(a) != nfacets) {
+ msg = "Length of 'a' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (length(b) == 1) {
+ b = rep(b, nfacets)
+ } else if (length(b) != nfacets) {
+ msg = "Length of 'b' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(col)) {
+ col = icol
+ }
+ if (length(col) == 1) {
+ col = rep(col, nfacets)
+ } else if (length(col) != nfacets) {
+ msg = "Length of 'col' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(lty)) {
+ lty = if (!is.null(ilty)) ilty else 1
+ }
+ if (length(lty) == 1) {
+ lty = rep(lty, nfacets)
+ } else if (length(lty) != nfacets) {
+ msg = "Length of 'lty' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(lwd)) {
+ lwd = if (!is.null(ilwd)) ilwd else 1
+ }
+ if (length(lwd) == 1) {
+ lwd = rep(lwd, nfacets)
+ } else if (length(lwd) != nfacets) {
+ msg = "Length of 'lwd' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ abline(a = a[ifacet], b = b[ifacet], col = col[ifacet], lty = lty[ifacet], lwd = lwd[ifacet])
+ }
+ return(fun)
+ }
+ out = list(
+ draw = draw_abline(),
+ data = data_abline,
+ name = "abline"
+ )
+ class(out) = "tinyplot_type"
+ return(out)
+}
diff --git a/R/type_hline.R b/R/type_hline.R
new file mode 100644
index 00000000..77d81716
--- /dev/null
+++ b/R/type_hline.R
@@ -0,0 +1,70 @@
+#' Trace a horizontal line on the plot
+#'
+#' @param h y-value(s) for horizontal line(s). Numeric of length 1 or equal to the number of facets.
+#' @inheritParams graphics::abline
+#' @inheritParams tinyplot
+#' @examples
+#' tinyplot(mpg ~ hp | factor(cyl), facet = ~ factor(cyl), data = mtcars)
+#' tinyplot_add(type = type_hline(h = 12, col = "pink", lty = 3, lwd = 3))
+#' @export
+type_hline = function(h = 0, col = NULL, lty = NULL, lwd = NULL) {
+ data_hline = function(datapoints, ...) {
+ if (nrow(datapoints) == 0) {
+ msg = "`type_hline() only works on existing plots with x and y data points."
+ stop(msg, call. = FALSE)
+ }
+ return(list())
+ }
+ draw_hline = function() {
+ fun = function(ifacet, data_facet, icol, ilty, ilwd, ...) {
+ nfacets = length(data_facet)
+
+ if (length(h) == 1) {
+ h = rep(h, nfacets)
+ } else if (length(h) != nfacets) {
+ msg = "Length of 'h' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(col)) {
+ col = icol
+ }
+ if (length(col) == 1) {
+ col = rep(col, nfacets)
+ } else if (length(col) != nfacets) {
+ msg = "Length of 'col' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(lty)) {
+ lty = if (!is.null(ilty)) ilty else 1
+ }
+ if (length(lty) == 1) {
+ lty = rep(lty, nfacets)
+ } else if (length(lty) != nfacets) {
+ msg = "Length of 'lty' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(lwd)) {
+ lwd = if (!is.null(ilwd)) ilwd else 1
+ }
+ if (length(lwd) == 1) {
+ lwd = rep(lwd, nfacets)
+ } else if (length(lwd) != nfacets) {
+ msg = "Length of 'lwd' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ abline(h = h[ifacet], col = col[ifacet], lty = lty[ifacet], lwd = lwd[ifacet])
+ }
+ return(fun)
+ }
+ out = list(
+ draw = draw_hline(),
+ data = data_hline,
+ name = "hline"
+ )
+ class(out) = "tinyplot_type"
+ return(out)
+}
diff --git a/R/type_vline.R b/R/type_vline.R
new file mode 100644
index 00000000..a581487e
--- /dev/null
+++ b/R/type_vline.R
@@ -0,0 +1,77 @@
+#' Trace a vertical line on the plot
+#'
+#' @param v x-value(s) for vertical line(s). Numeric of length 1 or equal to the number of facets.
+#' @inheritParams tinyplot
+#' @examples
+#' tinyplot(mpg ~ hp, data = mtcars)
+#' tinyplot_add(type = type_vline(150))
+#'
+#' # facet-specify location and colors
+#' cols = c("black", "green", "orange")
+#' tinyplot(mpg ~ hp | factor(cyl), facet = ~ factor(cyl), data = mtcars, col = cols)
+#' tinyplot_add(type = type_vline(
+#' v = c(100, 150, 200), col = cols, lty = 3, lwd = 3
+#' ))
+#' @export
+type_vline = function(v = 0, col = "black", lty = 1, lwd = 1) {
+ assert_numeric(v)
+ data_vline = function(datapoints, ...) {
+ if (nrow(datapoints) == 0) {
+ msg = "`type_vline() only works on existing plots with x and y data points."
+ stop(msg, call. = FALSE)
+ }
+ return(list())
+ }
+ draw_vline = function() {
+ fun = function(ifacet, data_facet, icol, ilty, ilwd, ...) {
+ nfacets = length(data_facet)
+
+ if (length(v) == 1) {
+ v = rep(v, nfacets)
+ } else if (length(v) != nfacets) {
+ msg = "Length of 'v' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(col)) {
+ col = icol
+ }
+ if (length(col) == 1) {
+ col = rep(col, nfacets)
+ } else if (length(col) != nfacets) {
+ msg = "Length of 'col' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(lty)) {
+ lty = if (!is.null(ilty)) ilty else 1
+ }
+ if (length(lty) == 1) {
+ lty = rep(lty, nfacets)
+ } else if (length(lty) != nfacets) {
+ msg = "Length of 'lty' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ if (is.null(lwd)) {
+ lwd = if (!is.null(ilwd)) ilwd else 1
+ }
+ if (length(lwd) == 1) {
+ lwd = rep(lwd, nfacets)
+ } else if (length(lwd) != nfacets) {
+ msg = "Length of 'lwd' must be 1 or equal to the number of facets"
+ stop(msg, call. = FALSE)
+ }
+
+ abline(v = v[ifacet], col = col[ifacet], lty = lty[ifacet], lwd = lwd[ifacet])
+ }
+ return(fun)
+ }
+ out = list(
+ draw = draw_vline(),
+ data = data_vline,
+ name = "vline"
+ )
+ class(out) = "tinyplot_type"
+ return(out)
+}
diff --git a/altdoc/quarto_website.yml b/altdoc/quarto_website.yml
index b8a0a49f..dd2e97ca 100644
--- a/altdoc/quarto_website.yml
+++ b/altdoc/quarto_website.yml
@@ -23,7 +23,6 @@ website:
file: vignettes/types.qmd
- text: Gallery
file: vignettes/gallery.qmd
- # - section: $ALTDOC_MAN_BLOCK
- section: Reference
contents:
- section: Plotting functions
@@ -36,12 +35,15 @@ website:
file: man/draw_legend.qmd
- section: Plot types
contents:
- #
- section: Shapes
contents:
- - text: type_area, type_ribbon
+ - text: type_area
file: man/type_ribbon.qmd
- - text: type_errorbar, type_pointrange
+ - text: type_ribbon
+ file: man/type_ribbon.qmd
+ - text: type_errorbar
+ file: man/type_errorbar.qmd
+ - text: type_pointrange
file: man/type_errorbar.qmd
- text: type_lines
file: man/type_lines.qmd
@@ -55,7 +57,6 @@ website:
file: man/type_rect.qmd
- text: type_segments
file: man/type_segments.qmd
- #
- section: Visualizations
contents:
- text: type_boxplot
@@ -66,7 +67,14 @@ website:
file: man/type_jitter.qmd
- text: type_spineplot
file: man/type_spineplot.qmd
- #
+ - section: Functions
+ contents:
+ - text: type_abline
+ file: man/type_abline.qmd
+ - text: type_hline
+ file: man/type_hline.qmd
+ - text: type_vline
+ file: man/type_vline.qmd
- section: Models
contents:
- text: type_glm
diff --git a/inst/tinytest/_tinysnapshot/hline.svg b/inst/tinytest/_tinysnapshot/hline.svg
new file mode 100644
index 00000000..af5b5c31
--- /dev/null
+++ b/inst/tinytest/_tinysnapshot/hline.svg
@@ -0,0 +1,232 @@
+
+
diff --git a/inst/tinytest/_tinysnapshot/vline_vector.svg b/inst/tinytest/_tinysnapshot/vline_vector.svg
new file mode 100644
index 00000000..98f670c8
--- /dev/null
+++ b/inst/tinytest/_tinysnapshot/vline_vector.svg
@@ -0,0 +1,232 @@
+
+
diff --git a/inst/tinytest/test-abline.R b/inst/tinytest/test-abline.R
new file mode 100644
index 00000000..98c4c92d
--- /dev/null
+++ b/inst/tinytest/test-abline.R
@@ -0,0 +1,37 @@
+source("helpers.R")
+using("tinysnapshot")
+
+expect_error(tinyplot(type = type_hline(h = 10)), pattern = "data points")
+expect_error(tinyplot(type = type_vline(v = 10)), pattern = "data points")
+expect_error(tinyplot(type = type_abline(a = 0, b = 1)), pattern = "data points")
+
+
+f = function() {
+ plt(mpg ~ hp | factor(cyl), facet = ~ factor(cyl), data = mtcars)
+ plt_add(type = type_hline(h = 12, col = "pink", lty = 3, lwd = 3))
+}
+expect_snapshot_plot(f, label = "hline")
+
+
+f = function() {
+ tinyplot(mpg ~ hp | factor(cyl),
+ facet = ~ factor(cyl), data = mtcars,
+ col = c("black", "green", "orange"))
+ tinyplot_add(type = type_vline(
+ v = c(100, 150, 200), lty = 3, lwd = 3,
+ col = c("black", "green", "orange")
+ ))
+}
+expect_snapshot_plot(f, label = "vline_vector")
+
+
+## TODO: uncomment this when ready to test. Probably after the tinyplot_add
+## refactor to save in an environment instead of global option
+# f = function() {
+# mod = lm(mpg ~ hp, data = mtcars)
+# y = mtcars$mpg
+# yhat = predict(mod)
+# tinyplot(y, yhat, type = type_abline(a = 0, b = 1), xlim = c(0, 40), ylim = c(0, 40))
+# tinyplot_add(type = type_abline(a = 0, b = 1), xlim = c(0, 40), ylim = c(0, 40))
+# }
+# expect_snapshot_plot(f, label = "abline")
diff --git a/man/type_abline.Rd b/man/type_abline.Rd
new file mode 100644
index 00000000..a25b1c94
--- /dev/null
+++ b/man/type_abline.Rd
@@ -0,0 +1,43 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/type_abline.R
+\name{type_abline}
+\alias{type_abline}
+\title{Add straight lines to a plot}
+\usage{
+type_abline(a = 0, b = 1, col = NULL, lty = NULL, lwd = NULL)
+}
+\arguments{
+\item{a, b}{the intercept and slope, single values.}
+
+\item{col}{plotting color. Character, integer, or vector of length equal to
+the number of categories in the \code{by} variable. See \code{col}. Note that the
+default behaviour in \code{tinyplot} is to vary group colors along any variables
+declared in the \code{by} argument. Thus, specifying colors manually should not
+be necessary unless users wish to override the automatic colors produced by
+this grouping process. Typically, this would only be done if grouping
+features are deferred to some other graphical parameter (i.e., passing the
+"by" keyword to one of \code{pch}, \code{lty}, \code{lwd}, or \code{bg}; see below.)}
+
+\item{lty}{line type. Character, integer, or vector of length equal to the
+number of categories in the \code{by} variable. See \code{lty}. In addition, users
+can supply a special \code{lty = "by"} convenience argument, in which case the
+line type will automatically loop over the number groups. This automatic
+looping will begin at the global line type value (i.e., \code{par("lty")}) and
+recycle as necessary.}
+
+\item{lwd}{line width. Numeric scalar or vector of length equal to the
+number of categories in the \code{by} variable. See \code{lwd}. In addition, users
+can supply a special \code{lwd = "by"} convenience argument, in which case the
+line width will automatically loop over the number of groups. This
+automatic looping will be centered at the global line width value (i.e.,}
+}
+\description{
+Add straight lines to a plot
+}
+\examples{
+mod = lm(mpg ~ hp, data = mtcars)
+y = mtcars$mpg
+yhat = predict(mod)
+tinyplot(y, yhat, xlim = c(0, 40), ylim = c(0, 40))
+tinyplot_add(type = type_abline(a = 0, b = 1))
+}
diff --git a/man/type_hline.Rd b/man/type_hline.Rd
new file mode 100644
index 00000000..84469da7
--- /dev/null
+++ b/man/type_hline.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/type_hline.R
+\name{type_hline}
+\alias{type_hline}
+\title{Trace a horizontal line on the plot}
+\usage{
+type_hline(h = 0, col = NULL, lty = NULL, lwd = NULL)
+}
+\arguments{
+\item{h}{y-value(s) for horizontal line(s). Numeric of length 1 or equal to the number of facets.}
+
+\item{col}{plotting color. Character, integer, or vector of length equal to
+the number of categories in the \code{by} variable. See \code{col}. Note that the
+default behaviour in \code{tinyplot} is to vary group colors along any variables
+declared in the \code{by} argument. Thus, specifying colors manually should not
+be necessary unless users wish to override the automatic colors produced by
+this grouping process. Typically, this would only be done if grouping
+features are deferred to some other graphical parameter (i.e., passing the
+"by" keyword to one of \code{pch}, \code{lty}, \code{lwd}, or \code{bg}; see below.)}
+
+\item{lty}{line type. Character, integer, or vector of length equal to the
+number of categories in the \code{by} variable. See \code{lty}. In addition, users
+can supply a special \code{lty = "by"} convenience argument, in which case the
+line type will automatically loop over the number groups. This automatic
+looping will begin at the global line type value (i.e., \code{par("lty")}) and
+recycle as necessary.}
+
+\item{lwd}{line width. Numeric scalar or vector of length equal to the
+number of categories in the \code{by} variable. See \code{lwd}. In addition, users
+can supply a special \code{lwd = "by"} convenience argument, in which case the
+line width will automatically loop over the number of groups. This
+automatic looping will be centered at the global line width value (i.e.,}
+}
+\description{
+Trace a horizontal line on the plot
+}
+\examples{
+tinyplot(mpg ~ hp | factor(cyl), facet = ~ factor(cyl), data = mtcars)
+tinyplot_add(type = type_hline(h = 12, col = "pink", lty = 3, lwd = 3))
+}
diff --git a/man/type_vline.Rd b/man/type_vline.Rd
new file mode 100644
index 00000000..d52a892c
--- /dev/null
+++ b/man/type_vline.Rd
@@ -0,0 +1,47 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/type_vline.R
+\name{type_vline}
+\alias{type_vline}
+\title{Trace a vertical line on the plot}
+\usage{
+type_vline(v = 0, col = "black", lty = 1, lwd = 1)
+}
+\arguments{
+\item{v}{x-value(s) for vertical line(s). Numeric of length 1 or equal to the number of facets.}
+
+\item{col}{plotting color. Character, integer, or vector of length equal to
+the number of categories in the \code{by} variable. See \code{col}. Note that the
+default behaviour in \code{tinyplot} is to vary group colors along any variables
+declared in the \code{by} argument. Thus, specifying colors manually should not
+be necessary unless users wish to override the automatic colors produced by
+this grouping process. Typically, this would only be done if grouping
+features are deferred to some other graphical parameter (i.e., passing the
+"by" keyword to one of \code{pch}, \code{lty}, \code{lwd}, or \code{bg}; see below.)}
+
+\item{lty}{line type. Character, integer, or vector of length equal to the
+number of categories in the \code{by} variable. See \code{lty}. In addition, users
+can supply a special \code{lty = "by"} convenience argument, in which case the
+line type will automatically loop over the number groups. This automatic
+looping will begin at the global line type value (i.e., \code{par("lty")}) and
+recycle as necessary.}
+
+\item{lwd}{line width. Numeric scalar or vector of length equal to the
+number of categories in the \code{by} variable. See \code{lwd}. In addition, users
+can supply a special \code{lwd = "by"} convenience argument, in which case the
+line width will automatically loop over the number of groups. This
+automatic looping will be centered at the global line width value (i.e.,}
+}
+\description{
+Trace a vertical line on the plot
+}
+\examples{
+tinyplot(mpg ~ hp, data = mtcars)
+tinyplot_add(type = type_vline(150))
+
+# facet-specify location and colors
+cols = c("black", "green", "orange")
+tinyplot(mpg ~ hp | factor(cyl), facet = ~ factor(cyl), data = mtcars, col = cols)
+tinyplot_add(type = type_vline(
+ v = c(100, 150, 200), col = cols, lty = 3, lwd = 3
+))
+}