Remove internal clamping of color values to facilitate proper mathematical calculations with colors.

Clamp only when producing CSS values. Closes #271.
This commit is contained in:
Jürg Lehni 2014-01-04 18:16:52 +01:00
parent bb77da22a8
commit 0dddd897ab
4 changed files with 45 additions and 63 deletions

View file

@ -179,9 +179,6 @@ Base.inject(/** @lends Base# */{
obj = Base.create(this.prototype);
if (readIndex)
obj.__read = true;
// If options were provided, pass them on to the constructed object
if (options)
obj.__options = options;
obj = obj.initialize.apply(obj, index > 0 || length < list.length
? Array.prototype.slice.call(list, index, index + length)
: list) || obj;
@ -191,8 +188,6 @@ Base.inject(/** @lends Base# */{
// last read() call
list.__read = obj.__read;
delete obj.__read;
if (options)
delete obj.__options;
}
return obj;
},

View file

@ -247,23 +247,20 @@ var Color = Base.extend(new function() {
}
return value;
}
: name === 'hue'
? function(value) {
// Keep negative values within modulo 360 too:
return isNaN(value) ? 0
: ((value % 360) + 360) % 360;
: type === 'gradient'
? function(/* value */) {
return Point.read(arguments, 0, 0, {
readNull: name === 'highlight',
clone: true
});
}
: type === 'gradient'
? function(/* value */) {
return Point.read(arguments, 0, 0, {
readNull: name === 'highlight',
clone: true
});
}
: function(value) {
return isNaN(value) ? 0
: Math.min(Math.max(value, 0), 1);
};
: function(value) {
// NOTE: We don't clamp values here, they're only
// clamped once the actual CSS values are produced.
// Gotta love the fact that isNaN(null) is false,
// while isNaN(undefined) is true.
return value == null || isNaN(value) ? 0 : value;
};
this['get' + part] = function() {
return this._type === type
@ -401,7 +398,7 @@ var Color = Base.extend(new function() {
* // the path and to position the gradient color:
* var topLeft = view.center - [80, 80];
* var bottomRight = view.center + [80, 80];
*
*
* var path = new Path.Rectangle({
* topLeft: topLeft,
* bottomRight: bottomRight,
@ -489,7 +486,6 @@ var Color = Base.extend(new function() {
var slice = Array.prototype.slice,
args = arguments,
read = 0,
parse = true,
type,
components,
alpha,
@ -520,8 +516,6 @@ var Color = Base.extend(new function() {
}
}
if (!components) {
// Only parse values if we're not told to not do so
parse = !(this.__options && this.__options.dontParse);
// Determine if there is a values array
values = argType === 'number'
? args
@ -586,7 +580,7 @@ var Color = Base.extend(new function() {
: 'rgb';
// Convert to array and parse in one loop, for efficiency
var properties = types[type];
parsers = parse && componentParsers[type];
parsers = componentParsers[type];
this._components = components = [];
for (var i = 0, l = properties.length; i < l; i++) {
var value = arg[properties[i]];
@ -600,8 +594,7 @@ var Color = Base.extend(new function() {
radial: arg.radial
};
}
if (parse)
value = parsers[i].call(this, value);
value = parsers[i].call(this, value);
if (value != null)
components[i] = value;
}
@ -623,9 +616,7 @@ var Color = Base.extend(new function() {
this._components = components = [];
var parsers = componentParsers[this._type];
for (var i = 0, l = parsers.length; i < l; i++) {
var value = values && values[i];
if (parse)
value = parsers[i].call(this, value);
var value = parsers[i].call(this, values && values[i]);
if (value != null)
components[i] = value;
}
@ -815,13 +806,16 @@ var Color = Base.extend(new function() {
// TODO: Support HSL / HSLA CSS3 colors directly, without conversion
var components = this._convert('rgb'),
alpha = hex || this._alpha == null ? 1 : this._alpha;
function convert(val) {
return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);
}
components = [
Math.round(components[0] * 255),
Math.round(components[1] * 255),
Math.round(components[2] * 255)
convert(components[0]),
convert(components[1]),
convert(components[2])
];
if (alpha < 1)
components.push(alpha);
components.push(val < 0 ? 0 : val);
return hex
? '#' + ((1 << 24) + (components[0] << 16)
+ (components[1] << 8)
@ -1119,48 +1113,33 @@ var Color = Base.extend(new function() {
}
});
}, new function() {
function clamp(value, hue) {
return value < 0
? 0
: hue && value > 360
? 360
: !hue && value > 1
? 1
: value;
}
var operators = {
add: function(a, b, hue) {
return clamp(a + b, hue);
add: function(a, b) {
return a + b;
},
subtract: function(a, b, hue) {
return clamp(a - b, hue);
subtract: function(a, b) {
return a - b;
},
multiply: function(a, b, hue) {
return clamp(a * b, hue);
multiply: function(a, b) {
return a * b;
},
divide: function(a, b, hue) {
return clamp(a / b, hue);
divide: function(a, b) {
return a / b;
}
};
return Base.each(operators, function(operator, name) {
// Tell the argument reader not to parse values for multiply and divide,
// so the are not clamped yet.
var options = { dontParse: /^(multiply|divide)$/.test(name) };
this[name] = function(color) {
color = Color.read(arguments, 0, 0, options);
color = Color.read(arguments, 0, 0);
var type = this._type,
properties = this._properties,
components1 = this._components,
components2 = color._convert(type);
for (var i = 0, l = components1.length; i < l; i++)
components2[i] = operator(components1[i], components2[i],
properties[i] === 'hue');
components2[i] = operator(components1[i], components2[i]);
return new Color(type, components2,
this._alpha != null
? operator(this._alpha, color.getAlpha())

View file

@ -51,6 +51,14 @@ test('Set color to array', function() {
});
test('Creating Colors', function() {
compareColors(new Color(), new Color(0, 0, 0),
'Color with no arguments should be black');
compareColors(new Color('black'), new Color(0, 0, 0),
'Color from name (black)');
compareColors(new Color('red'), new Color(1, 0, 0),
'Color from name (red)');
compareColors(new Color('#ff0000'), new Color(1, 0, 0),
'Color from hex code');
@ -203,7 +211,7 @@ test('Saturation from black rgb', function() {
test('Color#add', function() {
var color = new Color(0, 1, 1);
compareColors(color.add([1, 0, 0]), [1, 1, 1]);
compareColors(color.add([1, 0.5, 0]), [1, 1, 1]);
compareColors(color.add([1, 0.5, 0]), [1, 1.5, 1]);
var color = new Color(0, 0.5, 0);
compareColors(color.add(0.5), [0.5, 1, 0.5]);
});
@ -229,7 +237,7 @@ test('Color#divide', function() {
var color = new Color(1, 1, 1);
compareColors(color.divide([1, 2, 4]), [1, 0.5, 0.25]);
var color = new Color(1, 0.5, 0.25);
compareColors(color.divide(0.25), [1, 1, 1]);
compareColors(color.divide(0.25), [4, 2, 1]);
var color = new Color(1, 1, 1);
compareColors(color.divide(4), [0.25, 0.25, 0.25]);
});

View file

@ -19,7 +19,7 @@ test('PointText', function() {
point: [100, 100],
content: 'Hello World!'
});
equals(text.fillColor, { red: 0, green: 0, blue: 0 }, 'text.fillColor should be black by default');
compareColors(text.fillColor, new Color(0, 0, 0), 'text.fillColor should be black by default');
comparePoints(text.point, { x: 100, y: 100 });
compareRectangles(text.bounds, { x: 100, y: 87.4, width: 77, height: 16.8 });
equals(function() {