mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-01 02:38:43 -05:00
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:
parent
bb77da22a8
commit
0dddd897ab
4 changed files with 45 additions and 63 deletions
|
@ -179,9 +179,6 @@ Base.inject(/** @lends Base# */{
|
||||||
obj = Base.create(this.prototype);
|
obj = Base.create(this.prototype);
|
||||||
if (readIndex)
|
if (readIndex)
|
||||||
obj.__read = true;
|
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
|
obj = obj.initialize.apply(obj, index > 0 || length < list.length
|
||||||
? Array.prototype.slice.call(list, index, index + length)
|
? Array.prototype.slice.call(list, index, index + length)
|
||||||
: list) || obj;
|
: list) || obj;
|
||||||
|
@ -191,8 +188,6 @@ Base.inject(/** @lends Base# */{
|
||||||
// last read() call
|
// last read() call
|
||||||
list.__read = obj.__read;
|
list.__read = obj.__read;
|
||||||
delete obj.__read;
|
delete obj.__read;
|
||||||
if (options)
|
|
||||||
delete obj.__options;
|
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
|
|
|
@ -247,23 +247,20 @@ var Color = Base.extend(new function() {
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
: name === 'hue'
|
: type === 'gradient'
|
||||||
? function(value) {
|
? function(/* value */) {
|
||||||
// Keep negative values within modulo 360 too:
|
return Point.read(arguments, 0, 0, {
|
||||||
return isNaN(value) ? 0
|
readNull: name === 'highlight',
|
||||||
: ((value % 360) + 360) % 360;
|
clone: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
: type === 'gradient'
|
: function(value) {
|
||||||
? function(/* value */) {
|
// NOTE: We don't clamp values here, they're only
|
||||||
return Point.read(arguments, 0, 0, {
|
// clamped once the actual CSS values are produced.
|
||||||
readNull: name === 'highlight',
|
// Gotta love the fact that isNaN(null) is false,
|
||||||
clone: true
|
// while isNaN(undefined) is true.
|
||||||
});
|
return value == null || isNaN(value) ? 0 : value;
|
||||||
}
|
};
|
||||||
: function(value) {
|
|
||||||
return isNaN(value) ? 0
|
|
||||||
: Math.min(Math.max(value, 0), 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
this['get' + part] = function() {
|
this['get' + part] = function() {
|
||||||
return this._type === type
|
return this._type === type
|
||||||
|
@ -401,7 +398,7 @@ var Color = Base.extend(new function() {
|
||||||
* // the path and to position the gradient color:
|
* // the path and to position the gradient color:
|
||||||
* var topLeft = view.center - [80, 80];
|
* var topLeft = view.center - [80, 80];
|
||||||
* var bottomRight = view.center + [80, 80];
|
* var bottomRight = view.center + [80, 80];
|
||||||
*
|
*
|
||||||
* var path = new Path.Rectangle({
|
* var path = new Path.Rectangle({
|
||||||
* topLeft: topLeft,
|
* topLeft: topLeft,
|
||||||
* bottomRight: bottomRight,
|
* bottomRight: bottomRight,
|
||||||
|
@ -489,7 +486,6 @@ var Color = Base.extend(new function() {
|
||||||
var slice = Array.prototype.slice,
|
var slice = Array.prototype.slice,
|
||||||
args = arguments,
|
args = arguments,
|
||||||
read = 0,
|
read = 0,
|
||||||
parse = true,
|
|
||||||
type,
|
type,
|
||||||
components,
|
components,
|
||||||
alpha,
|
alpha,
|
||||||
|
@ -520,8 +516,6 @@ var Color = Base.extend(new function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!components) {
|
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
|
// Determine if there is a values array
|
||||||
values = argType === 'number'
|
values = argType === 'number'
|
||||||
? args
|
? args
|
||||||
|
@ -586,7 +580,7 @@ var Color = Base.extend(new function() {
|
||||||
: 'rgb';
|
: 'rgb';
|
||||||
// Convert to array and parse in one loop, for efficiency
|
// Convert to array and parse in one loop, for efficiency
|
||||||
var properties = types[type];
|
var properties = types[type];
|
||||||
parsers = parse && componentParsers[type];
|
parsers = componentParsers[type];
|
||||||
this._components = components = [];
|
this._components = components = [];
|
||||||
for (var i = 0, l = properties.length; i < l; i++) {
|
for (var i = 0, l = properties.length; i < l; i++) {
|
||||||
var value = arg[properties[i]];
|
var value = arg[properties[i]];
|
||||||
|
@ -600,8 +594,7 @@ var Color = Base.extend(new function() {
|
||||||
radial: arg.radial
|
radial: arg.radial
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (parse)
|
value = parsers[i].call(this, value);
|
||||||
value = parsers[i].call(this, value);
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
components[i] = value;
|
components[i] = value;
|
||||||
}
|
}
|
||||||
|
@ -623,9 +616,7 @@ var Color = Base.extend(new function() {
|
||||||
this._components = components = [];
|
this._components = components = [];
|
||||||
var parsers = componentParsers[this._type];
|
var parsers = componentParsers[this._type];
|
||||||
for (var i = 0, l = parsers.length; i < l; i++) {
|
for (var i = 0, l = parsers.length; i < l; i++) {
|
||||||
var value = values && values[i];
|
var value = parsers[i].call(this, values && values[i]);
|
||||||
if (parse)
|
|
||||||
value = parsers[i].call(this, value);
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
components[i] = value;
|
components[i] = value;
|
||||||
}
|
}
|
||||||
|
@ -815,13 +806,16 @@ var Color = Base.extend(new function() {
|
||||||
// TODO: Support HSL / HSLA CSS3 colors directly, without conversion
|
// TODO: Support HSL / HSLA CSS3 colors directly, without conversion
|
||||||
var components = this._convert('rgb'),
|
var components = this._convert('rgb'),
|
||||||
alpha = hex || this._alpha == null ? 1 : this._alpha;
|
alpha = hex || this._alpha == null ? 1 : this._alpha;
|
||||||
|
function convert(val) {
|
||||||
|
return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);
|
||||||
|
}
|
||||||
components = [
|
components = [
|
||||||
Math.round(components[0] * 255),
|
convert(components[0]),
|
||||||
Math.round(components[1] * 255),
|
convert(components[1]),
|
||||||
Math.round(components[2] * 255)
|
convert(components[2])
|
||||||
];
|
];
|
||||||
if (alpha < 1)
|
if (alpha < 1)
|
||||||
components.push(alpha);
|
components.push(val < 0 ? 0 : val);
|
||||||
return hex
|
return hex
|
||||||
? '#' + ((1 << 24) + (components[0] << 16)
|
? '#' + ((1 << 24) + (components[0] << 16)
|
||||||
+ (components[1] << 8)
|
+ (components[1] << 8)
|
||||||
|
@ -1119,48 +1113,33 @@ var Color = Base.extend(new function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, new function() {
|
}, new function() {
|
||||||
function clamp(value, hue) {
|
|
||||||
return value < 0
|
|
||||||
? 0
|
|
||||||
: hue && value > 360
|
|
||||||
? 360
|
|
||||||
: !hue && value > 1
|
|
||||||
? 1
|
|
||||||
: value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var operators = {
|
var operators = {
|
||||||
add: function(a, b, hue) {
|
add: function(a, b) {
|
||||||
return clamp(a + b, hue);
|
return a + b;
|
||||||
},
|
},
|
||||||
|
|
||||||
subtract: function(a, b, hue) {
|
subtract: function(a, b) {
|
||||||
return clamp(a - b, hue);
|
return a - b;
|
||||||
},
|
},
|
||||||
|
|
||||||
multiply: function(a, b, hue) {
|
multiply: function(a, b) {
|
||||||
return clamp(a * b, hue);
|
return a * b;
|
||||||
},
|
},
|
||||||
|
|
||||||
divide: function(a, b, hue) {
|
divide: function(a, b) {
|
||||||
return clamp(a / b, hue);
|
return a / b;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return Base.each(operators, function(operator, name) {
|
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) {
|
this[name] = function(color) {
|
||||||
color = Color.read(arguments, 0, 0, options);
|
color = Color.read(arguments, 0, 0);
|
||||||
var type = this._type,
|
var type = this._type,
|
||||||
properties = this._properties,
|
properties = this._properties,
|
||||||
components1 = this._components,
|
components1 = this._components,
|
||||||
components2 = color._convert(type);
|
components2 = color._convert(type);
|
||||||
for (var i = 0, l = components1.length; i < l; i++)
|
for (var i = 0, l = components1.length; i < l; i++)
|
||||||
components2[i] = operator(components1[i], components2[i],
|
components2[i] = operator(components1[i], components2[i]);
|
||||||
properties[i] === 'hue');
|
|
||||||
return new Color(type, components2,
|
return new Color(type, components2,
|
||||||
this._alpha != null
|
this._alpha != null
|
||||||
? operator(this._alpha, color.getAlpha())
|
? operator(this._alpha, color.getAlpha())
|
||||||
|
|
|
@ -51,6 +51,14 @@ test('Set color to array', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Creating Colors', 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),
|
compareColors(new Color('#ff0000'), new Color(1, 0, 0),
|
||||||
'Color from hex code');
|
'Color from hex code');
|
||||||
|
@ -203,7 +211,7 @@ test('Saturation from black rgb', function() {
|
||||||
test('Color#add', function() {
|
test('Color#add', function() {
|
||||||
var color = new Color(0, 1, 1);
|
var color = new Color(0, 1, 1);
|
||||||
compareColors(color.add([1, 0, 0]), [1, 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);
|
var color = new Color(0, 0.5, 0);
|
||||||
compareColors(color.add(0.5), [0.5, 1, 0.5]);
|
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);
|
var color = new Color(1, 1, 1);
|
||||||
compareColors(color.divide([1, 2, 4]), [1, 0.5, 0.25]);
|
compareColors(color.divide([1, 2, 4]), [1, 0.5, 0.25]);
|
||||||
var color = new Color(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);
|
var color = new Color(1, 1, 1);
|
||||||
compareColors(color.divide(4), [0.25, 0.25, 0.25]);
|
compareColors(color.divide(4), [0.25, 0.25, 0.25]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,7 +19,7 @@ test('PointText', function() {
|
||||||
point: [100, 100],
|
point: [100, 100],
|
||||||
content: 'Hello World!'
|
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 });
|
comparePoints(text.point, { x: 100, y: 100 });
|
||||||
compareRectangles(text.bounds, { x: 100, y: 87.4, width: 77, height: 16.8 });
|
compareRectangles(text.bounds, { x: 100, y: 87.4, width: 77, height: 16.8 });
|
||||||
equals(function() {
|
equals(function() {
|
||||||
|
|
Loading…
Reference in a new issue