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     return JXG;
800 });