mapping large number of values to unique colors in continuous gradient

in #programming5 years ago

this is a problem I've had for decades and never knew how to solve with standard RGB monitors. my Google-fu has failed me many times, such as in this StackOverflow result whose answers are hardly actionable to coders like me with little imagination and a short attention span.

so a few days ago I sat down with my notebook and netbook, scribbling on one and Pythoning on the other, and came up with two approaches, one in shades of cyan and another in shades of purplish-gray, that gives me close to 12000 unique colors, mapping the numbers -2212 to +9720. the image shows the values from -999 to 9000.

gradient.png

first I picked one of the simplest formulas for luminance I could find, from Wikipedia, and "corrected" their formula to add up exactly to 1: y = lambda r, g, b: .214 * r + .714 * g + .0720 * b.

then by playing around, I noticed that by varying the "darkest" color, blue, I could get several shades of cyan by using up to 10 shades of blue for each green value (.714 / .0720 is 9.717). this was easy to accomplish with a simple loop once I understood the formula.

    while green > blueratio:
        underwater[i] = (red, green, blue, OPAQUE)
        blue -= 1
        i -= 1
        if green - blue == blueratio:
            green -= 1
            blue = green

but for the 9000 (meters above sea level) consecutive values, I needed something more complicated. I don't have the mathematical background I need to come up with a formula, so I just generated all possible permutations of red and blue pixels within the ratio of each to green, filtered those that added up to less than 1, and sorted them. then I simply looped over those values for each value of green.

    samples = ((i, 0, j) for i in range(redratio + 1)
               for j in range(blueratio + 1))
    brightnesses = {sample: brightness(*sample) for sample in samples
                    if brightness(*sample) < 1}
    # sorted dict https://stackoverflow.com/a/613218/493161
    pattern = OrderedDict(sorted(brightnesses.items(), key=lambda kv: kv[1]))
    logging.debug('pattern: %s', pattern)
    logging.debug('possibilities: about %d values', 256 * len(brightnesses))
    permutations = list(pattern.keys())
    colormap.update({g * len(permutations) + i + 1:
                     (permutations[i][0] + g, g, permutations[i][2] + g, OPAQUE)
                     for g in range(maxgreen)
                     for i in range(len(permutations))
                    })

someday I should go back and revisit some of my early attempts at Mandelbrot viewers and use a variation on these ideas.