如何选择饼图的颜色?

时间:2008-10-25 19:53:09

标签: algorithm colors rgb

我有一些代码可以生成饼图的图像。它是一个通用类,因此任何数量的切片都可以作为输入。现在我有问题为切片选择好的颜色。是否有一些擅长的算法?

或许我应该亲自挑选并列出固定颜色?但是有多少。也许有10种颜色,希望永远不会超过10片?还有哪10种颜色可供选择?

颜色需要遵循一些规则:

  • 他们需要看起来不错
  • 相邻的颜色不应该相似(绿色旁边的蓝色是禁止的)
  • 饼背景颜色为白色,因此白色不在选项中

一些使用RGB值进行操作的算法将是首选解决方案。

8 个答案:

答案 0 :(得分:67)

我解决了它如下:

  1. 选择基色
  2. 计算色调baseHue)。
  3. 创建具有相同饱和度和亮度的颜色,其色调计算为:
      hue = baseHue + ((240 / pieces) * piece % 240
    
  4. 在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

    Google Charts example使用12件,基色为#8A56E2:

    Chart Example

答案 1 :(得分:17)

我会预编译大约20种颜色的列表,然后开始用 2nd 颜色重复。这样你就不会破坏你的第二条规则。此外,如果有人制作超过20个切片的饼图,他们会遇到更大的问题。 :)

答案 2 :(得分:12)

查看Color Brewer,这是一个有助于定义着色方案以传达定性或定量信息的工具:地图,图表等。此工具可以生成的三种“调色板类型” - 顺序,定性和分歧 - 你可能需要后者,分歧......

您甚至可以下载所有调色板的RGB定义的Excel文件。

答案 3 :(得分:10)

this solution的基础上解决问题的规则#2,以下算法在饼的中点周围交换颜色。这两个参数:

  1. pNbColors 是饼图中的切片数量
  2. pNonAdjacentSimilarColor 一个布尔值,表示您是否要使用相邻的相似颜色。
  3. 我使用 ColorHSL ColorRGB ColorUtils (下方提供)。

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

答案 4 :(得分: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. 将RGB值乘以255。
  11. 实施

    以下是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="'0123456789ABCDEF'"/>
      <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,root和其他数学函数留给读者练习。此外,没有人在他们正确的思想中想要在XSLT 1.0中编写所有这些代码。另一方面,XSLT 2.0有一个implementation here

    资源

    进一步阅读:

答案 5 :(得分:4)

1985年的文章“ROSS E. ROLEY,CAPT”给出了一种算法,可以最大化任意颜色组的颜色分离(complete with code in FORTRAN)。

(分色似乎是军队防止蓝蓝事件的重要可视化问题。)

然而,如果你想坚持一组20种颜色,一个快速而简单的解决方案是选择十二面体的顶点并将(x,y,z)坐标(适当缩放)转换为(r ,G,b)。

答案 6 :(得分:2)

有一个生成器here。它适用于网页设计,但颜色在饼图上看起来也很棒。

你可以预先编译漂亮的颜色列表,或者检查生成器背后的逻辑并自己做类似的事情。

答案 7 :(得分:1)

我发现这个伪代码公式可能有所帮助。你可以从一套开始播种它。

色差公式

以下是W3C建议的用于确定两种颜色之间差异的公式。

(最大值(红色值1,红色值2) - 最小值(红色值1,红色值2))+(最大值(绿色值1,绿色值2) - 最小值(绿色值1,绿色值2)) +(最大值(蓝色值1,蓝色值2) - 最小值(蓝色值1,蓝色值2))

背景颜色和前景色之间的差异应大于500.

Here is the source