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 gradient = new Gradient(['yellow', 'red', 'black'], true);
var from = path.position; var from = path.position;
var to = path.bounds.rightCenter; var to = path.bounds.rightCenter;
var gradientColor = new GradientColor(gradient, from, to); var gradientColor = new Color(gradient, from, to);
path.fillColor = gradientColor; path.fillColor = gradientColor;
path.strokeColor = 'black'; path.strokeColor = 'black';
window._json = project.exportJson(); window._json = project.exportJson();

View file

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

View file

@ -24,7 +24,7 @@
var path = new Path.Rectangle(view.bounds); var path = new Path.Rectangle(view.bounds);
var gradient = new Gradient(colors, true); var gradient = new Gradient(colors, true);
var radius = Math.max(view.size.width, view.size.height) * 0.75; 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 gradientColor = path.fillColor;
var mouseDown = false, var mouseDown = false,

View file

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

View file

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

View file

@ -47,7 +47,8 @@ var Color = this.Color = Base.extend(new function() {
gray: ['gray'], gray: ['gray'],
rgb: ['red', 'green', 'blue'], rgb: ['red', 'green', 'blue'],
hsb: ['hue', 'saturation', 'brightness'], hsb: ['hue', 'saturation', 'brightness'],
hsl: ['hue', 'saturation', 'lightness'] hsl: ['hue', 'saturation', 'lightness'],
gradient: ['gradient', 'origin', 'destination', 'hilite']
}; };
var colorCache = {}, var colorCache = {},
@ -192,7 +193,18 @@ var Color = this.Color = Base.extend(new function() {
'gray-hsl': function(g) { 'gray-hsl': function(g) {
// TODO: Is lightness really the same as brightness for gray? // TODO: Is lightness really the same as brightness for gray?
return [0, 0, g]; 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 // 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. // its component.
return Base.each(types, function(properties, type) { return Base.each(types, function(properties, type) {
Base.each(properties, function(name, index) { 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 // Both hue and saturation have overlapping properties between
// hsb and hsl. Handle this here separately, by testing for // hsb and hsl. Handle this here separately, by testing for
// overlaps and skipping conversion if the type is /hs[bl]/ // overlaps and skipping conversion if the type is /hs[bl]/
hasOverlap = /^(hue|saturation)$/.test(name), hasOverlap = /^(hue|saturation)$/.test(name);
part = Base.capitalize(name);
this['get' + part] = function() { this['get' + part] = function() {
return this._type === type return this._type === type
@ -222,11 +237,20 @@ var Color = this.Color = Base.extend(new function() {
this._components = this._convert(type); this._components = this._convert(type);
this._type = type; this._type = type;
} }
this._components[index] = isHue if (!isGradient)
value = isHue
// Keep negative values within modulo 360 too: // Keep negative values within modulo 360 too:
? ((value % 360) + 360) % 360 ? ((value % 360) + 360) % 360
// All other values are 0..1 : isPoint
: Math.min(Math.max(value, 0), 1); ? 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); }, this);
}, /** @lends Color# */{ }, /** @lends Color# */{
@ -242,6 +266,10 @@ var Color = this.Color = Base.extend(new function() {
args = arguments, args = arguments,
read = 0, read = 0,
type; type;
if (Array.isArray(arg)) {
args = arg;
arg = args[0];
}
// Try type arg first // Try type arg first
if (typeof arg === 'string' && arg in types) { if (typeof arg === 'string' && arg in types) {
type = arg; type = arg;
@ -286,17 +314,19 @@ var Color = this.Color = Base.extend(new function() {
components = arg._components.slice(); components = arg._components.slice();
alpha = arg._alpha; alpha = arg._alpha;
} else if (arg._class === 'Gradient') { } else if (arg._class === 'Gradient') {
// TODO: Construct gradient
type = 'gradient'; type = 'gradient';
components = slice.call(args);
} else { } else {
// Determine type by presence of object property names // Determine type by presence of object property names
type = 'hue' in arg type = 'hue' in arg
? 'lightness' in arg ? 'lightness' in arg
? 'hsl' ? 'hsl'
: 'hsb' : 'hsb'
: 'gray' in arg : 'gradient' in arg
? 'gray' ? 'gradient'
: 'rgb'; : 'gray' in arg
? 'gray'
: 'rgb';
var properties = types[type]; var properties = types[type];
components = []; components = [];
for (var i = 0, l = properties.length; i < l; i++) 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) if (this._read && type)
read = 1; read = 1;
} }
// Define this GradientColor's unique id.
if (type === 'gradient')
this._id = ++Base._uid;
// Default fallbacks: rgb, black // Default fallbacks: rgb, black
this._type = type || 'rgb'; this._type = type || 'rgb';
this._components = components || (type === 'gray' ? [0] : [0, 0, 0]); this._components = components || (type === 'gray' ? [0] : [0, 0, 0]);
this._alpha = alpha; 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) if (this._read)
this._read = read; this._read = read;
}, },
_serialize: function(options) { _serialize: function(options, dictionary) {
// We can ommit the type for gray and rgb: return Base.serialize(
return /^(gray|rgb)$/.test(this._type) // We can ommit the type for gray and rgb:
? this._components /^(gray|rgb)$/.test(this._type)
: [this._type].concat(this._components); ? this._components
: [this._type].concat(this._components),
options, true, dictionary);
}, },
/** /**
@ -477,8 +519,53 @@ var Color = this.Color = Base.extend(new function() {
return css; return css;
}, },
toCanvasStyle: function() { toCanvasStyle: function(ctx) {
return this.toCss(); 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._type = type;
color._components = components; color._components = components;
color._alpha = alpha; 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; return color;
}, },
@ -626,16 +723,18 @@ var Color = this.Color = Base.extend(new function() {
// Expose RgbColor, RGBColor, etc. constructors for backward compatibility. // Expose RgbColor, RGBColor, etc. constructors for backward compatibility.
Base.each(Color._types, function(properties, type) { 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) { function(arg) {
var argType = arg != null && typeof arg, var argType = arg != null && typeof arg,
components = argType === 'number' components = argType === 'object' && arg.length != null
? arguments ? arg
: argType === 'object' && arg.length != null : argType === 'string'
? arg ? null
: null; : arguments;
return components return components
? new Color(type, components) ? new Color(type, components)
: new Color(arg); : new Color(arg);
}; };
if (!/^(gray|gradient)$/.test(type))
this[type.toUpperCase() + 'Color'] = ctor;
}, this); }, this);

View file

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

View file

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

View file

@ -54,7 +54,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
}, },
_serialize: function(options, dictionary) { _serialize: function(options, dictionary) {
return Base.serialize([this._color, this._rampPoint], options, false, return Base.serialize([this._color, this._rampPoint], options, true,
dictionary); dictionary);
}, },
@ -92,7 +92,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
* var gradient = new Gradient(colors, true); * var gradient = new Gradient(colors, true);
* var from = path.position; * var from = path.position;
* var to = path.bounds.rightCenter; * var to = path.bounds.rightCenter;
* var gradientColor = new GradientColor(gradient, from, to); * var gradientColor = new Color(gradient, from, to);
* *
* path.fillColor = gradientColor; * path.fillColor = gradientColor;
* *
@ -141,7 +141,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
* // to the right center of its bounding rectangle: * // to the right center of its bounding rectangle:
* var from = path.position; * var from = path.position;
* var to = path.bounds.rightCenter; * var to = path.bounds.rightCenter;
* var gradientColor = new GradientColor(gradient, from, to); * var gradientColor = new Color(gradient, from, to);
* path.fillColor = gradientColor; * path.fillColor = gradientColor;
* *
* // This function is called each frame of the animation: * // 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]; ref = this.references[id];
if (!ref) { if (!ref) {
this.length++; 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]; ref = this.references[id] = [id];
} }
return ref; return ref;
@ -300,8 +305,8 @@ this.Base = Base.inject(/** @lends Base# */{
if (obj && obj._serialize) { if (obj && obj._serialize) {
res = obj._serialize(options, dictionary); res = obj._serialize(options, dictionary);
// If we don't serialize to compact form (meaning no type // If we don't serialize to compact form (meaning no type
// identifier), see if _serialize didn't already add the type, // identifier), see if _serialize didn't already add the class,
// e.g. for types that do not support compact form. // e.g. for classes that do not support compact form.
if (obj._class && !compact && res[0] !== obj._class) if (obj._class && !compact && res[0] !== obj._class)
res.unshift(obj._class); res.unshift(obj._class);
} else if (Array.isArray(obj)) { } else if (Array.isArray(obj)) {

View file

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

View file

@ -341,12 +341,11 @@ new function() {
// TODO: Implement gradient merging in SvgImport // TODO: Implement gradient merging in SvgImport
var gradientNode = getDefinition(color); var gradientNode = getDefinition(color);
if (!gradientNode) { if (!gradientNode) {
var gradient = color.gradient, var gradient = color.getGradient(),
radial = gradient._radial, radial = gradient._radial,
matrix = item._gradientMatrix, matrix = item._gradientMatrix,
origin = color._origin.transform(matrix), origin = color.getOrigin().transform(matrix),
destination = color._destination.transform(matrix), destination = color.getDestination().transform(matrix),
highlight = color._hilite && color._hilite.transform(matrix),
attrs; attrs;
if (radial) { if (radial) {
attrs = { attrs = {
@ -354,7 +353,9 @@ new function() {
cy: origin.y, cy: origin.y,
r: origin.getDistance(destination) r: origin.getDistance(destination)
}; };
var highlight = color.getHilite();
if (highlight) { if (highlight) {
highlight = highlight.transform(matrix);
attrs.fx = highlight.x; attrs.fx = highlight.x;
attrs.fy = highlight.y; 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 // We don't return the GradientColor, since we only need a reference to
// it in definitions, which is created in applyAttributes() // it in definitions, which is created in applyAttributes()
applyAttributes( applyAttributes(
new GradientColor(gradient, origin, destination, highlight), node); new Color(gradient, origin, destination, highlight), node);
return null; return null;
} }

View file

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

View file

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