2008-10-25 195 views
52

我有一些生成餅圖圖像的代碼。這是一個通用的類,所以可以給出任意數量的切片作爲輸入。現在我有問題爲切片挑選好顏色。是否有一些算法擅長?如何爲餅圖選擇顏色?

或者我應該手工挑選並列出固定顏色?但是有多少。也許10種顏色,希望有沒有超過10片有史以來?另外,要挑選哪10種顏色?

顏色需要遵循一些規則:

  • 他們需要好看
  • 相鄰顏色不應該是相似的(藍色旁邊的綠色是一個不走)
  • 派背景顏色爲白色,所以白色是不可選的

某些算法與RGB值操作將是一個首選的解決方案。

+4

怎麼是藍色的類似綠色的嗎? – peterchen 2008-10-26 01:59:34

+10

@peterchen - 如果你是藍綠色的色盲,則非常相似;) – redcalx 2011-11-05 17:18:27

回答

16

我會預編譯一個約20種顏色的列表,然後開始重複使用第二個顏色。這樣你就不會破壞你的第二條規則。另外,如果有人制作了超過20片的餅圖,它們的問題就更大了。 :)

+0

這正是我目前正在從事的Web應用程序所做的。創建20個,然後重複這20次,創建200個列表。但是,我有一個包含20多個切片的餅圖,顏色正在重複。有什麼建議麼? – 2009-02-06 20:40:51

+2

如果您真的需要〜200種不同的顏色,您可能需要考慮使用Web安全調色板(或子集)。 http://en.wikipedia.org/wiki/Web_colors – 2009-02-06 21:15:25

1

我發現這個僞代碼公式可能有幫助。你可以從一個集合開始播種。

色差公式

以下是由W3C推薦,以確定兩種顏色之間的差的公式。 (綠色值1,綠色值2) - 最小值(紅色值1,紅色值2)) - )+(最大(藍色值1,藍值2) - 最小(藍色值1,藍值2))

的背景顏色和前景顏色之間的差應大於500

Here is the source

2

有一個發電機here。它適用於網頁設計,但顏色在餅圖上看起來也很棒。

您可以預先編譯好的顏色列表,也可以檢查生成器背後的邏輯並自己做類似的事情。

12

看一看Color Brewer,這個工具可以幫助定義一個着色方案來傳達定性或定量信息:地圖,圖表等。這個工具可以生成三種「類型」的調色板 - 順序,定性,和發散 - 你可能需要後者,發散...

你甚至可以下載所有調色板的RGB定義的Excel文件。

64

我解決它,如下所示:

  1. 選擇基色
  2. 計算它的色相baseHue)。
  3. 創建具有相同的飽和度和亮度的彩色,用其色調計算爲:
     
        hue = baseHue + ((240/pieces) * piece % 240 
    

在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()); 

我用HSLColor class

的使用12件和#8A56E2基色Google Charts example

Chart Example

4

這1985紙由「ROSS E.羅利,CAPT」給出對於任意組最大化分色的算法的顏色(complete with code in FORTRAN)。如果你想堅持到了一套20種顏色,一個快速和簡單的解決辦法

(分色似乎是一個重要的可視化問題,爲軍隊,以防止藍上藍的事件。)

然而選擇十二面體的頂點並將(x,y,z)座標(適當縮放)轉換爲(r,g,b)。

9

建立在this solution的基礎上來解決問題的規則#2,下面的算法在餅圖的中點交換顏色。這兩個參數:

  1. pNbColors是切片的餡餅
  2. pNonAdjacentSimilarColor一個布爾值,表明如果你想有鄰近的類似顏色或數量不限。

我使用ColorHSLColorRGBColorUtils(下面提供)。

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

這產生了右手側輸出:

Comparison Image

ColorHSL類:

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類:

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類:

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

概述

從RGB轉換爲HSV,然後調整色相(如answered here)創建一個不一致的感知的亮度。將黃色/綠色明顯比藍色淡/紫色:

Inconsistent

沒有這種變化類似的結果是可能的:

Consistent

算法

的算法,然而,要複雜得多:

  1. 將HTML十六進制代碼轉換爲標稱RGB值(將組件除以255)。
  2. 將RGB值轉換爲XYZ colour space;使用D65 reference white sRGB working space
  3. 從XYZ轉換爲Lab colour space
  4. 轉換自L a b至LCH colour space
  5. 計算LCH色彩空間中的餡餅楔形色調:
    (360.0 div $wedges) * $wedge
  6. 重新計算以弧度表示的新色調。
  7. 使用新的色相從LCH轉換回Lab colour space
  8. 轉換自L a b至XYZ colour space
  9. 從XYZ轉換爲sRGB colour space
  10. 乘以255

實施

這裏的RGB值是在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> 

的Trig,根,和雜項數學函數被留下作爲爲讀者鍛鍊。而且,沒有一個人的頭腦想要在XSLT 1.0中編寫所有這些代碼。另一方面,XSLT 2.0有一個implementation here

資源

延伸閱讀: