mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-05 20:32:00 -05:00
Merge branch 'master' of github.com:paperjs/paper.js
This commit is contained in:
commit
59a55f7574
27 changed files with 1896 additions and 1027 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/files/
|
/files/
|
||||||
/patches/
|
/patches/
|
||||||
|
/node_modules/
|
||||||
|
|
2405
dist/paper.js
vendored
2405
dist/paper.js
vendored
File diff suppressed because it is too large
Load diff
34
examples/Scripts/Resize.html
Normal file
34
examples/Scripts/Resize.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>Resize</title>
|
||||||
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<script type="text/javascript" src="../../dist/paper.js"></script>
|
||||||
|
<script type="text/paperscript" canvas="canvas">
|
||||||
|
var redPath = new Path.Circle(view.center, 10);
|
||||||
|
redPath.fillColor = 'red';
|
||||||
|
|
||||||
|
var whitePath = new Path.Circle(view.center, 10);
|
||||||
|
whitePath.fillColor = 'white';
|
||||||
|
|
||||||
|
var text = new PointText();
|
||||||
|
text.content = 'Resize your window';
|
||||||
|
text.justification = 'center';
|
||||||
|
|
||||||
|
function onResize(event) {
|
||||||
|
// Resize the red circle to fill the bounds of the view:
|
||||||
|
redPath.fitBounds(view.bounds, true);
|
||||||
|
|
||||||
|
// Resize the white circle to fit within the bounds of the view:
|
||||||
|
whitePath.fitBounds(view.bounds, false);
|
||||||
|
|
||||||
|
// Move the text to the center of the view:
|
||||||
|
text.position = view.bounds.center;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="canvas" resize></canvas>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
index.js
1
index.js
|
@ -1 +0,0 @@
|
||||||
module.exports = require('./src/loadNode.js');
|
|
|
@ -1,9 +1,7 @@
|
||||||
var fs = require('fs'),
|
var fs = require('fs'),
|
||||||
vm = require('vm'),
|
vm = require('vm'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
// Have HTMLCanvasElement reference Canvas too, so we do not handle browser
|
Canvas = require('canvas');
|
||||||
// and server differently in some places of our code.
|
|
||||||
Canvas = HTMLCanvasElement =require('canvas');
|
|
||||||
|
|
||||||
__dirname = path.resolve(__dirname, '../src/');
|
__dirname = path.resolve(__dirname, '../src/');
|
||||||
|
|
||||||
|
@ -16,6 +14,7 @@ var context = vm.createContext({
|
||||||
fs: fs,
|
fs: fs,
|
||||||
// Node Canvas library: https://github.com/learnboost/node-canvas
|
// Node Canvas library: https://github.com/learnboost/node-canvas
|
||||||
Canvas: Canvas,
|
Canvas: Canvas,
|
||||||
|
HTMLCanvasElement: Canvas,
|
||||||
Image: Canvas.Image,
|
Image: Canvas.Image,
|
||||||
// Copy over global variables:
|
// Copy over global variables:
|
||||||
console: console,
|
console: console,
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
"keywords": ["canvas", "graphic", "graphics", "vector", "paper.js"],
|
"keywords": ["canvas", "graphic", "graphics", "vector", "paper.js"],
|
||||||
"repository": "git://github.com/paperjs/paper.js/",
|
"repository": "git://github.com/paperjs/paper.js/",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"canvas": "0.7.0"
|
"canvas": ">= 0.7.0"
|
||||||
},
|
},
|
||||||
"engines": { "node": ">= 0.4.0 && < 0.6.0" },
|
"engines": { "node": ">= 0.4.0" },
|
||||||
"main": "./node.js/index.js"
|
"main": "./node.js/index.js"
|
||||||
}
|
}
|
|
@ -428,7 +428,7 @@ var Point = this.Point = Base.extend(/** @lends Point# */{
|
||||||
// squared length should be returned. Hide it so it produces a bean
|
// squared length should be returned. Hide it so it produces a bean
|
||||||
// property called #length.
|
// property called #length.
|
||||||
var l = this.x * this.x + this.y * this.y;
|
var l = this.x * this.x + this.y * this.y;
|
||||||
return arguments[0] ? l : Math.sqrt(l);
|
return (arguments.length && arguments[0]) ? l : Math.sqrt(l);
|
||||||
},
|
},
|
||||||
|
|
||||||
setLength: function(length) {
|
setLength: function(length) {
|
||||||
|
|
|
@ -33,6 +33,39 @@ var Gradient = this.Gradient = Base.extend(/** @lends Gradient# */{
|
||||||
this.type = type || 'linear';
|
this.type = type || 'linear';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by various setters whenever a gradient value changes
|
||||||
|
*/
|
||||||
|
_changed: function() {
|
||||||
|
// Loop through the gradient-colors that use this gradient and notify
|
||||||
|
// them, so they can notify the items they belong to.
|
||||||
|
for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
|
||||||
|
this._owners[i]._changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by GradientColor#initialize
|
||||||
|
* This is required to pass on _changed() notifications to the _owners.
|
||||||
|
*/
|
||||||
|
_addOwner: function(color) {
|
||||||
|
if (!this._owners)
|
||||||
|
this._owners = [];
|
||||||
|
this._owners.push(color);
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO: Where and when should this be called:
|
||||||
|
/**
|
||||||
|
* Called by GradientColor whenever this gradient stops being used.
|
||||||
|
*/
|
||||||
|
_removeOwner: function(color) {
|
||||||
|
var index = this._owners ? this._owners.indexOf(color) : -1;
|
||||||
|
if (index != -1) {
|
||||||
|
this._owners.splice(index, 1);
|
||||||
|
if (this._owners.length == 0)
|
||||||
|
delete this._owners;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {Gradient} a copy of the gradient
|
* @return {Gradient} a copy of the gradient
|
||||||
*/
|
*/
|
||||||
|
@ -54,6 +87,13 @@ var Gradient = this.Gradient = Base.extend(/** @lends Gradient# */{
|
||||||
},
|
},
|
||||||
|
|
||||||
setStops: function(stops) {
|
setStops: function(stops) {
|
||||||
|
// If this gradient already contains stops, first remove
|
||||||
|
// this gradient as their owner.
|
||||||
|
if (this.stops) {
|
||||||
|
for (var i = 0, l = this._stops.length; i < l; i++) {
|
||||||
|
this._stops[i]._removeOwner(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (stops.length < 2)
|
if (stops.length < 2)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Gradient stop list needs to contain at least two stops.');
|
'Gradient stop list needs to contain at least two stops.');
|
||||||
|
@ -61,9 +101,11 @@ var Gradient = this.Gradient = Base.extend(/** @lends Gradient# */{
|
||||||
// Now reassign ramp points if they were not specified.
|
// Now reassign ramp points if they were not specified.
|
||||||
for (var i = 0, l = this._stops.length; i < l; i++) {
|
for (var i = 0, l = this._stops.length; i < l; i++) {
|
||||||
var stop = this._stops[i];
|
var stop = this._stops[i];
|
||||||
|
stop._addOwner(this);
|
||||||
if (stop._defaultRamp)
|
if (stop._defaultRamp)
|
||||||
stop.setRampPoint(i / (l - 1));
|
stop.setRampPoint(i / (l - 1));
|
||||||
}
|
}
|
||||||
|
this._changed();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -84,6 +84,7 @@ var GradientColor = this.GradientColor = Color.extend(/** @lends GradientColor#
|
||||||
*/
|
*/
|
||||||
initialize: function(gradient, origin, destination, hilite) {
|
initialize: function(gradient, origin, destination, hilite) {
|
||||||
this.gradient = gradient || new Gradient();
|
this.gradient = gradient || new Gradient();
|
||||||
|
this.gradient._addOwner(this);
|
||||||
this.setOrigin(origin);
|
this.setOrigin(origin);
|
||||||
this.setDestination(destination);
|
this.setDestination(destination);
|
||||||
if (hilite)
|
if (hilite)
|
||||||
|
|
|
@ -44,6 +44,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO: Do we really need to also clone the color here?
|
||||||
/**
|
/**
|
||||||
* @return {GradientColor} a copy of the gradient-stop
|
* @return {GradientColor} a copy of the gradient-stop
|
||||||
*/
|
*/
|
||||||
|
@ -51,6 +52,40 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
|
||||||
return new GradientStop(this._color.clone(), this._rampPoint);
|
return new GradientStop(this._color.clone(), this._rampPoint);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by various setters whenever a value changes
|
||||||
|
*/
|
||||||
|
_changed: function() {
|
||||||
|
// Loop through the gradients that use this stop and notify them about
|
||||||
|
// the change, so they can notify their gradient colors, which in turn
|
||||||
|
// will notify the items they are used in:
|
||||||
|
for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
|
||||||
|
this._owners[i]._changed(Change.STYLE);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by Gradient whenever this stop is used. This is required to pass
|
||||||
|
* on _changed() notifications to the _owners.
|
||||||
|
*/
|
||||||
|
_addOwner: function(gradient) {
|
||||||
|
if (!this._owners)
|
||||||
|
this._owners = [];
|
||||||
|
this._owners.push(gradient);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by Gradient whenever this GradientStop is no longer used by it.
|
||||||
|
*/
|
||||||
|
_removeOwner: function(gradient) {
|
||||||
|
var index = this._owners ? this._owners.indexOf(gradient) : -1;
|
||||||
|
if (index != -1) {
|
||||||
|
this._owners.splice(index, 1);
|
||||||
|
if (this._owners.length == 0)
|
||||||
|
delete this._owners;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ramp-point of the gradient stop as a value between {@code 0} and
|
* The ramp-point of the gradient stop as a value between {@code 0} and
|
||||||
* {@code 1}.
|
* {@code 1}.
|
||||||
|
@ -92,6 +127,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
|
||||||
setRampPoint: function(rampPoint) {
|
setRampPoint: function(rampPoint) {
|
||||||
this._defaultRamp = rampPoint == null;
|
this._defaultRamp = rampPoint == null;
|
||||||
this._rampPoint = rampPoint || 0;
|
this._rampPoint = rampPoint || 0;
|
||||||
|
this._changed();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,7 +165,13 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
|
||||||
},
|
},
|
||||||
|
|
||||||
setColor: function(color) {
|
setColor: function(color) {
|
||||||
|
// If the stop already contained a color,
|
||||||
|
// remove it as an owner:
|
||||||
|
if (this._color)
|
||||||
|
this._color._removeOwner(this);
|
||||||
this._color = Color.read(arguments);
|
this._color = Color.read(arguments);
|
||||||
|
this._color._addOwner(this);
|
||||||
|
this._changed();
|
||||||
},
|
},
|
||||||
|
|
||||||
equals: function(stop) {
|
equals: function(stop) {
|
||||||
|
|
|
@ -155,7 +155,7 @@ var PaperScript = this.PaperScript = new function() {
|
||||||
function evaluate(code, scope) {
|
function evaluate(code, scope) {
|
||||||
// Set currently active scope.
|
// Set currently active scope.
|
||||||
paper = scope;
|
paper = scope;
|
||||||
var view = scope.project.view,
|
var view = scope.project && scope.project.view,
|
||||||
res;
|
res;
|
||||||
// Define variables for potential handlers, so eval() calls below to
|
// Define variables for potential handlers, so eval() calls below to
|
||||||
// fetch their values do not require try-catch around them.
|
// fetch their values do not require try-catch around them.
|
||||||
|
|
|
@ -120,8 +120,11 @@ var Group = this.Group = Item.extend(/** @lends Group# */{
|
||||||
|
|
||||||
draw: function(ctx, param) {
|
draw: function(ctx, param) {
|
||||||
var clipItem = this._getClipItem();
|
var clipItem = this._getClipItem();
|
||||||
if (clipItem)
|
if (clipItem) {
|
||||||
|
param.clipping = true;
|
||||||
Item.draw(clipItem, ctx, param);
|
Item.draw(clipItem, ctx, param);
|
||||||
|
delete param.clipping;
|
||||||
|
}
|
||||||
for (var i = 0, l = this._children.length; i < l; i++) {
|
for (var i = 0, l = this._children.length; i < l; i++) {
|
||||||
var item = this._children[i];
|
var item = this._children[i];
|
||||||
if (item != clipItem)
|
if (item != clipItem)
|
||||||
|
|
|
@ -209,7 +209,7 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
|
||||||
if (this._name)
|
if (this._name)
|
||||||
this._removeFromNamed();
|
this._removeFromNamed();
|
||||||
this._name = name || undefined;
|
this._name = name || undefined;
|
||||||
if (name) {
|
if (name && this._parent) {
|
||||||
var children = this._parent._children,
|
var children = this._parent._children,
|
||||||
namedChildren = this._parent._namedChildren;
|
namedChildren = this._parent._namedChildren;
|
||||||
(namedChildren[name] = namedChildren[name] || []).push(this);
|
(namedChildren[name] = namedChildren[name] || []).push(this);
|
||||||
|
@ -1169,8 +1169,10 @@ function(name) {
|
||||||
* @return {Boolean} {@true it was inserted}
|
* @return {Boolean} {@true it was inserted}
|
||||||
*/
|
*/
|
||||||
insertAbove: function(item) {
|
insertAbove: function(item) {
|
||||||
return item._parent && item._parent.insertChild(
|
var index = item._index;
|
||||||
item._index + 1, this);
|
if (item._parent == this._parent && index < this._index)
|
||||||
|
index++;
|
||||||
|
return item._parent.insertChild(index, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1180,8 +1182,10 @@ function(name) {
|
||||||
* @return {Boolean} {@true it was inserted}
|
* @return {Boolean} {@true it was inserted}
|
||||||
*/
|
*/
|
||||||
insertBelow: function(item) {
|
insertBelow: function(item) {
|
||||||
return item._parent && item._parent.insertChild(
|
var index = item._index;
|
||||||
item._index - 1, this);
|
if (item._parent == this._parent && index > this._index)
|
||||||
|
index--;
|
||||||
|
return item._parent.insertChild(index, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1957,7 +1961,6 @@ function(name) {
|
||||||
scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
|
scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
|
||||||
? rectangle.width / bounds.width
|
? rectangle.width / bounds.width
|
||||||
: rectangle.height / bounds.height,
|
: rectangle.height / bounds.height,
|
||||||
delta = rectangle.getCenter().subtract(bounds.getCenter()),
|
|
||||||
newBounds = new Rectangle(new Point(),
|
newBounds = new Rectangle(new Point(),
|
||||||
Size.create(bounds.width * scale, bounds.height * scale));
|
Size.create(bounds.width * scale, bounds.height * scale));
|
||||||
newBounds.setCenter(rectangle.getCenter());
|
newBounds.setCenter(rectangle.getCenter());
|
||||||
|
@ -2449,14 +2452,16 @@ function(name) {
|
||||||
// so we draw onto it, instead of the parentCtx
|
// so we draw onto it, instead of the parentCtx
|
||||||
ctx = tempCanvas.getContext('2d');
|
ctx = tempCanvas.getContext('2d');
|
||||||
}
|
}
|
||||||
ctx.save();
|
if (!param.clipping)
|
||||||
|
ctx.save();
|
||||||
// Translate the context so the topLeft of the item is at (0, 0)
|
// Translate the context so the topLeft of the item is at (0, 0)
|
||||||
// on the temporary canvas.
|
// on the temporary canvas.
|
||||||
if (tempCanvas)
|
if (tempCanvas)
|
||||||
ctx.translate(-itemOffset.x, -itemOffset.y);
|
ctx.translate(-itemOffset.x, -itemOffset.y);
|
||||||
item._matrix.applyToContext(ctx);
|
item._matrix.applyToContext(ctx);
|
||||||
item.draw(ctx, param);
|
item.draw(ctx, param);
|
||||||
ctx.restore();
|
if (!param.clipping)
|
||||||
|
ctx.restore();
|
||||||
// If we created a temporary canvas before, composite it onto the
|
// If we created a temporary canvas before, composite it onto the
|
||||||
// parent canvas:
|
// parent canvas:
|
||||||
if (tempCanvas) {
|
if (tempCanvas) {
|
||||||
|
|
|
@ -124,6 +124,9 @@ var paper = new function() {
|
||||||
/*#*/ include('util/CanvasProvider.js');
|
/*#*/ include('util/CanvasProvider.js');
|
||||||
/*#*/ include('util/Numerical.js');
|
/*#*/ include('util/Numerical.js');
|
||||||
/*#*/ include('util/BlendMode.js');
|
/*#*/ include('util/BlendMode.js');
|
||||||
|
/*#*/ if (options.version == 'dev') {
|
||||||
|
/*#*/ include('util/ProxyContext.js');
|
||||||
|
/*#*/ } // options.browser
|
||||||
|
|
||||||
/*#*/ include('core/PaperScript.js');
|
/*#*/ include('core/PaperScript.js');
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,12 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
|
||||||
delete this._length;
|
delete this._length;
|
||||||
// Clockwise state becomes undefined as soon as geometry changes.
|
// Clockwise state becomes undefined as soon as geometry changes.
|
||||||
delete this._clockwise;
|
delete this._clockwise;
|
||||||
|
// Curves are no longer valid
|
||||||
|
if (this._curves != null) {
|
||||||
|
for (var i = 0, l = this._curves.length; i < l; i++) {
|
||||||
|
this._curves[i]._changed(Change.GEOMETRY);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (flags & ChangeFlag.STROKE) {
|
} else if (flags & ChangeFlag.STROKE) {
|
||||||
// TODO: We could preserve the purely geometric bounds that are not
|
// TODO: We could preserve the purely geometric bounds that are not
|
||||||
// affected by stroke: _bounds.bounds and _bounds.handleBounds
|
// affected by stroke: _bounds.bounds and _bounds.handleBounds
|
||||||
|
@ -828,6 +834,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
|
||||||
var handleIn = segment._handleIn;
|
var handleIn = segment._handleIn;
|
||||||
segment._handleIn = segment._handleOut;
|
segment._handleIn = segment._handleOut;
|
||||||
segment._handleOut = handleIn;
|
segment._handleOut = handleIn;
|
||||||
|
segment._index = i;
|
||||||
}
|
}
|
||||||
// Flip clockwise state if it's defined
|
// Flip clockwise state if it's defined
|
||||||
if (this._clockwise !== undefined)
|
if (this._clockwise !== undefined)
|
||||||
|
@ -1450,6 +1457,9 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
|
||||||
drawSegments(ctx, this);
|
drawSegments(ctx, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._closed)
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
if (this._clipMask) {
|
if (this._clipMask) {
|
||||||
ctx.clip();
|
ctx.clip();
|
||||||
} else if (!param.compound && (fillColor || strokeColor)) {
|
} else if (!param.compound && (fillColor || strokeColor)) {
|
||||||
|
|
|
@ -140,7 +140,7 @@ var PathFitter = Base.extend({
|
||||||
c1 = C[1][0] + C[1][1];
|
c1 = C[1][0] + C[1][1];
|
||||||
if (Math.abs(c0) > epsilon) {
|
if (Math.abs(c0) > epsilon) {
|
||||||
alpha1 = alpha2 = X[0] / c0;
|
alpha1 = alpha2 = X[0] / c0;
|
||||||
} else if (Math.abs(c0) > epsilon) {
|
} else if (Math.abs(c1) > epsilon) {
|
||||||
alpha1 = alpha2 = X[1] / c1;
|
alpha1 = alpha2 = X[1] / c1;
|
||||||
} else {
|
} else {
|
||||||
// Handle below
|
// Handle below
|
||||||
|
|
|
@ -108,7 +108,7 @@ var TextItem = this.TextItem = Item.extend(/** @lends TextItem# */{
|
||||||
|
|
||||||
setCharacterStyle: function(style) {
|
setCharacterStyle: function(style) {
|
||||||
this.setStyle(style);
|
this.setStyle(style);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The paragraph style of the text item.
|
* The paragraph style of the text item.
|
||||||
|
|
|
@ -392,4 +392,66 @@ var Tool = this.Tool = PaperScopeItem.extend(Callback, /** @lends Tool# */{
|
||||||
// Return if a callback was called or not.
|
// Return if a callback was called or not.
|
||||||
return called;
|
return called;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* {@grouptitle Event Handling}
|
||||||
|
*
|
||||||
|
* Attach an event handler to the tool.
|
||||||
|
*
|
||||||
|
* @name Tool#attach
|
||||||
|
* @function
|
||||||
|
* @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
|
||||||
|
* 'keydown', 'keyup')} type the event type
|
||||||
|
* @param {Function} function The function to be called when the event
|
||||||
|
* occurs
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Attach one or more event handlers to the tool.
|
||||||
|
*
|
||||||
|
* @name Tool#attach^2
|
||||||
|
* @function
|
||||||
|
* @param {Object} param An object literal containing one or more of the
|
||||||
|
* following properties: {@code mousedown, mouseup, mousedrag, mousemove,
|
||||||
|
* keydown, keyup}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detach an event handler from the tool.
|
||||||
|
*
|
||||||
|
* @name Tool#detach
|
||||||
|
* @function
|
||||||
|
* @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
|
||||||
|
* 'keydown', 'keyup')} type the event type
|
||||||
|
* @param {Function} function The function to be detached
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Detach one or more event handlers from the tool.
|
||||||
|
*
|
||||||
|
* @name Tool#detach^2
|
||||||
|
* @function
|
||||||
|
* @param {Object} param An object literal containing one or more of the
|
||||||
|
* following properties: {@code mousedown, mouseup, mousedrag, mousemove,
|
||||||
|
* keydown, keyup}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire an event on the tool.
|
||||||
|
*
|
||||||
|
* @name Tool#fire
|
||||||
|
* @function
|
||||||
|
* @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
|
||||||
|
* 'keydown', 'keyup')} type the event type
|
||||||
|
* @param {Object} event An object literal containing properties describing
|
||||||
|
* the event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the tool has one or more event handlers of the specified type.
|
||||||
|
*
|
||||||
|
* @name Tool#responds
|
||||||
|
* @function
|
||||||
|
* @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
|
||||||
|
* 'keydown', 'keyup')} type the event type
|
||||||
|
* @return {Boolean} {@true if the tool has one or more event handlers of
|
||||||
|
* the specified type}
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
|
|
|
@ -395,6 +395,62 @@ var View = this.View = Base.extend(Callback, /** @lends View# */{
|
||||||
* @property
|
* @property
|
||||||
* @type Function
|
* @type Function
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* {@grouptitle Event Handling}
|
||||||
|
*
|
||||||
|
* Attach an event handler to the view.
|
||||||
|
*
|
||||||
|
* @name View#attach
|
||||||
|
* @function
|
||||||
|
* @param {String('frame', 'resize')} type the event type
|
||||||
|
* @param {Function} function The function to be called when the event
|
||||||
|
* occurs
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Attach one or more event handlers to the view.
|
||||||
|
*
|
||||||
|
* @name View#attach^2
|
||||||
|
* @function
|
||||||
|
* @param {Object} param An object literal containing one or more of the
|
||||||
|
* following properties: {@code frame, resize}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detach an event handler from the view.
|
||||||
|
*
|
||||||
|
* @name View#detach
|
||||||
|
* @function
|
||||||
|
* @param {String('frame', 'resize')} type the event type
|
||||||
|
* @param {Function} function The function to be detached
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Detach one or more event handlers from the view.
|
||||||
|
*
|
||||||
|
* @name View#detach^2
|
||||||
|
* @function
|
||||||
|
* @param {Object} param An object literal containing one or more of the
|
||||||
|
* following properties: {@code frame, resize}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire an event on the view.
|
||||||
|
*
|
||||||
|
* @name View#fire
|
||||||
|
* @function
|
||||||
|
* @param {String('frame', 'resize')} type the event type
|
||||||
|
* @param {Object} event An object literal containing properties describing
|
||||||
|
* the event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the view has one or more event handlers of the specified type.
|
||||||
|
*
|
||||||
|
* @name View#responds
|
||||||
|
* @function
|
||||||
|
* @param {String('frame', 'resize')} type the event type
|
||||||
|
* @return {Boolean} {@true if the view has one or more event handlers of
|
||||||
|
* the specified type}
|
||||||
|
*/
|
||||||
}, {
|
}, {
|
||||||
statics: {
|
statics: {
|
||||||
_views: [],
|
_views: [],
|
||||||
|
|
93
src/util/ProxyContext.js
Normal file
93
src/util/ProxyContext.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Paper.js
|
||||||
|
*
|
||||||
|
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
|
||||||
|
* based on Scriptographer.org and designed to be largely API compatible.
|
||||||
|
* http://paperjs.org/
|
||||||
|
* http://scriptographer.org/
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
||||||
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
||||||
|
*
|
||||||
|
* Distributed under the MIT license. See LICENSE file for details.
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name ProxyContext
|
||||||
|
*
|
||||||
|
* @class The ProxyContext is a helper class that helps Canvas debugging
|
||||||
|
* by logging all interactions with a 2D Canvas context.
|
||||||
|
*
|
||||||
|
* @classexample
|
||||||
|
* view._context = new ProxyContext(view._context);
|
||||||
|
*/
|
||||||
|
var ProxyContext = new function() {
|
||||||
|
var descriptions = [
|
||||||
|
'save()', 'restore()', 'scale(x,y)', 'rotate(angle)', 'translate(x,y)',
|
||||||
|
'transform(a,b,c,d,e,f)', 'setTransform(a,b,c,d,e,f)', 'globalAlpha',
|
||||||
|
'globalCompositeOperation', 'strokeStyle', 'fillStyle',
|
||||||
|
'createLinearGradient(x0,y0,x1,y1)',
|
||||||
|
'createRadialGradient(x0,y0,r0,x1,y1,r1)',
|
||||||
|
'createPattern(image,repetition)', 'lineWidth', 'lineCap', 'lineJoin',
|
||||||
|
'miterLimit', 'shadowOffsetX', 'shadowOffsetY', 'shadowBlur',
|
||||||
|
'shadowColor', 'clearRect(x,y,w,h)', 'fillRect(x,y,w,h)',
|
||||||
|
'strokeRect(x,y,w,h)', 'beginPath()', 'closePath()', 'moveTo(x,y)',
|
||||||
|
'lineTo(x,y)', 'quadraticCurveTo(cpx,cpy,x,y)',
|
||||||
|
'bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)', 'arcTo(x1,y1,x2,y2,radius)',
|
||||||
|
'rect(x,y,w,h)', 'arc(x,y,radius,startAngle,endAngle,anticlockwise)',
|
||||||
|
'fill()', 'stroke()', 'drawSystemFocusRing()', 'drawCustomFocusRing()',
|
||||||
|
'scrollPathIntoView()', 'clip()', 'isPointInPath(x,y)', 'font',
|
||||||
|
'textAlign', 'textBaseline', 'fillText(text,x,y,maxWidth)',
|
||||||
|
'strokeText(text,x,y,maxWidth)', 'measureText(text)',
|
||||||
|
'drawImage(image,dx,dy)', 'drawImage(image,dx,dy,dw,dh)',
|
||||||
|
'drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh)', 'createImageData(sw,sh)',
|
||||||
|
'createImageData(imagedata)', 'getImageData(sx,sy,sw,sh)',
|
||||||
|
'putImageData(imagedata,dx,dy,dirtyX,dirtyY,dirtyWidth,dirtyHeight)'
|
||||||
|
];
|
||||||
|
var param = {
|
||||||
|
initialize: function(context) {
|
||||||
|
this._ctx = context;
|
||||||
|
this._indents = 0;
|
||||||
|
},
|
||||||
|
getIndentation: function() {
|
||||||
|
var str = '';
|
||||||
|
for (var i = 0; i < this._indents; i++) {
|
||||||
|
str += ' ';
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Base.each(descriptions, function(description) {
|
||||||
|
var matches = description.match(/^([^(]+)(\()*/),
|
||||||
|
name = matches[1],
|
||||||
|
isFunction = !!matches[2];
|
||||||
|
if (isFunction) {
|
||||||
|
param[name] = function() {
|
||||||
|
if (name == 'restore') {
|
||||||
|
this._indents--;
|
||||||
|
}
|
||||||
|
var args = Array.prototype.slice.call(arguments, 0).join(', '),
|
||||||
|
string = 'ctx.' + name + '(' + args + ');';
|
||||||
|
console.log(this.getIndentation() + string);
|
||||||
|
if (name == 'save') {
|
||||||
|
this._indents++;
|
||||||
|
}
|
||||||
|
return this._ctx[name].apply(this._ctx, arguments);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var capitalized = Base.capitalize(name);
|
||||||
|
param['set' + capitalized] = function(value) {
|
||||||
|
var logValue = value && value.substring ? '\'' + value + '\'' : value,
|
||||||
|
string = 'ctx.' + name + ' = ' + logValue + ';';
|
||||||
|
console.log(this.getIndentation() + string);
|
||||||
|
return this._ctx[name] = value;
|
||||||
|
};
|
||||||
|
param['get' + capitalized] = function() {
|
||||||
|
return this._ctx[name];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Base.extend(param);
|
||||||
|
};
|
|
@ -410,4 +410,52 @@ test('hitting guides', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('hitting raster items', function() {
|
||||||
|
// Create a path, rasterize it and then remove the path:
|
||||||
|
var path = new Path.Rectangle(new Point(), new Size(320, 240));
|
||||||
|
path.fillColor = 'red';
|
||||||
|
var raster = path.rasterize();
|
||||||
|
|
||||||
|
var hitResult = paper.project.hitTest(new Point(160, 120));
|
||||||
|
|
||||||
|
equals(function() {
|
||||||
|
return hitResult && hitResult.item == raster;
|
||||||
|
}, true, 'Hit raster item before moving');
|
||||||
|
|
||||||
|
// Move the raster:
|
||||||
|
raster.translate(100, 100);
|
||||||
|
|
||||||
|
var hitResult = paper.project.hitTest(new Point(160, 120));
|
||||||
|
|
||||||
|
equals(function() {
|
||||||
|
return hitResult && hitResult.item == raster;
|
||||||
|
}, true, 'Hit raster item after moving');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hitting path with a text item in the project', function() {
|
||||||
|
var path = new Path.Rectangle(new Point(50, 50), new Point(100, 100));
|
||||||
|
path.fillColor = 'blue';
|
||||||
|
|
||||||
|
var hitResult = paper.project.hitTest(new Point(75, 75));
|
||||||
|
|
||||||
|
equals(function() {
|
||||||
|
return hitResult && hitResult.item == path;
|
||||||
|
}, true, 'Hit path item before adding text item');
|
||||||
|
|
||||||
|
var text1 = new PointText(30, 30);
|
||||||
|
text1.content = "Text 1";
|
||||||
|
|
||||||
|
var hitResult = paper.project.hitTest(new Point(75, 75));
|
||||||
|
|
||||||
|
equals(function() {
|
||||||
|
return !!hitResult;
|
||||||
|
}, true, 'A hitresult should be returned.');
|
||||||
|
|
||||||
|
equals(function() {
|
||||||
|
return !!hitResult && hitResult.item == path;
|
||||||
|
}, true, 'We should have hit the path');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// TODO: project.hitTest(point, {type: AnItemType});
|
// TODO: project.hitTest(point, {type: AnItemType});
|
|
@ -464,6 +464,17 @@ test('Changing item#position.x', function() {
|
||||||
equals(path.position.toString(), '{ x: 55, y: 50 }', 'path.position.x += 5');
|
equals(path.position.toString(), '{ x: 55, y: 50 }', 'path.position.x += 5');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Naming a removed item', function() {
|
||||||
|
var path = new Path();
|
||||||
|
path.remove();
|
||||||
|
path.name = 'test';
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Naming a layer', function() {
|
||||||
|
var layer = new Layer();
|
||||||
|
layer.name = 'test';
|
||||||
|
});
|
||||||
|
|
||||||
test('Cloning a linked size', function() {
|
test('Cloning a linked size', function() {
|
||||||
var path = new Path([40, 75], [140, 75]);
|
var path = new Path([40, 75], [140, 75]);
|
||||||
var error = null;
|
var error = null;
|
||||||
|
|
|
@ -42,3 +42,12 @@ test('item.bounds caching', function() {
|
||||||
}, 2);
|
}, 2);
|
||||||
compareRectangles(group.bounds, { x: 50, y: 50, width: 125, height: 125 }, 'group.bounds with circle');
|
compareRectangles(group.bounds, { x: 50, y: 50, width: 125, height: 125 }, 'group.bounds with circle');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('group.bounds when group contains empty group', function() {
|
||||||
|
var group = new Group();
|
||||||
|
var rectangle = new Path.Rectangle(new Point(75, 75), new Point(175, 175));
|
||||||
|
group.addChild(rectangle);
|
||||||
|
compareRectangles(group.bounds, { x: 75, y: 75, width: 100, height: 100 }, 'group.bounds without empty group');
|
||||||
|
group.addChild(new Group());
|
||||||
|
compareRectangles(group.bounds, { x: 75, y: 75, width: 100, height: 100 }, 'group.bounds with empty group');
|
||||||
|
});
|
|
@ -57,3 +57,45 @@ test('Item Order', function() {
|
||||||
return group.isBelow(circle);
|
return group.isBelow(circle);
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Item#moveAbove(item) / Item#moveBelow(item)', function() {
|
||||||
|
var item0, item1, item2;
|
||||||
|
var testMove = function(command, indexes) {
|
||||||
|
paper.project.activeLayer.remove();
|
||||||
|
new Layer();
|
||||||
|
item0 = new Group();
|
||||||
|
item1 = new Group();
|
||||||
|
item2 = new Group();
|
||||||
|
command();
|
||||||
|
equals(function() {
|
||||||
|
return item0.index;
|
||||||
|
}, indexes[0], command.toString());
|
||||||
|
equals(function() {
|
||||||
|
return item1.index;
|
||||||
|
}, indexes[1]);
|
||||||
|
equals(function() {
|
||||||
|
return item2.index;
|
||||||
|
}, indexes[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
testMove(function() { item0.moveBelow(item0) }, [0,1,2]);
|
||||||
|
testMove(function() { item0.moveBelow(item1) }, [0,1,2]);
|
||||||
|
testMove(function() { item0.moveBelow(item2) }, [1,0,2]);
|
||||||
|
testMove(function() { item1.moveBelow(item0) }, [1,0,2]);
|
||||||
|
testMove(function() { item1.moveBelow(item1) }, [0,1,2]);
|
||||||
|
testMove(function() { item1.moveBelow(item2) }, [0,1,2]);
|
||||||
|
|
||||||
|
testMove(function() { item2.moveBelow(item0) }, [1,2,0]);
|
||||||
|
testMove(function() { item2.moveBelow(item1) }, [0,2,1]);
|
||||||
|
testMove(function() { item2.moveBelow(item2) }, [0,1,2]);
|
||||||
|
|
||||||
|
testMove(function() { item0.moveAbove(item0) }, [0,1,2]);
|
||||||
|
testMove(function() { item0.moveAbove(item1) }, [1,0,2]);
|
||||||
|
testMove(function() { item0.moveAbove(item2) }, [2,0,1]);
|
||||||
|
testMove(function() { item1.moveAbove(item0) }, [0,1,2]);
|
||||||
|
testMove(function() { item1.moveAbove(item1) }, [0,1,2]);
|
||||||
|
testMove(function() { item1.moveAbove(item2) }, [0,2,1]);
|
||||||
|
testMove(function() { item2.moveAbove(item0) }, [0,2,1]);
|
||||||
|
testMove(function() { item2.moveAbove(item1) }, [0,1,2]);
|
||||||
|
testMove(function() { item2.moveAbove(item2) }, [0,1,2]);
|
||||||
|
});
|
||||||
|
|
|
@ -73,7 +73,7 @@ test('insertAbove / insertBelow', function() {
|
||||||
// move the layer above the path, inside the firstLayer:
|
// move the layer above the path, inside the firstLayer:
|
||||||
secondLayer.insertAbove(path);
|
secondLayer.insertAbove(path);
|
||||||
equals(function() {
|
equals(function() {
|
||||||
return secondLayer.previousSibling == path;
|
return secondLayer.nextSibling == path;
|
||||||
}, true);
|
}, true);
|
||||||
equals(function() {
|
equals(function() {
|
||||||
return secondLayer.parent == firstLayer;
|
return secondLayer.parent == firstLayer;
|
||||||
|
|
|
@ -265,6 +265,14 @@ test('Path#reverse', function() {
|
||||||
equals(path.segments.toString(), '{ point: { x: 100, y: 130 }, handleIn: { x: -16.56854, y: 0 }, handleOut: { x: 16.56854, y: 0 } },{ point: { x: 130, y: 100 }, handleIn: { x: 0, y: 16.56854 }, handleOut: { x: 0, y: -16.56854 } },{ point: { x: 100, y: 70 }, handleIn: { x: 16.56854, y: 0 }, handleOut: { x: -16.56854, y: 0 } },{ point: { x: 70, y: 100 }, handleIn: { x: 0, y: -16.56854 }, handleOut: { x: 0, y: 16.56854 } }');
|
equals(path.segments.toString(), '{ point: { x: 100, y: 130 }, handleIn: { x: -16.56854, y: 0 }, handleOut: { x: 16.56854, y: 0 } },{ point: { x: 130, y: 100 }, handleIn: { x: 0, y: 16.56854 }, handleOut: { x: 0, y: -16.56854 } },{ point: { x: 100, y: 70 }, handleIn: { x: 16.56854, y: 0 }, handleOut: { x: -16.56854, y: 0 } },{ point: { x: 70, y: 100 }, handleIn: { x: 0, y: -16.56854 }, handleOut: { x: 0, y: 16.56854 } }');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Path#reverse should adjust segment indices', function() {
|
||||||
|
var path = new Path([[0, 0], [10, 10], [20, 20]]);
|
||||||
|
path.reverse();
|
||||||
|
equals(path.segments[0].index, 0);
|
||||||
|
equals(path.segments[1].index, 1);
|
||||||
|
equals(path.segments[2].index, 2);
|
||||||
|
});
|
||||||
|
|
||||||
test('Path#fullySelected', function() {
|
test('Path#fullySelected', function() {
|
||||||
var path = new Path.Circle([100, 100], 10);
|
var path = new Path.Circle([100, 100], 10);
|
||||||
path.fullySelected = true;
|
path.fullySelected = true;
|
||||||
|
@ -272,4 +280,4 @@ test('Path#fullySelected', function() {
|
||||||
equals(function() {
|
equals(function() {
|
||||||
return path.fullySelected;
|
return path.fullySelected;
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,6 +38,12 @@ test('path.curves Synchronisation', function() {
|
||||||
path.removeSegments(1, 2);
|
path.removeSegments(1, 2);
|
||||||
equals(path.segments.toString(), "{ point: { x: 0, y: 100 } },{ point: { x: 100, y: 100 } }", "path.segments: path.add(new Point(100, 100));\npath.removeSegments(1, 2);");
|
equals(path.segments.toString(), "{ point: { x: 0, y: 100 } },{ point: { x: 100, y: 100 } }", "path.segments: path.add(new Point(100, 100));\npath.removeSegments(1, 2);");
|
||||||
equals(path.curves.toString(), "{ point1: { x: 0, y: 100 }, point2: { x: 100, y: 100 } },{ point1: { x: 100, y: 100 }, point2: { x: 0, y: 100 } }", "path.curves: path.add(new Point(100, 100));\npath.removeSegments(1, 2);");
|
equals(path.curves.toString(), "{ point1: { x: 0, y: 100 }, point2: { x: 100, y: 100 } },{ point1: { x: 100, y: 100 }, point2: { x: 0, y: 100 } }", "path.curves: path.add(new Point(100, 100));\npath.removeSegments(1, 2);");
|
||||||
|
|
||||||
|
// Transform the path, and the curves length should be invalidated (first, force-cache the first segment's length by accessing it
|
||||||
|
path.curves[0].length;
|
||||||
|
ok(path.curves[0]._length, 'Curve length does not appear to be cached');
|
||||||
|
path.scale(2, [0, 0]);
|
||||||
|
equals(path.curves[0].length, 200, 'Curve length should be updated when path is transformed');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('path.flatten(maxDistance)', function() {
|
test('path.flatten(maxDistance)', function() {
|
||||||
|
@ -53,4 +59,4 @@ test('path.flatten(maxDistance)', function() {
|
||||||
equals(function() {
|
equals(function() {
|
||||||
return path.lastSegment.point.toString() != path.segments[path.segments.length - 2].point.toString();
|
return path.lastSegment.point.toString() != path.segments[path.segments.length - 2].point.toString();
|
||||||
}, true, 'The points of the last and before last segments should not be so close, that calling toString on them returns the same string value.');
|
}, true, 'The points of the last and before last segments should not be so close, that calling toString on them returns the same string value.');
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue