dat.gui/build/dat.color.js
George Michael Brower b9adfd5b02 0.5
2011-11-07 13:29:37 -08:00

755 lines
15 KiB
JavaScript

/**
* dat-gui JavaScript Controller Library
* http://code.google.com/p/dat-gui
*
* Copyright 2011 Data Arts Team, Google Creative Lab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
/** @namespace */
var dat = dat || {};
/** @namespace */
dat.color = dat.color || {};
/** @namespace */
dat.utils = dat.utils || {};
dat.utils.common = (function () {
var ARR_EACH = Array.prototype.forEach;
var ARR_SLICE = Array.prototype.slice;
/**
* Band-aid methods for things that should be a lot easier in JavaScript.
* Implementation and structure inspired by underscore.js
* http://documentcloud.github.com/underscore/
*/
return {
BREAK: {},
extend: function(target) {
this.each(ARR_SLICE.call(arguments, 1), function(obj) {
for (var key in obj)
if (!this.isUndefined(obj[key]))
target[key] = obj[key];
}, this);
return target;
},
defaults: function(target) {
this.each(ARR_SLICE.call(arguments, 1), function(obj) {
for (var key in obj)
if (this.isUndefined(target[key]))
target[key] = obj[key];
}, this);
return target;
},
compose: function() {
var toCall = ARR_SLICE.call(arguments);
return function() {
var args = ARR_SLICE.call(arguments);
for (var i = toCall.length -1; i >= 0; i--) {
args = [toCall[i].apply(this, args)];
}
return args[0];
}
},
each: function(obj, itr, scope) {
if (ARR_EACH && obj.forEach === ARR_EACH) {
obj.forEach(itr, scope);
} else if (obj.length === obj.length + 0) { // Is number but not NaN
for (var key = 0, l = obj.length; key < l; key++)
if (key in obj && itr.call(scope, obj[key], key) === this.BREAK)
return;
} else {
for (var key in obj)
if (itr.call(scope, obj[key], key) === this.BREAK)
return;
}
},
defer: function(fnc) {
setTimeout(fnc, 0);
},
toArray: function(obj) {
if (obj.toArray) return obj.toArray();
return ARR_SLICE.call(obj);
},
isUndefined: function(obj) {
return obj === undefined;
},
isNull: function(obj) {
return obj === null;
},
isNaN: function(obj) {
return obj !== obj;
},
isArray: Array.isArray || function(obj) {
return obj.constructor === Array;
},
isObject: function(obj) {
return obj === Object(obj);
},
isNumber: function(obj) {
return obj === obj+0;
},
isString: function(obj) {
return obj === obj+'';
},
isBoolean: function(obj) {
return obj === false || obj === true;
},
isFunction: function(obj) {
return Object.prototype.toString.call(obj) === '[object Function]';
}
};
})();
dat.color.toString = (function (common) {
return function(color) {
if (color.a == 1 || common.isUndefined(color.a)) {
var s = color.hex.toString(16);
while (s.length < 6) {
s = '0' + s;
}
return '#' + s;
} else {
return 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',' + color.a + ')';
}
}
})(dat.utils.common);
dat.Color = dat.color.Color = (function (interpret, math, toString, common) {
var Color = function() {
this.__state = interpret.apply(this, arguments);
if (this.__state === false) {
throw 'Failed to interpret color arguments';
}
this.__state.a = this.__state.a || 1;
};
Color.COMPONENTS = ['r','g','b','h','s','v','hex','a'];
common.extend(Color.prototype, {
toString: function() {
return toString(this);
},
toOriginal: function() {
return this.__state.conversion.write(this);
}
});
defineRGBComponent(Color.prototype, 'r', 2);
defineRGBComponent(Color.prototype, 'g', 1);
defineRGBComponent(Color.prototype, 'b', 0);
defineHSVComponent(Color.prototype, 'h');
defineHSVComponent(Color.prototype, 's');
defineHSVComponent(Color.prototype, 'v');
Object.defineProperty(Color.prototype, 'a', {
get: function() {
return this.__state.a;
},
set: function(v) {
this.__state.a = v;
}
});
Object.defineProperty(Color.prototype, 'hex', {
get: function() {
if (!this.__state.space !== 'HEX') {
this.__state.hex = math.rgb_to_hex(this.r, this.g, this.b);
}
return this.__state.hex;
},
set: function(v) {
this.__state.space = 'HEX';
this.__state.hex = v;
}
});
function defineRGBComponent(target, component, componentHexIndex) {
Object.defineProperty(target, component, {
get: function() {
if (this.__state.space === 'RGB') {
return this.__state[component];
}
recalculateRGB(this, component, componentHexIndex);
return this.__state[component];
},
set: function(v) {
if (this.__state.space !== 'RGB') {
recalculateRGB(this, component, componentHexIndex);
this.__state.space = 'RGB';
}
this.__state[component] = v;
}
});
}
function defineHSVComponent(target, component) {
Object.defineProperty(target, component, {
get: function() {
if (this.__state.space === 'HSV')
return this.__state[component];
recalculateHSV(this);
return this.__state[component];
},
set: function(v) {
if (this.__state.space !== 'HSV') {
recalculateHSV(this);
this.__state.space = 'HSV';
}
this.__state[component] = v;
}
});
}
function recalculateRGB(color, component, componentHexIndex) {
if (color.__state.space === 'HEX') {
color.__state[component] = math.component_from_hex(color.__state.hex, componentHexIndex);
} else if (color.__state.space === 'HSV') {
common.extend(color.__state, math.hsv_to_rgb(color.__state.h, color.__state.s, color.__state.v));
} else {
throw 'Corrupted color state';
}
}
function recalculateHSV(color) {
var result = math.rgb_to_hsv(color.r, color.g, color.b);
common.extend(color.__state,
{
s: result.s,
v: result.v
}
);
if (!common.isNaN(result.h)) {
color.__state.h = result.h;
} else if (common.isUndefined(color.__state.h)) {
color.__state.h = 0;
}
}
return Color;
})(dat.color.interpret = (function (toString, common) {
var result, toReturn;
var interpret = function() {
toReturn = false;
var original = arguments.length > 1 ? common.toArray(arguments) : arguments[0];
common.each(INTERPRETATIONS, function(family) {
if (family.litmus(original)) {
common.each(family.conversions, function(conversion, conversionName) {
result = conversion.read(original);
if (toReturn === false && result !== false) {
toReturn = result;
result.conversionName = conversionName;
result.conversion = conversion;
return common.BREAK;
}
});
return common.BREAK;
}
});
return toReturn;
};
var INTERPRETATIONS = [
// Strings
{
litmus: common.isString,
conversions: {
THREE_CHAR_HEX: {
read: function(original) {
var test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);
if (test === null) return false;
return {
space: 'HEX',
hex: parseInt(
'0x' +
test[1].toString() + test[1].toString() +
test[2].toString() + test[2].toString() +
test[3].toString() + test[3].toString())
};
},
write: toString
},
SIX_CHAR_HEX: {
read: function(original) {
var test = original.match(/^#([A-F0-9]{6})$/i);
if (test === null) return false;
return {
space: 'HEX',
hex: parseInt('0x' + test[1].toString())
};
},
write: toString
},
CSS_RGB: {
read: function(original) {
var test = original.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
if (test === null) return false;
return {
space: 'RGB',
r: parseFloat(test[1]),
g: parseFloat(test[2]),
b: parseFloat(test[3])
};
},
write: toString
},
CSS_RGBA: {
read: function(original) {
var test = original.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);
if (test === null) return false;
return {
space: 'RGB',
r: parseFloat(test[1]),
g: parseFloat(test[2]),
b: parseFloat(test[3]),
a: parseFloat(test[4])
};
},
write: toString
}
}
},
// Numbers
{
litmus: common.isNumber,
conversions: {
HEX: {
read: function(original) {
return {
space: 'HEX',
hex: original,
conversionName: 'HEX'
}
},
write: function(color) {
return color.hex;
}
}
}
},
// Arrays
{
litmus: common.isArray,
conversions: {
RGB_ARRAY: {
read: function(original) {
if (original.length != 3) return false;
return {
space: 'RGB',
r: original[0],
g: original[1],
b: original[2]
};
},
write: function(color) {
return [color.r, color.g, color.b];
}
},
RGBA_ARRAY: {
read: function(original) {
if (original.length != 4) return false;
return {
space: 'RGB',
r: original[0],
g: original[1],
b: original[2],
a: original[3]
};
},
write: function(color) {
return [color.r, color.g, color.b, color.a];
}
}
}
},
// Objects
{
litmus: common.isObject,
conversions: {
RGBA_OBJ: {
read: function(original) {
if (common.isNumber(original.r) &&
common.isNumber(original.g) &&
common.isNumber(original.b) &&
common.isNumber(original.a)) {
return {
space: 'RGB',
r: original.r,
g: original.g,
b: original.b,
a: original.a
}
}
return false;
},
write: function(color) {
return {
r: color.r,
g: color.g,
b: color.b,
a: color.a
}
}
},
RGB_OBJ: {
read: function(original) {
if (common.isNumber(original.r) &&
common.isNumber(original.g) &&
common.isNumber(original.b)) {
return {
space: 'RGB',
r: original.r,
g: original.g,
b: original.b
}
}
return false;
},
write: function(color) {
return {
r: color.r,
g: color.g,
b: color.b
}
}
},
HSVA_OBJ: {
read: function(original) {
if (common.isNumber(original.h) &&
common.isNumber(original.s) &&
common.isNumber(original.v) &&
common.isNumber(original.a)) {
return {
space: 'HSV',
h: original.h,
s: original.s,
v: original.v,
a: original.a
}
}
return false;
},
write: function(color) {
return {
h: color.h,
s: color.s,
v: color.v,
a: color.a
}
}
},
HSV_OBJ: {
read: function(original) {
if (common.isNumber(original.h) &&
common.isNumber(original.s) &&
common.isNumber(original.v)) {
return {
space: 'HSV',
h: original.h,
s: original.s,
v: original.v
}
}
return false;
},
write: function(color) {
return {
h: color.h,
s: color.s,
v: color.v
}
}
}
}
}
];
return interpret;
})(dat.color.toString,
dat.utils.common),
dat.color.math = (function () {
var tmpComponent;
return {
hsv_to_rgb: function(h, s, v) {
var hi = Math.floor(h / 60) % 6;
var f = h / 60 - Math.floor(h / 60);
var p = v * (1.0 - s);
var q = v * (1.0 - (f * s));
var t = v * (1.0 - ((1.0 - f) * s));
var c = [
[v, t, p],
[q, v, p],
[p, v, t],
[p, q, v],
[t, p, v],
[v, p, q]
][hi];
return {
r: c[0] * 255,
g: c[1] * 255,
b: c[2] * 255
};
},
rgb_to_hsv: function(r, g, b) {
var min = Math.min(r, g, b),
max = Math.max(r, g, b),
delta = max - min,
h, s;
if (max != 0) {
s = delta / max;
} else {
return {
h: NaN,
s: 0,
v: 0
};
}
if (r == max) {
h = (g - b) / delta;
} else if (g == max) {
h = 2 + (b - r) / delta;
} else {
h = 4 + (r - g) / delta;
}
h /= 6;
if (h < 0) {
h += 1;
}
return {
h: h * 360,
s: s,
v: max / 255
};
},
rgb_to_hex: function(r, g, b) {
var hex = this.hex_with_component(0, 2, r);
hex = this.hex_with_component(hex, 1, g);
hex = this.hex_with_component(hex, 0, b);
return hex;
},
component_from_hex: function(hex, componentIndex) {
return (hex >> (componentIndex * 8)) & 0xFF;
},
hex_with_component: function(hex, componentIndex, value) {
return value << (tmpComponent = componentIndex * 8) | (hex & ~ (0xFF << tmpComponent));
}
}
})(),
dat.color.toString,
dat.utils.common);