원형 차트의 색상을 선택하는 방법은 무엇입니까?
파이 차트 이미지를 생성하는 코드가 있습니다. 범용 클래스이므로 원하는 수의 슬라이스를 입력으로 제공 할 수 있습니다. 이제 슬라이스에 적합한 색상을 선택하는 데 문제가 있습니다. 그것에 능숙한 알고리즘이 있습니까?
아니면 고정 된 색상을 직접 골라 나열해야할까요? 그러나 얼마나 많은가. 아마도 10 가지 색상과 10 조각 이하가되기를 바랍니다. 또한 어떤 10 가지 색상을 고를까요?
색상은 몇 가지 규칙을 따라야합니다.
- 그들은 멋지게 보일 필요가있다
- 인접한 색상은 비슷하지 않아야합니다 (녹색 옆의 파란색은 금지됨).
- 파이 배경색은 흰색이므로 흰색은 옵션에서 제외됩니다.
RGB 값으로 조작하는 일부 알고리즘이 선호되는 솔루션입니다.
약 20 개의 색상 목록을 미리 컴파일 한 다음 두 번째 색상으로 반복하기 시작 합니다. 이렇게하면 두 번째 규칙을 어 기지 않습니다. 또한 누군가가 조각이 20 개 이상인 원형 차트를 만들면 더 큰 문제가 발생합니다. :)
다음과 같이 해결했습니다.
- 기본 색상을 선택합니다 .
- 색상 (
baseHue
)을 계산합니다 . - 채도와 광도가 같은 색상을 만듭니다. 색상은 다음과 같이 계산됩니다.
hue = baseHue + ((240 / pieces) * 조각 % 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 클래스를 사용했습니다 .
12 개 조각과 # 8A56E2의 기본 색상을 사용 하는 Google 차트 예 :
정 성적 또는 정량적 정보를 전달하기 위해 색 구성표를 정의하는 데 도움이되는 도구 인 Color Brewer를 살펴보십시오 :지도, 차트 등.이 도구가 생성 할 수있는 세 가지 "유형"팔레트 (순차적, 정 성적 및 발산) 중 후자가 필요할 것입니다.
모든 팔레트의 RGB 정의가 포함 된 Excel 파일을 다운로드 할 수도 있습니다.
에 구축 이 솔루션은 문제의 규칙 # 2, 파이의 중간 점을 중심으로 다음과 같은 알고리즘 스왑 색상을 해결합니다. 두 가지 매개 변수 :
- pNbColors 는 파이의 조각 수입니다.
- pNonAdjacentSimilarColor 인접한 유사한 색상을 가질 것인지 여부를 나타내는 부울입니다.
내가 사용하고 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;
}
그러면 오른쪽 출력이 생성됩니다.
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;
}
}
개요
RGB에서 HSV로 변환 한 다음 색조를 조정하면 ( 여기에 설명 된대로 ) 일관되지 않은 인식 밝기가 생성됩니다. 노란색 / 녹색은 파란색 / 보라색보다 눈에 띄게 밝습니다.
이러한 변화없이 유사한 결과가 가능합니다.
연산
그러나 알고리즘은 훨씬 더 복잡합니다.
- HTML 16 진수 코드를 공칭 RGB 값으로 변환합니다 (구성 요소를 255로 나눕니다).
- RGB 값을 XYZ 색상 공간으로 변환 합니다 . 사용 D65 참조 흰색 의 sRGB 작업 공간 .
- XYZ에서 L a b 색 공간으로 변환 합니다 .
- L a b에서 LCH 색 공간으로 변환 합니다 .
- LCH 색상 공간에서 파이 쐐기 색상 색조 계산 :
(360.0 div $ wedges) * $ wedge
- 라디안 단위로 새 색조를 다시 계산합니다.
- 새로운 색조를 사용하여 LCH에서 L a b 색 공간으로 다시 변환 합니다.
- L a b에서 XYZ 색 공간으로 변환 합니다 .
- XYZ에서 sRGB 색 공간으로 변환 합니다 .
- RGB 값에 255를 곱합니다.
이행
다음은 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 > 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 > $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 > ($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 > $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 > $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>
The trig, root, and miscellaneous math functions are left as an exercise for the reader. Also, nobody in their right mind would want to code all this in XSLT 1.0. XSLT 2.0, on the other hand, has an implementation here.
Resources
Further reading:
- Generating color palettes using intuitive parameters
- Synthesis of Color Palettes
- Useful Color Equations
- Introduction to colour spaces
- Evenly distributed colors in a perceptively coherent space
- An algorithm for the selection of high-contrast color sets
- Selecting colors for statistical graphics
- Colourspace conversions
- Brewer palettes
This 1985 paper by "ROSS E. ROLEY, CAPT" gives an algorithm for maximizing color separation for an arbitrary set of colors (complete with code in FORTRAN).
(Color separation appears to be an important visualization issue for military forces to prevent blue-on-blue incidents.)
However if you want to stick to a set of 20 colors, a quick and simple solution would be to pick the vertexes of a dodecahedron and convert the (x,y,z) co-ordinates (suitably scaled) to (r,g,b).
There is a generator here. It is intended for web design, but the colours would look great on a pie chart, too.
You could either pre-compile a list of nice colours, or examine the logic behind the generator and do something similar yourself.
I found this pseudocode formula that might help. You could start with a set to seed it.
Colour Difference Formula
The following is the formula suggested by the W3C to determine the difference between two colours.
(최대 (빨간색 값 1, 빨간색 값 2)-최소값 (빨간색 값 1, 빨간색 값 2)) + (최대 값 (녹색 값 1, 녹색 값 2)-최소값 (녹색 값 1, 녹색 값 2)) + (최대 (파란색 값 1, 파란색 값 2)-최소값 (파란색 값 1, 파란색 값 2))
배경색과 전경색의 차이는 500보다 커야합니다.
참조 URL : https://stackoverflow.com/questions/236936/how-pick-colors-for-a-pie-chart
'IT story' 카테고리의 다른 글
휠 형식에서 numpy를 설치할 수 없습니다. (0) | 2021.01.06 |
---|---|
수학 파서의 스마트 디자인? (0) | 2021.01.06 |
C #에서 믹스 인을 구현할 수 있습니까? (0) | 2021.01.06 |
C #의 부분 인터페이스 (0) | 2021.01.06 |
UIView의 clipsToBounds와 CALayer의 masksToBounds 사이의 관계는 어떻습니까? (0) | 2021.01.06 |