      1 /*
      2     Copyright 2008-2013
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Bianca Valentin,
      7         Alfred Wassermann,
      8         Peter Wilfahrt
      9
     10     This file is part of JSXGraph.
     11
     12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     13
     14     You can redistribute it and/or modify it under the terms of the
     15
     16       * GNU Lesser General Public License as published by
     17         the Free Software Foundation, either version 3 of the License, or
     18         (at your option) any later version
     19       OR
     20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     21
     22     JSXGraph is distributed in the hope that it will be useful,
     23     but WITHOUT ANY WARRANTY; without even the implied warranty of
     24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     25     GNU Lesser General Public License for more details.
     26
     27     You should have received a copy of the GNU Lesser General Public License and
     28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
     29     and <http://opensource.org/licenses/MIT/>.
     30  */
     31
     32
     33 /*global JXG: true, define: true*/
     34
     35 /*jslint nomen: true, plusplus: true*/
     36
     37 /* depends:
     38  jxg
     39  utils/type
     40  math/math
     41  */
     42
     43 /**
     44  * Functions for color conversions. This was originally based on a class to parse color values by
     45  * Stoyan Stefanov <sstoo@gmail.com> (see http://www.phpied.com/rgb-color-parser-in-javascript/)
     46  */
     47
     48 define(['jxg', 'utils/type', 'math/math'], function (JXG, Type, Mat) {
     49
     50     "use strict";
     51
     52     // private constants and helper functions
     53
     54     // simple colors contains string color constants that can be used in various browser
     55     // in javascript
     56     var simpleColors = {
     57             aliceblue: 'f0f8ff',
     58             antiquewhite: 'faebd7',
     59             aqua: '00ffff',
     60             aquamarine: '7fffd4',
     61             azure: 'f0ffff',
     62             beige: 'f5f5dc',
     63             bisque: 'ffe4c4',
     64             black: '000000',
     65             blanchedalmond: 'ffebcd',
     66             blue: '0000ff',
     67             blueviolet: '8a2be2',
     68             brown: 'a52a2a',
     69             burlywood: 'deb887',
     70             cadetblue: '5f9ea0',
     71             chartreuse: '7fff00',
     72             chocolate: 'd2691e',
     73             coral: 'ff7f50',
     74             cornflowerblue: '6495ed',
     75             cornsilk: 'fff8dc',
     76             crimson: 'dc143c',
     77             cyan: '00ffff',
     78             darkblue: '00008b',
     79             darkcyan: '008b8b',
     80             darkgoldenrod: 'b8860b',
     81             darkgray: 'a9a9a9',
     82             darkgreen: '006400',
     83             darkkhaki: 'bdb76b',
     84             darkmagenta: '8b008b',
     85             darkolivegreen: '556b2f',
     86             darkorange: 'ff8c00',
     87             darkorchid: '9932cc',
     88             darkred: '8b0000',
     89             darksalmon: 'e9967a',
     90             darkseagreen: '8fbc8f',
     91             darkslateblue: '483d8b',
     92             darkslategray: '2f4f4f',
     93             darkturquoise: '00ced1',
     94             darkviolet: '9400d3',
     95             deeppink: 'ff1493',
     96             deepskyblue: '00bfff',
     97             dimgray: '696969',
     98             dodgerblue: '1e90ff',
     99             feldspar: 'd19275',
    100             firebrick: 'b22222',
    101             floralwhite: 'fffaf0',
    102             forestgreen: '228b22',
    103             fuchsia: 'ff00ff',
    104             gainsboro: 'dcdcdc',
    105             ghostwhite: 'f8f8ff',
    106             gold: 'ffd700',
    107             goldenrod: 'daa520',
    108             gray: '808080',
    109             green: '008000',
    110             greenyellow: 'adff2f',
    111             honeydew: 'f0fff0',
    112             hotpink: 'ff69b4',
    113             indianred : 'cd5c5c',
    114             indigo : '4b0082',
    115             ivory: 'fffff0',
    116             khaki: 'f0e68c',
    117             lavender: 'e6e6fa',
    118             lavenderblush: 'fff0f5',
    119             lawngreen: '7cfc00',
    120             lemonchiffon: 'fffacd',
    121             lightblue: 'add8e6',
    122             lightcoral: 'f08080',
    123             lightcyan: 'e0ffff',
    124             lightgoldenrodyellow: 'fafad2',
    125             lightgrey: 'd3d3d3',
    126             lightgreen: '90ee90',
    127             lightpink: 'ffb6c1',
    128             lightsalmon: 'ffa07a',
    129             lightseagreen: '20b2aa',
    130             lightskyblue: '87cefa',
    131             lightslateblue: '8470ff',
    132             lightslategray: '778899',
    133             lightsteelblue: 'b0c4de',
    134             lightyellow: 'ffffe0',
    135             lime: '00ff00',
    136             limegreen: '32cd32',
    137             linen: 'faf0e6',
    138             magenta: 'ff00ff',
    139             maroon: '800000',
    140             mediumaquamarine: '66cdaa',
    141             mediumblue: '0000cd',
    142             mediumorchid: 'ba55d3',
    143             mediumpurple: '9370d8',
    144             mediumseagreen: '3cb371',
    145             mediumslateblue: '7b68ee',
    146             mediumspringgreen: '00fa9a',
    147             mediumturquoise: '48d1cc',
    148             mediumvioletred: 'c71585',
    149             midnightblue: '191970',
    150             mintcream: 'f5fffa',
    151             mistyrose: 'ffe4e1',
    152             moccasin: 'ffe4b5',
    153             navajowhite: 'ffdead',
    154             navy: '000080',
    155             oldlace: 'fdf5e6',
    156             olive: '808000',
    157             olivedrab: '6b8e23',
    158             orange: 'ffa500',
    159             orangered: 'ff4500',
    160             orchid: 'da70d6',
    161             palegoldenrod: 'eee8aa',
    162             palegreen: '98fb98',
    163             paleturquoise: 'afeeee',
    164             palevioletred: 'd87093',
    165             papayawhip: 'ffefd5',
    166             peachpuff: 'ffdab9',
    167             peru: 'cd853f',
    168             pink: 'ffc0cb',
    169             plum: 'dda0dd',
    170             powderblue: 'b0e0e6',
    171             purple: '800080',
    172             red: 'ff0000',
    173             rosybrown: 'bc8f8f',
    174             royalblue: '4169e1',
    175             saddlebrown: '8b4513',
    176             salmon: 'fa8072',
    177             sandybrown: 'f4a460',
    178             seagreen: '2e8b57',
    179             seashell: 'fff5ee',
    180             sienna: 'a0522d',
    181             silver: 'c0c0c0',
    182             skyblue: '87ceeb',
    183             slateblue: '6a5acd',
    184             slategray: '708090',
    185             snow: 'fffafa',
    186             springgreen: '00ff7f',
    187             steelblue: '4682b4',
    188             tan: 'd2b48c',
    189             teal: '008080',
    190             thistle: 'd8bfd8',
    191             tomato: 'ff6347',
    192             turquoise: '40e0d0',
    193             violet: 'ee82ee',
    194             violetred: 'd02090',
    195             wheat: 'f5deb3',
    196             white: 'ffffff',
    197             whitesmoke: 'f5f5f5',
    198             yellow: 'ffff00',
    199             yellowgreen: '9acd32'
    200         },
    201         // array of color definition objects
    202         colorDefs = [{
    203             re: /^\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([\d\.]{1,3})\s*\)\s*$/,
    204             example: ['rgba(123, 234, 45, 0.5)', 'rgba(255,234,245,1.0)'],
    205             process: function (bits) {
    206                 return [
    207                     parseInt(bits[1], 10),
    208                     parseInt(bits[2], 10),
    209                     parseInt(bits[3], 10)
    210                 ];
    211             }
    212         }, {
    213             re: /^\s*rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)\s*$/,
    214             example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
    215             process: function (bits) {
    216                 return [
    217                     parseInt(bits[1], 10),
    218                     parseInt(bits[2], 10),
    219                     parseInt(bits[3], 10)
    220                 ];
    221             }
    222         }, {
    223             re: /^(\w{2})(\w{2})(\w{2})$/,
    224             example: ['#00ff00', '336699'],
    225             process: function (bits) {
    226                 return [
    227                     parseInt(bits[1], 16),
    228                     parseInt(bits[2], 16),
    229                     parseInt(bits[3], 16)
    230                 ];
    231             }
    232         }, {
    233             re: /^(\w{1})(\w{1})(\w{1})$/,
    234             example: ['#fb0', 'f0f'],
    235             process: function (bits) {
    236                 return [
    237                     parseInt(bits[1] + bits[1], 16),
    238                     parseInt(bits[2] + bits[2], 16),
    239                     parseInt(bits[3] + bits[3], 16)
    240                 ];
    241             }
    242         }];
    243
    244     /**
    245      * Converts a valid HTML/CSS color string into a rgb value array. This is the base
    246      * function for the following wrapper functions which only adjust the output to
    247      * different flavors like an object, string or hex values.
    248      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
    249      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
    250      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
    251      * expects the parameters ag and ab.
    252      * @param {Number} ag
    253      * @param {Number} ab
    254      * @returns {Array} RGB color values as an array [r, g, b] with values ranging from 0 to 255.
    255      */
    256     JXG.rgbParser = function (color, ag, ab) {
    257         var color_string, channels, re, processor, bits, i,
    258             r, g, b,
    259             values = color,
    260             testFloat = false;
    261
    262         if (!Type.exists(color)) {
    263             return [];
    264         }
    265
    266         if (Type.exists(ag) && Type.exists(ab)) {
    267             values = [color, ag, ab];
    268         }
    269
    270         color_string = values;
    271
    272         if (Type.isArray(color_string)) {
    273             for (i = 0; i < 3; i++) {
    274                 testFloat = testFloat || /\./.test(values[i].toString());
    275             }
    276
    277             for (i = 0; i < 3; i++) {
    278                 testFloat = testFloat && (values[i] >= 0.0) && (values[i] <= 1.0);
    279             }
    280
    281             if (testFloat) {
    282                 return [Math.ceil(values[0] * 255), Math.ceil(values[1] * 255), Math.ceil(values[2] * 255)];
    283             }
    284
    285             return values;
    286         }
    287
    288         if (typeof values === 'string') {
    289             color_string = values;
    290         }
    291
    292         // strip any leading #
    293         if (color_string.charAt(0) === '#') { // remove # if any
    294             color_string = color_string.substr(1, 6);
    295         }
    296
    297         color_string = color_string.replace(/ /g, '').toLowerCase();
    298
    299         // before getting into regexps, try simple matches
    300         // and overwrite the input
    301         color_string = simpleColors[color_string] || color_string;
    302
    303         // search through the colorDefs definitions to find a match
    304         for (i = 0; i < colorDefs.length; i++) {
    305             re = colorDefs[i].re;
    306             processor = colorDefs[i].process;
    307             bits = re.exec(color_string);
    308
    309             if (bits) {
    310                 channels = processor(bits);
    311                 r = channels[0];
    312                 g = channels[1];
    313                 b = channels[2];
    314             }
    315
    316         }
    317
    318         if (isNaN(r) || isNaN(g) || isNaN(b)) {
    319             return [];
    320         }
    321
    322         // validate/cleanup values
    323         r = (r < 0 || isNaN(r)) ? 0 : ((r > 255) ? 255 : r);
    324         g = (g < 0 || isNaN(g)) ? 0 : ((g > 255) ? 255 : g);
    325         b = (b < 0 || isNaN(b)) ? 0 : ((b > 255) ? 255 : b);
    326
    327         return [r, g, b];
    328     };
    329
    330     /**
    331      * Converts a valid HTML/CSS color string into a string of the 'rgb(r, g, b)' format.
    332      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
    333      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
    334      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
    335      * expects the parameters ag and ab.
    336      * @param {Number} ag
    337      * @param {Number} ab
    338      * @returns {String} A 'rgb(r, g, b)' formatted string
    339      */
    340     JXG.rgb2css = function (color, ag, ab) {
    341         var r;
    342
    343         r = JXG.rgbParser(color, ag, ab);
    344
    345         return 'rgb(' + r[0] + ', ' + r[1] + ', ' + r[2] + ')';
    346     };
    347
    348     /**
    349      * Converts a valid HTML/CSS color string into a HTML rgb string.
    350      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
    351      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
    352      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
    353      * expects the parameters ag and ab.
    354      * @param {Number} ag
    355      * @param {Number} ab
    356      * @returns {String} A '#rrggbb' formatted string
    357      */
    358     JXG.rgb2hex = function (color, ag, ab) {
    359         var r, g, b;
    360
    361         r = JXG.rgbParser(color, ag, ab);
    362         g = r[1];
    363         b = r[2];
    364         r = r[0];
    365         r = r.toString(16);
    366         g = g.toString(16);
    367         b = b.toString(16);
    368
    369         if (r.length === 1) {
    370             r = '0' + r;
    371         }
    372
    373         if (g.length === 1) {
    374             g = '0' + g;
    375         }
    376
    377         if (b.length === 1) {
    378             b = '0' + b;
    379         }
    380
    381         return '#' + r + g + b;
    382     };
    383
    384     /**
    385      * Converts a valid HTML/CSS color string from the '#rrggbb' format into the 'rgb(r, g, b)' format.
    386      * @param {String} hex A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', or 'black'
    387      * @returns {String} A 'rgb(r, g, b)' formatted string
    388      */
    389     JXG.hex2rgb = function (hex) {
    390         var r;
    391
    392         r = JXG.rgbParser(hex);
    393
    394         return 'rgb(' + r[0] + ', ' + r[1] + ', ' + r[2] + ')';
    395     };
    396
    397     /**
    398      * Converts HSV color to RGB color.
    399      * Based on C Code in "Computer Graphics -- Principles and Practice,"
    400      * Foley et al, 1996, p. 593.
    401      * See also http://www.efg2.com/Lab/Graphics/Colors/HSV.htm
    402      * @param {Number} H value between 0 and 360
    403      * @param {Number} S value between 0.0 (shade of gray) to 1.0 (pure color)
    404      * @param {Number} V value between 0.0 (black) to 1.0 (white)
    405      * @return {String} RGB color string
    406      */
    407     JXG.hsv2rgb = function (H, S, V) {
    408         var R, G, B, f, i, hTemp, p, q, t;
    409
    410         H = ((H % 360.0) + 360.0) % 360;
    411
    412         if (S === 0) {
    413             if (isNaN(H) || H < Mat.eps) {
    414                 R = V;
    415                 G = V;
    416                 B = V;
    417             } else {
    418                 return '#ffffff';
    419             }
    420         } else {
    421             if (H >= 360) {
    422                 hTemp = 0.0;
    423             } else {
    424                 hTemp = H;
    425             }
    426
    427             // h is now IN [0,6)
    428             hTemp = hTemp / 60;
    429             // largest integer <= h
    430             i = Math.floor(hTemp);
    431             // fractional part of h
    432             f = hTemp - i;
    433             p = V * (1.0 - S);
    434             q = V * (1.0 - (S * f));
    435             t = V * (1.0 - (S * (1.0 - f)));
    436
    437             switch (i) {
    438             case 0:
    439                 R = V;
    440                 G = t;
    441                 B = p;
    442                 break;
    443             case 1:
    444                 R = q;
    445                 G = V;
    446                 B = p;
    447                 break;
    448             case 2:
    449                 R = p;
    450                 G = V;
    451                 B = t;
    452                 break;
    453             case 3:
    454                 R = p;
    455                 G = q;
    456                 B = V;
    457                 break;
    458             case 4:
    459                 R = t;
    460                 G = p;
    461                 B = V;
    462                 break;
    463             case 5:
    464                 R = V;
    465                 G = p;
    466                 B = q;
    467                 break;
    468             }
    469         }
    470
    471         R = Math.round(R * 255).toString(16);
    472         R = (R.length === 2) ? R : ((R.length === 1) ? '0' + R : '00');
    473         G = Math.round(G * 255).toString(16);
    474         G = (G.length === 2) ? G : ((G.length === 1) ? '0' + G : '00');
    475         B = Math.round(B * 255).toString(16);
    476         B = (B.length === 2) ? B : ((B.length === 1) ? '0' + B : '00');
    477
    478         return ['#', R, G, B].join('');
    479     };
    480
    481     /**
    482      * Converts a color from the RGB color space into the HSV space. Input can be any valid HTML/CSS color definition.
    483      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
    484      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
    485      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
    486      * expects the parameters ag and ab.
    487      * @param {Number} ag
    488      * @param {Number} ab
    489      * @returns {Array} Contains the h, s, and v value in this order.
    490      * @see http://zach.in.tu-clausthal.de/teaching/cg1_0708/folien/13_color_3_4up.pdf
    491      */
    492     JXG.rgb2hsv = function (color, ag, ab) {
    493         var r, g, b, fr, fg, fb, fmax, fmin, h, s, v, max, min;
    494
    495         r = JXG.rgbParser(color, ag, ab);
    496
    497         g = r[1];
    498         b = r[2];
    499         r = r[0];
    500         fr = r / 255.0;
    501         fg = g / 255.0;
    502         fb = b / 255.0;
    503         max = Math.max(r, g, b);
    504         min = Math.min(r, g, b);
    505         fmax = max / 255.0;
    506         fmin = min / 255.0;
    507
    508         v = fmax;
    509         s = 0.0;
    510
    511         if (v > 0) {
    512             s = (v - fmin) / v;
    513         }
    514
    515         h = 1.0 / (fmax - fmin);
    516
    517         if (s > 0) {
    518             if (max === r) {
    519                 h = (fg - fb) * h;
    520             } else if (max === g) {
    521                 h = 2 + (fb - fr) * h;
    522             } else {
    523                 h = 4 + (fr - fg) * h;
    524             }
    525         }
    526
    527         h *= 60;
    528
    529         if (h < 0) {
    530             h += 360;
    531         }
    532
    533         if (max === min) {
    534             h = 0.0;
    535         }
    536
    537         return [h, s, v];
    538     };
    539
    540
    541     /**
    542      * Converts a color from the RGB color space into the LMS space. Input can be any valid HTML/CSS color definition.
    543      * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black',
    544      * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or
    545      * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method
    546      * expects the parameters ag and ab.
    547      * @param {Number} ag
    548      * @param {Number} ab
    549      * @returns {Array} Contains the l, m, and s value in this order.
    550      */
    551     JXG.rgb2LMS = function (color, ag, ab) {
    552         var r, g, b, l, m, s, ret,
    553             // constants
    554             matrix = [[0.05059983, 0.08585369, 0.00952420], [0.01893033, 0.08925308, 0.01370054], [0.00292202, 0.00975732, 0.07145979]];
    555
    556         r = JXG.rgbParser(color, ag, ab);
    557         g = r[1];
    558         b = r[2];
    559         r = r[0];
    560
    561         // de-gamma
    562         // Maybe this can be made faster by using a cache
    563         r = Math.pow(r, 0.476190476);
    564         g = Math.pow(g, 0.476190476);
    565         b = Math.pow(b, 0.476190476);
    566
    567         l = r * matrix[0][0] + g * matrix[0][1] + b * matrix[0][2];
    568         m = r * matrix[1][0] + g * matrix[1][1] + b * matrix[1][2];
    569         s = r * matrix[2][0] + g * matrix[2][1] + b * matrix[2][2];
    570
    571         ret = [l, m, s];
    572         ret.l = l;
    573         ret.m = m;
    574         ret.s = s;
    575
    576         return ret;
    577     };
    578
    579     /**
    580      * Convert color information from LMS to RGB color space.
    581      * @param {Number} l
    582      * @param {Number} m
    583      * @param {Number} s
    584      * @returns {Array} Contains the r, g, and b value in this order.
    585      */
    586     JXG.LMS2rgb = function (l, m, s) {
    587         var r, g, b, ret,
    588             // constants
    589             matrix = [[30.830854, -29.832659, 1.610474], [-6.481468, 17.715578, -2.532642], [-0.375690, -1.199062, 14.273846]],
    590
    591             // re-gamma, inspired by GIMP modules/display-filter-color-blind.c:
    592             // Copyright (C) 2002-2003 Michael Natterer <mitch@gimp.org>,
    593             //                         Sven Neumann <sven@gimp.org>,
    594             //                         Robert Dougherty <bob@vischeck.com> and
    595             //                         Alex Wade <alex@vischeck.com>
    596             // This code is an implementation of an algorithm described by Hans Brettel,
    597             // Francoise Vienot and John Mollon in the Journal of the Optical Society of
    598             // America V14(10), pg 2647. (See http://vischeck.com/ for more info.)
    599             lut_lookup = function (value) {
    600                 var offset = 127, step = 64;
    601
    602                 while (step > 0) {
    603                     if (Math.pow(offset, 0.476190476) > value) {
    604                         offset -= step;
    605                     } else {
    606                         if (Math.pow(offset + 1, 0.476190476) > value) {
    607                             return offset;
    608                         }
    609
    610                         offset += step;
    611                     }
    612
    613                     step /= 2;
    614                 }
    615
    616                 /*  the algorithm above can't reach 255  */
    617                 if (offset === 254 && 13.994955247 < value) {
    618                     return 255;
    619                 }
    620
    621                 return offset;
    622             };
    623
    624         // transform back to rgb
    625         r = l * matrix[0][0] + m * matrix[0][1] + s * matrix[0][2];
    626         g = l * matrix[1][0] + m * matrix[1][1] + s * matrix[1][2];
    627         b = l * matrix[2][0] + m * matrix[2][1] + s * matrix[2][2];
    628
    629         r = lut_lookup(r);
    630         g = lut_lookup(g);
    631         b = lut_lookup(b);
    632
    633         ret = [r, g, b];
    634         ret.r = r;
    635         ret.g = g;
    636         ret.b = b;
    637
    638         return ret;
    639     };
    640
    641     /**
    642      * Splits a RGBA color value like #112233AA into it's RGB and opacity parts.
    643      * @param {String} rgba A RGBA color value
    644      * @returns {Array} An array containing the rgb color value in the first and the opacity in the second field.
    645      */
    646     JXG.rgba2rgbo = function (rgba) {
    647         var opacity;
    648
    649         if (rgba.length === 9 && rgba.charAt(0) === '#') {
    650             opacity = parseInt(rgba.substr(7, 2).toUpperCase(), 16) / 255;
    651             rgba = rgba.substr(0, 7);
    652         } else {
    653             opacity = 1;
    654         }
    655
    656         return [rgba, opacity];
    657     };
    658
    659     /**
    660      * Generates a RGBA color value like #112233AA from it's RGB and opacity parts.
    661      * @param {String} rgb A RGB color value.
    662      * @param {Number} o The desired opacity >=0, <=1.
    663      * @returns {String} The RGBA color value.
    664      */
    665     JXG.rgbo2rgba = function (rgb, o) {
    666         var rgba;
    667
    668         if (rgb === 'none') {
    669             return rgb;
    670         }
    671
    672         rgba = Math.round(o * 255).toString(16);
    673         if (rgba.length === 1) {
    674             rgba = "0" + rgba;
    675         }
    676
    677         return rgb + rgba;
    678     };
    679
    680     /**
    681      * Decolorizes the given color.
    682      * @param {String} color HTML string containing the HTML color code.
    683      * @returns {String} Returns a HTML color string
    684      */
    685     JXG.rgb2bw = function (color) {
    686         var x, tmp, arr,
    687             HexChars = "0123456789ABCDEF";
    688
    689         if (color === 'none') {
    690             return color;
    691         }
    692
    693         arr = JXG.rgbParser(color);
    694         x = Math.floor(0.3 * arr[0] + 0.59 * arr[1] + 0.11 * arr[2]);
    695
    696         // rgbParser and Math.floor ensure that x is 0 <= x <= 255.
    697         // Bitwise operators can be used.
    698         /*jslint bitwise: true*/
    699         tmp = HexChars.charAt((x >> 4) & 0xf) + HexChars.charAt(x & 0xf);
    700
    701         color = "#" + tmp + tmp + tmp;
    702
    703         return color;
    704     };
    705
    706     /**
    707      * Converts a color into how a colorblind human approximately would see it.
    708      * @param {String} color HTML string containing the HTML color code.
    709      * @param {String} deficiency The type of color blindness. Possible
    710      * options are <i>protanopia</i>, <i>deuteranopia</i>, and <i>tritanopia</i>.
    711      * @returns {String} Returns a HTML color string
    712      */
    713     JXG.rgb2cb = function (color, deficiency) {
    714         var rgb, l, m, s, lms, tmp,
    715             a1, b1, c1, a2, b2, c2,
    716             inflection,
    717             HexChars = "0123456789ABCDEF";
    718
    719         if (color === 'none') {
    720             return color;
    721         }
    722
    723         lms = JXG.rgb2LMS(color);
    724         l = lms[0];
    725         m = lms[1];
    726         s = lms[2];
    727
    728         deficiency = deficiency.toLowerCase();
    729
    730         switch (deficiency) {
    731         case "protanopia":
    732             a1 = -0.06150039994295001;
    733             b1 = 0.08277001656812001;
    734             c1 = -0.013200141220000003;
    735             a2 = 0.05858939668799999;
    736             b2 = -0.07934519995360001;
    737             c2 = 0.013289415272000003;
    738             inflection = 0.6903216543277437;
    739
    740             tmp = s / m;
    741
    742             if (tmp < inflection) {
    743                 l = -(b1 * m + c1 * s) / a1;
    744             } else {
    745                 l = -(b2 * m + c2 * s) / a2;
    746             }
    747             break;
    748         case "tritanopia":
    749             a1 = -0.00058973116217;
    750             b1 = 0.007690316482;
    751             c1 = -0.01011703519052;
    752             a2 = 0.025495080838999994;
    753             b2 = -0.0422740347;
    754             c2 = 0.017005316784;
    755             inflection = 0.8349489908460004;
    756
    757             tmp = m / l;
    758
    759             if (tmp < inflection) {
    760                 s = -(a1 * l + b1 * m) / c1;
    761             } else {
    762                 s = -(a2 * l + b2 * m) / c2;
    763             }
    764             break;
    765         default:
    766             a1 = -0.06150039994295001;
    767             b1 = 0.08277001656812001;
    768             c1 = -0.013200141220000003;
    769             a2 = 0.05858939668799999;
    770             b2 = -0.07934519995360001;
    771             c2 = 0.013289415272000003;
    772             inflection = 0.5763833686400911;
    773
    774             tmp = s / l;
    775
    776             if (tmp < inflection) {
    777                 m = -(a1 * l + c1 * s) / b1;
    778             } else {
    779                 m = -(a2 * l + c2 * s) / b2;
    780             }
    781             break;
    782         }
    783
    784         rgb = JXG.LMS2rgb(l, m, s);
    785
    786         // LMS2rgb returns an array of values ranging from 0 to 255 (both included)
    787         // bitwise operators are safe to use.
    788         /*jslint bitwise: true*/
    789         tmp = HexChars.charAt((rgb[0] >> 4) & 0xf) + HexChars.charAt(rgb[0] & 0xf);
    790         color = "#" + tmp;
    791         tmp = HexChars.charAt((rgb[1] >> 4) & 0xf) + HexChars.charAt(rgb[1] & 0xf);
    792         color += tmp;
    793         tmp = HexChars.charAt((rgb[2] >> 4) & 0xf) + HexChars.charAt(rgb[2] & 0xf);
    794         color += tmp;
    795
    796         return color;
    797     };
    798
    799     /**
    800      * Determines highlight color to a given color. Done by reducing (or increasing) the opacity,
    801      * @param {String} color HTML RGBA string containing the HTML color code.
    802      * @returns {String} Returns a HTML RGBA color string
    803      */
    804     JXG.autoHighlight = function (colstr) {
    805         var col = JXG.rgba2rgbo(colstr),
    806             c = col[0],
    807             opa = col[1];
    808
    809         if (colstr.charAt(0) === '#') {
    810             if (opa < 0.3) {
    811                 opa *= 1.8;
    812             } else {
    813                 opa *= 0.4;
    814             }
    815
    816             return JXG.rgbo2rgba(c, opa);
    817         }
    818
    819         return colstr;
    820     };
    821
    822     return JXG;
    823 });
    824