Domanda

Ho del codice che genera l'immagine di un grafico a torta. È una classe di uso generale, quindi qualsiasi numero di sezioni può essere fornito come input. Ora ho problemi a scegliere buoni colori per le fette. C'è qualche algoritmo che è bravo in questo?

O forse dovrei semplicemente selezionare manualmente ed elencare i colori fissi? Ma quanti. Forse 10 colori e spero che non ci saranno mai più di 10 sezioni? Inoltre, quali 10 colori scegliere?

I colori devono seguire alcune regole:

  • devono avere un bell'aspetto
  • i colori adiacenti non dovrebbero essere simili (il blu accanto al verde è un non-andare)
  • il colore di sfondo della torta è bianco, quindi il bianco non è disponibile

Alcuni algoritmi che manipolano i valori RGB sarebbero una soluzione preferita.

È stato utile?

Soluzione

Vorrei precompilare un elenco di circa 20 colori, quindi iniziare a ripetere con il 2 ° colore. In questo modo non infrangerai la tua seconda regola. Inoltre, se qualcuno crea un grafico a torta con più di 20 sezioni, ha problemi più grandi. :)

Altri suggerimenti

L'ho risolto come segue:

  1. Scegli un colore di base .
  2. Calcola la tonalità ( baseHue ).
  3. Crea un colore con la stessa saturazione e luminosità, con la sua tonalità calcolata come:
      hue = baseHue + ((240 / pieces) * piece % 240
    

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

Ho usato la classe HSLColor .

Il esempio di Google Charts che utilizza 12 pezzi e un colore di base di # 8A56E2:

Esempio di grafico

Dai un'occhiata a Color Brewer , uno strumento che aiuta a definire uno schema di colorazione per trasmettere informazioni qualitative o quantitative: mappe, grafici, ecc. Su tre "tipi" di tavolozze che questo strumento può generare - sequenziale, qualitativo e divergente - probabilmente hai bisogno di quest'ultimo, divergente ...

Puoi persino scaricare file Excel con definizioni RGB di tutte le tavolozze.

Basandosi su questa soluzione per risolvere la regola n. 2 della domanda, il seguente algoritmo scambia i colori attorno al punto centrale della torta . I due parametri:

  1. pNbColors è il numero di sezioni nella torta
  2. pNonAdjacentSimilarColor un valore booleano per indicare se si desidera avere colori simili adiacenti o meno.

Sto utilizzando ColorHSL , ColorRGB e ColorUtils (fornito di seguito).

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

Questo produce l'output sul lato destro:

Immagine di confronto

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

Panoramica

La conversione da RGB a HSV e quindi la regolazione della tonalità (come ha risposto qui ) crea una luminosità percepita incoerente. Il giallo / verde sono notevolmente più chiari del blu / viola:

Incoerente

Un risultato simile senza tale variazione è possibile:

Coerente

Algoritmo

L'algoritmo, tuttavia, è molto più complesso:

  1. Converti i codici esadecimali HTML in valori RGB nominali (dividi i componenti per 255).
  2. Converti i valori RGB in XYZ color space ; usa D65 riferimento bianco sRGB spazio di lavoro .
  3. Converti da XYZ a L a b spazio colore .
  4. Converti da L a b a Spazio colore LCH .
  5. Calcola la tonalità del colore del cuneo a torta nello spazio colore LCH:
    (360.0 div $wedges) * $wedge
  6. Ricalcola la nuova tonalità in radianti.
  7. Converti indietro da LCH in L a b spazio colore usando una nuova tonalità.
  8. Converti da L a b a spazio colore XYZ .
  9. Converti da XYZ a spazio colore sRGB .
  10. .
  11. Moltiplica i valori RGB per 255.

Attuazione

Ecco un'implementazione di esempio in 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>

Le funzioni matematiche di trig, root e varie sono lasciate come esercizio per il lettore. Inoltre, nessuno nella loro mente giusta vorrebbe codificare tutto questo in XSLT 1.0. XSLT 2.0, d'altra parte, ha un'implementazione qui .

Risorse

Ulteriori letture:

Questo articolo del 1985 di "ROSS E. ROLEY, CAPT" fornisce un algoritmo per massimizzare la separazione dei colori per un set arbitrario di colori ( completo di codice in FORTRAN ).

(La separazione dei colori sembra essere un importante problema di visualizzazione per le forze militari per prevenire incidenti blu su blu.)

Tuttavia, se si desidera attenersi a un set di 20 colori, una soluzione semplice e veloce sarebbe quella di scegliere i vertici di un dodecaedro e convertire le coordinate (x, y, z) (opportunamente ridimensionate) in (r , g, b).

C'è un generatore qui . È inteso per il web design, ma i colori sarebbero anche fantastici su un grafico a torta.

Puoi pre-compilare un elenco di bei colori, oppure esaminare la logica dietro il generatore e fare qualcosa di simile da solo.

Ho trovato questa formula pseudocodice che potrebbe aiutare. Potresti iniziare con un set per seminarlo.

Formula di differenza di colore

La seguente è la formula suggerita dal W3C per determinare la differenza tra due colori.

(massimo (valore rosso 1, valore rosso 2) - minimo (valore rosso 1, valore rosso 2)) + (massimo (valore verde 1, valore verde 2) - minimo (valore verde 1, valore verde 2)) + (massimo (valore blu 1, valore blu 2) - minimo (valore blu 1, valore blu 2))

La differenza tra il colore di sfondo e il colore di primo piano dovrebbe essere maggiore di 500.

Ecco la fonte

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top