Tuesday, 23 February 2021

Colour Names 2

The companion colournames.html web page, contains experiments with naming colours.

As mentioned previously, HTML5/CSS colour names are highly subjective and lists have developed organically over many decades. As the 140 unique names mentioned in the first post derive from the original X11 list, I'll refer to them as X11 colours.

When plotted in RGB space, it is obvious that named X11 colours aren't evenly distributed:

The 140 unique X11 colours plotted in RGB space

There is a large cluster of very pale (near white) colours. But what about hue distribution?

A simplistic mechanism for dividing a colour space is to slice it up according to HSL parameters. If we use the following pseudocode, we get nine colour groups:

if lightness >= 95% then group = "whites"
else if saturation <= 15% then group = "blacks"
else if hue < 20° or hue >= 320° then group = "reds"
else if hue < 45° then group = "oranges"
else if hue < 75° then group = "yellows"
else if hue < 155° then group = "greens"
else if hue < 185° then group = "aquas"
else if hue < 255° then group = "blues"
else group = "purples"

For the X11 colours, we get 12 whites, 9 blacks, 23 reds, 20 oranges, 12 yellows, 17 greens, 14 aquas, 19 blues and 14 purples. This sounds like a fairly good distribution until you plot them as the number of colour names per degree of HSL hue:

X11 colour names per degree of HSL hue

We can see that the red-orange-yellow hue range is much more crowded.

One way to deal with this lack of uniform distribution is to pick colour points that are uniformly distributed and then name those points according to some dictionary. Our first attempt could be to pick RGB colours at regular intervals.

Web-safe colours are limited to six levels for each RGB channel (0%, 20%, 40%, 60%, 80% and 100%) for a total of 216 distinct colours. However, they are mostly unnamed. If we reduce the number of levels to five (0%, 25%, 50%, 75% and 100%), we get 125 distinct colours, similar to the number of X11 colours. Obviously, the X11 colours won't align perfectly, so there must be some fettling.

In the process of performing this experiment, I found that jan Misali has attempted something very similar. But it's an experiment, right? So repeating it cannot hurt.

The names in the so-called RGB-125 dictionary come from a variety of sources:
I also wanted the colour names to be single words, without qualifiers and unambiguous. For instance, "Chocolate" has very different RGB values in the various source dictionaries. "Lavender" is another example.

To compare colours objectively, I used the CIELAB ΔE*(1994) colour difference metric purely because there was a JavaScript function readily available. I should probably have used CIEDE2000, not least because the CIE94 algorithm is frustratingly quasimetric , i.e. CIE94(a, b) ≠ CIE94(b, a).

I had to invent ten names: "majorelle", "leaf", "lagoon", "felicia", "frog", "lettuce", "roxo", "sororia", "limon" and "kovidar".

See the full RGB-125 table here.

Thursday, 18 February 2021

Tetrascii 3

I took the plunge and animated the tetromino character set with each glyph made up of 25 pieces falling in order: https://chilliant.com/tetrascii.html

The final code to produce the animations is surprisingly concise, but the process of generating the data tables was a bit more involved.

Firstly, I originally drew each glyph in PowerPoint, so the construction wasn't very data-friendly. I had to screen-grab the slide and process the resultant image via JavaScript and HTML5 canvas elements.

Each screen-grabbed glyph was "pixel walked" to work out which of the 10x10 texels were directly connected to their neighbours. From the 100 texel neighbour data, I was able to reconstruct the shape, position and orientation of the 25 tetrominoes that made up the glyph. The pixel intensity was used to determine if a tetromino was foreground or background.

The 25 tetrominoes for each glyph were ordered so that they stacked correctly, from bottom to top. This involved finding candidates (pieces whose lower boundaries all fit exactly on top of existing pieces) and picking a random candidate. Note that this simple scheme disallows overhangs, so pieces can drop vertically.

The glyphs are string-encoded as 25 groups of three characters: "<letter><x><y>".

"<letter>" indicates the shape and orientation of the piece. Lowercase letters are background pieces, uppercase are foreground:

     +---- +---- cyan
AB   |#### |#
     |     |#
     |     |#
     |     |#

     +---- +---- +---- +---- orange
EFGH |###  |##   |  #  |#
     |#    | #   |###  |#
     |     | #   |     |##
     |     |     |     |

     +---- +---- +---- +---- blue
IJKL |###  | #   |#    |##
     |  #  | #   |###  |#
     |     |##   |     |#
     |     |     |     |

     +---- +---- +---- +---- purple
MNOP |###  | #   | #   |#
     | #   |##   |###  |##
     |     | #   |     |#
     |     |     |     |

     +---- +---- red
QR   |##   | #
     | ##  |##
     |     |#
     |     |

     +---- +---- green
UV   | ##  |#
     |##   |##
     |     | #
     |     |

     +---- yellow
Y    |##
     |##
     |
     |

"<x><y>" is the coordinate within the 10x10 glyph grid of the top-left corner of the piece.

The resultant 75-character encoding for each glyph therefore encapsulates:

  • The order that the pieces fall
  • The shape and orientation of each piece
  • The column that each piece falls in
  • The row that each piece comes to rest on
  • The colour of each piece
A few more hoops have to be jumped through to convert this information into the final animated SVG elements, but that's because of the baroque relationships between SVG, HTML and CSS.

Wednesday, 10 February 2021

Tetrascii 2

Well, it turns out that the Tetrascii lowercase letters are relatively easy if you open up the smaller counters:

Tetrascii

It's not too bad a bitmap font, given the limitations imposed. Some of the glyphs are necessarily quirky, but that just adds character. Cough, cough.

Tetrascii 1

What do we get when you cross two old computer phenomena: ASCII and Tetris?

Tetrascii

The "rules" are as follows:
  1. Each glyph must fit within a 10x10 grid.
  2. The foreground pixels must be constructible from standard tetrominoes.
  3. The background pixels (including counters) must be constructible from standard tetrominoes.
It's surprisingly difficult to construct a readable font. Obviously, I've cheated with the lowercase letters; but in my defence, the counters of the uppercase letters are hard enough. The minimum counter size is four pixels and you can see the trouble I had with the dollar sign.

A project for another time would be to animate the construction of text by falling pieces.

Wednesday, 27 January 2021

Whitty T-Shirt

 


Whittycisms:*
  • Unpack your bags
  • Wash your hands
  • Next slide, please
* A three-word remark uttered by Chris Whitty

Sunday, 29 November 2020

egg Pointer Ambiguity

I've been beavering away on the egg programming language for the last few months. One of the major stumbling blocks has been the type system. As Hisham Muhammad points out, type systems are almost always more complex they first appear to be. I've had to re-implement the compiler and virtual machine several times because of fundamental mistakes I've made with the egg type system, even though it's supposedly "simple."

Here's an example of one problem I'm still struggling with right now.

I'd like to have "safe pointers" in the egg language to handle concepts such as pass-by-reference:

bool safeDivide(float num, float den, float* out) {
  if (den == 0) {
    return false;
  }
  *out = num / dev;
  return true;
}

This all looks hunky-dory, but now consider this:

any v = 123; // line 1
int* p = &v; // line 2
v = "hello"; // line 3

In line 1, we define a variable 'v' that can store most types of value and initialize it with an integer value. In line 2, we define a pointer variable 'p' and point it at 'v'. In line 3, we modify 'v' to be a string. The question is: "What is the value of '*p' after line 3?"

The type declaration of 'p' suggests that '*p' should (always) be an integer, but it's now pointing to a string. There's obviously something "wrong" here, but what exactly is it?

Option A

Line 2 should have produced a compile-time error along the lines of
Cannot initialize a pointer to a value of type 'int' with the address of a value of type 'any'

That is, we only allow pointers to point to values of exactly the appropriate type. This requires that the type of any operand of the address-of '&' operator is known precisely at compile-time.

Option B

Line 3 should have produced a compile-time error because the assignment invalidates the type constraint of 'p'. This requires us to perform some very sophisticated static analysis; I'm not even sure it's possible beyond trivial examples.

Option C

Line 3 produces a runtime error because the assignment invalidates the type constraint of 'p'. This requires us to keep track of all pointers pointing to a value and checking for invalidation on every assignment. This "observer" scheme sounds very expensive to me.

Option D

Produce a runtime error if or when 'p' is subsequently dereferenced and it is discovered that it no longer points to an integer. This would mean that the error is raised "at a distance" from the assignment that caused the issue, thereby making debugging more difficult.

Option E

We make egg pointers "typeless" or, put it another way, all pointers must be of type 'any?*'. This means that if the pointee changes type, we don't really care.

Option F

Nothing is wrong! Just live with the fact that '*p' isn't necessarily an integer, even though it's defined like that.

It all comes down to how the runtime type of the pointee and the compile-time declaration of the pointer interact. I cannot imagine I'm the first to have come across this issue, but a quick search of literature hasn't come up with anything. But then, I don't know what the problem is called, so I'm stumbling in the dark somewhat.

My current "least-hated solution" is a hybrid of Options A and D: try to detect inconsistencies at compile-time but fall back to checking at runtime whenever the pointer is dereferenced.

Sunday, 26 July 2020

Colour Names 1

It's common knowledge that web colour names are derived from the X11 colour names. But the relationship is in reality a series of compromises. Here's a comparison list:

ColourX11 Name(s)Web Name(s)RGB
    Alice Bluealiceblue#F0F8FF
    Antique Whiteantiquewhite#FAEBD7
    Aqua
Cyan
aqua
cyan
#00FFFFNote 1
    Aquamarineaquamarine#7FFFD4
    Azureazure#F0FFFF
    Beigebeige#F5F5DC
    Bisquebisque#FFE4C4
    Blackblack#000000
    Blanched Almondblanchedalmond#FFEBCD
    Blueblue#0000FF
    Blue Violetblueviolet#8A2BE2
    Brownbrown#A52A2A
    Burlywoodburlywood#DEB887
    Cadet Bluecadetblue#5F9EA0
    Chartreusechartreuse#7FFF00
    Chocolatechocolate#D2691E
    Coralcoral#FF7F50
    Cornflower Bluecornflowerblue#6495ED
    Cornsilkcornsilk#FFF8DC
    Crimsoncrimson#DC143C
    Dark Bluedarkblue#00008B
    Dark Cyandarkcyan#008B8B
    Dark Goldenroddarkgoldenrod#B8860B
    Dark Gray
Dark Grey
darkgray
darkgrey
#A9A9A9Note 2
    Dark Greendarkgreen#006400
    Dark Khakidarkkhaki#BDB76B
    Dark Magentadarkmagenta#8B008B
    Dark Olive Greendarkolivegreen#556B2F
    Dark Orangedarkorange#FF8C00
    Dark Orchiddarkorchid#9932CC
    Dark Reddarkred#8B0000
    Dark Salmondarksalmon#E9967A
    Dark Sea Greendarkseagreen#8FBC8F
    Dark Slate Bluedarkslateblue#483D8B
    Dark Slate Gray
Dark Slate Grey
darkslategray
darkslategrey
#2F4F4FNote 2
    Dark Turquoisedarkturquoise#00CED1
    Dark Violetdarkviolet#9400D3
    Deep Pinkdeeppink#FF1493
    Deep Sky Bluedeepskyblue#00BFFF
    Dim Gray
Dim Grey
dimgray
dimgrey
#696969Note 2
    Dodger Bluedodgerblue#1E90FF
    Firebrickfirebrick#B22222
    Floral Whitefloralwhite#FFFAF0
    Forest Greenforestgreen#228B22
    Fuchsia
Magenta
fuchsia
magenta
#FF00FFNote 3
    Gainsborogainsboro#DCDCDC
    Ghost Whiteghostwhite#F8F8FF
    Goldgold#FFD700
    Goldenrodgoldenrod#DAA520
    Gray
Grey
X11 Gray
X11 Grey
n/a#BEBEBENote 4
    Green Yellowgreenyellow#ADFF2F
    Honeydewhoneydew#F0FFF0
    Hot Pinkhotpink#FF69B4
    Indian Redindianred#CD5C5C
    Indigoindigo#4B0082
    Ivoryivory#FFFFF0
    Khakikhaki#F0E68C
    Lavenderlavender#E6E6FA
    Lavender Blushlavenderblush#FFF0F5
    Lawn Greenlawngreen#7CFC00
    Lemon Chiffonlemonchiffon#FFFACD
    Light Bluelightblue#ADD8E6
    Light Corallightcoral#F08080
    Light Cyanlightcyan#E0FFFF
    Light Goldenrodlightgoldenrod#FFEC8BNote 5
    Light Goldenrod Yellowlightgoldenrodyellow#FAFAD2Note 5
    Light Gray
Light Grey
lightgray
lightrey
#D3D3D3
    Light Greenlightgreen#90EE90
    Light Pinklightpink#FFB6C1
    Light Salmonlightsalmon#FFA07A
    Light Sea Greenlightseagreen#20B2AA
    Light Sky Bluelightskyblue#87CEFA
    Light Slate Gray
Light Slate Grey
lightslategray
lightslategrey
#778899
    Light Steel Bluelightsteelblue#B0C4DE
    Light Yellowlightyellow#FFFFE0
    Lime Greenlimegreen#32CD32
    Lime
Green
X11 Green
lime#00FF00Note 6
    Linenlinen#FAF0E6
    Maroon
X11 Maroon
n/a#B03060Note 7
    Medium Aquamarinemediumaquamarine#66CDAA
    Medium Bluemediumblue#0000CD
    Medium Orchidmediumorchid#BA55D3
    Medium Purplemediumpurple#9370DB
    Medium Sea Greenmediumseagreen#3CB371
    Medium Slate Bluemediumslateblue#7B68EE
    Medium Spring Greenmediumspringgreen#00FA9A
    Medium Turquoisemediumturquoise#48D1CC
    Medium Violet Redmediumvioletred#C71585
    Midnight Bluemidnightblue#191970
    Mint Creammintcream#F5FFFA
    Misty Rosemistyrose#FFE4E1
    Moccasinmoccasin#FFE4B5
    Navajo Whitenavajowhite#FFDEAD
    Navy
Navy Blue
navy#000080Note 8
    Old Laceoldlace#FDF5E6
    Oliveolive#808000
    Olive Drabolivedrab#6B8E23
    Orangeorange#FFA500
    Orange Redorangered#FF4500
    Orchidorchid#DA70D6
    Pale Goldenrodpalegoldenrod#EEE8AA
    Pale Greenpalegreen#98FB98
    Pale Turquoisepaleturquoise#AFEEEE
    Pale Violet Redpalevioletred#DB7093
    Papaya Whippapayawhip#FFEFD5
    Peach Puffpeachpuff#FFDAB9
    Peruperu#CD853F
    Pinkpink#FFC0CB
    Plumplum#DDA0DD
    Powder Bluepowderblue#B0E0E6
    Purple
X11 Purple
n/a#A020F0Note 9
    Rebecca Purplerebeccapurple#663399Note 10
    Redred#FF0000
    Rosy Brownrosybrown#BC8F8F
    Royal Blueroyalblue#4169E1
    Saddle Brownsaddlebrown#8B4513
    Salmonsalmon#FA8072
    Sandy Brownsandybrown#F4A460
    Sea Greenseagreen#2E8B57
    Seashellseashell#FFF5EE
    Siennasienna#A0522D
    Silversilver#C0C0C0
    Sky Blueskyblue#87CEEB
    Slate Blueslateblue#6A5ACD
    Slate Gray
Slate Grey
slategray
slategrey
#708090Note 2
    Snowsnow#FFFAFA
    Spring Greenspringgreen#00FF7F
    Steel Bluesteelblue#4682B4
    Tantan#D2B48C
    Tealteal#008080
    Thistlethistle#D8BFD8
    Tomatotomato#FF6347
    Turquoiseturquoise#40E0D0
    Violetviolet#EE82EE
    Web Gray
Web Grey
gray
grey
#808080Note 4
    Web Greengreen#008000Note 6
    Web Maroonmaroon#800000Note 7
    Web Purplepurple#800080Note 9
    Wheatwheat#F5DEB3
    Whitewhite#FFFFFF
    White Smokewhitesmoke#F5F5F5
    Yellowyellow#FFFF00
    Yellow Greenyellowgreen#9ACD32

Notes:
  1. "Aqua" and "Cyan" are synonyms and refer to the same RGB colours.
  2. In X11 and hence web names, "Gray" (en-us) and "Grey" (en-gb) are synonyms.
  3. "Fuchsia" and "Magenta" are synonyms and refer to the same RGB colours.
  4. There is no equivalent of X11 "Gray/Grey" in the web colours. The closest is named "Web Gray/Gray" in X11 and maps to "gray/grey" in the web namespace.
  5. "Light Goldenrod" and "Light Goldenrod Yellow" are two distinct colours in both namespaces. Many online lists conflate the two.
  6. "Green" is different in the X11 (#00FF00) and Web (#008000) namespaces.
  7. "Maroon" is different in the X11 (#B03060) and Web (#800000) namespaces.
  8. "Navy/Navy Blue" can only be officially named "navy" in the Web namespace.
  9. "Purple" is different in the X11 (#A020F0) and Web (#800080) namespaces.
  10. "Rebecca Purple" was added to X11 and CSS 4.1.
If we exclude the three X11 colours ("Gray/Grey", "Maroon" and "Purple") that have no web equivalent, we are left with the 140 unique web colours.