Magic of Colors

Up till now we used colors in form of their names (e.g. red, green) when painting some graphics. Let's now extend our understanding of them, particularly:

As about names - keep in mind, we make Lua working in web-page, so predictably, we use so-called Web Colors. You can find there is a handful of "basic" color names and probably about hundred "extended". Probably if you are an artist / painter / designer, these are helpful. However for me, as probably, for most of general folks, it seems impossible to recollect all these salmon, thistle, turquoise etc - and especially how they look relative to each other.

About RGB

More consistent way of specifying color is based upon the fact that digital displays usually represent them as combination of 3 components: red, green and blue. From the physical perspective it is good but not ideal approximation (as normal pure colors are rather waves of single specific wavelength in range between deep red and deep violet).

Anyway, this means we can specify the specific amount of each of three component to mix. For this there are several forms. First of them uses word rgb followed by three values in parentheses:

graph.init()
color = 'rgb(100, 200, 50)'
graph.rect(graph.w / 4, graph.h / 4, graph.w / 2, graph.h / 2, color)

Here we use color with red=100, green=200, blue=50. Important is that the whole rgb(...) construct is just a piece of text, not a function call (though it looks similar). It needs to be surrounded with quotes!

The values for components should be in range 0 .. 255 - i.e. single-byte value for each. Thus rgb(0,0,0) is the same as black and on the other hand rgb(255,255,255) is the same as white.

Hexadecimal forms

As byte values could be represented as two hexadecimal digits, there exist a way for such specification - it is also a text string, starting with hash symbol and then six hex digits (pair for red, pair for green, pair for blue):

graph.init()
color = '#FF0080'
graph.circle(graph.w / 2, graph.h / 2, graph.h / 3, color)

Here we set FF (which is 255) for red and 80 (which is 128) for blue. Green is absent. The whole result is similar to "fuchsia".

There is also "short hex" format, which uses single hex digit for every component. I.e. #f39 is the same as #ff3399.

Building color in runtime

The most interesting effect would be achieved if we construct the gradually changing color. For simplest example suppose we paint a series of circles decreasing in size, with the color simultaneously going from black to white:

graph.init()
for p = 0,255 do
    color = 'rgb(' .. p .. ',' .. p .. ',' .. p .. ')'
    graph.circle(graph.w / 2, graph.h / 2, 260 - p, color)
end

You see the colors change very smoothly. Somewhat annoying thing is we need to construct rgb(...) string with several concatenations in such a clumsy way, but later we'll learn how to deal with that.

Now let's try to modify all three components somewhat independently. Let's fill large rectangle with the following rules for color components: red component increases from left to right, i.e. along X coordinate, while blue component increases from top to bottom, i.e. along Y coordinate

graph.init()
for x = 0,255 do
    for y = 0,255 do
        red = x
        blue = y
        c = 'rgb(' .. red .. ',0,' .. blue .. ')'
        graph.rect(x*graph.w//255, y*graph.h//255, 3, 2, c)
    end
end

As you see, we do some simple arithmetic to scale the 255 by 255 square to the whole canvas area. We leave this as mental exercise for yourself. Actually it is not the only way to achieve the same.

If all works well, on running the example you'll see again smooth red-blue-purlpish rectangle.

For exercise try to add green component to this last example. Make it maximal at the top-left corner and minimal at right-bottom corner. Use the phrase below as a hint if you couldn't come up with formula.

Hint: green is two-hundred-fifty-five minus average of the both coordinates...