円グラフの画像を生成するコードがいくつかあります。これは汎用クラスなので、任意の数のスライスを入力として指定できます。今、スライスに適した色を選ぶのに問題があります。それが得意なアルゴリズムはありますか?
色はいくつかの規則に従う必要があります。
- 彼らは見栄えがする必要があります
- 隣接する色は同じであってはなりません (緑の隣に青は禁物です)。
- パイの背景色は白なので、白は選択できません
RGB 値を操作するアルゴリズムが推奨されるソリューションです。
約 20 色のリストを事前にコンパイルしてから、2 番目の色から繰り返します。こうすれば、2 番目の規則に違反することはありません。また、誰かが 20 を超えるスライスを含む円グラフを作成すると、より大きな問題が発生します。:)
Color Brewerを見てみましょう。このツールは、マップ、チャートなどの定性的または定量的な情報を伝えるためのカラーリング スキームを定義するのに役立ちます。おそらく後者が必要で、分岐しています...
すべてのパレットの RGB 定義を含む Excel ファイルをダウンロードすることもできます。
質問のルール 2 を解決するためにこのソリューションに基づいて構築された次のアルゴリズムは、円グラフの中間点の周りで色を交換します。2 つのパラメーター:
私は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 に変換してから色相を調整すると (回答はこちら)、一貫性のない知覚される明るさが作成されます。黄/緑は青/紫よりも著しく明るいです:
そのような変動のない同様の結果が可能です。
ただし、アルゴリズムははるかに複雑です。
(360.0 div $wedges) * $wedge
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>
三角関数、ルート関数、およびその他の数学関数は、読者の演習として残されています。また、まともな人なら、これらすべてを XSLT 1.0 でコーディングしたいとは思わないでしょう。一方、XSLT 2.0 はここに実装されています。
参考文献:
「ROSSE。ROLEY、CAPT」によるこの1985年の論文は、任意の色のセット(FORTRANのコードを含む)の色分解を最大化するためのアルゴリズムを提供します。
(色の分離は、青地に青の事件を防ぐために軍隊にとって重要な視覚化の問題であるように思われます。)
ただし、20色のセットに固執する場合、すばやく簡単な解決策は、12面体の頂点を選択し、(x、y、z)座標(適切にスケーリングされた)を(r、g、 b)。
ここに発電機があります。これは Web デザインを目的としていますが、色は円グラフでも見栄えがします。
素敵な色のリストを事前にコンパイルするか、ジェネレーターの背後にあるロジックを調べて、自分で同様のことを行うことができます。
役立つかもしれないこの疑似コード式を見つけました。それをシードするセットから始めることができます。
色差式
以下は、2 つの色の違いを判断するために W3C によって提案された式です。
(最大 (赤の値 1、赤の値 2) - 最小 (赤の値 1、赤の値 2)) + (最大 (緑の値 1、緑の値 2) - 最小 (緑の値 1、緑の値 2)) + (最大(青の値 1、青の値 2) - 最小 (青の値 1、青の値 2))
背景色と前景色の差は 500 より大きくする必要があります。