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