library(jpeg)
library(png)
#' Get image url quote list from Google.
#'
#' @param qword String. Image search key word.
#' @return An image url quote list with 20 entries.
#' Google limits number of images returned by 20.
#' @export
get.imglist <- function(qword) {
qword <- sub(" ", "+", qword)
qurl <- paste("https://www.google.com/search?q=",
qword, "&tbm=isch", sep = "")
page <- readLines(qurl, warn = FALSE)
imglist <- strsplit(page, "https://encr", fixed = TRUE)[[1]][-1]
imglist <- paste("https://encr", imglist, sep = "")
imglist <- sub("\\\".*width.*", "", imglist)
return(imglist)
}
#' Import one image from an url quote, output 2d or 3d array.
#'
#' @param imgquote image url quote generated by \code{\link{get.imglist}}.
#' @param out2d If \code{TRUE}, the 3D array of the image will be
#' transformed into 2D.
#' @return A 2D or 3D array representing the image.
#' @export
import <- function(imgquote, out2d = FALSE) {
handler <- tempfile()
download.file(imgquote, handler, mode="wb", quiet = TRUE)
if( is( try(readJPEG(handler), silent = TRUE), "try-error") ) {
img <- readPNG(handler) }
else { img <- readJPEG(handler) }
suppressWarnings(file.remove(handler))
if (out2d) img <- apply(img, 3, rbind)
return(img)
}
#' Print a raster image.
#'
#' @param img A raster image.
#' @return Draw raster image.
#' @export
draw <- function(img, ...) {
w <- dim(img)[1]; h <- dim(img)[2]
plot(c(0, 1), c(0, w/h), bty = "n", col = NA, axes = FALSE,
xlab = "", ylab = "", asp = 1, ...)
rasterImage(img, 0, 0, 1, w/h)
}
#' Plot a colour bar.
#'
#' @param colornames A vector of colour names.
#' @param texts Whether to show text labels of colour names.
#' @return Draw a colour bar.
#' @export
draw.bar <- function(colornames, texts = TRUE) {
n <- length(colornames)
plot(n, 0, col = NA, pch = 16, cex = 2, bty = "n",
axes = FALSE, xlab = "", ylab = "",
ylim = c(0, 0.5), xlim = c(0, n), asp = 1)
for (i in 1:n) {
rect(i-1,0,i,0.5, col = colornames[i], border = NA)
if (texts) {
text(i-1/2, -0.1, cex = max(5/n, 0.4), colornames[i])
text(i-1/2, 0.7, cex = max(5/n, 0.4), i)
}
}
}
#' View 20 Google image search results.
#'
#' @param qword String. Image search key word.
#' @details Google limits number of images returned by 20.
#' @seealso \code{\link{hunt.colors}}
#' @export
#' @examples
#' show.imgs("stardew valley")
#' hunt.colors("stardew valley", 10, 1:10)
show.imgs <- function(qword) {
imglist <- get.imglist(qword)
par(mfrow = c(5, 4), mar = c(0,0,0.6,0))
for(i in 1:20) draw(import(imglist[i]), main = i)
}
#' Find main colours by k-means.
#'
#' @param img A 2D or 3D array. 3D arrays will be transformed into 2D.
#' @param num Number of colours to extract.
#' @param seed Seed for k-means.
#' @export
k.colors <- function(img, num = 5, seed = 42) {
if (length(dim(img)) == 3) allpixels <- apply(img, 3, rbind)
else if (length(dim(img)) == 2) allpixels <- img
else stop("Image dimension error. Try harder.")
set.seed(seed)
allpixels.km <- kmeans(allpixels, num)
out <- apply(allpixels.km$centers, 1, function(r)
rgb(r[1], r[2], r[3]))
out <- out[order(out)]
return(out)
}
fill.seq <- function(sq) {
pos <- matrix(NA, nrow = 2, ncol = length(which(!is.na(sq))))
pos[1, ] <- which(!is.na(sq))
pos[2, ] <- c(pos[1, -1], length(sq))
for (i in 1:dim(pos)[2]) {
sq[pos[1, i] : pos[2, i]] <-
seq(sq[pos[1, i]], sq[pos[2, i]], length = pos[2,i]-pos[1,i]+1)
}
return(sq)
}
#' Extract colours from Google image search results.
#'
#' @param qword String. Image search key word.
#' @param num Number of colours to extract.
#' @param index An integer or a vector indicating which image(s) to use.
#' Images can be viewed using \code{show.imgs}.
#' @param seed Seed for k-means.
#' @param plot Whether to plot the colours.
#' @return A vector of colour names.
#' @seealso \code{\link{show.imgs}}.
#' @export
#' @examples
#' show.imgs("stardew valley")
#' hunt.colors("stardew valley", 10, 1:10)
hunt.colors <- function(qword, num = 5, index = 1, seed = 42,
plot = TRUE) {
imglist <- get.imglist(qword)
allimgs <- import(imglist[index[1]], out2d = TRUE)
if (length(index) > 1) {
for (i in index[-1]) {
img <- import(imglist[i], out2d = TRUE)
allimgs <- rbind(allimgs, img)
}
}
km <- k.colors(allimgs, num, seed)
names(km) <- NULL
if (plot) draw.bar(km)
return(km)
}
make.gradient2 <- function(from = NULL, to = NULL,
len = 5, fixed = NULL,
plot = TRUE) {
if (!is.numeric(len) | as.integer(len) != len) {
len <- 5
warning("Length must be an integer. Defaulted to 5.")
}
if (len == 1) {
if (plot) plot.bar(from)
return(from)
}
if (!is.null(from) & !is.null(to) & !is.null(fixed)) {
fixed <- NULL
warning("Two-color gradient cannot be used in combination
with fixed compents. No components fixed.")
}
method <- "rgb"
mat <- matrix(NA, nrow = 3, ncol = len)
lookup <- rep(c(1, 2, 3), 2)
names(lookup) <- c("r", "g", "b", "h", "s", "v")
if (!is.null (fixed)) {
fixedones <- unlist(strsplit(fixed, ""))
if (any(c("h", "s", "v") %in% fixedones) &
length(grep("[^hsv]", fixed)) == 0)
method <- "hsv"
else if (any(c("r", "g", "b") %in% fixedones) &
length(grep("[^rgb]", fixed)) == 0 )
method <- "rgb"
else {
fixed <- NULL
warning("Invalid fixed components. No components fixed.")
return(make.gradient(from, to, fixed = NULL, plot = plot,
len = len) )
}
locked.row <- lookup[fixedones]
color <- col2rgb(from)
if (method == "hsv") color <- rgb2hsv(color)
else if (method == "rgb") color <- color / 255
pos <- max(1, round (len * color[-locked.row][1]))
mat[, pos] <- color
mat[locked.row, ] <- mat[locked.row, pos]
mat[-locked.row, 1] <- 0
mat[-locked.row, len] <- 1
}
else {
if (!is.null(to)) {
startvalue <- col2rgb(from) / 255
endvalue <- col2rgb(to) / 255
if (method == "hsv") {
startvalue <- rgb2hsv(startvalue)
endvalue <- rgb2hsv(endvalue)
}
mat[, 1] <- startvalue; mat[, len] <- endvalue
}
else {
if (!is.null(from)) {
pos <- max(1, round(len * (mean(col2rgb(from) / 255))))
left <- make.gradient2("black", from, plot = FALSE,
len = pos)
right <- make.gradient2(from, "white", plot = FALSE,
len = (len - pos + 1))[-1]
out <- c(left, right)
if (plot) plot.bar(out)
return(out)
}
else {
return(make.gradient2("black", "white",
plot = plot, len = len))
}
}
}
mat <- apply(mat, 1, fill.seq)
if (method == "rgb") {
out <- apply(t(mat), 2, function(c) rgb(c[1], c[2], c[3]))
}
else if (method == "hsv") {
out <- apply(t(mat), 2, function(c) hsv(c[1], c[2], c[3]))
}
if (plot) draw.bar(out)
return(out)
}
#' Make a colour gradient.
#'
#' @param ... Color names.
#' @param len Length of the gradient. If more than two colors, the length of
#' the step (A 4-colour gradient with \code{len = 5} gives 15 colours).
#' @param fixed A concatenated string indicating the fixed component(s) of
#' the gradient. One or two components (letters) of either \code{"rgb"} or
#' \code{"hsv"} can be specified. See details.
#' @param order If \code{TRUE}, colours are ordered to make a continuous
#' gradient from dark to light.
#' @param plot Whether to plot the gradient bar. Default \code{TRUE}.
#' @param texts Whether to add text labels to the bar. Default \code{TRUE}
#' @return A vector of colour names.
#' @details Examples of valid \code{fixed} are \code{"hv", "h", "rg"} etc.
#' Two-colour gradient cannot be used in combination with \code{fixed}.
#' If only one colour is specified, the colour's position in the gradient
#' is decided by its brightness (if \code{fixed} is \code{NULL}) or by its
#' value of the first fixed component (if \code{fixed} is specified).
#' @export
#' @examples
#' make.gradient()
#' make.gradient("turquoise", fixed = "hv")
#' make.gradient(cyans[1], blues[9], len = 8)
#' make.gradient(blues[1], reds[1], yellows[1], cyans[1])
make.gradient <- function(..., len = 5, fixed = NULL, order = FALSE,
plot = TRUE, texts = TRUE) {
col.list <- c(...)
if (order) {
dist2black <- apply(sapply(col.list, col2rgb), 2,
function(c) dist(rbind(c, c(0,0,0))))
col.list <- col.list[order(dist2black)]
}
if (length(col.list) == 1)
out <- make.gradient2(..., len = len,
fixed = fixed, plot = FALSE)
else if (length(col.list) == 2)
out <- make.gradient2(col.list[1], col.list[2], len = len,
fixed = fixed, plot = FALSE)
else {
out <- NULL
for (i in 2: length(col.list)) {
if (i < length(col.list)) {
out <- c(out,
make.gradient2(col.list[i-1], col.list[i],
len = len + 1, fixed = fixed,
plot = FALSE)[- (len+1)])
}
else {
out <- c(out,
make.gradient2(col.list[i-1], col.list[i],
len = len, fixed = fixed,
plot = FALSE))
}
}
}
if(plot) draw.bar(out, texts)
return(out)
}
#' Draw rectangles/ grids.
#'
#' @param xl First/ left-most xleft.
#' @param yt First/ top-most ytop.
#' @param w Width of rectangles.
#' @param h Height of rectangles.
#' @param ncol Number of columns.
#' @param nrow Number of rows. Default 1.
#' @param border Default to transparent.
#' @param add Default to \code{FALSE}.
#' @param col Fill colours of rects.
#' @param texts Text labels in the rectangles/ grid. Order is by column.
#' @param texts.col Can be colours or one of the two keywords \code{"auto"}
#' and \code{"theme"}. \code{"auto"} would make black/ white texts based on
#' the underlying rectangle colour. \code{"theme"} would choose the colours
#' from the colours supplied to rectangles. Darkest one would be the black
#' equivalent and lightest one the white equivalent. Works well on one-
#' colour gradients.
#' @param scale If \code{TRUE}, the fill colours of cells/ rectangles will
#' be based on the values in the cell (need to be numeric). \code{\link{
#' make.gradient}} can order the gradient key colours beforehand.
#' @return Draw rectangles.
#' @export
#' @examples
#' rects(0, 5, 1, 1, 5, 5, col = occblues, texts = 1:25)
rects <- function(xl, yt, w = 1, h = 1, ncol = 3, nrow = 1,
border = "transparent", add = FALSE, col = occblues,
texts = NULL, texts.col = "auto", scale = FALSE, ...) {
if(length(xl) == 1 & length(yt) == 1) {
xlefts <- seq(xl, by = w, len = ncol)
xlefts <- rep(xlefts, each = nrow)
ybottoms <- seq(yt - h, by = -h, len = nrow)
ybottoms <- rep(ybottoms, ncol)
xrights <- xlefts + w
ytops <- ybottoms + h
}
if(length(xl) > 1 & length(yt) > 1) {
xlefts <- xl
ybottoms <- yt - h
xrights <- xl + w
ytops <- yt
}
if(add == FALSE) {
plot(NULL, xlab = "", ylab = "", axes = FALSE, bty = "n",
xlim = c(min(xlefts), min(xlefts) + ncol * w),
ylim = c(max(ytops) - nrow * h, max(ytops)))
}
if (scale == TRUE) {
texts <- as.numeric(texts)
cuts <- cut(texts, length(col))
col <- col[as.numeric(cuts)]
}
if(ncol * nrow < length(col) & scale == FALSE) col <- col[1:(ncol*nrow)]
dist2white <- apply(sapply(col, col2rgb), 2,
function(c) dist(rbind(c, c(255,255,255))))
dist2black <- apply(sapply(col, col2rgb), 2,
function(c) dist(rbind(c, c(0,0,0))))
if(texts.col == "auto") {
texts.col <- ifelse(dist2black < dist2white, "white", "black")
}
else if (texts.col == "theme") {
coldark <- col[which.min(dist2black)]
collight <- col[which.min(dist2white)]
texts.col <- ifelse(dist2black < dist2white, collight, coldark)
}
return({
rect(xlefts, ybottoms, xrights, ytops,
border = border, col = col, ...)
if (!is.null(texts)) {
text(xlefts + w/2, ybottoms + h/2, lab = texts,
col = texts.col)
}
})
}
#' Pick n most distinctive colours out of many colours.
#'
#' @param colornames A vetor of colour names.
#' @param n How many colours to pick.
#' @param plot Whether to plot the colour bar. Default \code{FALSE}.
#' @return Picked colour names (ordered from dark to light).
#' @details Makes the process of choosing colours easier especially when
#' choosing legend colours in a list of gradient theme colours.
#' @export
#' @examples
#' pink.n(make.gradient(occblues, len = 2), len = 8, plot = TRUE)
pick.n <- function(colornames, n, plot = FALSE) {
nofx <- function(n, x) {
x <- x[order(x)]
combo <- combn(x[-c(1, length(x))], n - 2)
combo <- rbind(x[1], combo, x[length(x)])
combo[, which.max(apply( combo, 2, function(c) min(diff(c)) ))]
}
dist2black <- apply(sapply(colornames, col2rgb), 2,
function(c) dist(rbind(c, c(0,0,0))))
if (n == 1) out <- colornames[sample(1:length(colornames), 1)]
else if (n == 2) out <- colornames[c(which.min(dist2black),
which.max(dist2black))]
else {
out <- colornames[which(dist2black %in% nofx(n, dist2black)) ]
out <- out[!is.na(out)]
}
out <- out[order(dist2black[out])]
if(plot) draw.bar(out)
out
}
smart.gradient <- function(..., len = 5) {
col.list <- list(...)
col.list <- lapply(col.list, function(l) l[sample(length(l), 3)])
grid <- as.data.frame(expand.grid(col.list))
if (nrow(grid) > 15) grid <- grid[sample(nrow(grid), 15), ]
par(mfrow = c(5, 3), mar = rep(0, 4))
apply(grid, 1, make.gradient, len = len)
}
#' Set up a null plot.
#'
#' @param xl0,xl1 xlim. Default c(0, 5).
#' @param yl0,yl1 ylim. Default c(0, 5).
#' @export
setup.null <- function(xl0 = 0, xl1 = 5, yl0 = 0 , yl1 = 5) {
plot(NULL, xlab = "", ylab = "", axes = FALSE,
xlim = c(xl0, xl1), ylim - c(yl0, yl1))
}
#' @export
pinks <- c("pink", "lightpink", "hotpink", "deeppink",
"palevioletred", "mediumvioletred")
#' @export
reds <- c("lightsalmon", "salmon", "darksalmon", "lightcoral",
"indianred", "firebrick", "darkred", "red")
#' @export
oranges <- c("orangered", "tomato", "coral", "darkorange",
"orange")
#' @export
yellows <- c("yellow", "lightyellow", "lemonchiffon",
"lightgoldenrodyellow", "papayawhip", "moccasin",
"peachpuff", "palegoldenrod", "khaki", "darkkhaki",
"gold")
#' @export
browns <- c("cornsilk", "blanchedalmond", "bisque", "navajowhite",
"wheat", "burlywood", "tan", "rosybrown", "sandybrown",
"goldenrod", "darkgoldenrod", "peru", "chocolate",
"saddlebrown", "sienna", "brown", "maroon")
#' @export
greens <- c("darkolivegreen", "olivedrab", "yellowgreen",
"limegreen", "lawngreen", "chartreuse",
"greenyellow", "springgreen", "mediumspringgreen",
"lightgreen", "palegreen", "darkseagreen", "mediumaquamarine",
"mediumseagreen", "seagreen", "forestgreen", "green",
"darkgreen")
#' @export
cyans <- c("cyan", "lightcyan", "paleturquoise", "aquamarine",
"turquoise", "mediumturquoise", "lightseagreen", "cadetblue",
"darkcyan")
#' @export
blues <- c("lightsteelblue", "powderblue", "lightblue", "skyblue",
"lightskyblue", "deepskyblue", "dodgerblue", "cornflowerblue",
"steelblue", "royalblue", "blue", "mediumblue", "darkblue",
"navy", "midnightblue")
#' @export
purples <- c("lavender", "thistle", "plum", "violet", "orchid",
"magenta", "mediumorchid", "mediumpurple",
"blueviolet", "darkviolet", "darkorchid", "darkmagenta",
"purple", "darkslateblue", "mediumslateblue")
#' @export
whites <- c("white", "snow", "honeydew", "mintcream", "azure",
"aliceblue", "ghostwhite", "whitesmoke", "seashell",
"beige", "oldlace", "floralwhite", "ivory", "antiquewhite",
"linen", "lavenderblush", "mistyrose")
#' @export
grays <- c("gainsboro", "lightgray", "darkgray", "gray",
"dimgray", "lightslategray", "slategray", "darkslategray",
"black")
#' @export
html.colors <- list(pinks=pinks, reds=reds, oranges=oranges,
yellows=yellows, browns=browns, greens=greens,
cyans=cyans, blues=blues, purples=purples,
whites=whites, grays=grays)
#' @export
occblues <- c("#CCDDEC", "#99BAD9", "#669DC5",
"#3375B2", "#00539F", "#002C54")
#' @export
occgrays <- c("#C0C0C0", "#969696", "#808080")
#' @export
occgreens <- c("#D1E3D1", "#AFCDAB", "#7FB27F",
"#579A57", "#2B802B", "#006600")
#' @export
occtaupes <- c("#E6DCD7", "#D7C8BE", "#BEAA96", "#968C78", "#827864")
#' @export
occteals <- c("#99D5D5", "#6CC0C0", "#48A6A6", "#3F8E8C", "#33726D" )
#' @export
occ.colors <- list(occblues = occblues,
occgrays = occgrays,
occgreens = occgreens,
occteals = occteals,
occtaupes = occtaupes)
#' @export
xlred <- rgb(248, 105, 107, max = 255)
#' @export
xlyellow <- rgb(255, 235,132, max = 255)
#' @export
xlgreen <- rgb(99, 190, 123, max = 255)
#' @export
xl.colors <- list(xlred = xlred, xlyellow = xlyellow, xlgreen = xlgreen)
#' @export
preset.colors <- list(html.colors = html.colors,
xl.colors = xl.colors,
occ.colors = occ.colors)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.