TITLE: R function to find nearest named colour
DATE: 2022-12-23
AUTHOR: John L. Godlee
====================================================================


I wrote an R function to find the nearest named colour to a given
colour hexcode.

   #' Find the closest named colour to a given six character hex
colour
   #'
   #' @param x vector of six digit hex colours
   #' @param method colour space, either "rgb" or "hsv".
   #' @param metric distance metric, either "euclidean" or
"manhattan".
   #'
   #' @return vector of nearest named colour
   #'
   #' @details If metric is "euclidean", distances are root
sum-of-squares
   #'     differences. "manhattan" distances are the sum of
absolute differences.
   #'     The named colours come from the list of 657 named
colours stored in R,
   #'     accessible using the \code{colors()} function.
   #'
   #' @examples
   #' hexName(c("#117733", "#b58900", "#855C75"))
   #'
   #' @export
   #'
   hexName <- function(x, method = "rgb", metric = "euclidean") {
     # Check input is valid
     if (any(nchar(x) != 7) | any(!grepl("^#", x))) {
       stop("Hex code(s) invalid")
     }

     if (!method %in% c("rgb", "hsv")) {
       stop("method must be 'rgb' or 'hsv'")
     }

     if (!metric %in% c("euclidean", "manhattan")) {
       stop("metric must be 'euclidean' or 'manhattan'")
     }

     # Convert hex string to RGB
     x <- col2rgb(x)

     # Create matrix of named colours as RGB values
     coltab <- col2rgb(colors())

     # If HSV
     if (method == "hsv") {
       x <- rgb2hsv(x)
       coltab <- rgb2hsv(coltab)
     }

     # Find nearest named colour by metric
     if (metric == "euclidean") {
       out <- colors()[apply(x, 2, function(y) {
         which.min(apply(apply(coltab, 2, "-", y)^2, 2, sum))
       })]
     } else if (metric == "manhattan") {
       out <- colors()[apply(x, 2, function(y) {
         which.min(apply(abs(apply(coltab, 2, "-", y)), 2, sum))
       })]
     }

     # Return
     return(out)
   }

The function lets you choose between either the RGB (Red, Green,
Blue) or HSV (Hue, Saturation, Value) colour space. HSV is an
alternative representation of the RGB colour space which more
closely aligns with the way the human eye perceives colour
differences. You can also choose between using Manhattan or
Euclidean distances to find the nearest neighbour named colour.

R stores a list of 657 named colours, accessible from the colors()
function.

Bonus: while learning about colour spaces I also wrote a function
which takes a six digit colour hexcode and then finds the nearest
three digit hexcode:

   #' Find the closest hex triplet to a given six character hex
colour
   #'
   #' @param x vector of six digit hex colours
   #'
   #' @return vector of nearest hex colour represented as a triplet
   #'
   #' @examples
   #' hexTrip(c("#117733", "#b58900", "#855C75"))
   #' hexTrip("#fffffff")
   #' hexTrip("855C75")
   #'
   #' @export
   #'
   hexTrip <- function(x) {
     if (any(nchar(x) != 7) | any(!grepl("^#", x))) {
       stop ("Hex code(s) invalid")
     }

     unlist(lapply(x, function(y) {
       paste(c("#", sprintf("%x", (col2rgb(y) + 8) %/% 17)),
collapse = "")
     }))
   }