Merge GradientColor into Color.

Work in progress.
This commit is contained in:
Jürg Lehni 2013-04-09 01:21:36 -07:00
parent 380fce3946
commit 5209e97c8d
15 changed files with 154 additions and 51 deletions

View file

@ -10,7 +10,7 @@
var gradient = new Gradient(['yellow', 'red', 'black'], true);
var from = path.position;
var to = path.bounds.rightCenter;
var gradientColor = new GradientColor(gradient, from, to);
var gradientColor = new Color(gradient, from, to);
path.fillColor = gradientColor;
path.strokeColor = 'black';
window._json = project.exportJson();

View file

@ -35,7 +35,7 @@
radius: radius / 3
})
],
fillColor: new GradientColor(gradient, 0, radius, radius / 8),
fillColor: new Color(gradient, 0, radius, radius / 8),
position: this.point
});
}

View file

@ -24,7 +24,7 @@
var path = new Path.Rectangle(view.bounds);
var gradient = new Gradient(colors, true);
var radius = Math.max(view.size.width, view.size.height) * 0.75;
path.fillColor = new GradientColor(gradient, point, point + [radius, 0]);
path.fillColor = new Color(gradient, point, point + [radius, 0]);
var gradientColor = path.fillColor;
var mouseDown = false,

View file

@ -16,7 +16,7 @@
var circle = new Path.Circle({
center: from,
radius: radius,
fillColor: new GradientColor(radial, from, to),
fillColor: new Color(radial, from, to),
strokeColor: 'black'
});
@ -26,7 +26,7 @@
var rect = new Path.Rectangle({
from: from,
to: to,
fillColor: new GradientColor(linear, from, to),
fillColor: new Color(linear, from, to),
strokeColor: 'black'
});

View file

@ -41,7 +41,7 @@
var gradient = new Gradient(colors, true);
var from = center;
var to = center + vector;
var gradientColor = new GradientColor(gradient, from, to);
var gradientColor = new Color(gradient, from, to);
path.fillColor = path.strokeColor = gradientColor;
}
}

View file

@ -47,7 +47,8 @@ var Color = this.Color = Base.extend(new function() {
gray: ['gray'],
rgb: ['red', 'green', 'blue'],
hsb: ['hue', 'saturation', 'brightness'],
hsl: ['hue', 'saturation', 'lightness']
hsl: ['hue', 'saturation', 'lightness'],
gradient: ['gradient', 'origin', 'destination', 'hilite']
};
var colorCache = {},
@ -192,7 +193,18 @@ var Color = this.Color = Base.extend(new function() {
'gray-hsl': function(g) {
// TODO: Is lightness really the same as brightness for gray?
return [0, 0, g];
},
'gradient-rgb': function(gradient) {
// TODO: Implement
return [];
},
'rgb-gradient': function(r, g, b) {
// TODO: Implement
return [];
}
};
// Produce getters and setter methods for the various color components known
@ -201,12 +213,15 @@ var Color = this.Color = Base.extend(new function() {
// its component.
return Base.each(types, function(properties, type) {
Base.each(properties, function(name, index) {
var isHue = name === 'hue',
var part = Base.capitalize(name),
isHue = name === 'hue',
isGradient = name === 'gradient',
isPoint = type === 'gradient' && !isGradient,
isHilite = name === 'hilite',
// Both hue and saturation have overlapping properties between
// hsb and hsl. Handle this here separately, by testing for
// overlaps and skipping conversion if the type is /hs[bl]/
hasOverlap = /^(hue|saturation)$/.test(name),
part = Base.capitalize(name);
hasOverlap = /^(hue|saturation)$/.test(name);
this['get' + part] = function() {
return this._type === type
@ -222,11 +237,20 @@ var Color = this.Color = Base.extend(new function() {
this._components = this._convert(type);
this._type = type;
}
this._components[index] = isHue
if (!isGradient)
value = isHue
// Keep negative values within modulo 360 too:
? ((value % 360) + 360) % 360
// All other values are 0..1
: Math.min(Math.max(value, 0), 1);
: isPoint
? Point.read(arguments, 0, 0, true, isHilite) // clone, readNull
// All other values are 0..1
: Math.min(Math.max(value, 0), 1);
if (value != null) {
this._components[index] = value;
if (isGradient)
value._addOwner(this);
this._changed();
}
};
}, this);
}, /** @lends Color# */{
@ -242,6 +266,10 @@ var Color = this.Color = Base.extend(new function() {
args = arguments,
read = 0,
type;
if (Array.isArray(arg)) {
args = arg;
arg = args[0];
}
// Try type arg first
if (typeof arg === 'string' && arg in types) {
type = arg;
@ -286,17 +314,19 @@ var Color = this.Color = Base.extend(new function() {
components = arg._components.slice();
alpha = arg._alpha;
} else if (arg._class === 'Gradient') {
// TODO: Construct gradient
type = 'gradient';
components = slice.call(args);
} else {
// Determine type by presence of object property names
type = 'hue' in arg
? 'lightness' in arg
? 'hsl'
: 'hsb'
: 'gray' in arg
? 'gray'
: 'rgb';
: 'gradient' in arg
? 'gradient'
: 'gray' in arg
? 'gray'
: 'rgb';
var properties = types[type];
components = [];
for (var i = 0, l = properties.length; i < l; i++)
@ -307,19 +337,31 @@ var Color = this.Color = Base.extend(new function() {
if (this._read && type)
read = 1;
}
// Define this GradientColor's unique id.
if (type === 'gradient')
this._id = ++Base._uid;
// Default fallbacks: rgb, black
this._type = type || 'rgb';
this._components = components || (type === 'gray' ? [0] : [0, 0, 0]);
this._alpha = alpha;
// Trigger setters for each component by looping through properties
// and resseting component value.
// TODO: This is a hack and should implemented better, e.g. with
// value handlers.
var properties = types[this._type];
for (var i = 0, l = properties.length; i < l; i++)
this[properties[i]] = this._components[i];
if (this._read)
this._read = read;
},
_serialize: function(options) {
// We can ommit the type for gray and rgb:
return /^(gray|rgb)$/.test(this._type)
? this._components
: [this._type].concat(this._components);
_serialize: function(options, dictionary) {
return Base.serialize(
// We can ommit the type for gray and rgb:
/^(gray|rgb)$/.test(this._type)
? this._components
: [this._type].concat(this._components),
options, true, dictionary);
},
/**
@ -477,8 +519,53 @@ var Color = this.Color = Base.extend(new function() {
return css;
},
toCanvasStyle: function() {
return this.toCss();
toCanvasStyle: function(ctx) {
if (this._type !== 'gradient')
return this.toCss();
// Gradient code form here onwards, incudling caching
if (this._canvasGradient)
return this._canvasGradient;
var components = this._components,
gradient = components[0],
stops = gradient._stops,
origin = components[1],
destination = components[2],
canvasGradient;
if (gradient._radial) {
var radius = destination.getDistance(origin),
hilite = components[3];
if (hilite) {
var vector = hilite.subtract(origin);
if (vector.getLength() > radius)
hilite = origin.add(vector.normalize(radius - 0.1));
}
var start = hilite || origin;
canvasGradient = ctx.createRadialGradient(start.x, start.y,
0, origin.x, origin.y, radius);
} else {
canvasGradient = ctx.createLinearGradient(origin.x, origin.y,
destination.x, destination.y);
}
for (var i = 0, l = stops.length; i < l; i++) {
var stop = stops[i];
canvasGradient.addColorStop(stop._rampPoint, stop._color.toCss());
}
return this._canvasGradient = canvasGradient;
},
/**
* Transform the gradient color by the specified matrix.
*
* @param {Matrix} matrix the matrix to transform the gradient color by
*/
transform: function(matrix) {
if (this._type === 'gradient') {
var components = this._components;
for (var i = 1, l = components.length; i < l; i++) {
var point = components[i];
matrix._transformPoint(point, point, true);
}
}
},
/**
@ -613,6 +700,16 @@ var Color = this.Color = Base.extend(new function() {
color._type = type;
color._components = components;
color._alpha = alpha;
if (type === 'gradient') {
// Make sure gradients always have an id
color._id = ++Base._uid;
// Clone all points:
for (var i = 1, l = components.length; i < l; i++) {
var point = components[i];
if (point)
components[i] = point.clone();
}
}
return color;
},
@ -626,16 +723,18 @@ var Color = this.Color = Base.extend(new function() {
// Expose RgbColor, RGBColor, etc. constructors for backward compatibility.
Base.each(Color._types, function(properties, type) {
this[Base.capitalize(type) + 'Color'] = this[type.toUpperCase() + 'Color'] =
var ctor = this[Base.capitalize(type) + 'Color'] =
function(arg) {
var argType = arg != null && typeof arg,
components = argType === 'number'
? arguments
: argType === 'object' && arg.length != null
? arg
: null;
components = argType === 'object' && arg.length != null
? arg
: argType === 'string'
? null
: arguments;
return components
? new Color(type, components)
: new Color(arg);
};
if (!/^(gray|gradient)$/.test(type))
this[type.toUpperCase() + 'Color'] = ctor;
}, this);

View file

@ -29,7 +29,7 @@ var Gradient = this.Gradient = Base.extend(/** @lends Gradient# */{
_serialize: function(options, dictionary) {
return dictionary.add(this, function() {
return Base.serialize([this._type, this._stops],
return Base.serialize([this._stops, this._radial],
options, true, dictionary);
});
},

View file

@ -16,7 +16,6 @@
* @class The GradientColor object.
*/
var GradientColor = this.GradientColor = Color.extend(/** @lends GradientColor# */{
_type: 'gradient',
/**
* Creates a gradient color object.
@ -45,7 +44,7 @@ var GradientColor = this.GradientColor = Color.extend(/** @lends GradientColor#
*
* // Have the gradient color run between the topLeft and
* // bottomRight points we defined earlier:
* var gradientColor = new GradientColor(gradient, topLeft, bottomRight);
* var gradientColor = new Color(gradient, topLeft, bottomRight);
*
* // Set the fill color of the path to the gradient color:
* path.fillColor = gradientColor;
@ -77,7 +76,7 @@ var GradientColor = this.GradientColor = Color.extend(/** @lends GradientColor#
* var to = path.position + [80, 0];
*
* // Create the gradient color:
* var gradientColor = new GradientColor(gradient, from, to);
* var gradientColor = new Color(gradient, from, to);
*
* // Set the fill color of the path to the gradient color:
* path.fillColor = gradientColor;
@ -145,7 +144,7 @@ var GradientColor = this.GradientColor = Color.extend(/** @lends GradientColor#
* // to the bottom right point of the view:
* var from = view.bounds.topLeft;
* var to = view.bounds.bottomRight;
* var gradientColor = new GradientColor(gradient, from, to);
* var gradientColor = new Color(gradient, from, to);
* path.fillColor = gradientColor;
*
* function onMouseMove(event) {
@ -185,7 +184,7 @@ var GradientColor = this.GradientColor = Color.extend(/** @lends GradientColor#
* var gradient = new Gradient(['yellow', 'red', 'black'], true);
* var from = view.center;
* var to = view.bounds.bottomRight;
* var gradientColor = new GradientColor(gradient, from, to);
* var gradientColor = new Color(gradient, from, to);
* path.fillColor = gradientColor;
*
* function onMouseMove(event) {
@ -221,7 +220,7 @@ var GradientColor = this.GradientColor = Color.extend(/** @lends GradientColor#
* var gradient = new Gradient(['yellow', 'red', 'black'], true);
* var from = path.position;
* var to = path.bounds.rightCenter;
* var gradientColor = new GradientColor(gradient, from, to);
* var gradientColor = new Color(gradient, from, to);
*
* path.fillColor = gradientColor;
*

View file

@ -54,7 +54,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
},
_serialize: function(options, dictionary) {
return Base.serialize([this._color, this._rampPoint], options, false,
return Base.serialize([this._color, this._rampPoint], options, true,
dictionary);
},
@ -92,7 +92,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
* var gradient = new Gradient(colors, true);
* var from = path.position;
* var to = path.bounds.rightCenter;
* var gradientColor = new GradientColor(gradient, from, to);
* var gradientColor = new Color(gradient, from, to);
*
* path.fillColor = gradientColor;
*
@ -141,7 +141,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
* // to the right center of its bounding rectangle:
* var from = path.position;
* var to = path.bounds.rightCenter;
* var gradientColor = new GradientColor(gradient, from, to);
* var gradientColor = new Color(gradient, from, to);
* path.fillColor = gradientColor;
*
* // This function is called each frame of the animation:

View file

@ -290,7 +290,12 @@ this.Base = Base.inject(/** @lends Base# */{
ref = this.references[id];
if (!ref) {
this.length++;
this.definitions[id] = create.call(item);
var res = create.call(item);
// Also automatically insert class for dictionary
// entries.
if (item._class && res[0] !== item._class)
res.unshift(item._class);
this.definitions[id] = res;
ref = this.references[id] = [id];
}
return ref;
@ -300,8 +305,8 @@ this.Base = Base.inject(/** @lends Base# */{
if (obj && obj._serialize) {
res = obj._serialize(options, dictionary);
// If we don't serialize to compact form (meaning no type
// identifier), see if _serialize didn't already add the type,
// e.g. for types that do not support compact form.
// identifier), see if _serialize didn't already add the class,
// e.g. for classes that do not support compact form.
if (obj._class && !compact && res[0] !== obj._class)
res.unshift(obj._class);
} else if (Array.isArray(obj)) {

View file

@ -87,7 +87,6 @@ var paper = new function() {
/*#*/ include('style/CharacterStyle.js');
/*#*/ include('color/Color.js');
/*#*/ include('color/GradientColor.js');
/*#*/ include('color/Gradient.js');
/*#*/ include('color/GradientStop.js');

View file

@ -341,12 +341,11 @@ new function() {
// TODO: Implement gradient merging in SvgImport
var gradientNode = getDefinition(color);
if (!gradientNode) {
var gradient = color.gradient,
var gradient = color.getGradient(),
radial = gradient._radial,
matrix = item._gradientMatrix,
origin = color._origin.transform(matrix),
destination = color._destination.transform(matrix),
highlight = color._hilite && color._hilite.transform(matrix),
origin = color.getOrigin().transform(matrix),
destination = color.getDestination().transform(matrix),
attrs;
if (radial) {
attrs = {
@ -354,7 +353,9 @@ new function() {
cy: origin.y,
r: origin.getDistance(destination)
};
var highlight = color.getHilite();
if (highlight) {
highlight = highlight.transform(matrix);
attrs.fx = highlight.x;
attrs.fy = highlight.y;
}

View file

@ -156,7 +156,7 @@ new function() {
// We don't return the GradientColor, since we only need a reference to
// it in definitions, which is created in applyAttributes()
applyAttributes(
new GradientColor(gradient, origin, destination, highlight), node);
new Color(gradient, origin, destination, highlight), node);
return null;
}

View file

@ -54,7 +54,7 @@ test('Path#clone()', function() {
test('Path#clone() with GradientColor', function() {
var colors = ['red', 'green', 'black'];
var gradient = new Gradient(colors, true);
var color = new GradientColor(gradient, [0, 0], [20, 20], [10, 10]);
var color = new Color(gradient, [0, 0], [20, 20], [10, 10]);
var path = new Path([10, 20], [30, 40]);
path.fillColor = color;

View file

@ -61,7 +61,7 @@ test('Gradients', function() {
var gradient = new Gradient(['yellow', 'red', 'black'], true);
var from = path.position;
var to = path.bounds.rightCenter;
var gradientColor = new GradientColor(gradient, from, to);
var gradientColor = new Color(gradient, from, to);
path.fillColor = gradientColor;
path.strokeColor = 'black';
testExportImportJson(paper.project);