mirror of https://github.com/nanoy42/coope
root
4 years ago
3 changed files with 524 additions and 12 deletions
@ -0,0 +1,519 @@ |
|||
// 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) { |
|||
|
|||
// Since we're generating multiple colors,
|
|||
// incremement the seed. Otherwise we'd just
|
|||
// generate the same color each time...
|
|||
if (seed && options.seed) options.seed += 1; |
|||
|
|||
colors.push(randomColor(options)); |
|||
} |
|||
|
|||
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', |
|||
[19,46], |
|||
[[20,100],[30,93],[40,88],[50,86],[60,85],[70,70],[100,70]] |
|||
); |
|||
|
|||
defineColor( |
|||
'yellow', |
|||
[47,62], |
|||
[[25,100],[40,94],[50,89],[60,86],[70,84],[80,82],[90,80],[100,75]] |
|||
); |
|||
|
|||
defineColor( |
|||
'green', |
|||
[63,178], |
|||
[[30,100],[40,90],[50,85],[60,81],[70,74],[80,64],[90,50],[100,40]] |
|||
); |
|||
|
|||
defineColor( |
|||
'blue', |
|||
[179, 257], |
|||
[[20,100],[30,86],[40,80],[50,74],[60,60],[70,52],[80,44],[90,39],[100,35]] |
|||
); |
|||
|
|||
defineColor( |
|||
'purple', |
|||
[258, 282], |
|||
[[20,100],[30,87],[40,79],[50,70],[60,65],[70,59],[80,52],[90,45],[100,42]] |
|||
); |
|||
|
|||
defineColor( |
|||
'pink', |
|||
[283, 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; |
|||
})); |
Loading…
Reference in new issue