2008-10-25 95 views
52

Tôi có một số mã tạo hình ảnh biểu đồ hình tròn. Đó là một lớp học có mục đích chung, vì vậy bất kỳ số lượng lát nào cũng có thể được cung cấp làm đầu vào. Bây giờ tôi có vấn đề chọn màu sắc tốt cho các lát. Có một số thuật toán đó là tốt ở đó?Cách chọn màu cho biểu đồ hình tròn?

Hoặc có lẽ tôi chỉ nên chọn thủ công và liệt kê các màu cố định? Nhưng có bao nhiêu. Có lẽ 10 màu sắc và hy vọng sẽ không có nhiều hơn 10 lát bao giờ? Ngoài ra, trong đó 10 màu sắc để chọn?

Màu sắc cần phải làm theo một số quy tắc:

  • họ cần phải nhìn đẹp
  • màu sắc liền kề không nên giống nhau (màu xanh bên cạnh màu xanh lá cây là một no-go)
  • màu nền chiếc bánh là màu trắng , vì vậy màu trắng nằm ngoài tùy chọn

Một số thuật toán thao tác với giá trị RGB sẽ là giải pháp ưu tiên.

+4

thế nào là màu xanh giống như màu xanh lá cây? – peterchen

+10

@peterchen - rất giống nếu bạn mù màu xanh lục;) – redcalx

Trả lời

16

Tôi sẽ lập danh sách khoảng 20 màu, sau đó bắt đầu lặp lại với màu thứ hai. Bằng cách này, bạn sẽ không phá vỡ quy tắc thứ hai của mình. Ngoài ra, nếu ai đó làm một biểu đồ hình tròn với hơn 20 lát, họ có vấn đề lớn hơn. :)

+0

Đây chính xác là ứng dụng web mà tôi hiện đang làm việc. Tạo 20, sau đó lặp lại 20 lần đó để tạo danh sách 200. Tuy nhiên, tôi có biểu đồ hình tròn với hơn 20 lát và màu sắc được lặp lại. Bất kỳ đề xuất? –

+2

Nếu bạn thực sự cần ~ 200 màu khác nhau, bạn có thể cân nhắc sử dụng bảng màu an toàn cho Web (hoặc một tập con). http://en.wikipedia.org/wiki/Web_colors –

+0

Chỉ cần triển khai đề xuất của @ BilltheLizard: 'if (pie.length% palette.length == 1) {palette.push (bảng màu [1])}'. Có vẻ như quy tắc 2 chỉ phá vỡ khi chúng ta có nhiều chiều dài màu + 1 lát; điều này chỉ đảm bảo chúng ta lặp lại từ trường hợp thứ hai trong trường hợp phá vỡ (hữu ích khi sử dụng thứ gì đó như d3 [thang đo thứ tự] (https://github.com/mbostock/d3/wiki/Ordinal-Scales) để chọn màu). Bây giờ chúng ta có thể sử dụng chỉ một vài màu từ một cái tốt đẹp (tức là quy tắc 1 thỏa mãn) [bảng màu] (http://colorbrewer2.org/) và đừng lo lắng về việc phá quy tắc 2. – funseiki

1

Tôi tìm thấy công thức giả mã này có thể hữu ích. Bạn có thể bắt đầu với một bộ để hạt giống nó.

Công thức khác biệt màu

Sau đây là công thức được đề xuất bởi W3C để xác định sự khác biệt giữa hai màu.

(tối đa (Giá trị màu đỏ 1, Giá trị đỏ 2) - tối thiểu (Giá trị màu đỏ 1, Giá trị màu đỏ 2)) + (tối đa (Giá trị xanh 1, Giá trị xanh 2) - tối thiểu (Giá trị xanh 1, Giá trị xanh 2)) + (tối đa (Blue giá trị 1, Blue giá trị 2) - tối thiểu (Blue giá trị 1, Blue giá trị 2))

sự khác biệt giữa màu nền và màu nền trước phải lớn hơn 500.

Here is the source

2

Có máy phát điện here. Nó dành cho thiết kế web, nhưng các màu sắc sẽ trông tuyệt vời trên một biểu đồ hình tròn.

Bạn có thể biên dịch trước một danh sách các màu đẹp, hoặc kiểm tra logic đằng sau trình tạo và tự làm một việc tương tự.

+1

Một số giải thích về bộ tạo hoặc thuật toán của nó sẽ tốt đẹp , như thịt của bài đăng này được ẩn đằng sau liên kết tại thời điểm này, và các liên kết có một thói quen khó chịu khi chết. –

12

Hãy xem Color Brewer, một công cụ giúp xác định lược đồ màu để truyền đạt thông tin định tính hoặc định lượng: bản đồ, biểu đồ, v.v. Trong ba "loại" bảng màu mà công cụ này có thể tạo - tuần tự, định tính, và phân tách - bạn có thể cần thứ hai, phân tách ...

Bạn thậm chí có thể tải xuống tệp Excel với định nghĩa RGB của tất cả các bảng màu.

64

Tôi giải quyết nó như sau:

  1. Chọn một màu cơ sở.
  2. Tính màu sắc (baseHue) của chúng tôi.
  3. Tạo một màu với độ bão hòa và độ sáng như nhau, với màu sắc của nó tính như sau:
     
        hue = baseHue + ((240/pieces) * piece % 240 
    

Trong C#:

int n = 12; 

Color baseColor = System.Drawing.ColorTranslator.FromHtml("#8A56E2"); 
double baseHue = (new HSLColor(baseColor)).Hue; 

List<Color> colors = new List<Color>(); 
colors.Add(baseColor); 

double step = (240.0/(double)n); 

for (int i = 1; i < n; ++i) 
{ 
    HSLColor nextColor = new HSLColor(baseColor); 
    nextColor.Hue = (baseHue + step * ((double)i)) % 240.0; 
    colors.Add((Color)nextColor); 
} 

string colors = string.Join(",", colors.Select(e => e.Name.Substring(2)).ToArray()); 

tôi đã sử dụng HSLColor class.

Các Google Charts example có sử dụng 12 miếng và một màu cơ bản của # 8A56E2:

Chart Example

+2

Trong ví dụ của bạn, hai màu trên bên trái (10 giờ đến nửa đêm) trông giống hệt nhau trên màn hình của tôi! – Brann

+0

Làm thế nào điều này không hoàn toàn bỏ qua quy tắC# 2, rằng màu sắc tương tự không nên được liền kề? Nó tạo ra một bánh xe màu * đẹp, nhưng không tốt cho một biểu đồ hình tròn. – Geobits

+0

Đây tương đương với Android: https://gist.github.com/siddharth96/cbb2d31509f9149e4565 Điều này cũng kết hợp câu trả lời của Pic Mickael được đưa ra bên dưới –

4

này 1985 giấy bằng cách "ROSS E. ROLEY, CAPT" đưa ra một thuật toán để tối đa hóa tách màu cho một bộ tùy ý màu sắc (complete with code in FORTRAN).

(Màu tách dường như là một vấn đề trực quan quan trọng đối với lực lượng quân sự để ngăn chặn sự cố màu xanh-on-xanh.)

Tuy nhiên nếu bạn muốn dính vào một bộ 20 màu sắc, một giải pháp nhanh chóng và đơn giản sẽ là chọn các đỉnh của một mặt dodecahedron và chuyển đổi các tọa độ (x, y, z) (tỉ lệ thích hợp) thành (r, g, b).

9

Xây dựng theo số this solution để giải quyết quy tắc của câu hỏi # 2, thuật toán sau hoán đổi màu xung quanh điểm giữa của pie. Hai thông số:

  1. pNbColors là số lát trong chiếc bánh
  2. pNonAdjacentSimilarColor một Boolean để chỉ ra nếu bạn muốn có màu sắc tương tự liền kề hay không.

Tôi đang sử dụng ColorHSL, ColorRGBColorUtils (được cung cấp dưới đây).

public static function ColorArrayGenerator(
    pNbColors:int, 
    pNonAdjacentSimilarColor:Boolean = false):Array 
{  
    var colors:Array = new Array(); 
    var baseRGB:ColorRGB = new ColorRGB(); 
    baseRGB.setRGBFromUint(0x8A56E2); 

    var baseHSL:ColorHSL = new ColorHSL(); 
    rgbToHsl(baseHSL, baseRGB); 

    var currentHue:Number = baseHSL.Hue; 

    colors.push(baseRGB.getUintFromRGB()); 

    var step:Number = (360.0/pNbColors); 
    var nextHSL:ColorHSL; 
    var nextRGB:ColorRGB; 
    var i:int; 

    for (i = 1; i < pNbColors; i++) 
    { 
     currentHue += step; 
     if (currentHue > 360) 
     { 
      currentHue -= 360; 
     } 

     nextHSL = new ColorHSL(currentHue, baseHSL.Saturation, aseHSL.Luminance); 
     nextRGB = new ColorRGB(); 
     hslToRgb(nextRGB, nextHSL); 

     colors.push(nextRGB.getUintFromRGB()); 
    } 

    if (pNonAdjacentSimilarColor == true && 
     pNbColors > 2) 
    { 
     var holder:uint = 0; 
     var j:int; 

     for (i = 0, j = pNbColors/2; i < pNbColors/2; i += 2, j += 2) 
     { 
      holder = colors[i]; 
      colors[i] = colors[j]; 
      colors[j] = holder; 
     } 
    } 

    return colors; 
} 

này tạo ra đầu ra phía bên tay phải:

Comparison Image

ColorHSL lớp:

final public class ColorHSL 
{ 
    private var _hue:Number; // 0.0 .. 359.99999 

    private var _sat:Number; // 0.0 .. 100.0 

    private var _lum:Number; // 0.0 .. 100.0 

    public function ColorHSL(
     hue:Number = 0, 
     sat:Number = 0, 
     lum:Number = 0) 
    { 
     _hue = hue; 
     _sat = sat; 
     _lum = lum; 
    } 

    [Bindable]public function get Hue():Number 
    { 
     return _hue; 
    } 

    public function set Hue(value:Number):void 
    { 
     if (value > 360) 
     { 
      _hue = value % 360; 
     } // remember, hue is modulo 360 
     else if (value < 0) 
     { 
      _hue = 0; 
     } 
     else 
     { 
      _hue = value; 
     } 
    } 

    [Bindable]public function get Saturation():Number 
    { 
     return _sat; 
    } 

    public function set Saturation(value:Number):void 
    { 
     if (value > 100.0) 
     { 
      _sat = 100.0; 
     } 
     else if (value < 0) 
     { 
      _sat = 0; 
     } 
     else 
     { 
      _sat = value; 
     } 
    } 

    [Bindable]public function get Luminance():Number 
    { 
     return _lum; 
    } 

    public function set Luminance(value:Number):void 
    { 
     if (value > 100.0) 
     { 
      _lum = 100.0; 
     } 
     else if (value < 0) 
     { 
      _lum = 0; 
     } 
     else 
     { 
      _lum = value; 
     } 
    } 
} 

ColorRGB lớp:

final public class ColorRGB 
{ 
    private var _red:uint; 
    private var _grn:uint; 
    private var _blu:uint; 
    private var _rgb:uint;  // composite form: 0xRRGGBB or #RRGGBB 

    public function ColorRGB(red:uint = 0, grn:uint = 0, blu:uint = 0) 
    { 
     setRGB(red, grn, blu); 
    } 

    [Bindable]public function get red():uint 
    { 
     return _red; 
    } 

    public function set red(value:uint):void 
    { 
     _red = (value & 0xFF); 
     updateRGB(); 
    } 

    [Bindable]public function get grn():uint 
    { 
     return _grn; 
    } 

    public function set grn(value:uint):void 
    { 
     _grn = (value & 0xFF); 
     updateRGB(); 
    } 

    [Bindable]public function get blu():uint 
    { 
     return _blu; 
    } 

    public function set blu(value:uint):void 
    { 
     _blu = (value & 0xFF); 
     updateRGB(); 
    } 

    [Bindable]public function get rgb():uint 
    { 
     return _rgb; 
    } 

    public function set rgb(value:uint):void 
    { 
     _rgb = value; 
     _red = (value >> 16) & 0xFF; 
     _grn = (value >> 8) & 0xFF; 
     _blu = value  & 0xFF; 
    } 

    public function setRGB(red:uint, grn:uint, blu:uint):void 
    { 
     this.red = red; 
     this.grn = grn; 
     this.blu = blu; 
    } 

    public function setRGBFromUint(pValue:uint):void 
    { 
     setRGB(((pValue >> 16) & 0xFF), ((pValue >> 8) & 0xFF), (pValue & 0xFF)); 
    } 

    public function getUintFromRGB():uint 
    { 
     return ((red << 16) | (grn << 8) | blu); 
    } 

    private function updateRGB():void 
    { 
     _rgb = (_red << 16) + (_grn << 8) + blu; 
    } 
} 

ColorUtils lớp:

final public class ColorUtils 
{ 
    public static function HSV2RGB(hue:Number, sat:Number, val:Number):uint 
    { 
     var red:Number = 0; 
     var grn:Number = 0; 
     var blu:Number = 0; 
     var i:Number; 
     var f:Number; 
     var p:Number; 
     var q:Number; 
     var t:Number; 
     hue%=360; 
     sat/=100; 
     val/=100; 
     hue/=60; 
     i = Math.floor(hue); 
     f = hue-i; 
     p = val*(1-sat); 
     q = val*(1-(sat*f)); 
     t = val*(1-(sat*(1-f))); 
     if (i==0) 
     { 
      red=val; 
      grn=t; 
      blu=p; 
     } 
     else if (i==1) 
     { 
      red=q; 
      grn=val; 
      blu=p; 
     } 
     else if (i==2) 
     { 
      red=p; 
      grn=val; 
      blu=t; 
     } 
     else if (i==3) 
     { 
      red=p; 
      grn=q; 
      blu=val; 
     } 
     else if (i==4) 
     { 
      red=t; 
      grn=p; 
      blu=val; 
     } 
     else if (i==5) 
     { 
      red=val; 
      grn=p; 
      blu=q; 
     } 
     red = Math.floor(red*255); 
     grn = Math.floor(grn*255); 
     blu = Math.floor(blu*255); 

     return (red<<16) | (grn << 8) | (blu); 
    } 

    // 
    public static function RGB2HSV(pColor:uint):Object 
    { 
     var red:uint = (pColor >> 16) & 0xff; 
     var grn:uint = (pColor >> 8) & 0xff; 
     var blu:uint = pColor & 0xff; 

     var x:Number; 
     var val:Number; 
     var f:Number; 
     var i:Number; 
     var hue:Number; 
     var sat:Number; 
     red/=255; 
     grn/=255; 
     blu/=255; 
     x = Math.min(Math.min(red, grn), blu); 
     val = Math.max(Math.max(red, grn), blu); 
     if (x==val){ 
      return({h:undefined, s:0, v:val*100}); 
     } 
     f = (red == x) ? grn-blu : ((grn == x) ? blu-red : red-grn); 
     i = (red == x) ? 3 : ((grn == x) ? 5 : 1); 
     hue = Math.floor((i-f/(val-x))*60)%360; 
     sat = Math.floor(((val-x)/val)*100); 
     val = Math.floor(val*100); 
     return({h:hue, s:sat, v:val}); 
    } 

    /** 
    * Generates an array of pNbColors colors (uint) 
    * The colors are generated to fill a pie chart (meaning that they circle back to the starting color) 
    * @param pNbColors The number of colors to generate (ex: Number of slices in the pie chart) 
    * @param pNonAdjacentSimilarColor Should the colors stay Adjacent or not ? 
    */ 
    public static function ColorArrayGenerator(
     pNbColors:int, 
     pNonAdjacentSimilarColor:Boolean = false):Array 
    { 
     // Based on http://www.flexspectrum.com/?p=10 

     var colors:Array = []; 
     var baseRGB:ColorRGB = new ColorRGB(); 
     baseRGB.setRGBFromUint(0x8A56E2); 

     var baseHSL:ColorHSL = new ColorHSL(); 
     rgbToHsl(baseHSL, baseRGB); 

     var currentHue:Number = baseHSL.Hue; 

     colors.push(baseRGB.getUintFromRGB()); 

     var step:Number = (360.0/pNbColors); 
     var nextHSL:ColorHSL; 
     var nextRGB:ColorRGB; 
     var i:int; 

     for (i = 1; i < pNbColors; i++) 
     { 
      currentHue += step; 

      if (currentHue > 360) 
      { 
       currentHue -= 360; 
      } 

      nextHSL = new ColorHSL(currentHue, baseHSL.Saturation, baseHSL.Luminance); 
      nextRGB = new ColorRGB(); 
      hslToRgb(nextRGB, nextHSL); 

      colors.push(nextRGB.getUintFromRGB()); 
     } 

     if (pNonAdjacentSimilarColor == true && 
      pNbColors > 2) 
     { 
      var holder:uint = 0; 
      var j:int; 

      for (i = 0, j = pNbColors/2; i < pNbColors/2; i += 2, j += 2) 
      { 
       holder = colors[i]; 
       colors[i] = colors[j]; 
       colors[j] = holder; 
      } 
     } 

     return colors; 
    } 

    static public function rgbToHsl(hsl:ColorHSL, rgb:ColorRGB):void 
    { 
     var h:Number = 0; 
     var s:Number = 0; 
     var l:Number = 0; 

     // Normalizes incoming RGB values. 
     // 
     var dRed:Number = (Number)(rgb.red/255.0); 
     var dGrn:Number = (Number)(rgb.grn/255.0); 
     var dBlu:Number = (Number)(rgb.blu/255.0); 

     var dMax:Number = Math.max(dRed, Math.max(dGrn, dBlu)); 
     var dMin:Number = Math.min(dRed, Math.min(dGrn, dBlu)); 

     //------------------------- 
     // hue 
     // 
     if (dMax == dMin) 
     { 
      h = 0;     // undefined 
     } 
     else if (dMax == dRed && dGrn >= dBlu) 
     { 
      h = 60.0 * (dGrn - dBlu)/(dMax - dMin); 
     } 
     else if (dMax == dRed && dGrn < dBlu) 
     { 
      h = 60.0 * (dGrn - dBlu)/(dMax - dMin) + 360.0; 
     } 
     else if (dMax == dGrn) 
     { 
      h = 60.0 * (dBlu - dRed)/(dMax-dMin) + 120.0; 
     } 
     else if (dMax == dBlu) 
     { 
      h = 60.0 * (dRed - dGrn)/(dMax - dMin) + 240.0; 
     } 

     //------------------------- 
     // luminance 
     // 
     l = (dMax + dMin)/2.0; 

     //------------------------- 
     // saturation 
     // 
     if (l == 0 || dMax == dMin) 
     { 
      s = 0; 
     } 
     else if (0 < l && l <= 0.5) 
     { 
      s = (dMax - dMin)/(dMax + dMin); 
     } 
     else if (l>0.5) 
     { 
      s = (dMax - dMin)/(2 - (dMax + dMin)); //(dMax-dMin > 0)? 
     } 

     hsl.Hue = h; 
     hsl.Luminance = l; 
     hsl.Saturation = s; 

    } // rgbToHsl 

    //--------------------------------------- 
    // Convert the input RGB values to the corresponding HSL values. 
    // 
    static public function hslToRgb(rgb:ColorRGB, hsl:ColorHSL):void 
    { 
     if (hsl.Saturation == 0) 
     { 
      // Achromatic color case, luminance only. 
      // 
      var lumScaled:int = (int)(hsl.Luminance * 255.0); 
      rgb.setRGB(lumScaled, lumScaled, lumScaled); 
      return; 
     } 

     // Chromatic case... 
     // 
     var dQ:Number = (hsl.Luminance < 0.5) ? (hsl.Luminance * (1.0 + hsl.Saturation)): ((hsl.Luminance + hsl.Saturation) - (hsl.Luminance * hsl.Saturation)); 
     var dP:Number = (2.0 * hsl.Luminance) - dQ; 

     var dHueAng:Number = hsl.Hue/360.0; 

     var dFactor:Number = 1.0/3.0; 

     var adT:Array = []; 

     adT[0] = dHueAng + dFactor;    // Tr 
     adT[1] = dHueAng;      // Tg 
     adT[2] = dHueAng - dFactor;    // Tb 

     for (var i:int = 0; i < 3; i++) 
     { 
      if (adT[i] < 0) 
      { 
       adT[i] += 1.0; 
      } 

      if (adT[i] > 1) 
      { 
       adT[i] -= 1.0; 
      } 

      if ((adT[i] * 6) < 1) 
      { 
       adT[i] = dP + ((dQ - dP) * 6.0 * adT[i]); 
      } 
      else if ((adT[i] * 2.0) < 1)  // (1.0/6.0) <= adT[i] && adT[i] < 0.5 
      { 
       adT[i] = dQ; 
      } 
      else if ((adT[i] * 3.0) < 2)  // 0.5 <= adT[i] && adT[i] < (2.0/3.0) 
      { 
       adT[i] = dP + (dQ-dP) * ((2.0/3.0) - adT[i]) * 6.0; 
      } 
      else 
      { 
       adT[i] = dP; 
      } 
     } 

     rgb.setRGB(adT[0] * 255.0, adT[1] * 255.0, adT[2] * 255.0); 

    } // hslToRgb 

    //--------------------------------------- 
    // Adjust the luminance value by the specified factor. 
    // 
    static public function adjustRgbLuminance(rgb:ColorRGB, factor:Number):void 
    { 
     var hsl:ColorHSL = new ColorHSL(); 

     rgbToHsl(hsl, rgb); 

     hsl.Luminance *= factor; 

     if (hsl.Luminance < 0.0) 
     { 
      hsl.Luminance = 0.0; 
     } 

     if (hsl.Luminance > 1.0) 
     { 
      hsl.Luminance = 1.0; 
     } 

     hslToRgb(rgb, hsl); 
    } 

    //--------------------------------------- 
    // 
    static public function uintTo2DigitHex(value:uint):String 
    { 
     var str:String = value.toString(16).toUpperCase(); 

     if (1 == str.length) 
     { 
      str = "0" + str; 
     } 

     return str; 
    } 

    //--------------------------------------- 
    // 
    static public function uintTo6DigitHex(value:uint):String 
    { 
     var str:String = value.toString(16).toUpperCase(); 

     if (1 == str.length) {return "00000" + str;} 
     if (2 == str.length) {return "0000" + str;} 
     if (3 == str.length) {return "000" + str;} 
     if (4 == str.length) {return "00" + str;} 
     if (5 == str.length) {return "0" + str;} 

     return str; 
    } 
} 
5

Tổng quan

Chuyển đổi từ RGB để HSV và sau đó điều chỉnh màu sắc (như answered here) tạo ra một độ sáng nhận thức không phù hợp.Màu vàng/xanh là đáng chú ý nhẹ hơn màu xanh/tím:

Inconsistent

Một kết quả tương tự mà không thay đổi như vậy có thể:

Consistent

Algorithm

Thuật toán, tuy nhiên, phức tạp hơn nhiều:

  1. Chuyển đổi mã thập lục phân HTML thành giá trị RGB danh nghĩa (chia thành phần 255).
  2. Chuyển đổi giá trị RGB thành XYZ colour space; sử dụng D65 reference white sRGB working space.
  3. Chuyển đổi từ XYZ thành Lab colour space.
  4. Chuyển đổi từ L a b thành LCH colour space.
  5. Tính màu sắc của màu sắc của bánh xe trong không gian màu LCH:
    (360.0 div $wedges) * $wedge
  6. Tính toán lại màu mới theo radian.
  7. Chuyển đổi từ LCH thành Lab colour space bằng màu sắc mới.
  8. Chuyển đổi từ L a b sang XYZ colour space.
  9. Chuyển đổi từ XYZ thành sRGB colour space.
  10. Multiply các giá trị RGB của 255.

Thực hiện

Dưới đây là một ví dụ trong thực hiện XSLT 1.0:

<?xml version="1.0"?> 
<!-- 
| The MIT License 
| 
| Copyright 2014 White Magic Software, Inc. 
| 
| Permission is hereby granted, free of charge, to any person 
| obtaining a copy of this software and associated documentation 
| files (the "Software"), to deal in the Software without 
| restriction, including without limitation the rights to use, 
| copy, modify, merge, publish, distribute, sublicense, and/or 
| sell copies of the Software, and to permit persons to whom the 
| Software is furnished to do so, subject to the following 
| conditions: 
| 
| The above copyright notice and this permission notice shall be 
| included in all copies or substantial portions of the Software. 
| 
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
| OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
| HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
| WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
| OTHER DEALINGS IN THE SOFTWARE. 
+--> 
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<!-- Reference white (X, Y, and Z components) --> 
<xsl:variable name="X_r" select="0.950456"/> 
<xsl:variable name="Y_r" select="1.000000"/> 
<xsl:variable name="Z_r" select="1.088754"/> 
<xsl:variable name="LAB_EPSILON" select="216.0 div 24389.0"/> 
<xsl:variable name="LAB_K" select="24389.0 div 27.0"/> 

<!-- Pie wedge colours based on this hue. --> 
<xsl:variable name="base_colour" select="'46A5E5'"/> 

<!-- Pie wedge stroke colour. --> 
<xsl:variable name="stroke_colour" select="'white'"/> 

<!-- 
| Creates a colour for a particular pie wedge. 
| 
| http://en.wikipedia.org/wiki/HSL_and_HSV 
+--> 
<xsl:template name="fill"> 
    <!-- Current wedge number for generating a colour. --> 
    <xsl:param name="wedge"/> 
    <!-- Total number of wedges in the pie. --> 
    <xsl:param name="wedges"/> 
    <!-- RGB colour in hexadecimal. --> 
    <xsl:param name="colour"/> 

    <!-- Derive the colour decimal values from $colour's HEX code. --> 
    <xsl:variable name="r"> 
    <xsl:call-template name="hex2dec"> 
     <xsl:with-param name="hex" 
     select="substring($colour, 1, 2)"/> 
    </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="g"> 
    <xsl:call-template name="hex2dec"> 
     <xsl:with-param name="hex" 
     select="substring($colour, 3, 2)"/> 
    </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="b"> 
    <xsl:call-template name="hex2dec"> 
     <xsl:with-param name="hex" 
     select="substring($colour, 5, 2)"/> 
    </xsl:call-template> 
    </xsl:variable> 

    <!-- 
    | Convert RGB to XYZ, using nominal range for RGB. 
    | http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html 
    +--> 
    <xsl:variable name="r_n" select="$r div 255" /> 
    <xsl:variable name="g_n" select="$g div 255" /> 
    <xsl:variable name="b_n" select="$b div 255" /> 

    <!-- 
    | Assume colours are in sRGB. 
    | http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html 
    --> 
    <xsl:variable name="x" 
    select=".4124564 * $r_n + .3575761 * $g_n + .1804375 * $b_n"/> 
    <xsl:variable name="y" 
    select=".2126729 * $r_n + .7151522 * $g_n + .0721750 * $b_n"/> 
    <xsl:variable name="z" 
    select=".0193339 * $r_n + .1191920 * $g_n + .9503041 * $b_n"/> 

    <!-- 
    | Convert XYZ to L*a*b. 
    | http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html 
    +--> 
    <xsl:variable name="if_x"> 
    <xsl:call-template name="lab_f"> 
     <xsl:with-param name="xyz_n" select="$x div $X_r"/> 
    </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="if_y"> 
    <xsl:call-template name="lab_f"> 
     <xsl:with-param name="xyz_n" select="$y div $Y_r"/> 
    </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="if_z"> 
    <xsl:call-template name="lab_f"> 
     <xsl:with-param name="xyz_n" select="$z div $Z_r"/> 
    </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="lab_l" select="(116.0 * $if_y) - 16.0"/> 
    <xsl:variable name="lab_a" select="500.0 * ($if_x - $if_y)"/> 
    <xsl:variable name="lab_b" select="200.0 * ($if_y - $if_z)"/> 

    <!-- 
    | Convert L*a*b to LCH. 
    | http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html 
    +--> 
    <xsl:variable name="lch_l" select="$lab_l"/> 

    <xsl:variable name="lch_c"> 
    <xsl:call-template name="sqrt"> 
     <xsl:with-param name="n" select="($lab_a * $lab_a) + ($lab_b * $lab_b)"/> 
    </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="lch_h"> 
    <xsl:call-template name="atan2"> 
     <xsl:with-param name="x" select="$lab_b"/> 
     <xsl:with-param name="y" select="$lab_a"/> 
    </xsl:call-template> 
    </xsl:variable> 

    <!-- 
    | Prevent similar adjacent colours. 
    | http://math.stackexchange.com/a/936767/7932 
    +--> 
    <xsl:variable name="wi" select="$wedge"/> 
    <xsl:variable name="wt" select="$wedges"/> 
    <xsl:variable name="w"> 
    <xsl:choose> 
     <xsl:when test="$wt &gt; 5"> 
     <xsl:variable name="weven" select="(($wi+4) mod ($wt + $wt mod 2))"/> 
     <xsl:value-of 
      select="$weven * (1-($wi mod 2)) + ($wi mod 2 * $wi)"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="$wedge"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:variable> 
    <!-- lch_l, lch_c, and lch_h are now set; rotate the hue. --> 
    <xsl:variable name="lch_wedge_h" select="(360.0 div $wedges) * $wedge"/> 

    <!-- 
    | Convert wedge's hue-adjusted LCH to L*a*b. 
    | http://www.brucelindbloom.com/index.html?Eqn_LCH_to_Lab.html 
    +--> 
    <xsl:variable name="lab_sin_h"> 
    <xsl:call-template name="sine"> 
     <xsl:with-param name="degrees" select="$lch_wedge_h"/> 
    </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="lab_cos_h"> 
    <xsl:call-template name="cosine"> 
     <xsl:with-param name="degrees" select="$lch_wedge_h"/> 
    </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="final_lab_l" select="$lch_l"/> 
    <xsl:variable name="final_lab_a" select="$lch_c * $lab_cos_h"/> 
    <xsl:variable name="final_lab_b" select="$lch_c * $lab_sin_h"/> 

    <!-- 
    | Convert L*a*b to XYZ. 
    | http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html 
    +--> 
    <xsl:variable name="of_y" select="($final_lab_l + 16.0) div 116.0"/> 
    <xsl:variable name="of_x" select="($final_lab_a div 500.0) + $of_y"/> 
    <xsl:variable name="of_z" select="$of_y - ($final_lab_b div 200.0)"/> 

    <xsl:variable name="of_x_pow"> 
    <xsl:call-template name="power"> 
     <xsl:with-param name="base" select="$of_x"/> 
     <xsl:with-param name="exponent" select="3"/> 
    </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="of_z_pow"> 
    <xsl:call-template name="power"> 
     <xsl:with-param name="base" select="$of_z"/> 
     <xsl:with-param name="exponent" select="3"/> 
    </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="ox_r"> 
    <xsl:choose> 
     <xsl:when test="$of_x_pow &gt; $LAB_EPSILON"> 
     <xsl:value-of select="$of_x_pow"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="((116.0 * $of_x) - 16.0) div $LAB_K"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:variable> 
    <xsl:variable name="oy_r"> 
    <xsl:choose> 
     <xsl:when test="$final_lab_l &gt; ($LAB_K * $LAB_EPSILON)"> 
     <xsl:call-template name="power"> 
      <xsl:with-param name="base" 
      select="($final_lab_l + 16.0) div 116.0"/> 
      <xsl:with-param name="exponent" 
      select="3"/> 
     </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="$final_lab_l div $LAB_K"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:variable> 
    <xsl:variable name="oz_r"> 
    <xsl:choose> 
     <xsl:when test="$of_z_pow &gt; $LAB_EPSILON"> 
     <xsl:value-of select="$of_z_pow"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="((116.0 * $of_z) - 16.0) div $LAB_K"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:variable> 

    <xsl:variable name="X" select="$ox_r * $X_r"/> 
    <xsl:variable name="Y" select="$oy_r * $Y_r"/> 
    <xsl:variable name="Z" select="$oz_r * $Z_r"/> 

    <!-- 
    | Convert XYZ to sRGB. 
    | http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html 
    +--> 
    <xsl:variable name="R" 
    select="3.2404542 * $X + -1.5371385 * $Y + -0.4985314 * $Z"/> 
    <xsl:variable name="G" 
    select="-0.9692660 * $X + 1.8760108 * $Y + 0.0415560 * $Z"/> 
    <xsl:variable name="B" 
    select="0.0556434 * $X + -0.2040259 * $Y + 1.0572252 * $Z"/> 

    <!-- Round the result. --> 
    <xsl:variable name="R_r" select="round($R * 255)"/> 
    <xsl:variable name="G_r" select="round($G * 255)"/> 
    <xsl:variable name="B_r" select="round($B * 255)"/> 

    <xsl:text>rgb(</xsl:text> 
    <xsl:value-of select="concat($R_r, ',', $G_r, ',', $B_r)"/> 
    <xsl:text>)</xsl:text> 
</xsl:template> 

<xsl:template name="lab_f"> 
    <xsl:param name="xyz_n"/> 

    <xsl:choose> 
    <xsl:when test="$xyz_n &gt; $LAB_EPSILON"> 
     <xsl:call-template name="nthroot"> 
     <xsl:with-param name="index" select="3"/> 
     <xsl:with-param name="radicand" select="$xyz_n"/> 
     </xsl:call-template> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:value-of select="($LAB_K * $xyz_n + 16.0) div 116.0" /> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

<!-- Converts a two-digit hexadecimal number to decimal. --> 
<xsl:template name="hex2dec"> 
    <xsl:param name="hex"/> 

    <xsl:variable name="digits" select="'ABCDEF'"/> 
    <xsl:variable name="X" select="substring($hex, 1, 1)"/> 
    <xsl:variable name="Y" select="substring($hex, 2, 1)"/> 
    <xsl:variable name="Xval" 
    select="string-length(substring-before($digits,$X))"/> 
    <xsl:variable name="Yval" 
    select="string-length(substring-before($digits,$Y))"/> 
    <xsl:value-of select="16 * $Xval + $Yval"/> 
</xsl:template> 

</xsl:stylesheet> 

Các trang điểm, root, và các chức năng toán linh tinh còn lại như một tập thể dục cho người đọc. Ngoài ra, không ai trong tâm trí của họ muốn mã hóa tất cả điều này trong XSLT 1.0. Mặt khác, XSLT 2.0 có một số implementation here.

Tài

Đọc thêm:

Các vấn đề liên quan