2

Is it possible to "add" another color and essentially have it pick up where it left off? For example, say you start with 30 items to color, but the user adds a 31st item. Is there a way to have it continue with its currently generated set and simply generate a 31st color that adheres to the prior hues and steps? I found an example, but I don’t understand how it can be used

function rainbow(numOfSteps, step) {
    // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps.
    // Adam Cole, 2011-Sept-14
    // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
    var r, g, b;
    var h = step / numOfSteps;
    var i = ~~(h * 6);
    var f = h * 6 - i;
    var q = 1 - f;
    switch(i % 6){
        case 0: r = 1; g = f; b = 0; break;
        case 1: r = q; g = 1; b = 0; break;
        case 2: r = 0; g = 1; b = f; break;
        case 3: r = 0; g = q; b = 1; break;
        case 4: r = f; g = 0; b = 1; break;
        case 5: r = 1; g = 0; b = q; break;
    }
    var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
    return (c);
}
Liam
  • 27,717
  • 28
  • 128
  • 190
UnitDen
  • 83
  • 5

3 Answers3

3

It's not easy to have 32 colors that actually look unique. Especially if all you want to do is change the hue you can see here are 32 colors changing only the hue

enter image description here

const hsl = (h, s, l) => `hsl(${h * 360 | 0},${s * 100}%,${l * 100}%)`;
for (let i = 0; i < 32; ++i) {
  const div = document.createElement('div');
  div.style.background = hsl(i / 32, 1, 0.5);
  div.className = 'b';
  document.body.appendChild(div);
}
.b {
 width: 32px;
 height: 32px;
 display: inline-block;
}

You can try using a different color space like HCL but it's just as problematic

enter image description here

for (let i = 0; i < 32; ++i) {
  const div = document.createElement('div');
  div.style.background  = chroma.hcl(i / 32 * 360, 80, 70).css();
  div.className = 'b';
  document.body.appendChild(div);
}
.b {
 width: 32px;
 height: 32px;
 display: inline-block;
}
<script src="https://cdn.jsdelivr.net/npm/chroma-js@2.1.0/chroma.min.js"></script>

I would suggest you consider changing the brightness as well. For example 16 light colors and 16 dark colors

But more I'd suggest whatever you're doing you consider adding patterns. For example plane vs checked vs vertical stripes vs horizontal stripes vs diagonal stripes vs diamonds, etc...

enter image description here

Or symbols ♠️ ♦️ ♣️ ♥ ◾️ ● ▲ etc..

As for choosing colors with the most distance you can reverse the bits of an int but you need to choose the range upfront. For example if we pick a range of 0 to 255 (8 bits) then by reversing the bits the first 2 items will be the maximum distance apart. (0 and 128). The next 2 will be the the maximum distance between those 2 (64 and 192). The next 4 will be the max between each of those (32, 96, 160, 224), etc...

See this

gman
  • 100,619
  • 31
  • 269
  • 393
1

If you want to keep the hue, it would be easier to start as HSV/HSL and then convert to RGB:

    function generateHslaColors (saturation, lightness, alpha, amount) {
      let colors = []
      let huedelta = Math.trunc(360 / amount)

      for (let i = 0; i < amount; i++) {
        let hue = i * huedelta
        colors.push(`hsla(${hue},${saturation}%,${lightness}%,${alpha})`)
      }

      return colors
    }
    /**
     * Converts an HSL color value to RGB. Conversion formula
     * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
     * Assumes h, s, and l are contained in the set [0, 1] and
     * returns r, g, and b in the set [0, 255].
     *
     * @param   {number}  h       The hue
     * @param   {number}  s       The saturation
     * @param   {number}  l       The lightness
     * @return  {Array}           The RGB representation
     */
    function hslToRgb(h, s, l){
        var r, g, b;

        if(s == 0){
            r = g = b = l; // achromatic
        }else{
            var hue2rgb = function hue2rgb(p, q, t){
                if(t < 0) t += 1;
                if(t > 1) t -= 1;
                if(t < 1/6) return p + (q - p) * 6 * t;
                if(t < 1/2) return q;
                if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
                return p;
            }

            var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            var p = 2 * l - q;
            r = hue2rgb(p, q, h + 1/3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1/3);
        }

        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    }
Haroldo_OK
  • 6,612
  • 3
  • 43
  • 80
  • Please avoid posting [link only answers](https://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers). This should either be a comment or you need to include the essential parts of those links in the question itself – Liam May 27 '20 at 10:01
1

I found an example, but I don’t understand how it can be used

You need to decide how many different segments you want to split your gradient in. Then you to loop over that total and generate each segment of the gradient like this:

function rainbow(numOfSteps, step) {
    // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps.
    // Adam Cole, 2011-Sept-14
    // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
    var r, g, b;
    var h = step / numOfSteps;
    var i = ~~(h * 6);
    var f = h * 6 - i;
    var q = 1 - f;
    switch(i % 6){
        case 0: r = 1; g = f; b = 0; break;
        case 1: r = q; g = 1; b = 0; break;
        case 2: r = 0; g = 1; b = f; break;
        case 3: r = 0; g = q; b = 1; break;
        case 4: r = f; g = 0; b = 1; break;
        case 5: r = 1; g = 0; b = q; break;
    }
    var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
    return (c);
}

const total = 100;
for (let i = 0; i < total; i++) {
  const $div = document.createElement('div');
  $div.className = 'box';
  $div.style.background = rainbow(total, i);
  document.body.appendChild($div);
}
.box {
  position: relative;
  width: 100%;
  height: 10px;
}
Olian04
  • 6,480
  • 2
  • 27
  • 54
  • The green shades are indistinguishable, though. – D. Pardal May 27 '20 at 10:24
  • @D.Pardal OP asked how he should use the code he had found. I didn't write the algorithm. If OP:s problem is with the results of the algorithm then that should have been expressed in the question. – Olian04 May 27 '20 at 10:26
  • Olian04 thank you) how can i implement this in java ? ~~ I changed this on Math.floor, but it does not work for me ( – UnitDen May 27 '20 at 22:18