diff --git a/examples/JSON/Gradients.html b/examples/JSON/Gradients.html
index b7882742..ce2c961e 100644
--- a/examples/JSON/Gradients.html
+++ b/examples/JSON/Gradients.html
@@ -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();
diff --git a/examples/Paperjs.org/BouncingBalls.html b/examples/Paperjs.org/BouncingBalls.html
index 3d2a69f4..2b306c05 100644
--- a/examples/Paperjs.org/BouncingBalls.html
+++ b/examples/Paperjs.org/BouncingBalls.html
@@ -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
});
}
diff --git a/examples/Paperjs.org/RadialRainbows.html b/examples/Paperjs.org/RadialRainbows.html
index 51874024..a480b7d1 100644
--- a/examples/Paperjs.org/RadialRainbows.html
+++ b/examples/Paperjs.org/RadialRainbows.html
@@ -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,
diff --git a/examples/SVG Export/Gradients.html b/examples/SVG Export/Gradients.html
index 85b3dd98..dbde5c82 100644
--- a/examples/SVG Export/Gradients.html
+++ b/examples/SVG Export/Gradients.html
@@ -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'
});
diff --git a/examples/Scripts/HslColor.html b/examples/Scripts/HslColor.html
index 38a4a403..b3b53cb0 100644
--- a/examples/Scripts/HslColor.html
+++ b/examples/Scripts/HslColor.html
@@ -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;
}
}
diff --git a/src/color/Color.js b/src/color/Color.js
index ae70be8b..50cc931f 100644
--- a/src/color/Color.js
+++ b/src/color/Color.js
@@ -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);
diff --git a/src/color/Gradient.js b/src/color/Gradient.js
index 07a11646..b2931c26 100644
--- a/src/color/Gradient.js
+++ b/src/color/Gradient.js
@@ -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);
});
},
diff --git a/src/color/GradientColor.js b/src/color/GradientColor.js
index 6edc90ef..4572670a 100644
--- a/src/color/GradientColor.js
+++ b/src/color/GradientColor.js
@@ -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;
*
diff --git a/src/color/GradientStop.js b/src/color/GradientStop.js
index a68d0c88..91aa1dd9 100644
--- a/src/color/GradientStop.js
+++ b/src/color/GradientStop.js
@@ -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:
diff --git a/src/core/Base.js b/src/core/Base.js
index 3c384085..c53ae2e1 100644
--- a/src/core/Base.js
+++ b/src/core/Base.js
@@ -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)) {
diff --git a/src/paper.js b/src/paper.js
index 871887d3..fc500e0d 100644
--- a/src/paper.js
+++ b/src/paper.js
@@ -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');
diff --git a/src/svg/SvgExport.js b/src/svg/SvgExport.js
index d92c48a8..a8f6556c 100644
--- a/src/svg/SvgExport.js
+++ b/src/svg/SvgExport.js
@@ -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;
}
diff --git a/src/svg/SvgImport.js b/src/svg/SvgImport.js
index 0dc9edcb..fa90d84f 100644
--- a/src/svg/SvgImport.js
+++ b/src/svg/SvgImport.js
@@ -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;
}
diff --git a/test/tests/Item_Cloning.js b/test/tests/Item_Cloning.js
index 7310592b..3ca3218c 100644
--- a/test/tests/Item_Cloning.js
+++ b/test/tests/Item_Cloning.js
@@ -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;
diff --git a/test/tests/JSON.js b/test/tests/JSON.js
index ab7d8943..9f01a04d 100644
--- a/test/tests/JSON.js
+++ b/test/tests/JSON.js
@@ -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);