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