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; 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 testFloat = false; 273 if (Type.isArray(color_string)) { 274 for (i = 0; i < 3; i++) { 275 testFloat = testFloat || /\./.test(values[i].toString()); 276 } 277 278 for (i = 0; i < 3; i++) { 279 testFloat = testFloat && (values[i] >= 0.0) && (values[i] <= 1.0); 280 } 281 282 if (testFloat) { 283 return [Math.ceil(values[0] * 255), Math.ceil(values[1] * 255), Math.ceil(values[2] * 255)]; 284 } 285 286 return values; 287 } 288 289 if (typeof values === 'string') { 290 color_string = values; 291 } 292 293 // strip any leading # 294 if (color_string.charAt(0) === '#') { // remove # if any 295 color_string = color_string.substr(1, 6); 296 } 297 298 color_string = color_string.replace(/ /g, '').toLowerCase(); 299 300 // before getting into regexps, try simple matches 301 // and overwrite the input 302 color_string = simpleColors[color_string] || color_string; 303 304 // search through the colorDefs definitions to find a match 305 for (i = 0; i < colorDefs.length; i++) { 306 re = colorDefs[i].re; 307 processor = colorDefs[i].process; 308 bits = re.exec(color_string); 309 310 if (bits) { 311 channels = processor(bits); 312 r = channels[0]; 313 g = channels[1]; 314 b = channels[2]; 315 } 316 317 } 318 319 if (isNaN(r) || isNaN(g) || isNaN(b)) { 320 return []; 321 } 322 323 // validate/cleanup values 324 r = (r < 0 || isNaN(r)) ? 0 : ((r > 255) ? 255 : r); 325 g = (g < 0 || isNaN(g)) ? 0 : ((g > 255) ? 255 : g); 326 b = (b < 0 || isNaN(b)) ? 0 : ((b > 255) ? 255 : b); 327 328 return [r, g, b]; 329 }; 330 331 /** 332 * Converts a valid HTML/CSS color string into a string of the 'rgb(r, g, b)' format. 333 * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black', 334 * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or 335 * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method 336 * expects the parameters ag and ab. 337 * @param {Number} ag 338 * @param {Number} ab 339 * @returns {String} A 'rgb(r, g, b)' formatted string 340 */ 341 JXG.rgb2css = function (color, ag, ab) { 342 var r; 343 344 r = JXG.rgbParser(color, ag, ab); 345 346 return 'rgb(' + r[0] + ', ' + r[1] + ', ' + r[2] + ')'; 347 }; 348 349 /** 350 * Converts a valid HTML/CSS color string into a HTML rgb string. 351 * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black', 352 * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or 353 * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method 354 * expects the parameters ag and ab. 355 * @param {Number} ag 356 * @param {Number} ab 357 * @returns {String} A '#rrggbb' formatted string 358 */ 359 JXG.rgb2hex = function (color, ag, ab) { 360 var r, g, b; 361 362 r = JXG.rgbParser(color, ag, ab); 363 g = r[1]; 364 b = r[2]; 365 r = r[0]; 366 r = r.toString(16); 367 g = g.toString(16); 368 b = b.toString(16); 369 370 if (r.length === 1) { 371 r = '0' + r; 372 } 373 374 if (g.length === 1) { 375 g = '0' + g; 376 } 377 378 if (b.length === 1) { 379 b = '0' + b; 380 } 381 382 return '#' + r + g + b; 383 }; 384 385 /** 386 * Converts a valid HTML/CSS color string from the '#rrggbb' format into the 'rgb(r, g, b)' format. 387 * @param {String} hex A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', or 'black' 388 * @deprecated Use {@link JXG#rgb2css} instead. 389 * @returns {String} A 'rgb(r, g, b)' formatted string 390 */ 391 JXG.hex2rgb = function (hex) { 392 JXG.deprecated('JXG.hex2rgb()', 'JXG.rgb2css()'); 393 return JXG.rgb2css(hex); 394 }; 395 396 /** 397 * Converts HSV color to RGB color. 398 * Based on C Code in "Computer Graphics -- Principles and Practice," 399 * Foley et al, 1996, p. 593. 400 * See also http://www.efg2.com/Lab/Graphics/Colors/HSV.htm 401 * @param {Number} H value between 0 and 360 402 * @param {Number} S value between 0.0 (shade of gray) to 1.0 (pure color) 403 * @param {Number} V value between 0.0 (black) to 1.0 (white) 404 * @returns {String} RGB color string 405 */ 406 JXG.hsv2rgb = function (H, S, V) { 407 var R, G, B, f, i, hTemp, p, q, t; 408 409 H = ((H % 360.0) + 360.0) % 360; 410 411 if (S === 0) { 412 if (isNaN(H) || H < Mat.eps) { 413 R = V; 414 G = V; 415 B = V; 416 } else { 417 return '#ffffff'; 418 } 419 } else { 420 if (H >= 360) { 421 hTemp = 0.0; 422 } else { 423 hTemp = H; 424 } 425 426 // h is now IN [0,6) 427 hTemp = hTemp / 60; 428 // largest integer <= h 429 i = Math.floor(hTemp); 430 // fractional part of h 431 f = hTemp - i; 432 p = V * (1.0 - S); 433 q = V * (1.0 - (S * f)); 434 t = V * (1.0 - (S * (1.0 - f))); 435 436 switch (i) { 437 case 0: 438 R = V; 439 G = t; 440 B = p; 441 break; 442 case 1: 443 R = q; 444 G = V; 445 B = p; 446 break; 447 case 2: 448 R = p; 449 G = V; 450 B = t; 451 break; 452 case 3: 453 R = p; 454 G = q; 455 B = V; 456 break; 457 case 4: 458 R = t; 459 G = p; 460 B = V; 461 break; 462 case 5: 463 R = V; 464 G = p; 465 B = q; 466 break; 467 } 468 } 469 470 R = Math.round(R * 255).toString(16); 471 R = (R.length === 2) ? R : ((R.length === 1) ? '0' + R : '00'); 472 G = Math.round(G * 255).toString(16); 473 G = (G.length === 2) ? G : ((G.length === 1) ? '0' + G : '00'); 474 B = Math.round(B * 255).toString(16); 475 B = (B.length === 2) ? B : ((B.length === 1) ? '0' + B : '00'); 476 477 return ['#', R, G, B].join(''); 478 }; 479 480 /** 481 * Converts a color from the RGB color space into the HSV space. Input can be any valid HTML/CSS color definition. 482 * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black', 483 * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or 484 * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method 485 * expects the parameters ag and ab. 486 * @param {Number} ag 487 * @param {Number} ab 488 * @returns {Array} Contains the h, s, and v value in this order. 489 * @see http://zach.in.tu-clausthal.de/teaching/cg1_0708/folien/13_color_3_4up.pdf 490 */ 491 JXG.rgb2hsv = function (color, ag, ab) { 492 var r, g, b, fr, fg, fb, fmax, fmin, h, s, v, max, min; 493 494 r = JXG.rgbParser(color, ag, ab); 495 496 g = r[1]; 497 b = r[2]; 498 r = r[0]; 499 fr = r / 255.0; 500 fg = g / 255.0; 501 fb = b / 255.0; 502 max = Math.max(r, g, b); 503 min = Math.min(r, g, b); 504 fmax = max / 255.0; 505 fmin = min / 255.0; 506 507 v = fmax; 508 s = 0.0; 509 510 if (v > 0) { 511 s = (v - fmin) / v; 512 } 513 514 h = 1.0 / (fmax - fmin); 515 516 if (s > 0) { 517 if (max === r) { 518 h = (fg - fb) * h; 519 } else if (max === g) { 520 h = 2 + (fb - fr) * h; 521 } else { 522 h = 4 + (fr - fg) * h; 523 } 524 } 525 526 h *= 60; 527 528 if (h < 0) { 529 h += 360; 530 } 531 532 if (max === min) { 533 h = 0.0; 534 } 535 536 return [h, s, v]; 537 }; 538 539 540 /** 541 * Converts a color from the RGB color space into the LMS space. Input can be any valid HTML/CSS color definition. 542 * @param {String,Array,Number} color A valid HTML or CSS styled color value, e.g. '#12ab21', '#abc', 'black', 543 * or 'rgb(12, 132, 233)'. This can also be an array containing three color values either from 0.0 to 1.0 or 544 * from 0 to 255. They will be interpreted as red, green, and blue values. In case this is a number this method 545 * expects the parameters ag and ab. 546 * @param {Number} ag 547 * @param {Number} ab 548 * @returns {Array} Contains the l, m, and s value in this order. 549 */ 550 JXG.rgb2LMS = function (color, ag, ab) { 551 var r, g, b, l, m, s, ret, 552 // constants 553 matrix = [[0.05059983, 0.08585369, 0.00952420], 554 [0.01893033, 0.08925308, 0.01370054], 555 [0.00292202, 0.00975732, 0.07145979]]; 556 557 r = JXG.rgbParser(color, ag, ab); 558 g = r[1]; 559 b = r[2]; 560 r = r[0]; 561 562 // de-gamma 563 // Maybe this can be made faster by using a cache 564 r = Math.pow(r, 0.476190476); 565 g = Math.pow(g, 0.476190476); 566 b = Math.pow(b, 0.476190476); 567 568 l = r * matrix[0][0] + g * matrix[0][1] + b * matrix[0][2]; 569 m = r * matrix[1][0] + g * matrix[1][1] + b * matrix[1][2]; 570 s = r * matrix[2][0] + g * matrix[2][1] + b * matrix[2][2]; 571 572 ret = [l, m, s]; 573 ret.l = l; 574 ret.m = m; 575 ret.s = s; 576 577 return ret; 578 }; 579 580 /** 581 * Convert color information from LMS to RGB color space. 582 * @param {Number} l 583 * @param {Number} m 584 * @param {Number} s 585 * @returns {Array} Contains the r, g, and b value in this order. 586 */ 587 JXG.LMS2rgb = function (l, m, s) { 588 var r, g, b, ret, 589 // constants 590 matrix = [[30.830854, -29.832659, 1.610474], 591 [-6.481468, 17.715578, -2.532642], 592 [-0.375690, -1.199062, 14.273846]], 593 594 // re-gamma, inspired by GIMP modules/display-filter-color-blind.c: 595 // Copyright (C) 2002-2003 Michael Natterer <mitch@gimp.org>, 596 // Sven Neumann <sven@gimp.org>, 597 // Robert Dougherty <bob@vischeck.com> and 598 // Alex Wade <alex@vischeck.com> 599 // This code is an implementation of an algorithm described by Hans Brettel, 600 // Francoise Vienot and John Mollon in the Journal of the Optical Society of 601 // America V14(10), pg 2647. (See http://vischeck.com/ for more info.) 602 lut_lookup = function (value) { 603 var offset = 127, step = 64; 604 605 while (step > 0) { 606 if (Math.pow(offset, 0.476190476) > value) { 607 offset -= step; 608 } else { 609 if (Math.pow(offset + 1, 0.476190476) > value) { 610 return offset; 611 } 612 613 offset += step; 614 } 615 616 step /= 2; 617 } 618 619 /* the algorithm above can't reach 255 */ 620 if (offset === 254 && 13.994955247 < value) { 621 return 255; 622 } 623 624 return offset; 625 }; 626 627 // transform back to rgb 628 r = l * matrix[0][0] + m * matrix[0][1] + s * matrix[0][2]; 629 g = l * matrix[1][0] + m * matrix[1][1] + s * matrix[1][2]; 630 b = l * matrix[2][0] + m * matrix[2][1] + s * matrix[2][2]; 631 632 r = lut_lookup(r); 633 g = lut_lookup(g); 634 b = lut_lookup(b); 635 636 ret = [r, g, b]; 637 ret.r = r; 638 ret.g = g; 639 ret.b = b; 640 641 return ret; 642 }; 643 644 /** 645 * Splits a RGBA color value like #112233AA into it's RGB and opacity parts. 646 * @param {String} rgba A RGBA color value 647 * @returns {Array} An array containing the rgb color value in the first and the opacity in the second field. 648 */ 649 JXG.rgba2rgbo = function (rgba) { 650 var opacity; 651 652 if (rgba.length === 9 && rgba.charAt(0) === '#') { 653 opacity = parseInt(rgba.substr(7, 2).toUpperCase(), 16) / 255; 654 rgba = rgba.substr(0, 7); 655 } else { 656 opacity = 1; 657 } 658 659 return [rgba, opacity]; 660 }; 661 662 /** 663 * Generates a RGBA color value like #112233AA from it's RGB and opacity parts. 664 * @param {String} rgb A RGB color value. 665 * @param {Number} o The desired opacity >=0, <=1. 666 * @returns {String} The RGBA color value. 667 */ 668 JXG.rgbo2rgba = function (rgb, o) { 669 var rgba; 670 671 if (rgb === 'none') { 672 return rgb; 673 } 674 675 rgba = Math.round(o * 255).toString(16); 676 if (rgba.length === 1) { 677 rgba = "0" + rgba; 678 } 679 680 return rgb + rgba; 681 }; 682 683 /** 684 * Decolorizes the given color. 685 * @param {String} color HTML string containing the HTML color code. 686 * @returns {String} Returns a HTML color string 687 */ 688 JXG.rgb2bw = function (color) { 689 var x, tmp, arr, 690 HexChars = "0123456789ABCDEF"; 691 692 if (color === 'none') { 693 return color; 694 } 695 696 arr = JXG.rgbParser(color); 697 x = Math.floor(0.3 * arr[0] + 0.59 * arr[1] + 0.11 * arr[2]); 698 699 // rgbParser and Math.floor ensure that x is 0 <= x <= 255. 700 // Bitwise operators can be used. 701 /*jslint bitwise: true*/ 702 tmp = HexChars.charAt((x >> 4) & 0xf) + HexChars.charAt(x & 0xf); 703 704 color = "#" + tmp + tmp + tmp; 705 706 return color; 707 }; 708 709 /** 710 * Converts a color into how a colorblind human approximately would see it. 711 * @param {String} color HTML string containing the HTML color code. 712 * @param {String} deficiency The type of color blindness. Possible 713 * options are <i>protanopia</i>, <i>deuteranopia</i>, and <i>tritanopia</i>. 714 * @returns {String} Returns a HTML color string 715 */ 716 JXG.rgb2cb = function (color, deficiency) { 717 var rgb, l, m, s, lms, tmp, 718 a1, b1, c1, a2, b2, c2, 719 inflection, 720 HexChars = "0123456789ABCDEF"; 721 722 if (color === 'none') { 723 return color; 724 } 725 726 lms = JXG.rgb2LMS(color); 727 l = lms[0]; 728 m = lms[1]; 729 s = lms[2]; 730 731 deficiency = deficiency.toLowerCase(); 732 733 switch (deficiency) { 734 case "protanopia": 735 a1 = -0.06150039994295001; 736 b1 = 0.08277001656812001; 737 c1 = -0.013200141220000003; 738 a2 = 0.05858939668799999; 739 b2 = -0.07934519995360001; 740 c2 = 0.013289415272000003; 741 inflection = 0.6903216543277437; 742 743 tmp = s / m; 744 745 if (tmp < inflection) { 746 l = -(b1 * m + c1 * s) / a1; 747 } else { 748 l = -(b2 * m + c2 * s) / a2; 749 } 750 break; 751 case "tritanopia": 752 a1 = -0.00058973116217; 753 b1 = 0.007690316482; 754 c1 = -0.01011703519052; 755 a2 = 0.025495080838999994; 756 b2 = -0.0422740347; 757 c2 = 0.017005316784; 758 inflection = 0.8349489908460004; 759 760 tmp = m / l; 761 762 if (tmp < inflection) { 763 s = -(a1 * l + b1 * m) / c1; 764 } else { 765 s = -(a2 * l + b2 * m) / c2; 766 } 767 break; 768 default: 769 a1 = -0.06150039994295001; 770 b1 = 0.08277001656812001; 771 c1 = -0.013200141220000003; 772 a2 = 0.05858939668799999; 773 b2 = -0.07934519995360001; 774 c2 = 0.013289415272000003; 775 inflection = 0.5763833686400911; 776 777 tmp = s / l; 778 779 if (tmp < inflection) { 780 m = -(a1 * l + c1 * s) / b1; 781 } else { 782 m = -(a2 * l + c2 * s) / b2; 783 } 784 break; 785 } 786 787 rgb = JXG.LMS2rgb(l, m, s); 788 789 // LMS2rgb returns an array of values ranging from 0 to 255 (both included) 790 // bitwise operators are safe to use. 791 /*jslint bitwise: true*/ 792 tmp = HexChars.charAt((rgb[0] >> 4) & 0xf) + HexChars.charAt(rgb[0] & 0xf); 793 color = "#" + tmp; 794 tmp = HexChars.charAt((rgb[1] >> 4) & 0xf) + HexChars.charAt(rgb[1] & 0xf); 795 color += tmp; 796 tmp = HexChars.charAt((rgb[2] >> 4) & 0xf) + HexChars.charAt(rgb[2] & 0xf); 797 color += tmp; 798 799 return color; 800 }; 801 802 /** 803 * Determines highlight color to a given color. Done by reducing (or increasing) the opacity, 804 * @param {String} color HTML RGBA string containing the HTML color code. 805 * @returns {String} Returns a HTML RGBA color string 806 */ 807 JXG.autoHighlight = function (colstr) { 808 var col = JXG.rgba2rgbo(colstr), 809 c = col[0], 810 opa = col[1]; 811 812 if (colstr.charAt(0) === '#') { 813 if (opa < 0.3) { 814 opa *= 1.8; 815 } else { 816 opa *= 0.4; 817 } 818 819 return JXG.rgbo2rgba(c, opa); 820 } 821 822 return colstr; 823 }; 824 825 return JXG; 826 }); 827