2011-03-06 19:50:44 -05:00
|
|
|
/*
|
|
|
|
* Paper.js
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-03-06 19:50:44 -05:00
|
|
|
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
|
|
|
|
* based on Scriptographer.org and designed to be largely API compatible.
|
2011-03-07 20:41:50 -05:00
|
|
|
* http://paperjs.org/
|
2011-03-06 19:50:44 -05:00
|
|
|
* http://scriptographer.org/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-03-06 19:50:44 -05:00
|
|
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 06:17:45 -04:00
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* All rights reserved.
|
2011-03-06 19:50:44 -05:00
|
|
|
*/
|
|
|
|
|
2011-06-22 18:56:05 -04:00
|
|
|
/**
|
|
|
|
* @name CompoundPath
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-22 18:56:05 -04:00
|
|
|
* @class A compound path contains two or more paths, holes are drawn
|
|
|
|
* where the paths overlap. All the paths in a compound path take on the
|
|
|
|
* style of the backmost path and can be accessed through its
|
|
|
|
* {@link Item#children} list.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-22 18:56:05 -04:00
|
|
|
* @extends PathItem
|
|
|
|
*/
|
|
|
|
var CompoundPath = this.CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
2012-11-04 01:43:18 -05:00
|
|
|
_type: 'compoundpath',
|
2011-05-22 19:49:01 -04:00
|
|
|
/**
|
|
|
|
* Creates a new compound path item and places it in the active layer.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-20 09:27:54 -04:00
|
|
|
* @param {Path[]} [paths] the paths to place within the compound path.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-04 09:32:28 -04:00
|
|
|
* @example {@paperscript}
|
|
|
|
* // Create a circle shaped path with a hole in it:
|
2011-05-30 13:42:17 -04:00
|
|
|
* var circle = new Path.Circle(new Point(50, 50), 30);
|
|
|
|
* var innerCircle = new Path.Circle(new Point(50, 50), 10);
|
|
|
|
* var compoundPath = new CompoundPath([circle, innerCircle]);
|
|
|
|
* compoundPath.fillColor = 'red';
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-30 13:42:17 -04:00
|
|
|
* // Move the inner circle 5pt to the right:
|
|
|
|
* compoundPath.children[1].position.x += 5;
|
2011-05-22 19:49:01 -04:00
|
|
|
*/
|
2011-05-15 13:05:47 -04:00
|
|
|
initialize: function(paths) {
|
2011-03-03 07:23:46 -05:00
|
|
|
this.base();
|
2011-06-17 09:11:37 -04:00
|
|
|
// Allow CompoundPath to have children and named children.
|
2011-05-14 13:07:10 -04:00
|
|
|
this._children = [];
|
2011-06-17 09:11:37 -04:00
|
|
|
this._namedChildren = {};
|
2011-05-15 14:14:09 -04:00
|
|
|
// Do not reassign to paths, since arguments would get modified, which
|
|
|
|
// we potentially use as array, depending on what is passed.
|
|
|
|
var items = !paths || !Array.isArray(paths)
|
|
|
|
|| typeof paths[0] !== 'object' ? arguments : paths;
|
2011-07-27 14:30:39 -04:00
|
|
|
this.addChildren(items);
|
|
|
|
},
|
|
|
|
|
|
|
|
insertChild: function(index, item) {
|
2012-11-06 17:01:52 -05:00
|
|
|
// Only allow the insertion of paths
|
|
|
|
if (!(item instanceof Path))
|
|
|
|
return null;
|
2012-10-10 22:54:32 -04:00
|
|
|
var res = this.base(index, item);
|
2011-07-27 14:30:39 -04:00
|
|
|
// All children except for the bottom one (first one in list) are set
|
|
|
|
// to anti-clockwise orientation, so that they appear as holes, but
|
|
|
|
// only if their orientation was not already specified before
|
|
|
|
// (= _clockwise is defined).
|
2012-10-10 22:54:32 -04:00
|
|
|
if (res && item._clockwise === undefined)
|
2011-07-27 14:30:39 -04:00
|
|
|
item.setClockwise(item._index == 0);
|
2012-10-10 22:54:32 -04:00
|
|
|
return res;
|
2011-03-03 07:23:46 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-03 07:23:46 -05:00
|
|
|
/**
|
|
|
|
* If this is a compound path with only one path inside,
|
|
|
|
* the path is moved outside and the compound path is erased.
|
|
|
|
* Otherwise, the compound path is returned unmodified.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2012-11-06 13:16:03 -05:00
|
|
|
* @return {CompoundPath|Path} the flattened compound path
|
2011-03-03 07:23:46 -05:00
|
|
|
*/
|
2012-11-06 13:16:03 -05:00
|
|
|
flatten: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
if (this._children.length == 1) {
|
|
|
|
var child = this._children[0];
|
2011-06-17 10:58:22 -04:00
|
|
|
child.insertAbove(this);
|
2011-03-03 07:23:46 -05:00
|
|
|
this.remove();
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-03 07:23:46 -05:00
|
|
|
smooth: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
for (var i = 0, l = this._children.length; i < l; i++)
|
|
|
|
this._children[i].smooth();
|
2011-03-03 07:23:46 -05:00
|
|
|
},
|
|
|
|
|
2012-10-10 23:11:11 -04:00
|
|
|
isEmpty: function() {
|
|
|
|
return this._children.length == 0;
|
|
|
|
},
|
|
|
|
|
2012-10-22 18:48:51 -04:00
|
|
|
contains: function(point) {
|
|
|
|
point = Point.read(arguments);
|
|
|
|
var count = 0;
|
|
|
|
for (var i = 0, l = this._children.length; i < l; i++) {
|
|
|
|
if (this._children[i].contains(point))
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
return (count & 1) == 1;
|
|
|
|
},
|
|
|
|
|
|
|
|
_hitTest: function(point, options) {
|
|
|
|
return this.base(point, Base.merge(options, { fill: false }))
|
|
|
|
|| options.fill && this._style._fillColor && this.contains(point)
|
|
|
|
? new HitResult('fill', this)
|
|
|
|
: null;
|
|
|
|
},
|
|
|
|
|
2011-03-03 07:23:46 -05:00
|
|
|
draw: function(ctx, param) {
|
2012-10-22 18:48:51 -04:00
|
|
|
var children = this._children,
|
|
|
|
style = this._style;
|
2011-07-25 15:41:09 -04:00
|
|
|
// Return early if the compound path doesn't have any children:
|
2011-12-19 16:40:14 -05:00
|
|
|
if (children.length == 0)
|
2011-07-25 15:41:09 -04:00
|
|
|
return;
|
2011-03-03 07:23:46 -05:00
|
|
|
ctx.beginPath();
|
|
|
|
param.compound = true;
|
2011-12-19 16:40:14 -05:00
|
|
|
for (var i = 0, l = children.length; i < l; i++)
|
|
|
|
Item.draw(children[i], ctx, param);
|
2012-11-06 05:58:27 -05:00
|
|
|
if (this._clipMask)
|
|
|
|
ctx.clip();
|
2012-10-22 18:48:51 -04:00
|
|
|
param.compound = false;
|
|
|
|
this._setStyles(ctx);
|
2011-12-19 17:07:14 -05:00
|
|
|
if (style._fillColor)
|
2011-03-03 07:23:46 -05:00
|
|
|
ctx.fill();
|
2011-12-19 17:07:14 -05:00
|
|
|
if (style._strokeColor)
|
2011-03-03 07:23:46 -05:00
|
|
|
ctx.stroke();
|
|
|
|
}
|
2011-05-07 12:11:06 -04:00
|
|
|
}, new function() { // Injection scope for PostScript-like drawing functions
|
2011-06-16 17:07:00 -04:00
|
|
|
/**
|
|
|
|
* Helper method that returns the current path and checks if a moveTo()
|
|
|
|
* command is required first.
|
|
|
|
*/
|
2011-03-03 07:25:41 -05:00
|
|
|
function getCurrentPath(that) {
|
2011-05-15 14:31:25 -04:00
|
|
|
if (!that._children.length)
|
2011-03-03 12:29:40 -05:00
|
|
|
throw new Error('Use a moveTo() command first');
|
2011-05-15 14:31:25 -04:00
|
|
|
return that._children[that._children.length - 1];
|
2011-02-17 08:33:25 -05:00
|
|
|
}
|
2011-02-17 17:58:56 -05:00
|
|
|
|
2011-03-03 07:23:46 -05:00
|
|
|
var fields = {
|
2011-06-16 17:07:00 -04:00
|
|
|
// Note: Documentation for these methods is found in PathItem, as they
|
|
|
|
// are considered abstract methods of PathItem and need to be defined in
|
|
|
|
// all implementing classes.
|
2011-05-15 14:57:48 -04:00
|
|
|
moveTo: function(point) {
|
2011-05-07 12:11:06 -04:00
|
|
|
var path = new Path();
|
2011-06-17 10:58:22 -04:00
|
|
|
this.addChild(path);
|
2011-05-07 12:11:06 -04:00
|
|
|
path.moveTo.apply(path, arguments);
|
|
|
|
},
|
|
|
|
|
2011-05-15 14:58:09 -04:00
|
|
|
moveBy: function(point) {
|
|
|
|
this.moveTo(getCurrentPath(this).getLastSegment()._point.add(
|
|
|
|
Point.read(arguments)));
|
2011-02-17 18:01:18 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
closePath: function() {
|
2011-04-30 18:22:29 -04:00
|
|
|
getCurrentPath(this).setClosed(true);
|
2011-02-17 07:36:40 -05:00
|
|
|
}
|
2011-02-17 17:58:56 -05:00
|
|
|
};
|
2011-02-17 18:01:18 -05:00
|
|
|
|
2011-05-07 12:11:06 -04:00
|
|
|
// Redirect all other drawing commands to the current path
|
2011-02-17 18:34:03 -05:00
|
|
|
Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
|
|
|
|
'arcTo', 'lineBy', 'curveBy', 'arcBy'], function(key) {
|
|
|
|
fields[key] = function() {
|
2011-02-17 17:58:56 -05:00
|
|
|
var path = getCurrentPath(this);
|
2011-02-17 18:34:03 -05:00
|
|
|
path[key].apply(path, arguments);
|
2011-02-17 07:36:40 -05:00
|
|
|
};
|
2011-02-17 18:34:03 -05:00
|
|
|
});
|
2011-02-17 07:36:40 -05:00
|
|
|
|
2011-02-17 17:58:56 -05:00
|
|
|
return fields;
|
2011-03-03 07:19:43 -05:00
|
|
|
});
|