omniJS – .hue getter conversion to HSB colorspace fails?

UPDATE – Ignore – this was my misunderstanding.


The omniJS API docs suggest that the .hue getter should return a correct value from a Color created with the Color.RGB constructor (converting the color value to an HSB color space).

I may be misunderstanding, but it looks to me as if that conversion is failing at the moment.

  1. If I create every one of the 100+ HTML colors with Color.RGB, and then read their .hue value, all hue values are returned as either 0.5 or 1.0 - i.e. where we might expect to see at least 8 bits of information we only see one.
  2. A similar result is obtained from the following code, which tries every Red value from 0 to 255, and only ever gets a hue of 0.5 or 1.0
(() => {
   'use strict';

    // enumFromTo :: Int -> Int -> [Int]
        const enumFromTo = (m, n) =>
            Array.from({
                length: Math.floor(n - m) + 1
            }, (_, i) => m + i);

        // map :: (a -> b) -> [a] -> [b]
        const map = (f, xs) => xs.map(f);

        // show :: Int -> a -> Indented String
        // show :: a -> String
        const show = (...x) =>
            JSON.stringify.apply(
                null, x.length > 1 ? [x[1], null, x[0]] : x
            );

        return show(
            map(
                r => Color.RGB(r / 255, 0.5, 0.5)
                .hue,
                enumFromTo(0, 255)
            )
        );
    })();

We get a similar result if we map over:

  1. all of the Green values 0-255 (all green levels are collapsed to .hue values of either .3333 or .8333)
  2. all of the Blue values (all collapsed to just two .hue values - .1666 or .6666)

(Note that the .hue getter does return correct values with Colors constructed by Color.HSB - so I would guess that the other 7 bits of color information are getting lost in the conversion)

Or, for a more direct example, which does appear to confirm a conversion failure, if we look up the RGB and HSB for HTML yellowGreen, we will find:

RGB 154 205 50
HSB 0.778 0.756 0.804

but the .hue .saturation and .brightness getters are returning something with quite a different Hue (0.222, 0.756, 0.804 ) where we expected ( 0.778, 0.756, 0.804).

(() => {
   'use strict';

     // log :: a -> IO ()
    const log = (...args) =>
        console.log(
            args
            .map(JSON.stringify)
            .join(' -> ')
        );

    // HTML yellowgreen
    //          RGB 154 205 50
    // Expected HSB 0.778 0.756 0.804

    const yg = Color.RGB(154/255, 205/255, 50/255);

    log('yg HSB', yg.hue, yg.saturation, yg.brightness);

    // --> 0.222  0.756  0.804  where we expected 0.778 0.756 0.804
})();

Finally, if we try to convert HTML yellowGreen in the opposite direction – from HSB to RGB

The result looks better at first glance, but then it becomes apparent that the Green and Blue values have somehow been swapped with each other …

Perhaps this sheds light on the incorrect values returned by .hue ?

(() => {
   'use strict';

     // log :: a -> IO ()
        const log = (...args) =>
            console.log(
                args
                .map(JSON.stringify)
                .join(' -> ')
            );

        // HTML yellowgreen
        //          RGB 154 205 50
        //          HSB 0.778 0.756 0.804

        const yg = Color.HSB(0.778, 0.756, 0.804);

        log('yg RGB', yg.red * 255, yg.green * 255, yg.blue * 255);

        // --> 154, 50, 205    ( the Green and Blue appear to be reversed ... )
    })();

But I may be getting this wrong - I took the HSB values from this sheet, and I think it may be confusing HSB/HSL/HSV

https://docs.zoho.com/sheet/published.do?rid=hd3vbc97ea02f9d414ab1b2ac072fc044581d

And, what’s more I notice that the .hue outputs are consistent with the results of this derivation of HSV:

(() => {
   'use strict';

    // A list of functions applied to a list of arguments
        // <*> :: [(a -> b)] -> [a] -> [b]
        const ap = (fs, xs) => //
            [].concat.apply([], fs.map(f => //
                [].concat.apply([], xs.map(x => [f(x)]))));

        // id :: a -> a
        const id = x => x;

        // maximum :: [a] -> a
        const maximum = xs =>
            xs.reduce((a, x) => (x > a || a === undefined ? x : a), undefined);

        // minimum :: [a] -> a
        const minimum = xs =>
            xs.reduce((a, x) => (x < a || a === undefined ? x : a), undefined);

        // rgb2HSV :: Int -> Int -> Int -> (Float, Float, Float)
        function rgb2HSV(intRed, intGreen, intBlue) {
            const
                [max, min, [r, g, b]] = ap([maximum, minimum, id], [
                    Array.from(arguments).map(x => x / 255)
                ]),
                d = max - min,
                mh = (0 === d) ? (
                    0
                ) : max === r ? (
                    (g - b) / d + (g < b ? 6 : 0)
                ) : max === g ? (
                    (b - r) / d + 2
                ) : (r - g) / d + 4;

            return [mh / 6, (max === 0 ? 0 : d / max), max];
        };

        return rgb2HSV(154, 205, 50)
            .map(x => x * 255);
    })();

(Which also clarifies the intervals of the Hue values)