web-dev-qa-db-fra.com

Comment choisir les couleurs d'un graphique à secteurs?

J'ai du code qui génère l'image d'un graphique à secteurs. C'est une classe à usage général, donc n'importe quel nombre de tranches peut être donné en entrée. Maintenant, j'ai du mal à choisir de bonnes couleurs pour les tranches. Y a-t-il un algorithme qui est bon à cela?

Ou peut-être devrais-je simplement choisir et lister les couleurs fixes? Mais combien. Peut-être 10 couleurs et j'espère qu'il n'y aura jamais plus de 10 tranches? De plus, quelles 10 couleurs choisir?

Les couleurs doivent suivre certaines règles:

  • ils doivent avoir l'air sympa
  • les couleurs adjacentes ne doivent pas être similaires (le bleu à côté du vert est interdit)
  • la couleur d'arrière-plan de la tarte est blanche, donc le blanc est hors option

Un algorithme manipulant avec des valeurs RVB serait une solution préférée.

55
codeguru

Je voudrais pré-compiler une liste d'environ 20 couleurs, puis commencer à répéter avec la couleur 2nd. De cette façon, vous ne violerez pas votre deuxième règle. De plus, si quelqu'un crée un graphique à secteurs avec plus de 20 tranches, il a de plus gros problèmes. :)

17
Bill the Lizard

Je l'ai résolu comme suit:

  1. Choisissez une couleur de base .
  2. Calculez sa teinte (baseHue).
  3. Créez une couleur avec la même saturation et luminosité, avec sa teinte calculée comme:
     teinte = baseHue + ((240/pièces) * pièce% 240 
    

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

J'ai utilisé la classe HSLColor .

Le exemple Google Charts qui utilise 12 pièces et une couleur de base de # 8A56E2:

Chart Example

68
Niels Bosma

Jetez un oeil à Color Brewer , un outil qui aide à définir un schéma de coloration pour transmettre des informations qualitatives ou quantitatives: cartes, graphiques, etc. Sur trois "types" de palettes que cet outil peut générer - séquentielle, qualitative et divergente - vous avez probablement besoin de cette dernière, divergente ...

Vous pouvez même télécharger des fichiers Excel avec les définitions RVB de toutes les palettes.

12
Yarik

En s'appuyant sur cette solution pour résoudre la règle n ° 2 de la question, l'algorithme suivant échange les couleurs autour du point médian du secteur. Les deux paramètres:

  1. pNbColors est le nombre de tranches dans la tarte
  2. pNonAdjacentSimilarColor un booléen pour indiquer si vous voulez avoir des couleurs similaires adjacentes ou non.

J'utilise ColorHSL , ColorRGB et ColorUtils (fourni ci-dessous).

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

Cela produit la sortie de droite:

Comparison Image

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

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

Classe 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;
    }
}
10
Pic Mickael

Aperçu

La conversion de RVB en HSV, puis en ajustant la teinte (comme répondu ici ) crée une luminosité perçue incohérente. Le jaune/vert est nettement plus clair que le bleu/violet:

Inconsistent

Un résultat similaire sans une telle variation est possible:

Consistent

Algorithme

Cependant, l'algorithme est beaucoup plus complexe:

  1. Convertissez les codes HTML hexadécimaux en valeurs RVB nominales (divisez les composants par 255).
  2. Convertir les valeurs RVB en espace colorimétrique XYZ ; utilisez D65 référence blanc sRGB espace de travail .
  3. Convertissez de XYZ en L a b espace colorimétrique .
  4. Convertir de L a b en espace colorimétrique LCH .
  5. Calculez la teinte de la couleur du coin de tarte dans l'espace colorimétrique LCH:
    (360.0 div $ cales) * $ cale
  6. Recalculez la nouvelle teinte en radians.
  7. Convertissez de LCH en L a b espace colorimétrique en utilisant une nouvelle teinte.
  8. Convertir de L a b en espace colorimétrique XYZ .
  9. Convertir de XYZ en espace colorimétrique sRGB .
  10. Multipliez les valeurs RVB par 255.

La mise en oeuvre

Voici un exemple d'implémentation dans 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>

Les fonctions mathématiques trig, root et diverses sont laissées en exercice au lecteur. De plus, personne sensé ne voudrait coder tout cela dans XSLT 1.0. XSLT 2.0, d'autre part, a un implémentation ici .

Ressources

Lectures complémentaires:

5
Dave Jarvis

Cet article de 1985 par "ROSS E. ROLEY, CAPT" donne un algorithme pour maximiser la séparation des couleurs pour un ensemble arbitraire de couleurs ( complet avec code en FORTRAN ).

(La séparation des couleurs semble être un problème de visualisation important pour les forces militaires afin de prévenir les incidents bleu sur bleu.)

Cependant, si vous souhaitez vous en tenir à un ensemble de 20 couleurs, une solution rapide et simple serait de choisir les sommets d'un dodécaèdre et de convertir les coordonnées (x, y, z) (convenablement mises à l'échelle) en (r, g, b).

4
Declan Brennan

Il y a un générateur ici . Il est destiné à la conception Web, mais les couleurs auraient également fière allure sur un graphique à secteurs.

Vous pouvez soit pré-compiler une liste de belles couleurs, soit examiner la logique derrière le générateur et faire quelque chose de similaire vous-même.

2
Kramii

J'ai trouvé cette formule de pseudocode qui pourrait aider. Vous pouvez commencer avec un ensemble pour le semer.

Formule de différence de couleur

Voici la formule suggérée par le W3C pour déterminer la différence entre deux couleurs.

(maximum (valeur rouge 1, valeur rouge 2) - minimum (valeur rouge 1, valeur rouge 2)) + (maximum (valeur verte 1, valeur verte 2) - minimum (valeur verte 1, valeur verte 2)) + (maximum (Valeur bleue 1, Valeur bleue 2) - minimum (Valeur bleue 1, Valeur bleue 2))

La différence entre la couleur d'arrière-plan et la couleur de premier plan doit être supérieure à 500.

Voici la source

1
branchgabriel