GTC 2.20 Pro designer

GTC 2.20 brings a huge amount of new features again (described in this post) and is starting an additional, more trimmed down, high level API, geared more toward what designers expect. But it also comes with new documentation and error handling, the two most important things that were missing for a professional distribution.

Let's start with an overview: a new color set creator named analogous arrived and 7 new color calculators (designer API). The method complement got extended and works now in any color space of the HSL family, not just HSL. The 3 new color spaces are OKHSL, OKHSV and OKHWB, which also allow for better defaults and prepare one big upcoming feature. Lots of fine tuning happened to make the API more consistent and the error handling on the user facing side can now be configured. And the greatest effort was a complete rewrite and restructuring of the documentation.

2.1 == 2.10

But before we dive into the details - one housekeeping issue - the versioning schema. I announced the last version as 2.1 and I call it now 2.10 - my mistake. GTC has a three tier (digit) versioning scheme. Top tier is API versions, middle tier is for feature versions and low tier is mere bug fix versions. I hesitate to introduce the second dot to keep it numerical, which avoids a whole host of issues. So please read 2.20 as 2.2.0 aka 2.2.

document this

GTC 2.1 aka 2.10 had most of the user docs on the main page: Graphics::Toolkit::Color, which definitely became too long and yet another page of content was waiting to be added, due to the 8 new methods. The rest of the docs were mixed in with internal docs that are interesting to nobody. So I did what all great CPAN distros do: I added the namespace Graphics::Toolkit::Color::Manual for a comprehensive and well-structured documentation with an index. The main page of the distribution became a quick overview of all methods with links to the longer explanations at the reference pages. I also added a cheatsheet, a cookbook and pages on special topics like color spaces, color definition formats or error handling.

5 ways to die

Speaking of errors - GTC used Carp for the longest time. Then I disliked the idea that normal method calls can interrupt your program, so I switched to error messages via say (to STDOUT). But this solves nothing since calling a method on this error message instead of the expected color object crashes the program too. Now you have five options and can program toward your needs (TIMTOWTDI):

1. carp (warn with caller context - the new default)
2. croak (fatal, catchable, shows where YOU called it)
3. die (fatal, catchable, shows where GTC threw it)
4. say (print error message, continue - previous behavior)
5. quiet (no output, no error - for batch scripts)

OK with 3 new spaces

Version 1.95 brought the OKLAB and OKLCH spaces which are currently the industry standard for perceptual uniformity. In layman's terms: they make for the smoothest gradients and it was a big step for GTC to include them as the only library on CPAN. With GTC 2.20 I add OKHSL, OKHSV and OKHWB, which are drop-in replacements for the spaces designers often use with the same qualities. As a consequence, I changed the default space for color computing methods to OKLAB, except when the method needs a cylindrical space (complement). Then I use OKHSL. And yes OKLCH is cylindrical too, but it doesn't hurt to go with established expectations when there is no reason to break them. Most of the new functionality in this release is powered by this switch to the "OK" spaces. sRGB remains the default space only for the functionality geared toward IO.

universal complement and family business

The complement method is powerful, since it gives you any number of complements (not only triadic and tetradic) that can be tilted in many directions including the one which gives you what designers call a split complement. But it was so far tied to the HSL color space. Most other methods accept the in argument, which sets the color space the operation is calculated in. Now that I got the better suited OKHSL I could change the settings to OKHSL. But it is GTC design philosophy to always give the user options (that make sense). In that case only OKHSL, OKLCH, CIELCHab, CIELCHuv and HSL make sense. They are cylindrical spaces, which is a prerequisite to compute the complement. But the colors are also ordered similarly inside them unlike in the cylindrical but differently ordered and shaped HSV space. To avoid monkeying some special case into the code, I extended the color space DSL with the concept of color space families. All the above mentioned spaces are now part of the HSL family and thus enabled for complement calculation. But another concept had to be introduced to make it possible. The complement is mainly computed on the angular hue axis. This is the first value in a HSL or OKHSL tuple, but the third in a LCH tuple. I needed a way to tell that they refer to a comparable thing. Well, it is now the job of the color space definition to tell me that LCH is in the HSL family and that LCH lightness has the same role as HSL lightness, chroma maps to saturation and hue to hue. Now we can get a space-agnostic complement calculation, which of course defaults to OKHSL.

complement received also a new argument: skew. It lets you move the circle of complementary colors in the one direction that was missing: up and down the lightness axis. To clarify on how all the arguments work together: The caller (invocant or given color) is always part of the circle (you choose it for a reason), unless you want only one complementary color, THE complement (let's call it target). The target argument lets you move this color along all three HSL axes. It is basically an inline add_value operation. Since the whole circle always has to touch the given and target color, you move the whole circle by moving the target. tilt moves the colors along the described circle toward the given (if negative) or toward the target (if positive). You see the given and target colors are the fixtures in this operation. If you draw an axis through these two colors, you might imagine to rotate the circle around this axis so that one half is higher in lightness than the other. This is what skew does. Positive values of skew result in higher lightness in the colors of the second half of the circle and vice versa.

analogous colors

is something designers care for so I added it to meet their language (designer API). It performs a simple interpolation as in: See how this color differs from that? Make me a few more colors where neighbors differ in the same way! You might create complementary colors and gradients this way, just the logic is different here and there are cases where results vastly differ. Just keep in mind that gradient and complement are much more powerful methods, plus there is no guarantee you will get the amount of colors requested since it will not create out of range colors.

@colors = $color->analogous( to => 'teal', steps => 5, in => 'WideGamutRGB' );

designer API

Maybe the absolute highlight of this release that needed the least amount of effort are 7 new methods of the designer API : lighten, darken, saturate, desaturate, tint, tone and shade. They all have the same signature (take only one or two arguments named by and in ). In most cases by will be provided as the only positional argument with a value between zero and one. If chosen zero, the method will do nothing (create an object with the same color) and one has usually a fixed outcome. The methods can be further broken up into two pairs and one triplet. The pair lighten / darken do exactly what you might expect. You get a lighter or darker color with the same saturation.

$color->lighten( 1 ); # or
$color->lighten( by => 1 );

This will always result in white. Same is true for darken(-1) even though that is not advertised. Just imagine a scale of colors between the current one (0 %) and white (100%) and you select a color on that scale. All just mentioned is also true for the pair saturate, desaturate where 0% is the current color and 100% the fully saturated color (saturate) or a gray with the same lightness (desaturate), since lightness and saturation are orthogonal. This also means lighten does not change saturation, since it is not a mix with white. That would be a different operation, one called tint the first of a method triplet. tone mixes with mid gray (gray50) and shade is mixing with black.

All seven are computed in OKHSL per default but you can select many other spaces dependent on the method. Because if you want to change the lightness, it can only happen in a space with a lightness axis. Same is true for saturation. The last three can be computed in any color space.

$color->tint( by => 0.1, in => 'Rec2020' );

Leave a comment

About lichtkind

user-pic Kephra, Articles, Books, Perl, Programming