Sorting colors / color values

I want to align the following array of colors as accurately as possible.

After searching and testing the many solutions offered on Stackoverflow, the pusher.color library has the best solution, however it is also far from perfect. I would love to hear solutions on how we can align them perfectly.

JSFIDDLE LINK : http://jsfiddle.net/dxux7y3e/

code:

var coloursArray=['#FFE9E9','#B85958','#FFB1AE','#FFC2BF','#C55E58','#FFC7C4','#FF9A94','#FF9D96','#FA9790','#A78B88','#A78B88','#CE675B','#DB8073','#FF9D90','#FF7361','#FFD6D1','#F9A092','#FF7B67','#EBACA2','#FF806D','#DD6D5B','#D16654','#ED8673','#FFC4B8','#E2725B','#ED7A64','#8F3926','#BD492F','#9D3C27','#AD533E','#BF4024','#FFC9BC','#6B6766','#E1CDC8','#C2654C','#B3978F','#FFC7B8','#CE2B00','#C2654C','#A24D34','#FF926D','#E78667','#FFB198','#8C756D','#9E6D5B','#FFC7B0','#FFBEA4','#D2B9AF','#FFB193','#632710','#B26746','#976854','#F44900','#E79873','#EFA27F','#532510','#BC866B','#FDE5D9','#FF5B00','#D18C67','#FF5B00','#9E4312','#763713','#BB6B39','#B5622E','#CC7742','#6D4227','#B56B38','#FF7518','#F3B080','#995C30','#995C30','#FF6A00','#D89769','#71472A','#EDAC7B','#EEAB79','#EBCFB9','#FBE3D1','#E19255','#5E381B','#FFDCC1','#FFF0E4','#F68D39','#7B5B40','#FF8313','#FFCEA4','#AA8667','#975414','#CB9867','#8C5B2B','#FFCE9E','#7B4714','#FFF3E7','#FFA449','#CEAF90','#CDB69E','#EFD6BC','#DDA66B','#B27737','#B88A57','#CE9B61','#F4C38B','#543817','#BC9C78','#DBB07A','#FF8E04','#F6EADB','#DBC2A4','#C49B64','#CBA26B','#80551E','#FF9200','#FFECD3','#FFC87C','#FFB755','#DBB680','#D2D0CD','#EFDBBE','#E5C18B','#FFE5BC','#F2EADB','#885F12','#FFE7B6','#825A08','#906712','#F2D18E','#C8C6C2','#FFB000','#FFC243','#C6BEAD','#D0C3A4','#916800','#8C6700','#F4E9CA','#FFF0C5','#FFE080','#FFEBA8','#846600','#FFE692','#F5F0DB','#433F2F','#BBB394','#FFEFAA','#FFE76D','#FFFAE0','#3E3B28','#554900','#E1E0D8','#74725C','#605F54','#F8F7DD','#A5A467','#DDDDDA','#FFFFEE','#A3A39D','#E0E0D7','#BEBEB9','#E8E8E5','#454531','#ACACAA','#E9E9DF','#FFFFDC','#EBEBE7','#979831','#C5C6BE','#B9C866','#898D72','#F3FAD1','#616452','#CED5B0','#A1A787','#595C4E','#B0BB8C','#EEFFB6','#ACB78E','#8FA359','#858F6C','#86916E','#374912','#AEB0AA','#79904C','#627739','#747F60','#9FA98E','#E7F9CB','#E1F9BE','#495637','#8A9978','#4E5F39','#86996E','#C3CEB7','#78866B','#CEDDC1','#B5CEA2','#536149','#D6E6CC','#D6E6CC','#809873','#4F564C','#4F6C45','#555F52','#4F7942','#5F705B','#D0DFCD','#2B3929','#F0F7EF','#AAD5A4','#99BC95','#B6D4B4','#869E86','#618661','#006700','#E9EEE9','#739E73','#005B06','#EDF7EE','#D0E0D2','#809784','#ABCEB1','#C0E0C8','#3A5241','#435549','#E6ECE8','#E3EAE6','#3B604C','#00602F','#92B7A5','#2F5B49','#318061','#30745B','#316955','#00A275','#C2D1CE','#80A7A0','#00A082','#C2D1CF','#5C6E6C','#607473','#EDF7F7','#1E8285','#D5E7E8','#AADEE1','#188086','#107F87','#566364','#007B86','#66949A','#CAE2E5','#18656F','#004F61','#0C5B6C','#668E98','#BBD0DA','#91B4C5','#AFC3CD','#738A99','#3A5467','#476174','#244967','#556C80','#667A8C','#516D87','#1E4263','#7C8791','#849CB6','#738CAA','#1E3A5F','#1E3655','#9EB0CE','#B6BAC2','#67738D','#BEC1CD','#555559','#616180','#000049','#000031','#F8F8FC','#938BA4','#47375D','#F7F6F8','#3D0067','#514C53','#9566A2','#7F5482','#A279A4','#6D1261','#A06492','#925582','#945B80','#CE94BA','#ECCFE1','#A20058','#A6005B','#BC0061','#BB0061','#F3CEE1','#B3005B','#AB165F','#8A184D','#AA185B','#F3DAE4','#DB3779','#E71261','#E74F86','#FFD6E5','#BE9BA7','#D0396A','#DB1855','#F798B6','#9C294A','#D62B5B','#DE3969','#BC1641','#E7547A','#D52756','#9C7D85','#DB244F','#A1354F','#C22443','#FFBDCA','#8B6D73','#DC3D5B','#FF738C','#F13154','#BC4055','#FED4DB','#FFCFD6','#CB4E61','#ED455A','#F36C7B','#C94F5B','#F3959D','#A8444C','#FFCCD0','#735B5D','#D15D67','#B44B52','#FD868D','#FFD5D8','#C3767B','#FF8087','#C8242B','#FFEAEB','#F95A61','#E96D73','#E6656B','#FF6D73','#FF555B','#A35A5B','#FFD3D4','#B84B4D'];
        var body=document.getElementsByTagName('body')[0];

        function hexToRgb(hex) {
            hex = hex.substring(1, hex.length);
            var r = parseInt((hex).substring(0, 2), 16);
            var g = parseInt((hex).substring(2, 4), 16);
            var b = parseInt((hex).substring(4, 6), 16);

            return r + "," + g + "," + b;
        }

        function rgbToHex(r, g, b) {
            return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
        }

        var rgbArr=new Array();
        var div=document.createElement('div');
        div.id='Original';

        body.appendChild(div);
        for(var color in coloursArray){
            color=coloursArray[color];
            displayColor(color,div); 
            rgbArr.push(hexToRgb(color));
        }

        var hslArr=new Array();
        for(var i=0;i<rgbArr.length;i++){
            //Transforming rgb to hsl
            //`hslArr[i][1]` (`i`) is a reference to the rgb color, in order to retrieve it later
            hslArr[i]=[rgbToHsl(rgbArr[i]),i]; 
        }
        var sortedHslArr=new Array();
        //Sorting `hslArr` into `sortedHslArr`
        outerloop:
            for(var i=0;i<hslArr.length;i++){
            for(var j=0;j<sortedHslArr.length;j++){
                if(sortedHslArr[j][0][0]>hslArr[i][0][0]){
                    sortedHslArr.splice(j,0,hslArr[i]);
                    continue outerloop;
                }
            }
            sortedHslArr.push(hslArr[i]);
        }
        var sortedRgbArr=new Array();
        //Retrieving rgb colors
        for(var i=0;i<sortedHslArr.length;i++){
            sortedRgbArr[i]=rgbArr[sortedHslArr[i][1]];
        }

        function displayColor(color,parent){
            var div;

            div=document.createElement('div');
            div.style.backgroundColor=color;
            div.style.width='22px';
            div.style.height='22px';
            div.style.cssFloat='left';
            div.style.position='relative';
            parent.appendChild(div);
        }

        var finalArray=new Array();
        var div=document.createElement('div');
        div.id='Sorted';
        body.appendChild(div);
        for(var color in sortedRgbArr){
            color=sortedRgbArr[color];
            color=color.split(',');
            color=rgbToHex(parseInt(color[0]),parseInt(color[1]),parseInt(color[2]));                
            displayColor(color,div);                
            finalArray.push(color);
        }

        function rgbToHsl(c){
            var r = c[0]/255, g = c[1]/255, b = c[2]/255;
            var max = Math.max(r, g, b), min = Math.min(r, g, b);
            var h, s, l = (max + min) / 2;

            if(max == min){
                h = s = 0; // achromatic
            }else{
                var d = max - min;
                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
                switch(max){
                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                    case g: h = (b - r) / d + 2; break;
                    case b: h = (r - g) / d + 4; break;
                }
                h /= 6;
            }
            return new Array(h * 360, s * 100, l * 100);
        } 

        var sorted = coloursArray.sort(function(colorA, colorB) {
            return pusher.color(colorA).hue() - pusher.color(colorB).hue();
        });
      //  console.log(sorted);

        var div=document.createElement('div');
        div.id='Pusher';
        body.appendChild(div);
        for(var color in sorted){
            color=sorted[color];                               
            displayColor(color,div);    
        }            

        var div=document.createElement('div');            
        body.appendChild(div);
        var str='';
        for(var color in sorted){
            color=sorted[color];                              
            str+='\''+color+'\',';
        } 
        div.innerHTML=str;


        function sorthueColors (colors) {
            for (var c = 0; c < colors.length; c++) {
                /* Get the hex value without hash symbol. */
                var hex = colors[c].substring(1);
                //var hex = colors[c].hex.substring(1);

                /* Get the RGB values to calculate the Hue. */
                var r = parseInt(hex.substring(0,2),16)/255;
                var g = parseInt(hex.substring(2,4),16)/255;
                var b = parseInt(hex.substring(4,6),16)/255;

                /* Getting the Max and Min values for Chroma. */
                var max = Math.max.apply(Math, [r,g,b]);
                var min = Math.min.apply(Math, [r,g,b]);

                /* Variables for HSV value of hex color. */
                var chr = max-min;
                var hue = 0;
                var val = max;
                var sat = 0;

                if (val > 0) {
                    /* Calculate Saturation only if Value isn't 0. */
                    sat = chr/val;
                    if (sat > 0) {
                        if (r == max) { 
                            hue = 60*(((g-min)-(b-min))/chr);
                            if (hue < 0) {hue += 360;}
                        } else if (g == max) { 
                            hue = 120+60*(((b-min)-(r-min))/chr); 
                        } else if (b == max) { 
                            hue = 240+60*(((r-min)-(g-min))/chr); 
                        }
                    }
                }

                /* Modifies existing objects by adding HSV values. */
                colors[c].hue = hue;
                colors[c].sat = sat;
                colors[c].val = val;
            }

            /* Sort by Hue. */
            return colors.sort(function(a,b){return a.hue - b.hue;});
        }

      

+3


source to share


3 answers


The problem is that sorting requires a well-defined order - in other words, you need to match all colors to the same dimension. While there are some approaches to displaying a color space in two dimensions, I do not know of one that displays it in one dimension and still makes sense to the human eye.

However, if you don't insist on some kind of generic ordering, and just want to arrange a given list of colors in a way that looks good, then some clustering approach may lead to better results. I've tried a naive approach, the idea is to put the same colors in the same cluster and combine those clusters until you have one. Here is the code I have:

function colorDistance(color1, color2) {
    // This is actually the square of the distance but
    // this doesn't matter for sorting.
    var result = 0;
    for (var i = 0; i < color1.length; i++)
        result += (color1[i] - color2[i]) * (color1[i] - color2[i]);
    return result;
}

function sortColors(colors) {
    // Calculate distance between each color
    var distances = [];
    for (var i = 0; i < colors.length; i++) {
        distances[i] = [];
        for (var j = 0; j < i; j++)
            distances.push([colors[i], colors[j], colorDistance(colors[i], colors[j])]);
    }
    distances.sort(function(a, b) {
        return a[2] - b[2];
    });

    // Put each color into separate cluster initially
    var colorToCluster = {};
    for (var i = 0; i < colors.length; i++)
        colorToCluster[colors[i]] = [colors[i]];

    // Merge clusters, starting with lowest distances
    var lastCluster;
    for (var i = 0; i < distances.length; i++) {
        var color1 = distances[i][0];
        var color2 = distances[i][1];
        var cluster1 = colorToCluster[color1];
        var cluster2 = colorToCluster[color2];
        if (!cluster1 || !cluster2 || cluster1 == cluster2)
            continue;

        // Make sure color1 is at the end of its cluster and
        // color2 at the beginning.
        if (color1 != cluster1[cluster1.length - 1])
            cluster1.reverse();
        if (color2 != cluster2[0])
            cluster2.reverse();

        // Merge cluster2 into cluster1
        cluster1.push.apply(cluster1, cluster2);
        delete colorToCluster[color1];
        delete colorToCluster[color2];
        colorToCluster[cluster1[0]] = cluster1;
        colorToCluster[cluster1[cluster1.length - 1]] = cluster1;
        lastCluster = cluster1;
    }

    // By now all colors should be in one cluster
    return lastCluster;
}

      



Complete violin

The function colorDistance()

works with RGB colors, which are expressed as arrays with three elements. There are certainly much better approaches for this , and they can produce better looking results. Also note that this is not the most efficient algorithm as it calculates the distance between each color (O (n 2 )), so if you have many colors you can optimize it.

+15


source


Assuming there is no solution best. This is how I will find what I like best.

First I would use the color distance function:

var balance = [10, 0, 0.01];
function colorDistance(color1, color2) {
    var result = 0;
    color1 = rgbToHsl(color1[0], color1[1], color1[2]);
    color2 = rgbToHsl(color2[0], color2[1], color2[2]);
    for (var i = 0; i < color1.length; i++)
        result += (color1[i] - color2[i]) * (color1[i] - color2[i]) * balance[i];
    return result;
}

      

I would use the balance function:



  • Shade distance var balance = [1, 0, 0];

    enter image description here
  • saturation distance var balance = [0, 1, 0];

    enter image description here
  • lightness distance

    var balance = [0, 0, 1]; enter image description here

After that, you can just experiment and choose what you like. Note that the values โ€‹โ€‹are not normalized to appear between 0 and 1 , but different to make it work well. You can also use another distance function, this is just trivial and well known to give decent results.

This approach doesn't give you a better solution, but describes a way to tinker with it and get the result you are looking for. Enjoy.

+7


source


Using a shade of color is usually a pleasant way to map color on a one-dimensional scale. You can convert rgb color to hsv using answers here: RGB for HSV color in javascript?

0


source







All Articles