// randomColor by David Merfield under the CC0 license // https://github.com/davidmerfield/randomColor/ (function (root, factory) { // Support CommonJS if (typeof exports === "object") { var randomColor = factory(); // Support NodeJS & Component, which allow module.exports to be a function if (typeof module === "object" && module && module.exports) { exports = module.exports = randomColor; } // Support CommonJS 1.1.1 spec exports.randomColor = randomColor; // Support AMD } else if (typeof define === "function" && define.amd) { define([], factory); // Support vanilla script loading } else { root.randomColor = factory(); } })(this, function () { // Seed to get repeatable colors var seed = null; // Shared color dictionary var colorDictionary = {}; // Populate the color dictionary loadColorBounds(); // check if a range is taken var colorRanges = []; var randomColor = function (options) { options = options || {}; // Check if there is a seed and ensure it's an // integer. Otherwise, reset the seed value. if ( options.seed !== undefined && options.seed !== null && options.seed === parseInt(options.seed, 10) ) { seed = options.seed; // A string was passed as a seed } else if (typeof options.seed === "string") { seed = stringToInteger(options.seed); // Something was passed as a seed but it wasn't an integer or string } else if (options.seed !== undefined && options.seed !== null) { throw new TypeError("The seed value must be an integer or string"); // No seed, reset the value outside. } else { seed = null; } var H, S, B; // Check if we need to generate multiple colors if (options.count !== null && options.count !== undefined) { var totalColors = options.count, colors = []; // Value false at index i means the range i is not taken yet. for (var i = 0; i < options.count; i++) { colorRanges.push(false); } options.count = null; while (totalColors > colors.length) { var color = randomColor(options); if (seed !== null) { options.seed = seed; } colors.push(color); } options.count = totalColors; return colors; } // First we pick a hue (H) H = pickHue(options); // Then use H to determine saturation (S) S = pickSaturation(H, options); // Then use S and H to determine brightness (B). B = pickBrightness(H, S, options); // Then we return the HSB color in the desired format return setFormat([H, S, B], options); }; function pickHue(options) { if (colorRanges.length > 0) { var hueRange = getRealHueRange(options.hue); var hue = randomWithin(hueRange); //Each of colorRanges.length ranges has a length equal approximatelly one step var step = (hueRange[1] - hueRange[0]) / colorRanges.length; var j = parseInt((hue - hueRange[0]) / step); //Check if the range j is taken if (colorRanges[j] === true) { j = (j + 2) % colorRanges.length; } else { colorRanges[j] = true; } var min = (hueRange[0] + j * step) % 359, max = (hueRange[0] + (j + 1) * step) % 359; hueRange = [min, max]; hue = randomWithin(hueRange); if (hue < 0) { hue = 360 + hue; } return hue; } else { var hueRange = getHueRange(options.hue); hue = randomWithin(hueRange); // Instead of storing red as two seperate ranges, // we group them, using negative numbers if (hue < 0) { hue = 360 + hue; } return hue; } } function pickSaturation(hue, options) { if (options.hue === "monochrome") { return 0; } if (options.luminosity === "random") { return randomWithin([0, 100]); } var saturationRange = getSaturationRange(hue); var sMin = saturationRange[0], sMax = saturationRange[1]; switch (options.luminosity) { case "bright": sMin = 55; break; case "dark": sMin = sMax - 10; break; case "light": sMax = 55; break; } return randomWithin([sMin, sMax]); } function pickBrightness(H, S, options) { var bMin = getMinimumBrightness(H, S), bMax = 100; switch (options.luminosity) { case "dark": bMax = bMin + 20; break; case "light": bMin = (bMax + bMin) / 2; break; case "random": bMin = 0; bMax = 100; break; } return randomWithin([bMin, bMax]); } function setFormat(hsv, options) { switch (options.format) { case "hsvArray": return hsv; case "hslArray": return HSVtoHSL(hsv); case "hsl": var hsl = HSVtoHSL(hsv); return "hsl(" + hsl[0] + ", " + hsl[1] + "%, " + hsl[2] + "%)"; case "hsla": var hslColor = HSVtoHSL(hsv); var alpha = options.alpha || Math.random(); return ( "hsla(" + hslColor[0] + ", " + hslColor[1] + "%, " + hslColor[2] + "%, " + alpha + ")" ); case "rgbArray": return HSVtoRGB(hsv); case "rgb": var rgb = HSVtoRGB(hsv); return "rgb(" + rgb.join(", ") + ")"; case "rgba": var rgbColor = HSVtoRGB(hsv); var alpha = options.alpha || Math.random(); return "rgba(" + rgbColor.join(", ") + ", " + alpha + ")"; default: return HSVtoHex(hsv); } } function getMinimumBrightness(H, S) { var lowerBounds = getColorInfo(H).lowerBounds; for (var i = 0; i < lowerBounds.length - 1; i++) { var s1 = lowerBounds[i][0], v1 = lowerBounds[i][1]; var s2 = lowerBounds[i + 1][0], v2 = lowerBounds[i + 1][1]; if (S >= s1 && S <= s2) { var m = (v2 - v1) / (s2 - s1), b = v1 - m * s1; return m * S + b; } } return 0; } function getHueRange(colorInput) { if (typeof parseInt(colorInput) === "number") { var number = parseInt(colorInput); if (number < 360 && number > 0) { return [number, number]; } } if (typeof colorInput === "string") { if (colorDictionary[colorInput]) { var color = colorDictionary[colorInput]; if (color.hueRange) { return color.hueRange; } } else if (colorInput.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) { var hue = HexToHSB(colorInput)[0]; return [hue, hue]; } } return [0, 360]; } function getSaturationRange(hue) { return getColorInfo(hue).saturationRange; } function getColorInfo(hue) { // Maps red colors to make picking hue easier if (hue >= 334 && hue <= 360) { hue -= 360; } for (var colorName in colorDictionary) { var color = colorDictionary[colorName]; if ( color.hueRange && hue >= color.hueRange[0] && hue <= color.hueRange[1] ) { return colorDictionary[colorName]; } } return "Color not found"; } function randomWithin(range) { if (seed === null) { //generate random evenly destinct number from : https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ var golden_ratio = 0.618033988749895; var r = Math.random(); r += golden_ratio; r %= 1; return Math.floor(range[0] + r * (range[1] + 1 - range[0])); } else { //Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/ var max = range[1] || 1; var min = range[0] || 0; seed = (seed * 9301 + 49297) % 233280; var rnd = seed / 233280.0; return Math.floor(min + rnd * (max - min)); } } function HSVtoHex(hsv) { var rgb = HSVtoRGB(hsv); function componentToHex(c) { var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; } var hex = "#" + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]); return hex; } function defineColor(name, hueRange, lowerBounds) { var sMin = lowerBounds[0][0], sMax = lowerBounds[lowerBounds.length - 1][0], bMin = lowerBounds[lowerBounds.length - 1][1], bMax = lowerBounds[0][1]; colorDictionary[name] = { hueRange: hueRange, lowerBounds: lowerBounds, saturationRange: [sMin, sMax], brightnessRange: [bMin, bMax], }; } function loadColorBounds() { defineColor("monochrome", null, [ [0, 0], [100, 0], ]); defineColor( "red", [-26, 18], [ [20, 100], [30, 92], [40, 89], [50, 85], [60, 78], [70, 70], [80, 60], [90, 55], [100, 50], ] ); defineColor( "orange", [18, 46], [ [20, 100], [30, 93], [40, 88], [50, 86], [60, 85], [70, 70], [100, 70], ] ); defineColor( "yellow", [46, 62], [ [25, 100], [40, 94], [50, 89], [60, 86], [70, 84], [80, 82], [90, 80], [100, 75], ] ); defineColor( "green", [62, 178], [ [30, 100], [40, 90], [50, 85], [60, 81], [70, 74], [80, 64], [90, 50], [100, 40], ] ); defineColor( "blue", [178, 257], [ [20, 100], [30, 86], [40, 80], [50, 74], [60, 60], [70, 52], [80, 44], [90, 39], [100, 35], ] ); defineColor( "purple", [257, 282], [ [20, 100], [30, 87], [40, 79], [50, 70], [60, 65], [70, 59], [80, 52], [90, 45], [100, 42], ] ); defineColor( "pink", [282, 334], [ [20, 100], [30, 90], [40, 86], [60, 84], [80, 80], [90, 75], [100, 73], ] ); } function HSVtoRGB(hsv) { // this doesn't work for the values of 0 and 360 // here's the hacky fix var h = hsv[0]; if (h === 0) { h = 1; } if (h === 360) { h = 359; } // Rebase the h,s,v values h = h / 360; var s = hsv[1] / 100, v = hsv[2] / 100; var h_i = Math.floor(h * 6), f = h * 6 - h_i, p = v * (1 - s), q = v * (1 - f * s), t = v * (1 - (1 - f) * s), r = 256, g = 256, b = 256; switch (h_i) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } var result = [ Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255), ]; return result; } function HexToHSB(hex) { hex = hex.replace(/^#/, ""); hex = hex.length === 3 ? hex.replace(/(.)/g, "$1$1") : hex; var red = parseInt(hex.substr(0, 2), 16) / 255, green = parseInt(hex.substr(2, 2), 16) / 255, blue = parseInt(hex.substr(4, 2), 16) / 255; var cMax = Math.max(red, green, blue), delta = cMax - Math.min(red, green, blue), saturation = cMax ? delta / cMax : 0; switch (cMax) { case red: return [60 * (((green - blue) / delta) % 6) || 0, saturation, cMax]; case green: return [60 * ((blue - red) / delta + 2) || 0, saturation, cMax]; case blue: return [60 * ((red - green) / delta + 4) || 0, saturation, cMax]; } } function HSVtoHSL(hsv) { var h = hsv[0], s = hsv[1] / 100, v = hsv[2] / 100, k = (2 - s) * v; return [ h, Math.round(((s * v) / (k < 1 ? k : 2 - k)) * 10000) / 100, (k / 2) * 100, ]; } function stringToInteger(string) { var total = 0; for (var i = 0; i !== string.length; i++) { if (total >= Number.MAX_SAFE_INTEGER) break; total += string.charCodeAt(i); } return total; } // get The range of given hue when options.count!=0 function getRealHueRange(colorHue) { if (!isNaN(colorHue)) { var number = parseInt(colorHue); if (number < 360 && number > 0) { return getColorInfo(colorHue).hueRange; } } else if (typeof colorHue === "string") { if (colorDictionary[colorHue]) { var color = colorDictionary[colorHue]; if (color.hueRange) { return color.hueRange; } } else if (colorHue.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) { var hue = HexToHSB(colorHue)[0]; return getColorInfo(hue).hueRange; } } return [0, 360]; } return randomColor; });