NamedColors.frink

Download or view NamedColors.frink in plain text format


/** This class contains routines for working with named colors.  It uses the
    color names from xkcd:
    See: https://blog.xkcd.com/2010/05/03/color-survey-results/

    And requires the file available at:
    https://xkcd.com/color/rgb.txt
    which should be renamed to xkcdrgb.txt to match the value in the readFile
    method.

    This class contains static methods that can be used to find named colors,
    to find colors with similar names, or to find colors that are similar to
    a specified color.

    In this file, colors are specified as [r,g,b] arrays where each value
    is expected to be an integer between 0 and 255 inclusive.
*/

class NamedColors
{
   /** Name to [r,g,b] dictionary */
   class var nameDict = NamedColors.readFile[]

   /** Takes a name and returns the [name, [r,g,b]] value for that color.  If
       the exact color name does not exist, this uses the closest name by
       Levenshtein-Damerau edit distance.
   */

   class byName[name] :=
   {
      name = lc[name]
      color = nameDict@name
      if color != undef
         return [name, color]

      closestDist = million
      closestName = undef
      closestColor = undef
      for [n, color] = nameDict
      {
         dist = editDistanceDamerau[n, name]
         if dist < closestDist
         {
            closestDist = dist
            closestName = n
            closestColor = color
         }
      }

      return [closestName, closestColor]
   }

   /** Retains all the [name, [r,g,b]] values containing the specified text. */
   class containingName[name] :=
   {
      name = lc[name]
      reg = regex[name]
      result = new array
      for [n, color] = nameDict
      {
         if n =~ reg
            result.push[[n, color]]
      }

      return sort[result, {|a,b| length[a@0] <=> length[b@0]}]
   }

   /** Finds the nearest named color from an array representing colors
       as [r,g,b].
       Returns [name, [r,g,b]]
   */

   class nearestColor[array] :=
   {
      return nearestColor[array@0, array@1, array@2]
   }

   /** Finds the nearest named color.  Returns [name, [r,g,b]] */
   class nearestColor[r, g, b] :=
   {
      c1 = [r, g, b]
      nearestDist = million
      nearestName = undef
      nearestColor = undef
      for [name, c2] = nameDict
      {
         dist = hypotenuse[abs[subtract[c2,c1]]]
         if dist < nearestDist
         {
            nearestDist = dist
            nearestName = name
            nearestColor = c2
         }
      }

      return [nearestName, nearestColor]
   }

   /** Finds the nearest named colors to a color specified as an array [r,g,b].
       Returns an array of [name, [r,g,b]]  of length at most num. */

   class nearestColors[array, num] :=
   {
      return nearestColors[array@0, array@1, array@2, num]
   }

   /** Finds the nearest named colors.  Returns an array of [name, [r,g,b]]
       of length at most num.
   */

   class nearestColors[r, g, b, num] :=
   {
      c1 = [r, g, b]
      result = new array
      for [name, c2] = nameDict
      {
         dist = hypotenuse[abs[subtract[c2,c1]]]
         result.push[[name, c2, dist]]
      }

      return first[sort[result, byColumn[2]], num]
   }

   /** Turns a [r,g,b] color array from 0 to 255 into a color object. */
   class toColor[array] :=
   {
      return new color[array@0 / 255, array@1 / 255, array@2 / 255]
   }
   
   /** Turns a [r,g,b] color array from 0 to 255 into a hex string. */
   class toHex[array] :=
   {
      return padLeft[hex[array@0], 2, "0"] +
             padLeft[hex[array@1], 2, "0"] +
             padLeft[hex[array@2], 2, "0"]
   }

   /** Parses a color from hexadecimal notation with an optional preceding
       # sign. */

   class fromHex[str] :=
   {
      if [r, g, b] = str =~ %r/#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/
      {
         r = parseInt[r, 16]
         g = parseInt[g, 16]
         b = parseInt[b, 16]
         return [r, g, b]
      }

      return undef
   }
   
   /** Reads and initializes from the data file.  You do not need to call this;
       it is called automatically on class initialization. */

   class readFile[] :=
   {
      nd = new dict
      LINE:
      for line = lines["file:xkcdrgb.txt"]
      {
         if line =~ %r/^\s*#/
            next LINE

         if [name, r, g, b] = line =~ %r/(.*?)\s*#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i
         {
            r = parseInt[r, 16]
            g = parseInt[g, 16]
            b = parseInt[b, 16]
            nd@name = [r,g,b]
         }
      }

      return nd
   }
}


Download or view NamedColors.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen, eliasen@mindspring.com