2014-09-13 3 views
3

다음 색상 배열을 가능한 정확하게 정렬하려고합니다.색상/색상 값 정렬

& Stackoverflow에 제안 된 많은 솔루션을 검색 한 후 pusher.color 라이브러리가 최상의 솔루션을 제공하지만 완벽한 것은 아닙니다. 나는 그들을 어떻게 완벽하게 맞출 수 있는지에 대한 해결책을 듣고 싶다.

JSFIDDLE LINK

: http://jsfiddle.net/dxux7y3e/

코드 :

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;}); 
     } 

답변

16

문제는 정렬이 잘 정의 된 순서를 필요로한다 - 즉, 하나 개의 차원으로 모든 색상을 매핑해야합니다. 2 차원에서 색 공간을 표시하는 몇 가지 접근 방법이 있지만, 한 차원에서 색 공간을 표시하고 인간의 눈에는 여전히 의미가있는 색을 인식하지 못합니다.

그러나 보편적 인 주문을 고집하지 않고 주어진 색상 목록을 멋지게 표시하려는 경우 일부 클러스터링 방식을 사용하면 더 나은 결과를 얻을 수 있습니다. 나는 순진한 접근을 시도했다. 아이디어는 비슷한 색을 같은 클러스터에 넣고이 클러스터를 병합 할 때까지 병합하는 것이다.

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 fiddle

colorDistance() 기능은 세 가지 요소를 가진 배열로 표현하는 RGB 색상으로 작동합니다 여기에 내가있어 코드입니다. 확실히 much better approaches to do this이 있으며 더 나은 결과를 낼 수 있습니다. 또한 각 색상 사이의 거리를 계산하므로 가장 효율적인 알고리즘이 아닙니다 (O (n)). 색상이 많을 경우 최적화 할 수 있습니다.

+0

노력에 감사하지만, 밝음을 기준으로 한 색상 값을 확실히 조정하는 것처럼 보이지만, 색상은 모든 곳에서 & 슬픔을 많이 낳습니다. 나는 HSL이나 HSV가 하나의 색조에서 다른 색조로 점프 할 때 링크를 찾아 낼 수 있다면 이것들을 사용하는 방법이라고 생각한다. – Kayote

+5

@Kayote : 당신의 기준에 맞는'colorDistance()'함수를 자유롭게 구현할 수있다. 더 나은 - 당신은 질문에 이러한 기준을 지정하지 않았고, 제 마음 읽기 능력은 약간 녹슬 었습니다. 알고리즘의 나머지 부분은 당신이 어떤 비교 함수를 선택 하든지 아무런 변화가 없어야합니다. –

+0

데모를 +1하고 싶습니다. – Margus

0

색상의 색조를 사용하면 일반적으로 1 차원 눈금에 색상을 매핑하는 즐거운 방법입니다. 여기서 대답을 사용하여 RGB 색상을 hsv로 변환 할 수 있습니다. RGB to HSV color in javascript?

+0

질문의 코드는 제안하고있는 것을 정확하게 수행하는 매우 복잡한 방식 인 것처럼 보입니다. 결과는 잘못되었지만, 내 생각 엔 거기에 구현 된 맞춤 정렬 알고리즘은 버그가있는 것입니다. –

7

최상의 [] 해결책이 없다고 가정합니다. 여기 내가 가장 좋아하는 것을 찾는 방법이 있습니다.

  • 색조 거리var balance = [1, 0, 0]; enter image description here
  • 포화 거리var balance = [0, 1, 0]; : I 밸런스 기능을 사용

    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; 
    } 
    

    :

    우선 색 거리 함수를 사용하는 것

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

그런 다음 원하는대로 실험하고 선택할 수 있습니다. 값은 0에서 1 사이에 나타나도록 정규화되지 않았지만 그 밖의 값은 제대로 작동해야합니다. 당신은 또 다른 거리 함수를 쓸 수 있습니다. 이것은 아주 사소하고 잘 알려진 결과입니다.

이 접근법은 최상의 솔루션을 제공하지는 않지만이를 사용하여 원하는 결과를 얻을 수있는 방법을 설명합니다. 재미있게 보내십시오.