    package flare.util.palette
    {
        import flare.util.Colors;

        /**
         * Palette for color values, including utility methods for generating
         * both categorical and ordinal color palettes.
         */
        public class ColorPalette extends Palette
        {
            private var _keyframes:Array;

            /** Keyframes at which color values change in the palette. Useful
             *  for configuring gradient paint fills. */
            public function get keyframes():Array { return _keyframes; }

            /**
             * Creates a new ColorPalette.
             * @param colors an array of colors defining the palette
             * @param keyframes array of keyframes of color interpolations
             */
            public function ColorPalette(colors:Array=null, keyframes:Array=null) {
                _values = colors;
                _keyframes = keyframes;
            }

            /**
             * Retrieves the color corresponding to input interpolation fraction.
             * @param f an interpolation fraction
             * @return the color corresponding to the input fraction
             */
            public function getColor(v:Number):uint
            {
                if (_values==null || _values.length==0)
                    return 0;
                return _values[uint(Math.round(v*(_values.length-1)))];
            }

            /**
             * Retrieves the color corresponding to the input array index.
             * @param idx an integer index. The actual index value used is
             *  the modulo of the input index by the length of the palette.
             * @return the color in the palette at the given index
             */
            public function getColorByIndex(idx:int):uint
            {
                if (_values == null || _values.length == 0 || idx < 0)
                    return 0;
                else
                    return _values[idx % _values.length];
            }

            // --------------------------------------------------------------------

            /** Default size of generated color palettes. */
            public static const DEFAULT_SIZE:int = 64;
            /** A set of 10 colors for encoding category values. */
            public static const CATEGORY_COLORS_10:/*uint*/Array = [
                0xFF1F77B4, 0xFFFF7F0E, 0xFF2CA02C, 0xFFD62728, 0xFF9467BD,
                0xFF8C564B, 0xFFE377C2, 0xFF7F7F7F, 0xFFBCBD22, 0xFF17BECF
            ];
            /** A set of 20 colors for encoding category values. Includes
             *  the colors of <code>CATEGORY_COLORS_10</code> plus lighter
             *  shades of each. */
            public static const CATEGORY_COLORS_20:/*uint*/Array = [
                0xFF1F77B4, 0xFFAEC7E8, 0xFFFF7F0E, 0xFFFFBB78, 0xFF2CA02C,
                0xFF98DF8A, 0xFFD62728, 0xFFFF9896, 0xFF9467BD, 0xFFC5B0D5,
                0xFF8C564B, 0xFFC49C94, 0xFFE377C2, 0xFFF7B6D2, 0xFF7F7F7F,
                0xFFC7C7C7, 0xFFBCBD22, 0xFFDBDB8D, 0xFF17BECF, 0xFF9EDAE5
            ];
            /** An alternative set of 19 colors for encoding category values. */
            public static const CATEGORY_COLORS_ALT_19:/*uint*/Array = [
                0xff9C9EDE, 0xff7375B5, 0xff4A5584, 0xffCEDB9C, 0xffB5CF6B,
                0xff8CA252, 0xff637939, 0xffE7CB94, 0xffE7BA52, 0xffBD9E39,
                0xff8C6D31, 0xffE7969C, 0xffD6616B, 0xffAD494A, 0xff843C39,
                0xffDE9ED6, 0xffCE6DBD, 0xffA55194, 0xff7B4173
            ];

            /**
             * Generates a categorical color palette
             * @param size the number of colors to include
             * @param colors an array of category colors to use. If null, a
             *  default category color palette will be used.
             * @param alpha the alpha value for this palette's colors
             * @return the categorical color palette
             */
            public static function category(size:int=20, colors:Array=null,
                                            alpha:Number=1.0):ColorPalette
            {
                if (colors==null)
                    colors = size<=10 ? CATEGORY_COLORS_10 : CATEGORY_COLORS_20;
                var a:uint = uint(255 * alpha) % 256;
                var cm:Array = new Array(size);
                for (var i:uint=0; i<size; ++i) {
                    cm[i] = Colors.setAlpha(colors[i % colors.length], a);
                }
                return new ColorPalette(cm);
            }

            /**
             * Generates a color palette that uses a "cool", blue-heavy color scheme.
             * @param size the size of the color palette
             * @return the color palette
             */
            public static function cool(size:int=DEFAULT_SIZE):ColorPalette
            {
                return ramp(0xff00ffff, 0xffff00ff, size);
            }

            /**
             * Generates a color palette that moves from black to red to yellow
             * to white.
             * @param size the size of the color palette
             * @return the color palette
             */
            public static function hot(size:int=DEFAULT_SIZE):ColorPalette
            {
                var cm:Array = new Array(size), r:Number, g:Number, b:Number;
                var n:int = int(2*size/8);

                for (var i:uint=0; i<size; i++) {
                    r = i<n ? (i+1)/n : 1;
                    g = i<n ? 0 : (i<2*n ? (i-n)/n : 1);
                    b = i<2*n ? 0 : (i-2*n)/(size-2*n);
                    cm[i] = Colors.rgba(255*r, 255*g, 255*b);
                }
                var f:Number = 1/4;
                return new ColorPalette(cm, [0, f, 2*f, 1]);
            }

            /**
             * Generates a color palette that "ramps" from one color to another.
             * @param min the color corresponding to the minimum scale value
             * @param max the color corresponding to the maximum scale value
             * @param size the size of the color palette
             * @return the color palette
             */
            public static function ramp(min:uint=0xfff1eef6, max:uint=0xff045a8d,
                size:int=DEFAULT_SIZE):ColorPalette
            {
                var cm:Array = new Array(size);
                for (var i:uint=0; i<size; ++i) {
                    cm[i] = Colors.interpolate(min, max, i/(size-1));
                }
                return new ColorPalette(cm, [0,1]);
            }

            /**
             * Generates a color palette of color ramps diverging from a central
             * value.
             * @param min the color corresponding to the minimum scale value
             * @param mid the color corresponding to the central scale value
             * @param max the color corresponding to the maximum scale value
             * @param f an interpolation fraction specifying the position of the
             *  central value
             * @param size the size of the color palette
             * @return the color palette
             */
            public static function diverging(min:uint=0xffd73027,
                mid:uint=0xffffffbf, max:uint=0xff1a9850,
                f:Number=0.5, size:int=DEFAULT_SIZE):ColorPalette
            {
                var cm:Array = new Array(size);
                var mp:int = int(f*size), i:uint, j:uint;
                for (i=0; i<mp; ++i) {
                    cm[i] = Colors.interpolate(min, mid, i/mp);
                }
                mp = size - mp - 1;
                for (j=0; i<size; ++i, ++j) {
                    cm[i] = Colors.interpolate(mid, max, j/mp);
                }
                return new ColorPalette(cm, [0,f,1]);
            }

        } // end of class ColorPalette
    }