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 });