--- a/client/lib/paper.js Tue Sep 09 17:49:31 2014 +0200
+++ b/client/lib/paper.js Wed Sep 10 16:12:07 2014 +0200
@@ -1,28 +1,28 @@
/*!
- * Paper.js v0.9.15 - The Swiss Army Knife of Vector Graphics Scripting.
+ * Paper.js v0.9.20 - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/
*
- * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
- * http://lehni.org/ & http://jonathanpuckey.com/
+ * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
+ * http://scratchdisk.com/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*
- * Date: Sun Dec 1 23:54:52 2013 +0100
+ * Date: Mon Aug 25 14:21:13 2014 +0200
*
***
*
- * straps.js - Class inheritance library with support for bean-style accessors
+ * Straps.js - Class inheritance library with support for bean-style accessors
*
* Copyright (c) 2006 - 2013 Juerg Lehni
- * http://lehni.org/
+ * http://scratchdisk.com/
*
* Distributed under the MIT license.
*
***
*
- * acorn.js
+ * Acorn.js
* http://marijnhaverbeke.nl/acorn/
*
* Acorn is a tiny, fast JavaScript parser written in JavaScript,
@@ -33,8 +33,7 @@
var paper = new function(undefined) {
var Base = new function() {
- var hidden = /^(statics|generics|preserve|enumerable|prototype|toString|valueOf)$/,
- slice = [].slice,
+ var hidden = /^(statics|enumerable|beans|preserve)$/,
forEach = [].forEach || function(iter, bind) {
for (var i = 0, l = this.length; i < l; i++)
@@ -79,28 +78,28 @@
return _define(obj, name, desc);
};
- function inject(dest, src, enumerable, base, preserve, generics) {
- var beans;
-
- function field(name, val, dontCheck, generics) {
- var val = val || (val = describe(src, name))
+ function inject(dest, src, enumerable, beans, preserve) {
+ var beansNames = {};
+
+ function field(name, val) {
+ val = val || (val = describe(src, name))
&& (val.get ? val : val.value);
if (typeof val === 'string' && val[0] === '#')
val = dest[val.substring(1)] || val;
var isFunc = typeof val === 'function',
res = val,
prev = preserve || isFunc
- ? (val && val.get ? name in dest : dest[name]) : null,
+ ? (val && val.get ? name in dest : dest[name])
+ : null,
bean;
- if ((dontCheck || val !== undefined && src.hasOwnProperty(name))
- && (!preserve || !prev)) {
+ if (!preserve || !prev) {
if (isFunc && prev)
val.base = prev;
- if (isFunc && beans && val.length === 0
- && (bean = name.match(/^(get|is)(([A-Z])(.*))$/)))
- beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]);
+ if (isFunc && beans !== false
+ && (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
+ beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
if (!res || isFunc || !res.get || typeof res.get !== 'function'
- || res.get.length !== 0)
+ || !Base.isPlainObject(res))
res = { value: res, writable: true };
if ((describe(dest, name)
|| { configurable: true }).configurable) {
@@ -109,27 +108,18 @@
}
define(dest, name, res);
}
- if (generics && isFunc && (!preserve || !generics[name])) {
- generics[name] = function(bind) {
- return bind && dest[name].apply(bind,
- slice.call(arguments, 1));
- };
- }
}
if (src) {
- beans = [];
- for (var name in src)
+ for (var name in src) {
if (src.hasOwnProperty(name) && !hidden.test(name))
- field(name, null, true, generics);
- field('toString');
- field('valueOf');
- for (var i = 0, l = beans.length; i < l; i++) {
- var bean = beans[i],
- part = bean[1];
- field(bean[0], {
- get: dest['get' + part] || dest['is' + part],
- set: dest['set' + part]
- }, true);
+ field(name);
+ }
+ for (var name in beansNames) {
+ var part = beansNames[name],
+ set = dest['set' + part],
+ get = dest['get' + part] || set && dest['is' + part];
+ if (get && (beans === true || get.length === 0))
+ field(name, { get: get, set: set });
}
}
return dest;
@@ -144,30 +134,25 @@
return bind;
}
- function copy(dest, source) {
- for (var i in source)
- if (source.hasOwnProperty(i))
- dest[i] = source[i];
- return dest;
- }
-
- function clone(obj) {
- return copy(new obj.constructor(), obj);
+ function set(obj, props) {
+ for (var i in props)
+ if (props.hasOwnProperty(i))
+ obj[i] = props[i];
+ return obj;
}
return inject(function Base() {
for (var i = 0, l = arguments.length; i < l; i++)
- copy(this, arguments[i]);
+ set(this, arguments[i]);
}, {
inject: function(src) {
if (src) {
- var proto = this.prototype,
- base = Object.getPrototypeOf(proto).constructor,
- statics = src.statics === true ? src : src.statics;
- if (statics != src)
- inject(proto, src, src.enumerable, base && base.prototype,
- src.preserve, src.generics && this);
- inject(this, statics, true, base, src.preserve);
+ var statics = src.statics === true ? src : src.statics,
+ beans = src.beans,
+ preserve = src.preserve;
+ if (statics !== src)
+ inject(this.prototype, src, src.enumerable, beans, preserve);
+ inject(this, statics, true, beans, preserve);
}
for (var i = 1, l = arguments.length; i < l; i++)
this.inject(arguments[i]);
@@ -184,6 +169,7 @@
base.apply(this, arguments);
};
ctor.prototype = create(this.prototype);
+ ctor.base = base;
define(ctor.prototype, 'constructor',
{ value: ctor, writable: true, configurable: true });
inject(ctor, this, true);
@@ -191,8 +177,11 @@
}
}, true).inject({
inject: function() {
- for (var i = 0, l = arguments.length; i < l; i++)
- inject(this, arguments[i], arguments[i].enumerable);
+ for (var i = 0, l = arguments.length; i < l; i++) {
+ var src = arguments[i];
+ if (src)
+ inject(this, src, src.enumerable, src.beans, src.preserve);
+ }
return this;
},
@@ -205,6 +194,10 @@
return each(this, iter, bind);
},
+ set: function(props) {
+ return set(this, props);
+ },
+
clone: function() {
return new this.constructor(this);
},
@@ -214,10 +207,10 @@
create: create,
define: define,
describe: describe,
- copy: copy,
+ set: set,
clone: function(obj) {
- return copy(new obj.constructor(), obj);
+ return set(new obj.constructor(), obj);
},
isPlainObject: function(obj) {
@@ -230,7 +223,6 @@
for (var i = 0, l = arguments.length; i < l; i++)
if (arguments[i] !== undefined)
return arguments[i];
- return null;
}
}
});
@@ -239,9 +231,17 @@
if (typeof module !== 'undefined')
module.exports = Base;
+if (!Array.isArray) {
+ Array.isArray = function(obj) {
+ return Object.prototype.toString.call(obj) === '[object Array]';
+ };
+}
+
+if (!document.head) {
+ document.head = document.getElementsByTagName('head')[0];
+}
+
Base.inject({
- generics: true,
-
toString: function() {
return this._id != null
? (this._class || 'Object') + (this._name
@@ -265,8 +265,8 @@
return Base.serialize(this);
},
- _set: function(props, exclude) {
- if (props && Base.isPlainObject(props)) {
+ _set: function(props, exclude, dontCheck) {
+ if (props && (dontCheck || Base.isPlainObject(props))) {
var orig = props._filtering || props;
for (var key in orig) {
if (key in this && orig.hasOwnProperty(key)
@@ -282,7 +282,9 @@
statics: {
- exports: {},
+ exports: {
+ enumerable: true
+ },
extend: function extend() {
var res = extend.base.apply(this, arguments),
@@ -319,7 +321,8 @@
if (!checkKeys(obj1, obj2) || !checkKeys(obj2, obj1))
return false;
for (var i in obj1) {
- if (obj1.hasOwnProperty(i) && !Base.equals(obj1[i], obj2[i]))
+ if (obj1.hasOwnProperty(i)
+ && !Base.equals(obj1[i], obj2[i]))
return false;
}
return true;
@@ -327,58 +330,57 @@
return false;
},
- read: function(list, start, length, options) {
+ read: function(list, start, options, length) {
if (this === Base) {
var value = this.peek(list, start);
- list._index++;
- list.__read = 1;
+ list.__index++;
return value;
}
var proto = this.prototype,
readIndex = proto._readIndex,
- index = start || readIndex && list._index || 0;
+ index = start || readIndex && list.__index || 0;
if (!length)
length = list.length - index;
var obj = list[index];
if (obj instanceof this
|| options && options.readNull && obj == null && length <= 1) {
if (readIndex)
- list._index = index + 1;
+ list.__index = index + 1;
return obj && options && options.clone ? obj.clone() : obj;
}
obj = Base.create(this.prototype);
if (readIndex)
obj.__read = true;
- if (options)
- obj.__options = options;
obj = obj.initialize.apply(obj, index > 0 || length < list.length
? Array.prototype.slice.call(list, index, index + length)
: list) || obj;
if (readIndex) {
- list._index = index + obj.__read;
- list.__read = obj.__read;
- delete obj.__read;
- if (options)
- delete obj.__options;
+ list.__index = index + obj.__read;
+ obj.__read = undefined;
}
return obj;
},
peek: function(list, start) {
- return list[list._index = start || list._index || 0];
+ return list[list.__index = start || list.__index || 0];
+ },
+
+ remain: function(list) {
+ return list.length - (list.__index || 0);
},
readAll: function(list, start, options) {
- var res = [], entry;
+ var res = [],
+ entry;
for (var i = start || 0, l = list.length; i < l; i++) {
res.push(Array.isArray(entry = list[i])
- ? this.read(entry, 0, 0, options)
- : this.read(list, i, 1, options));
+ ? this.read(entry, 0, options)
+ : this.read(list, i, options, 1));
}
return res;
},
- readNamed: function(list, name, start, length, options) {
+ readNamed: function(list, name, start, options, length) {
var value = this.getNamed(list, name),
hasObject = value !== undefined;
if (hasObject) {
@@ -389,7 +391,7 @@
}
filtered[name] = undefined;
}
- return this.read(hasObject ? [value] : list, start, length, options);
+ return this.read(hasObject ? [value] : list, start, options, length);
},
getNamed: function(list, name) {
@@ -404,8 +406,9 @@
return !!this.getNamed(list, name);
},
- isPlainValue: function(obj) {
- return this.isPlainObject(obj) || Array.isArray(obj);
+ isPlainValue: function(obj, asString) {
+ return this.isPlainObject(obj) || Array.isArray(obj)
+ || asString && typeof obj === 'string';
},
serialize: function(obj, options, compact, dictionary) {
@@ -497,7 +500,10 @@
},
exportJSON: function(obj, options) {
- return JSON.stringify(Base.serialize(obj, options));
+ var json = Base.serialize(obj, options);
+ return options && options.asString === false
+ ? json
+ : JSON.stringify(json);
},
importJSON: function(json, target) {
@@ -509,7 +515,7 @@
: Base.create(type.prototype),
isTarget = obj === target;
if (args.length === 1 && obj instanceof Item
- && (!(obj instanceof Layer) || isTarget)) {
+ && (isTarget || !(obj instanceof Layer))) {
var arg = args[0];
if (Base.isPlainObject(arg))
arg.insert = false;
@@ -538,7 +544,7 @@
args.push.apply(args, items);
var removed = list.splice.apply(list, args);
for (var i = 0, l = removed.length; i < l; i++)
- delete removed[i]._index;
+ removed[i]._index = undefined;
for (var i = index + amount, l = list.length; i < l; i++)
list[i]._index = i;
return removed;
@@ -575,7 +581,7 @@
if (entry) {
var handlers = this._handlers = this._handlers || {};
handlers = handlers[type] = handlers[type] || [];
- if (handlers.indexOf(func) == -1) {
+ if (handlers.indexOf(func) == -1) {
handlers.push(func);
if (entry.install && handlers.length == 1)
entry.install.call(this, type);
@@ -617,26 +623,13 @@
if (!handlers)
return false;
var args = [].slice.call(arguments, 1),
- PaperScript = paper.PaperScript,
- handleException = PaperScript && PaperScript.handleException,
that = this;
-
- function callHandlers() {
- for (var i in handlers) {
- if (handlers[i].apply(that, args) === false
- && event && event.stop)
- event.stop();
- }
- }
-
- if (handleException) {
- try {
- callHandlers();
- } catch (e) {
- handleException(e);
- }
- } else {
- callHandlers();
+ for (var i = 0, l = handlers.length; i < l; i++) {
+ if (handlers[i].apply(that, args) === false
+ && event && event.stop) {
+ event.stop();
+ break;
+ }
}
return true;
},
@@ -649,6 +642,19 @@
off: '#detach',
trigger: '#fire',
+ _installEvents: function(install) {
+ var handlers = this._handlers,
+ key = install ? 'install' : 'uninstall';
+ for (var type in handlers) {
+ if (handlers[type].length > 0) {
+ var entry = this._eventTypes[type],
+ func = entry[key];
+ if (func)
+ func.call(this, type);
+ }
+ }
+ },
+
statics: {
inject: function inject() {
for (var i = 0, l = arguments.length; i < l; i++) {
@@ -667,11 +673,11 @@
return this[name];
};
src['set' + part] = function(func) {
- if (func) {
+ var prev = this[name];
+ if (prev)
+ this.detach(type, prev);
+ if (func)
this.attach(type, func);
- } else if (this[name]) {
- this.detach(type, this[name]);
- }
this[name] = func;
};
});
@@ -687,16 +693,18 @@
var PaperScope = Base.extend({
_class: 'PaperScope',
- initialize: function PaperScope(script) {
+ initialize: function PaperScope() {
paper = this;
+ this.settings = new Base({
+ applyMatrix: true,
+ handleSize: 4,
+ hitTolerance: 0
+ });
this.project = null;
this.projects = [];
this.tools = [];
this.palettes = [];
- this._id = script && (script.getAttribute('id') || script.src)
- || ('paperscope-' + (PaperScope._id++));
- if (script)
- script.setAttribute('id', this._id);
+ this._id = PaperScope._id++;
PaperScope._scopes[this._id] = this;
if (!this.support) {
var ctx = CanvasProvider.getContext(1, 1);
@@ -708,26 +716,19 @@
}
},
- version: '0.9.15',
+ version: '0.9.20',
getView: function() {
- return this.project && this.project.view;
- },
-
- getTool: function() {
- if (!this._tool)
- this._tool = new Tool();
- return this._tool;
+ return this.project && this.project.getView();
},
getPaper: function() {
return this;
},
- evaluate: function(code) {
- var res = paper.PaperScript.evaluate(code, this);
+ execute: function(code, url, options) {
+ paper.PaperScript.execute(code, this, url, options);
View.updateFocus();
- return res;
},
install: function(scope) {
@@ -740,15 +741,14 @@
}
});
});
- for (var key in this) {
- if (!/^(version|_id)/.test(key))
+ for (var key in this)
+ if (!/^_/.test(key) && this[key])
scope[key] = this[key];
- }
- },
-
- setup: function(canvas) {
+ },
+
+ setup: function(element) {
paper = this;
- this.project = new Project(canvas);
+ this.project = new Project(element);
return this;
},
@@ -783,8 +783,6 @@
_id: 0,
get: function(id) {
- if (typeof id === 'object')
- id = id.getAttribute('id');
return this._scopes[id] || null;
},
@@ -839,6 +837,10 @@
return Math.round(val * this.multiplier) / this.multiplier;
},
+ pair: function(val1, val2, separator) {
+ return this.number(val1) + (separator || ',') + this.number(val2);
+ },
+
point: function(val, separator) {
return this.number(val.x) + (separator || ',') + this.number(val.y);
},
@@ -898,15 +900,29 @@
sqrt = Math.sqrt,
pow = Math.pow,
cos = Math.cos,
- PI = Math.PI;
+ PI = Math.PI,
+ TOLERANCE = 10e-6,
+ EPSILON = 10e-12;
+
+ function setupRoots(roots, min, max) {
+ var unbound = min === undefined,
+ minE = min - EPSILON,
+ maxE = max + EPSILON,
+ count = 0;
+ return function(root) {
+ if (unbound || root > minE && root < maxE)
+ roots[count++] = root < min ? min : root > max ? max : root;
+ return count;
+ };
+ }
return {
- TOLERANCE: 10e-6,
- EPSILON: 10e-12,
+ TOLERANCE: TOLERANCE,
+ EPSILON: EPSILON,
KAPPA: 4 * (sqrt(2) - 1) / 3,
isZero: function(val) {
- return abs(val) <= Numerical.EPSILON;
+ return abs(val) <= EPSILON;
},
integrate: function(f, a, b, n) {
@@ -916,7 +932,7 @@
B = A + a,
i = 0,
m = (n + 1) >> 1,
- sum = n & 1 ? w[i++] * f(B) : 0;
+ sum = n & 1 ? w[i++] * f(B) : 0;
while (i < m) {
var Ax = A * x[i];
sum += w[i++] * (f(B + Ax) + f(B - Ax));
@@ -927,10 +943,10 @@
findRoot: function(f, df, x, a, b, n, tolerance) {
for (var i = 0; i < n; i++) {
var fx = f(x),
- dx = fx / df(x);
+ dx = fx / df(x),
+ nx = x - dx;
if (abs(dx) < tolerance)
- return x;
- var nx = x - dx;
+ return nx;
if (fx > 0) {
b = x;
x = nx <= a ? 0.5 * (a + b) : nx;
@@ -939,72 +955,52 @@
x = nx >= b ? 0.5 * (a + b) : nx;
}
}
+ return x;
},
solveQuadratic: function(a, b, c, roots, min, max) {
- var epsilon = Numerical.EPSILON,
- unbound = min === undefined,
- minE = min - epsilon,
- maxE = max + epsilon,
- count = 0;
-
- function add(root) {
- if (unbound || root > minE && root < maxE)
- roots[count++] = root < min ? min : root > max ? max : root;
- return count;
- }
-
- if (abs(a) < epsilon) {
- if (abs(b) >= epsilon)
+ var add = setupRoots(roots, min, max);
+
+ if (abs(a) < EPSILON) {
+ if (abs(b) >= EPSILON)
return add(-c / b);
- return abs(c) < epsilon ? -1 : 0;
+ return abs(c) < EPSILON ? -1 : 0;
}
var p = b / (2 * a);
var q = c / a;
var p2 = p * p;
- if (p2 < q - epsilon)
+ if (p2 < q - EPSILON)
return 0;
- var s = p2 > q ? sqrt(p2 - q) : 0;
- add (s - p);
+ var s = p2 > q ? sqrt(p2 - q) : 0,
+ count = add(s - p);
if (s > 0)
- add(-s - p);
+ count = add(-s - p);
return count;
},
solveCubic: function(a, b, c, d, roots, min, max) {
- var epsilon = Numerical.EPSILON;
- if (abs(a) < epsilon)
+ if (abs(a) < EPSILON)
return Numerical.solveQuadratic(b, c, d, roots, min, max);
- var unbound = min === undefined,
- minE = min - epsilon,
- maxE = max + epsilon,
- count = 0;
-
- function add(root) {
- if (unbound || root > minE && root < maxE)
- roots[count++] = root < min ? min : root > max ? max : root;
- return count;
- }
-
b /= a;
c /= a;
d /= a;
- var bb = b * b,
+ var add = setupRoots(roots, min, max),
+ bb = b * b,
p = (bb - 3 * c) / 9,
q = (2 * bb * b - 9 * b * c + 27 * d) / 54,
ppp = p * p * p,
D = q * q - ppp;
b /= 3;
- if (abs(D) < epsilon) {
- if (abs(q) < epsilon)
+ if (abs(D) < EPSILON) {
+ if (abs(q) < EPSILON)
return add(-b);
var sqp = sqrt(p),
snq = q > 0 ? 1 : -1;
add(-snq * 2 * sqp - b);
return add(snq * sqp - b);
}
- if (D < 0) {
+ if (D < 0) {
var sqp = sqrt(p),
phi = Math.acos(q / (sqp * sqp * sqp)) / 3,
t = -2 * sqp,
@@ -1066,10 +1062,11 @@
},
equals: function(point) {
- return point === this || point && (this.x === point.x
- && this.y === point.y
- || Array.isArray(point) && this.x === point[0]
- && this.y === point[1]) || false;
+ return this === point || point
+ && (this.x === point.x && this.y === point.y
+ || Array.isArray(point)
+ && this.x === point[0] && this.y === point[1])
+ || false;
},
clone: function() {
@@ -1086,50 +1083,8 @@
return [f.number(this.x), f.number(this.y)];
},
- add: function(point) {
- point = Point.read(arguments);
- return new Point(this.x + point.x, this.y + point.y);
- },
-
- subtract: function(point) {
- point = Point.read(arguments);
- return new Point(this.x - point.x, this.y - point.y);
- },
-
- multiply: function(point) {
- point = Point.read(arguments);
- return new Point(this.x * point.x, this.y * point.y);
- },
-
- divide: function(point) {
- point = Point.read(arguments);
- return new Point(this.x / point.x, this.y / point.y);
- },
-
- modulo: function(point) {
- point = Point.read(arguments);
- return new Point(this.x % point.x, this.y % point.y);
- },
-
- negate: function() {
- return new Point(-this.x, -this.y);
- },
-
- transform: function(matrix) {
- return matrix ? matrix._transformPoint(this) : this;
- },
-
- getDistance: function(point, squared) {
- point = Point.read(arguments);
- var x = point.x - this.x,
- y = point.y - this.y,
- d = x * x + y * y;
- return squared ? d : Math.sqrt(d);
- },
-
getLength: function() {
- var length = this.x * this.x + this.y * this.y;
- return arguments.length && arguments[0] ? length : Math.sqrt(length);
+ return Math.sqrt(this.x * this.x + this.y * this.y);
},
setLength: function(length) {
@@ -1148,37 +1103,20 @@
this.y * scale
);
}
- return this;
- },
-
- normalize: function(length) {
- if (length === undefined)
- length = 1;
- var current = this.getLength(),
- scale = current !== 0 ? length / current : 0,
- point = new Point(this.x * scale, this.y * scale);
- point._angle = this._angle;
- return point;
- },
-
+ },
getAngle: function() {
- return this.getAngleInRadians(arguments[0]) * 180 / Math.PI;
+ return this.getAngleInRadians.apply(this, arguments) * 180 / Math.PI;
},
setAngle: function(angle) {
- angle = this._angle = angle * Math.PI / 180;
- if (!this.isZero()) {
- var length = this.getLength();
- this.set(
- Math.cos(angle) * length,
- Math.sin(angle) * length
- );
- }
- return this;
- },
+ this.setAngleInRadians.call(this, angle * Math.PI / 180);
+ },
+
+ getAngleInDegrees: '#getAngle',
+ setAngleInDegrees: '#setAngle',
getAngleInRadians: function() {
- if (arguments[0] === undefined) {
+ if (!arguments.length) {
return this.isZero()
? this._angle || 0
: this._angle = Math.atan2(this.y, this.x);
@@ -1188,24 +1126,54 @@
if (Numerical.isZero(div)) {
return NaN;
} else {
- return Math.acos(this.dot(point) / div);
- }
- }
- },
-
- getAngleInDegrees: function() {
- return this.getAngle(arguments[0]);
+ var a = this.dot(point) / div;
+ return Math.acos(a < -1 ? -1 : a > 1 ? 1 : a);
+ }
+ }
+ },
+
+ setAngleInRadians: function(angle) {
+ this._angle = angle;
+ if (!this.isZero()) {
+ var length = this.getLength();
+ this.set(
+ Math.cos(angle) * length,
+ Math.sin(angle) * length
+ );
+ }
},
getQuadrant: function() {
return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3;
- },
-
- getDirectedAngle: function(point) {
- point = Point.read(arguments);
+ }
+}, {
+ beans: false,
+
+ getDirectedAngle: function() {
+ var point = Point.read(arguments);
return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI;
},
+ getDistance: function() {
+ var point = Point.read(arguments),
+ x = point.x - this.x,
+ y = point.y - this.y,
+ d = x * x + y * y,
+ squared = Base.read(arguments);
+ return squared ? d : Math.sqrt(d);
+ },
+
+ normalize: function(length) {
+ if (length === undefined)
+ length = 1;
+ var current = this.getLength(),
+ scale = current !== 0 ? length / current : 0,
+ point = new Point(this.x * scale, this.y * scale);
+ if (scale >= 0)
+ point._angle = this._angle;
+ return point;
+ },
+
rotate: function(angle, center) {
if (angle === 0)
return this.clone();
@@ -1215,11 +1183,44 @@
c = Math.cos(angle);
point = new Point(
point.x * c - point.y * s,
- point.y * c + point.x * s
+ point.x * s + point.y * c
);
return center ? point.add(center) : point;
},
+ transform: function(matrix) {
+ return matrix ? matrix._transformPoint(this) : this;
+ },
+
+ add: function() {
+ var point = Point.read(arguments);
+ return new Point(this.x + point.x, this.y + point.y);
+ },
+
+ subtract: function() {
+ var point = Point.read(arguments);
+ return new Point(this.x - point.x, this.y - point.y);
+ },
+
+ multiply: function() {
+ var point = Point.read(arguments);
+ return new Point(this.x * point.x, this.y * point.y);
+ },
+
+ divide: function() {
+ var point = Point.read(arguments);
+ return new Point(this.x / point.x, this.y / point.y);
+ },
+
+ modulo: function() {
+ var point = Point.read(arguments);
+ return new Point(this.x % point.x, this.y % point.y);
+ },
+
+ negate: function() {
+ return new Point(-this.x, -this.y);
+ },
+
isInside: function(rect) {
return rect.contains(this);
},
@@ -1229,11 +1230,11 @@
},
isColinear: function(point) {
- return this.cross(point) < 0.00001;
+ return Math.abs(this.cross(point)) < 0.00001;
},
isOrthogonal: function(point) {
- return this.dot(point) < 0.00001;
+ return Math.abs(this.dot(point)) < 0.00001;
},
isZero: function() {
@@ -1244,18 +1245,18 @@
return isNaN(this.x) || isNaN(this.y);
},
- dot: function(point) {
- point = Point.read(arguments);
+ dot: function() {
+ var point = Point.read(arguments);
return this.x * point.x + this.y * point.y;
},
- cross: function(point) {
- point = Point.read(arguments);
+ cross: function() {
+ var point = Point.read(arguments);
return this.x * point.y - this.y * point.x;
},
- project: function(point) {
- point = Point.read(arguments);
+ project: function() {
+ var point = Point.read(arguments);
if (point.isZero()) {
return new Point(0, 0);
} else {
@@ -1269,7 +1270,7 @@
statics: {
min: function() {
- var point1 = Point.read(arguments);
+ var point1 = Point.read(arguments),
point2 = Point.read(arguments);
return new Point(
Math.min(point1.x, point2.x),
@@ -1278,7 +1279,7 @@
},
max: function() {
- var point1 = Point.read(arguments);
+ var point1 = Point.read(arguments),
point2 = Point.read(arguments);
return new Point(
Math.max(point1.x, point2.x),
@@ -1397,28 +1398,28 @@
f.number(this.height)];
},
- add: function(size) {
- size = Size.read(arguments);
+ add: function() {
+ var size = Size.read(arguments);
return new Size(this.width + size.width, this.height + size.height);
},
- subtract: function(size) {
- size = Size.read(arguments);
+ subtract: function() {
+ var size = Size.read(arguments);
return new Size(this.width - size.width, this.height - size.height);
},
- multiply: function(size) {
- size = Size.read(arguments);
+ multiply: function() {
+ var size = Size.read(arguments);
return new Size(this.width * size.width, this.height * size.height);
},
- divide: function(size) {
- size = Size.read(arguments);
+ divide: function() {
+ var size = Size.read(arguments);
return new Size(this.width / size.width, this.height / size.height);
},
- modulo: function(size) {
- size = Size.read(arguments);
+ modulo: function() {
+ var size = Size.read(arguments);
return new Size(this.width % size.width, this.height % size.height);
},
@@ -1496,6 +1497,7 @@
var Rectangle = Base.extend({
_class: 'Rectangle',
_readIndex: true,
+ beans: true,
initialize: function Rectangle(arg0, arg1, arg2, arg3) {
var type = typeof arg0,
@@ -1550,7 +1552,7 @@
this.width = size.width;
this.height = size.height;
}
- read = arguments._index;
+ read = arguments.__index;
}
if (this.__read)
this.__read = read;
@@ -1569,11 +1571,12 @@
},
equals: function(rect) {
- if (Base.isPlainValue(rect))
- rect = Rectangle.read(arguments);
- return rect === this
- || rect && this.x === rect.x && this.y === rect.y
- && this.width === rect.width && this.height === rect.height
+ var rt = Base.isPlainValue(rect)
+ ? Rectangle.read(arguments)
+ : rect;
+ return rt === this
+ || rt && this.x === rt.x && this.y === rt.y
+ && this.width === rt.width && this.height === rt.height
|| false;
},
@@ -1594,24 +1597,24 @@
f.number(this.height)];
},
- getPoint: function() {
- return new (arguments[0] ? Point : LinkedPoint)
- (this.x, this.y, this, 'setPoint');
- },
-
- setPoint: function(point) {
- point = Point.read(arguments);
+ getPoint: function(_dontLink) {
+ var ctor = _dontLink ? Point : LinkedPoint;
+ return new ctor(this.x, this.y, this, 'setPoint');
+ },
+
+ setPoint: function() {
+ var point = Point.read(arguments);
this.x = point.x;
this.y = point.y;
},
- getSize: function() {
- return new (arguments[0] ? Size : LinkedSize)
- (this.width, this.height, this, 'setSize');
- },
-
- setSize: function(size) {
- size = Size.read(arguments);
+ getSize: function(_dontLink) {
+ var ctor = _dontLink ? Size : LinkedSize;
+ return new ctor(this.width, this.height, this, 'setSize');
+ },
+
+ setSize: function() {
+ var size = Size.read(arguments);
if (this._fixX)
this.x += (this.width - size.width) * this._fixX;
if (this._fixY)
@@ -1690,20 +1693,24 @@
this._fixY = 0.5;
},
- getCenter: function() {
- return new (arguments[0] ? Point : LinkedPoint)
- (this.getCenterX(), this.getCenterY(), this, 'setCenter');
- },
-
- setCenter: function(point) {
- point = Point.read(arguments);
+ getCenter: function(_dontLink) {
+ var ctor = _dontLink ? Point : LinkedPoint;
+ return new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter');
+ },
+
+ setCenter: function() {
+ var point = Point.read(arguments);
this.setCenterX(point.x);
this.setCenterY(point.y);
return this;
},
+ getArea: function() {
+ return this.width * this.height;
+ },
+
isEmpty: function() {
- return this.width == 0 || this.height == 0;
+ return this.width === 0 || this.height === 0;
},
contains: function(arg) {
@@ -1729,42 +1736,42 @@
&& y + rect.height <= this.y + this.height;
},
- intersects: function(rect) {
- rect = Rectangle.read(arguments);
+ intersects: function() {
+ var rect = Rectangle.read(arguments);
return rect.x + rect.width > this.x
&& rect.y + rect.height > this.y
&& rect.x < this.x + this.width
&& rect.y < this.y + this.height;
},
- touches: function(rect) {
- rect = Rectangle.read(arguments);
+ touches: function() {
+ var rect = Rectangle.read(arguments);
return rect.x + rect.width >= this.x
&& rect.y + rect.height >= this.y
&& rect.x <= this.x + this.width
&& rect.y <= this.y + this.height;
},
- intersect: function(rect) {
- rect = Rectangle.read(arguments);
- var x1 = Math.max(this.x, rect.x),
+ intersect: function() {
+ var rect = Rectangle.read(arguments),
+ x1 = Math.max(this.x, rect.x),
y1 = Math.max(this.y, rect.y),
x2 = Math.min(this.x + this.width, rect.x + rect.width),
y2 = Math.min(this.y + this.height, rect.y + rect.height);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
},
- unite: function(rect) {
- rect = Rectangle.read(arguments);
- var x1 = Math.min(this.x, rect.x),
+ unite: function() {
+ var rect = Rectangle.read(arguments),
+ x1 = Math.min(this.x, rect.x),
y1 = Math.min(this.y, rect.y),
x2 = Math.max(this.x + this.width, rect.x + rect.width),
y2 = Math.max(this.y + this.height, rect.y + rect.height);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
},
- include: function(point) {
- point = Point.read(arguments);
+ include: function() {
+ var point = Point.read(arguments);
var x1 = Math.min(this.x, point.x),
y1 = Math.min(this.y, point.y),
x2 = Math.max(this.x + this.width, point.x),
@@ -1772,9 +1779,10 @@
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
},
- expand: function(hor, ver) {
- if (ver === undefined)
- ver = hor;
+ expand: function() {
+ var amount = Size.read(arguments),
+ hor = amount.width,
+ ver = amount.height;
return new Rectangle(this.x - hor / 2, this.y - ver / 2,
this.width + hor, this.height + ver);
},
@@ -1783,37 +1791,38 @@
return this.expand(this.width * hor - this.width,
this.height * (ver === undefined ? hor : ver) - this.height);
}
-}, new function() {
- return Base.each([
- ['Top', 'Left'], ['Top', 'Right'],
- ['Bottom', 'Left'], ['Bottom', 'Right'],
- ['Left', 'Center'], ['Top', 'Center'],
- ['Right', 'Center'], ['Bottom', 'Center']
- ],
- function(parts, index) {
- var part = parts.join('');
- var xFirst = /^[RL]/.test(part);
- if (index >= 4)
- parts[1] += xFirst ? 'Y' : 'X';
- var x = parts[xFirst ? 0 : 1],
- y = parts[xFirst ? 1 : 0],
- getX = 'get' + x,
- getY = 'get' + y,
- setX = 'set' + x,
- setY = 'set' + y,
- get = 'get' + part,
- set = 'set' + part;
- this[get] = function() {
- return new (arguments[0] ? Point : LinkedPoint)
- (this[getX](), this[getY](), this, set);
- };
- this[set] = function(point) {
- point = Point.read(arguments);
- this[setX](point.x);
- this[setY](point.y);
- };
- }, {});
-});
+}, Base.each([
+ ['Top', 'Left'], ['Top', 'Right'],
+ ['Bottom', 'Left'], ['Bottom', 'Right'],
+ ['Left', 'Center'], ['Top', 'Center'],
+ ['Right', 'Center'], ['Bottom', 'Center']
+ ],
+ function(parts, index) {
+ var part = parts.join('');
+ var xFirst = /^[RL]/.test(part);
+ if (index >= 4)
+ parts[1] += xFirst ? 'Y' : 'X';
+ var x = parts[xFirst ? 0 : 1],
+ y = parts[xFirst ? 1 : 0],
+ getX = 'get' + x,
+ getY = 'get' + y,
+ setX = 'set' + x,
+ setY = 'set' + y,
+ get = 'get' + part,
+ set = 'set' + part;
+ this[get] = function(_dontLink) {
+ var ctor = _dontLink ? Point : LinkedPoint;
+ return new ctor(this[getX](), this[getY](), this, set);
+ };
+ this[set] = function() {
+ var point = Point.read(arguments);
+ this[setX](point.x);
+ this[setY](point.y);
+ };
+ }, {
+ beans: true
+ }
+));
var LinkedRectangle = Rectangle.extend({
initialize: function Rectangle(x, y, width, height, owner, setter) {
@@ -1855,7 +1864,7 @@
this[name] = function() {
this._dontNotify = true;
proto[name].apply(this, arguments);
- delete this._dontNotify;
+ this._dontNotify = false;
this._owner[this._setter](this);
};
}, {
@@ -1916,8 +1925,14 @@
},
_changed: function() {
- if (this._owner)
- this._owner._changed(5);
+ var owner = this._owner;
+ if (owner) {
+ if (owner._applyMatrix) {
+ owner.transform(null, true);
+ } else {
+ owner._changed(9);
+ }
+ }
},
clone: function() {
@@ -1940,16 +1955,36 @@
f.number(this._ty)].join(', ') + ']]';
},
- reset: function() {
+ reset: function(_dontNotify) {
this._a = this._d = 1;
this._c = this._b = this._tx = this._ty = 0;
+ if (!_dontNotify)
+ this._changed();
+ return this;
+ },
+
+ apply: function() {
+ var owner = this._owner;
+ if (owner) {
+ owner.transform(null, true);
+ return this.isIdentity();
+ }
+ return false;
+ },
+
+ translate: function() {
+ var point = Point.read(arguments),
+ x = point.x,
+ y = point.y;
+ this._tx += x * this._a + y * this._b;
+ this._ty += x * this._c + y * this._d;
this._changed();
return this;
},
scale: function() {
var scale = Point.read(arguments),
- center = Point.read(arguments, 0, 0, { readNull: true });
+ center = Point.read(arguments, 0, { readNull: true });
if (center)
this.translate(center);
this._a *= scale.x;
@@ -1962,20 +1997,10 @@
return this;
},
- translate: function(point) {
- point = Point.read(arguments);
- var x = point.x,
- y = point.y;
- this._tx += x * this._a + y * this._b;
- this._ty += x * this._c + y * this._d;
- this._changed();
- return this;
- },
-
- rotate: function(angle, center) {
- center = Point.read(arguments, 1);
- angle = angle * Math.PI / 180;
- var x = center.x,
+ rotate: function(angle ) {
+ angle *= Math.PI / 180;
+ var center = Point.read(arguments, 1),
+ x = center.x,
y = center.y,
cos = Math.cos(angle),
sin = Math.sin(angle),
@@ -1996,59 +2021,106 @@
},
shear: function() {
- var point = Point.read(arguments),
- center = Point.read(arguments, 0, 0, { readNull: true });
+ var shear = Point.read(arguments),
+ center = Point.read(arguments, 0, { readNull: true });
if (center)
this.translate(center);
var a = this._a,
c = this._c;
- this._a += point.y * this._b;
- this._c += point.y * this._d;
- this._b += point.x * a;
- this._d += point.x * c;
+ this._a += shear.y * this._b;
+ this._c += shear.y * this._d;
+ this._b += shear.x * a;
+ this._d += shear.x * c;
if (center)
this.translate(center.negate());
this._changed();
return this;
},
+ skew: function() {
+ var skew = Point.read(arguments),
+ center = Point.read(arguments, 0, { readNull: true }),
+ toRadians = Math.PI / 180,
+ shear = new Point(Math.tan(skew.x * toRadians),
+ Math.tan(skew.y * toRadians));
+ return this.shear(shear, center);
+ },
+
concatenate: function(mx) {
- var a = this._a,
- b = this._b,
- c = this._c,
- d = this._d;
- this._a = mx._a * a + mx._c * b;
- this._b = mx._b * a + mx._d * b;
- this._c = mx._a * c + mx._c * d;
- this._d = mx._b * c + mx._d * d;
- this._tx += mx._tx * a + mx._ty * b;
- this._ty += mx._tx * c + mx._ty * d;
+ var a1 = this._a,
+ b1 = this._b,
+ c1 = this._c,
+ d1 = this._d,
+ a2 = mx._a,
+ b2 = mx._b,
+ c2 = mx._c,
+ d2 = mx._d,
+ tx2 = mx._tx,
+ ty2 = mx._ty;
+ this._a = a2 * a1 + c2 * b1;
+ this._b = b2 * a1 + d2 * b1;
+ this._c = a2 * c1 + c2 * d1;
+ this._d = b2 * c1 + d2 * d1;
+ this._tx += tx2 * a1 + ty2 * b1;
+ this._ty += tx2 * c1 + ty2 * d1;
this._changed();
return this;
},
preConcatenate: function(mx) {
- var a = this._a,
- b = this._b,
- c = this._c,
- d = this._d,
- tx = this._tx,
- ty = this._ty;
- this._a = mx._a * a + mx._b * c;
- this._b = mx._a * b + mx._b * d;
- this._c = mx._c * a + mx._d * c;
- this._d = mx._c * b + mx._d * d;
- this._tx = mx._a * tx + mx._b * ty + mx._tx;
- this._ty = mx._c * tx + mx._d * ty + mx._ty;
+ var a1 = this._a,
+ b1 = this._b,
+ c1 = this._c,
+ d1 = this._d,
+ tx1 = this._tx,
+ ty1 = this._ty,
+ a2 = mx._a,
+ b2 = mx._b,
+ c2 = mx._c,
+ d2 = mx._d,
+ tx2 = mx._tx,
+ ty2 = mx._ty;
+ this._a = a2 * a1 + b2 * c1;
+ this._b = a2 * b1 + b2 * d1;
+ this._c = c2 * a1 + d2 * c1;
+ this._d = c2 * b1 + d2 * d1;
+ this._tx = a2 * tx1 + b2 * ty1 + tx2;
+ this._ty = c2 * tx1 + d2 * ty1 + ty2;
this._changed();
return this;
},
+ chain: function(mx) {
+ var a1 = this._a,
+ b1 = this._b,
+ c1 = this._c,
+ d1 = this._d,
+ tx1 = this._tx,
+ ty1 = this._ty,
+ a2 = mx._a,
+ b2 = mx._b,
+ c2 = mx._c,
+ d2 = mx._d,
+ tx2 = mx._tx,
+ ty2 = mx._ty;
+ return new Matrix(
+ a2 * a1 + c2 * b1,
+ a2 * c1 + c2 * d1,
+ b2 * a1 + d2 * b1,
+ b2 * c1 + d2 * d1,
+ tx1 + tx2 * a1 + ty2 * b1,
+ ty1 + tx2 * c1 + ty2 * d1);
+ },
+
isIdentity: function() {
return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1
&& this._tx === 0 && this._ty === 0;
},
+ orNullIfIdentity: function() {
+ return this.isIdentity() ? null : this;
+ },
+
isInvertible: function() {
return !!this._getDeterminant();
},
@@ -2057,10 +2129,10 @@
return !this._getDeterminant();
},
- transform: function( src, srcOffset, dst, dstOffset, count) {
- return arguments.length < 5
+ transform: function( src, dst, count) {
+ return arguments.length < 3
? this._transformPoint(Point.read(arguments))
- : this._transformCoordinates(src, srcOffset, dst, dstOffset, count);
+ : this._transformCoordinates(src, dst, count);
},
_transformPoint: function(point, dest, _dontNotify) {
@@ -2075,10 +2147,10 @@
);
},
- _transformCoordinates: function(src, srcOffset, dst, dstOffset, count) {
- var i = srcOffset,
- j = dstOffset,
- max = i + 2 * count;
+ _transformCoordinates: function(src, dst, count) {
+ var i = 0,
+ j = 0,
+ max = 2 * count;
while (i < max) {
var x = src[i++],
y = src[i++];
@@ -2094,7 +2166,7 @@
x2 = x1 + rect.width,
y2 = y1 + rect.height,
coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];
- return this._transformCoordinates(coords, 0, coords, 0, 4);
+ return this._transformCoordinates(coords, coords, 4);
},
_transformBounds: function(bounds, dest, _dontNotify) {
@@ -2167,7 +2239,6 @@
}
return {
- translation: this.getTranslation(),
scaling: new Point(scaleX, scaleY),
rotation: -Math.atan2(b, a) * 180 / Math.PI,
shearing: shear
@@ -2182,36 +2253,14 @@
return new Point(this._tx, this._ty);
},
- setTranslation: function() {
- var point = Point.read(arguments);
- this._tx = point.x;
- this._ty = point.y;
- this._changed();
- },
-
getScaling: function() {
return (this.decompose() || {}).scaling;
},
- setScaling: function() {
- var scaling = this.getScaling();
- if (scaling != null) {
- var scale = Point.read(arguments);
- (this._owner || this).scale(
- scale.x / scaling.x, scale.y / scaling.y);
- }
- },
-
getRotation: function() {
return (this.decompose() || {}).rotation;
},
- setRotation: function(angle) {
- var rotation = this.getRotation();
- if (rotation != null)
- (this._owner || this).rotate(angle - rotation);
- },
-
inverted: function() {
var det = this._getDeterminant();
return det && new Matrix(
@@ -2230,25 +2279,17 @@
applyToContext: function(ctx) {
ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
}
-}, new function() {
- return Base.each({
- scaleX: '_a',
- scaleY: '_d',
- translateX: '_tx',
- translateY: '_ty',
- shearX: '_b',
- shearY: '_c'
- }, function(prop, name) {
- name = Base.capitalize(name);
- this['get' + name] = function() {
- return this[prop];
- };
- this['set' + name] = function(value) {
- this[prop] = value;
- this._changed();
- };
- }, {});
-});
+}, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) {
+ var part = Base.capitalize(name),
+ prop = '_' + name;
+ this['get' + part] = function() {
+ return this[prop];
+ };
+ this['set' + part] = function(value) {
+ this[prop] = value;
+ this._changed();
+ };
+}, {}));
var Line = Base.extend({
_class: 'Line',
@@ -2335,9 +2376,9 @@
}
var v2x = x - px,
v2y = y - py,
- ccw = v2x * vy - v2y * vx;
+ ccw = v2x * vy - v2y * vx;
if (ccw === 0) {
- ccw = v2x * vx + v2y * vy;
+ ccw = v2x * vx + v2y * vy;
if (ccw > 0) {
v2x -= vx;
v2y -= vy;
@@ -2354,8 +2395,8 @@
vx -= px;
vy -= py;
}
- var m = vy / vx,
- b = py - m * px;
+ var m = vy / vx,
+ b = py - m * px;
return (y - (m * x) - b) / Math.sqrt(m * m + 1);
}
}
@@ -2366,18 +2407,17 @@
_list: 'projects',
_reference: 'project',
- initialize: function Project(view) {
+ initialize: function Project(element) {
PaperScopeItem.call(this, true);
this.layers = [];
this.symbols = [];
- this._currentStyle = new Style();
+ this._currentStyle = new Style(null, null, this);
this.activeLayer = new Layer();
- if (view)
- this.view = view instanceof View ? view : View.create(view);
+ this._view = View.create(this,
+ element || CanvasProvider.getCanvas(1, 1));
this._selectedItems = {};
this._selectedItemCount = 0;
- this._drawCount = 0;
- this.options = {};
+ this._updateVersion = 0;
},
_serialize: function(options, dictionary) {
@@ -2398,11 +2438,15 @@
remove: function remove() {
if (!remove.base.call(this))
return false;
- if (this.view)
- this.view.remove();
+ if (this._view)
+ this._view.remove();
return true;
},
+ getView: function() {
+ return this._view;
+ },
+
getCurrentStyle: function() {
return this._currentStyle;
},
@@ -2422,7 +2466,7 @@
this.activeLayer = child;
} else if (child instanceof Item) {
(this.activeLayer
- || this.addChild(new Layer({ insert: false }))).addChild(child);
+ || this.addChild(new Layer(Item.NO_INSERT))).addChild(child);
} else {
child = null;
}
@@ -2439,6 +2483,10 @@
return items;
},
+ getOptions: function() {
+ return this._scope.settings;
+ },
+
_updateSelection: function(item) {
var id = item._id,
selectedItems = this._selectedItems;
@@ -2465,39 +2513,23 @@
selectedItems[i].setFullySelected(false);
},
- hitTest: function(point, options) {
- point = Point.read(arguments);
- options = HitResult.getOptions(Base.read(arguments));
+ hitTest: function() {
+ var point = Point.read(arguments),
+ options = HitResult.getOptions(Base.read(arguments));
for (var i = this.layers.length - 1; i >= 0; i--) {
- var res = this.layers[i].hitTest(point, options);
+ var res = this.layers[i]._hitTest(point, options);
if (res) return res;
}
return null;
- }
-}, new function() {
- function getItems(project, match, list) {
- var layers = project.layers,
- items = list && [];
- for (var i = 0, l = layers.length; i < l; i++) {
- var res = layers[i][list ? 'getItems' : 'getItem'](match);
- if (list) {
- items.push.apply(items, res);
- } else if (res)
- return res;
- }
- return list ? items : null;
- }
-
- return {
- getItems: function(match) {
- return getItems(this, match, true);
- },
-
- getItem: function(match) {
- return getItems(this, match, false);
- }
- };
-}, {
+ },
+
+ getItems: function(match) {
+ return Item._getItems(this.layers, match, true);
+ },
+
+ getItem: function(match) {
+ return Item._getItems(this.layers, match, false);
+ },
importJSON: function(json) {
this.activate();
@@ -2505,51 +2537,29 @@
return Base.importJSON(json, layer && layer.isEmpty() && layer);
},
- draw: function(ctx, matrix, ratio) {
- this._drawCount++;
+ draw: function(ctx, matrix, pixelRatio) {
+ this._updateVersion++;
ctx.save();
matrix.applyToContext(ctx);
var param = new Base({
offset: new Point(0, 0),
- ratio: ratio,
- transforms: [matrix],
- trackTransforms: true
+ pixelRatio: pixelRatio,
+ viewMatrix: matrix.isIdentity() ? null : matrix,
+ matrices: [new Matrix()],
+ updateMatrix: true
});
- for (var i = 0, l = this.layers.length; i < l; i++)
- this.layers[i].draw(ctx, param);
+ for (var i = 0, layers = this.layers, l = layers.length; i < l; i++)
+ layers[i].draw(ctx, param);
ctx.restore();
if (this._selectedItemCount > 0) {
ctx.save();
ctx.strokeWidth = 1;
- for (var id in this._selectedItems) {
- var item = this._selectedItems[id];
- if (item._drawCount === this._drawCount
- && (item._drawSelected || item._boundsSelected)) {
- var color = item.getSelectedColor()
- || item.getLayer().getSelectedColor();
- ctx.strokeStyle = ctx.fillStyle = color
- ? color.toCanvasStyle(ctx) : '#009dec';
- var mx = item._globalMatrix;
- if (item._drawSelected)
- item._drawSelected(ctx, mx);
- if (item._boundsSelected) {
- var coords = mx._transformCorners(
- item._getBounds('getBounds'));
- ctx.beginPath();
- for (var i = 0; i < 8; i++)
- ctx[i === 0 ? 'moveTo' : 'lineTo'](
- coords[i], coords[++i]);
- ctx.closePath();
- ctx.stroke();
- for (var i = 0; i < 8; i++) {
- ctx.beginPath();
- ctx.rect(coords[i] - 2, coords[++i] - 2, 4, 4);
- ctx.fill();
- }
- }
- }
- }
+ var items = this._selectedItems,
+ size = this._scope.settings.handleSize,
+ version = this._updateVersion;
+ for (var id in items)
+ items[id]._drawSelection(ctx, matrix, size, items, version);
ctx.restore();
}
}
@@ -2564,7 +2574,6 @@
this.project.symbols.push(this);
if (item)
this.setDefinition(item, dontCenter);
- this._instances = {};
},
_serialize: function(options, dictionary) {
@@ -2575,27 +2584,30 @@
},
_changed: function(flags) {
- Base.each(this._instances, function(item) {
- item._changed(flags);
- });
+ if (flags & 8) {
+ Item._clearBoundsCache(this);
+ }
+ if (flags & 1) {
+ this.project._needsUpdate = true;
+ }
},
getDefinition: function() {
return this._definition;
},
- setDefinition: function(item ) {
+ setDefinition: function(item, _dontCenter) {
if (item._parentSymbol)
item = item.clone();
if (this._definition)
- delete this._definition._parentSymbol;
+ this._definition._parentSymbol = null;
this._definition = item;
item.remove();
item.setSelected(false);
- if (!arguments[1])
+ if (!_dontCenter)
item.setPosition(new Point());
item._parentSymbol = this;
- this._changed(5);
+ this._changed(9);
},
place: function(position) {
@@ -2613,21 +2625,22 @@
if (src._serializeFields)
src._serializeFields = new Base(
this.prototype._serializeFields, src._serializeFields);
- var res = extend.base.apply(this, arguments),
- proto = res.prototype,
- name = proto._class;
- if (name)
- proto._type = Base.hyphenate(name);
- return res;
- }
+ return extend.base.apply(this, arguments);
+ },
+
+ NO_INSERT: { insert: false }
},
_class: 'Item',
- _transformContent: true,
+ _applyMatrix: true,
+ _canApplyMatrix: true,
_boundsSelected: false,
+ _selectChildren: false,
_serializeFields: {
name: null,
+ applyMatrix: null,
matrix: new Matrix(),
+ pivot: null,
locked: false,
visible: true,
blendMode: 'normal',
@@ -2642,21 +2655,29 @@
},
_initialize: function(props, point) {
- this._id = Item._id = (Item._id || 0) + 1;
- if (!this._project) {
- var project = paper.project;
- if (props && props.insert === false) {
- this._setProject(project);
- } else {
- (project.activeLayer || new Layer()).addChild(this);
- }
- }
- this._style = new Style(this._project._currentStyle, this);
- var matrix = this._matrix = new Matrix();
+ var hasProps = props && Base.isPlainObject(props),
+ internal = hasProps && props.internal === true,
+ matrix = this._matrix = new Matrix(),
+ project = paper.project;
+ if (!internal)
+ this._id = Item._id = (Item._id || 0) + 1;
+ this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix;
if (point)
matrix.translate(point);
matrix._owner = this;
- return props ? this._set(props, { insert: true }) : true;
+ this._style = new Style(project._currentStyle, this, project);
+ if (!this._project) {
+ if (internal || hasProps && props.insert === false) {
+ this._setProject(project);
+ } else if (hasProps && props.parent) {
+ this.setParent(props.parent);
+ } else {
+ (project.activeLayer || new Layer()).addChild(this);
+ }
+ }
+ if (hasProps && props !== Item.NO_INSERT)
+ this._set(props, { insert: true, parent: true }, true);
+ return hasProps;
},
_events: new function() {
@@ -2684,7 +2705,7 @@
var mouseEvent = {
install: function(type) {
- var counters = this._project.view._eventCounters;
+ var counters = this.getView()._eventCounters;
if (counters) {
for (var key in mouseFlags) {
counters[key] = (counters[key] || 0)
@@ -2693,7 +2714,7 @@
}
},
uninstall: function(type) {
- var counters = this._project.view._eventCounters;
+ var counters = this.getView()._eventCounters;
if (counters) {
for (var key in mouseFlags)
counters[key] -= mouseFlags[key][type] || 0;
@@ -2721,7 +2742,7 @@
},
_animateItem: function(animate) {
- this._project.view._animateItem(this, animate);
+ this.getView()._animateItem(this, animate);
},
_serialize: function(options, dictionary) {
@@ -2746,24 +2767,23 @@
},
_changed: function(flags) {
- var parent = this._parent,
- project = this._project,
- symbol = this._parentSymbol;
- this._drawCount = null;
- if (flags & 4) {
- delete this._bounds;
- delete this._position;
- }
- if (parent && (flags
- & (4 | 8))) {
- parent._clearBoundsCache();
+ var symbol = this._parentSymbol,
+ cacheParent = this._parent || symbol,
+ project = this._project;
+ if (flags & 8) {
+ this._bounds = this._position = this._decomposed =
+ this._globalMatrix = this._currentPath = undefined;
+ }
+ if (cacheParent
+ && (flags & 40)) {
+ Item._clearBoundsCache(cacheParent);
}
if (flags & 2) {
- this._clearBoundsCache();
+ Item._clearBoundsCache(this);
}
if (project) {
if (flags & 1) {
- project._needsRedraw = true;
+ project._needsUpdate = true;
}
if (project._changes) {
var entry = project._changesById[this._id];
@@ -2790,8 +2810,8 @@
return this._id;
},
- getType: function() {
- return this._type;
+ getClassName: function() {
+ return this._class;
},
getName: function() {
@@ -2805,9 +2825,10 @@
if (name === (+name) + '')
throw new Error(
'Names consisting only of numbers are not supported.');
- if (name && this._parent) {
- var children = this._parent._children,
- namedChildren = this._parent._namedChildren,
+ var parent = this._parent;
+ if (name && parent) {
+ var children = parent._children,
+ namedChildren = parent._namedChildren,
orig = name,
i = 1;
while (unique && children[name])
@@ -2816,7 +2837,7 @@
children[name] = this;
}
this._name = name || undefined;
- this._changed(32);
+ this._changed(128);
},
getStyle: function() {
@@ -2825,18 +2846,6 @@
setStyle: function(style) {
this.getStyle().set(style);
- },
-
- hasFill: function() {
- return this.getStyle().hasFill();
- },
-
- hasStroke: function() {
- return this.getStyle().hasStroke();
- },
-
- hasShadow: function() {
- return this.getStyle().hasShadow();
}
}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
function(name) {
@@ -2849,10 +2858,12 @@
if (value != this[name]) {
this[name] = value;
this._changed(name === '_locked'
- ? 32 : 33);
+ ? 128 : 129);
}
};
-}, {}), {
+ },
+{}), {
+ beans: true,
_locked: false,
@@ -2865,32 +2876,35 @@
_guide: false,
isSelected: function() {
- if (this._children) {
- for (var i = 0, l = this._children.length; i < l; i++)
- if (this._children[i].isSelected())
+ if (this._selectChildren) {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++)
+ if (children[i].isSelected())
return true;
}
return this._selected;
},
- setSelected: function(selected ) {
- if (this._children && !arguments[1]) {
- for (var i = 0, l = this._children.length; i < l; i++)
- this._children[i].setSelected(selected);
- }
- if ((selected = !!selected) != this._selected) {
+ setSelected: function(selected, noChildren) {
+ if (!noChildren && this._selectChildren) {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].setSelected(selected);
+ }
+ if ((selected = !!selected) ^ this._selected) {
this._selected = selected;
this._project._updateSelection(this);
- this._changed(33);
+ this._changed(129);
}
},
_selected: false,
isFullySelected: function() {
- if (this._children && this._selected) {
- for (var i = 0, l = this._children.length; i < l; i++)
- if (!this._children[i].isFullySelected())
+ var children = this._children;
+ if (children && this._selected) {
+ for (var i = 0, l = children.length; i < l; i++)
+ if (!children[i].isFullySelected())
return false;
return true;
}
@@ -2898,9 +2912,10 @@
},
setFullySelected: function(selected) {
- if (this._children) {
- for (var i = 0, l = this._children.length; i < l; i++)
- this._children[i].setFullySelected(selected);
+ var children = this._children;
+ if (children) {
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].setFullySelected(selected);
}
this.setSelected(selected, true);
},
@@ -2916,9 +2931,9 @@
this.setFillColor(null);
this.setStrokeColor(null);
}
- this._changed(33);
+ this._changed(129);
if (this._parent)
- this._parent._changed(256);
+ this._parent._changed(1024);
}
},
@@ -2931,73 +2946,64 @@
},
setData: function(data) {
- this._data = data;
- },
-
- getPosition: function() {
- var pos = this._position
- || (this._position = this.getBounds().getCenter(true));
- return new (arguments[0] ? Point : LinkedPoint)
- (pos.x, pos.y, this, 'setPosition');
+ this._data = data;
+ },
+
+ getPosition: function(_dontLink) {
+ var position = this._position,
+ ctor = _dontLink ? Point : LinkedPoint;
+ if (!position) {
+ var pivot = this._pivot;
+ position = this._position = pivot
+ ? this._matrix._transformPoint(pivot)
+ : this.getBounds().getCenter(true);
+ }
+ return new ctor(position.x, position.y, this, 'setPosition');
},
setPosition: function() {
this.translate(Point.read(arguments).subtract(this.getPosition(true)));
- }
-}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
- function(name) {
- this[name] = function() {
- var getter = this._boundsGetter,
- bounds = this._getCachedBounds(typeof getter == 'string'
- ? getter : getter && getter[name] || name, arguments[0]);
- return name === 'getBounds'
+ },
+
+ getPivot: function(_dontLink) {
+ var pivot = this._pivot;
+ if (pivot) {
+ var ctor = _dontLink ? Point : LinkedPoint;
+ pivot = new ctor(pivot.x, pivot.y, this, 'setPivot');
+ }
+ return pivot;
+ },
+
+ setPivot: function() {
+ this._pivot = Point.read(arguments);
+ this._position = undefined;
+ },
+
+ _pivot: null,
+
+ getRegistration: '#getPivot',
+ setRegistration: '#setPivot'
+}, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds',
+ 'internalBounds', 'internalRoughBounds'],
+ function(key) {
+ var getter = 'get' + Base.capitalize(key),
+ match = key.match(/^internal(.*)$/),
+ internalGetter = match ? 'get' + match[1] : null;
+ this[getter] = function(_matrix) {
+ var boundsGetter = this._boundsGetter,
+ name = !internalGetter && (typeof boundsGetter === 'string'
+ ? boundsGetter : boundsGetter && boundsGetter[getter])
+ || getter,
+ bounds = this._getCachedBounds(name, _matrix, this,
+ internalGetter);
+ return key === 'bounds'
? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
- bounds.height, this, 'setBounds')
+ bounds.height, this, 'setBounds')
: bounds;
};
},
{
- _getCachedBounds: function(getter, matrix, cacheItem) {
- var cache = (!matrix || matrix.equals(this._matrix)) && getter;
- if (cacheItem && this._parent) {
- var id = cacheItem._id,
- ref = this._parent._boundsCache
- = this._parent._boundsCache || {
- ids: {},
- list: []
- };
- if (!ref.ids[id]) {
- ref.list.push(cacheItem);
- ref.ids[id] = cacheItem;
- }
- }
- if (cache && this._bounds && this._bounds[cache])
- return this._bounds[cache].clone();
- var identity = this._matrix.isIdentity();
- matrix = !matrix || matrix.isIdentity()
- ? identity ? null : this._matrix
- : identity ? matrix : matrix.clone().concatenate(this._matrix);
- var bounds = this._getBounds(getter, matrix, cache ? this : cacheItem);
- if (cache) {
- if (!this._bounds)
- this._bounds = {};
- this._bounds[cache] = bounds.clone();
- }
- return bounds;
- },
-
- _clearBoundsCache: function() {
- if (this._boundsCache) {
- for (var i = 0, list = this._boundsCache.list, l = list.length;
- i < l; i++) {
- var item = list[i];
- delete item._bounds;
- if (item != this && item._boundsCache)
- item._clearBoundsCache();
- }
- delete this._boundsCache;
- }
- },
+ beans: true,
_getBounds: function(getter, matrix, cacheItem) {
var children = this._children;
@@ -3022,9 +3028,9 @@
: new Rectangle();
},
- setBounds: function(rect) {
- rect = Rectangle.read(arguments);
- var bounds = this.getBounds(),
+ setBounds: function() {
+ var rect = Rectangle.read(arguments),
+ bounds = this.getBounds(),
matrix = new Matrix(),
center = rect.getCenter();
matrix.translate(center);
@@ -3036,53 +3042,164 @@
center = bounds.getCenter();
matrix.translate(-center.x, -center.y);
this.transform(matrix);
+ },
+
+ _getCachedBounds: function(getter, matrix, cacheItem, internalGetter) {
+ matrix = matrix && matrix.orNullIfIdentity();
+ var _matrix = internalGetter ? null : this._matrix.orNullIfIdentity(),
+ cache = (!matrix || matrix.equals(_matrix)) && getter;
+ var cacheParent = this._parent || this._parentSymbol;
+ if (cacheParent) {
+ var id = cacheItem._id,
+ ref = cacheParent._boundsCache = cacheParent._boundsCache || {
+ ids: {},
+ list: []
+ };
+ if (!ref.ids[id]) {
+ ref.list.push(cacheItem);
+ ref.ids[id] = cacheItem;
+ }
+ }
+ if (cache && this._bounds && this._bounds[cache])
+ return this._bounds[cache].clone();
+ matrix = !matrix
+ ? _matrix
+ : _matrix
+ ? matrix.chain(_matrix)
+ : matrix;
+ var bounds = this._getBounds(internalGetter || getter, matrix,
+ cacheItem);
+ if (cache) {
+ if (!this._bounds)
+ this._bounds = {};
+ var cached = this._bounds[cache] = bounds.clone();
+ cached._internal = !!internalGetter;
+ }
+ return bounds;
+ },
+
+ statics: {
+ _clearBoundsCache: function(item) {
+ var cache = item._boundsCache;
+ if (cache) {
+ item._bounds = item._position = item._boundsCache = undefined;
+ for (var i = 0, list = cache.list, l = list.length; i < l; i++) {
+ var other = list[i];
+ if (other !== item) {
+ other._bounds = other._position = undefined;
+ if (other._boundsCache)
+ Item._clearBoundsCache(other);
+ }
+ }
+ }
+ }
}
}), {
+ beans: true,
+
+ _decompose: function() {
+ return this._decomposed = this._matrix.decompose();
+ },
+
+ getRotation: function() {
+ var decomposed = this._decomposed || this._decompose();
+ return decomposed && decomposed.rotation;
+ },
+
+ setRotation: function(rotation) {
+ var current = this.getRotation();
+ if (current != null && rotation != null) {
+ var decomposed = this._decomposed;
+ this.rotate(rotation - current);
+ decomposed.rotation = rotation;
+ this._decomposed = decomposed;
+ }
+ },
+
+ getScaling: function() {
+ var decomposed = this._decomposed || this._decompose();
+ return decomposed && decomposed.scaling;
+ },
+
+ setScaling: function() {
+ var current = this.getScaling();
+ if (current != null) {
+ var scaling = Point.read(arguments, 0, { clone: true }),
+ decomposed = this._decomposed;
+ this.scale(scaling.x / current.x, scaling.y / current.y);
+ decomposed.scaling = scaling;
+ this._decomposed = decomposed;
+ }
+ },
+
getMatrix: function() {
return this._matrix;
},
setMatrix: function(matrix) {
this._matrix.initialize(matrix);
- if (this._transformContent)
- this.applyMatrix(true);
- this._changed(5);
- },
-
- getGlobalMatrix: function() {
- return this._drawCount === this._project._drawCount
- && this._globalMatrix || null;
- },
-
- getTransformContent: function() {
- return this._transformContent;
- },
-
- setTransformContent: function(transform) {
- this._transformContent = transform;
- if (transform)
- this.applyMatrix();
- },
-
+ if (this._applyMatrix) {
+ this.transform(null, true);
+ } else {
+ this._changed(9);
+ }
+ },
+
+ getGlobalMatrix: function(_dontClone) {
+ var matrix = this._globalMatrix,
+ updateVersion = this._project._updateVersion;
+ if (matrix && matrix._updateVersion !== updateVersion)
+ matrix = null;
+ if (!matrix) {
+ matrix = this._globalMatrix = this._matrix.clone();
+ var parent = this._parent;
+ if (parent)
+ matrix.preConcatenate(parent.getGlobalMatrix(true));
+ matrix._updateVersion = updateVersion;
+ }
+ return _dontClone ? matrix : matrix.clone();
+ },
+
+ getApplyMatrix: function() {
+ return this._applyMatrix;
+ },
+
+ setApplyMatrix: function(transform) {
+ if (this._applyMatrix = this._canApplyMatrix && !!transform)
+ this.transform(null, true);
+ },
+
+ getTransformContent: '#getApplyMatrix',
+ setTransformContent: '#setApplyMatrix',
+}, {
getProject: function() {
return this._project;
},
- _setProject: function(project) {
- if (this._project != project) {
- var hasOnFrame = this.responds('frame');
- if (hasOnFrame)
- this._animateItem(false);
+ _setProject: function(project, installEvents) {
+ if (this._project !== project) {
+ if (this._project)
+ this._installEvents(false);
this._project = project;
- if (hasOnFrame)
- this._animateItem(true);
- if (this._children) {
- for (var i = 0, l = this._children.length; i < l; i++) {
- this._children[i]._setProject(project);
- }
- }
- }
+ var children = this._children;
+ for (var i = 0, l = children && children.length; i < l; i++)
+ children[i]._setProject(project);
+ installEvents = true;
+ }
+ if (installEvents)
+ this._installEvents(true);
+ },
+
+ getView: function() {
+ return this._project.getView();
+ },
+
+ _installEvents: function _installEvents(install) {
+ _installEvents.base.call(this, install);
+ var children = this._children;
+ for (var i = 0, l = children && children.length; i < l; i++)
+ children[i]._installEvents(install);
},
getLayer: function() {
@@ -3132,13 +3249,9 @@
return this._index;
},
- isInserted: function() {
- return this._parent ? this._parent.isInserted() : false;
- },
-
equals: function(item) {
return item === this || item && this._class === item._class
- && this._style.equals(item._style)
+ && this._style.equals(item._style)
&& this._matrix.equals(item._matrix)
&& this._locked === item._locked
&& this._visible === item._visible
@@ -3155,7 +3268,7 @@
},
clone: function(insert) {
- return this._clone(new this.constructor({ insert: false }), insert);
+ return this._clone(new this.constructor(Item.NO_INSERT), insert);
},
_clone: function(copy, insert) {
@@ -3167,7 +3280,7 @@
if (insert || insert === undefined)
copy.insertAbove(this);
var keys = ['_locked', '_visible', '_blendMode', '_opacity',
- '_clipMask', '_guide'];
+ '_clipMask', '_guide', '_applyMatrix'];
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
if (this.hasOwnProperty(key))
@@ -3187,22 +3300,21 @@
rasterize: function(resolution) {
var bounds = this.getStrokeBounds(),
- scale = (resolution || 72) / 72,
+ scale = (resolution || this.getView().getResolution()) / 72,
topLeft = bounds.getTopLeft().floor(),
- bottomRight = bounds.getBottomRight().ceil()
+ bottomRight = bounds.getBottomRight().ceil(),
size = new Size(bottomRight.subtract(topLeft)),
- canvas = CanvasProvider.getCanvas(size),
+ canvas = CanvasProvider.getCanvas(size.multiply(scale)),
ctx = canvas.getContext('2d'),
matrix = new Matrix().scale(scale).translate(topLeft.negate());
ctx.save();
matrix.applyToContext(ctx);
- this.draw(ctx, new Base({ transforms: [matrix] }));
+ this.draw(ctx, new Base({ matrices: [matrix] }));
ctx.restore();
- var raster = new Raster({
- canvas: canvas,
- insert: false
- });
- raster.setPosition(topLeft.add(size.divide(2)));
+ var raster = new Raster(Item.NO_INSERT);
+ raster.setCanvas(canvas);
+ raster.transform(new Matrix().translate(topLeft.add(size.divide(2)))
+ .scale(1 / scale));
raster.insertAbove(this);
return raster;
},
@@ -3220,33 +3332,52 @@
}
return false;
}
- return point.isInside(this._getBounds('getBounds'));
- },
-
- hitTest: function(point, options) {
- point = Point.read(arguments);
- options = HitResult.getOptions(Base.read(arguments));
-
- if (this._locked || !this._visible || this._guide && !options.guides)
+ return point.isInside(this.getInternalBounds());
+ },
+
+ hitTest: function() {
+ return this._hitTest(
+ Point.read(arguments),
+ HitResult.getOptions(Base.read(arguments)));
+ },
+
+ _hitTest: function(point, options) {
+ if (this._locked || !this._visible || this._guide && !options.guides
+ || this.isEmpty())
return null;
- if (!this._children && !this.getRoughBounds()
- .expand(2 * options.tolerance)._containsPoint(point))
+ var matrix = this._matrix,
+ parentTotalMatrix = options._totalMatrix,
+ view = this.getView(),
+ totalMatrix = options._totalMatrix = parentTotalMatrix
+ ? parentTotalMatrix.chain(matrix)
+ : this.getGlobalMatrix().preConcatenate(view._matrix),
+ tolerancePadding = options._tolerancePadding = new Size(
+ Path._getPenPadding(1, totalMatrix.inverted())
+ ).multiply(
+ Math.max(options.tolerance, 0.00001)
+ );
+ point = matrix._inverseTransform(point);
+
+ if (!this._children && !this.getInternalRoughBounds()
+ .expand(tolerancePadding.multiply(2))._containsPoint(point))
return null;
- point = this._matrix._inverseTransform(point);
-
- var that = this,
+ var checkSelf = !(options.guides && !this._guide
+ || options.selected && !this._selected
+ || options.type && options.type !== Base.hyphenate(this._class)
+ || options.class && !(this instanceof options.class)),
+ that = this,
res;
+
function checkBounds(type, part) {
var pt = bounds['get' + part]();
- if (point.getDistance(pt) < options.tolerance)
+ if (point.subtract(pt).divide(tolerancePadding).length <= 1)
return new HitResult(type, that,
{ name: Base.hyphenate(part), point: pt });
}
- if ((options.center || options.bounds) &&
- !(this instanceof Layer && !this._parent)) {
- var bounds = this._getBounds('getBounds');
+ if (checkSelf && (options.center || options.bounds) && this._parent) {
+ var bounds = this.getInternalBounds();
if (options.center)
res = checkBounds('center', 'Center');
if (!res && options.bounds) {
@@ -3259,24 +3390,27 @@
}
}
- if ((res || (res = this._children || !(options.guides && !this._guide
- || options.selected && !this._selected)
- ? this._hitTest(point, options) : null))
- && res.point) {
- res.point = that._matrix.transform(res.point);
- }
+ var children = !res && this._children;
+ if (children) {
+ var opts = this._getChildHitTestOptions(options);
+ for (var i = children.length - 1; i >= 0 && !res; i--)
+ res = children[i]._hitTest(point, opts);
+ }
+ if (!res && checkSelf)
+ res = this._hitTestSelf(point, options);
+ if (res && res.point)
+ res.point = matrix.transform(res.point);
+ options._totalMatrix = parentTotalMatrix;
return res;
},
- _hitTest: function(point, options) {
- var children = this._children;
- if (children) {
- for (var i = children.length - 1, res; i >= 0; i--)
- if (res = children[i].hitTest(point, options))
- return res;
- } else if (options.fill && this.hasFill() && this._contains(point)) {
+ _getChildHitTestOptions: function(options) {
+ return options;
+ },
+
+ _hitTestSelf: function(point, options) {
+ if (options.fill && this.hasFill() && this._contains(point))
return new HitResult('fill', this);
- }
},
matches: function(match) {
@@ -3299,7 +3433,12 @@
if (match.hasOwnProperty(key)) {
var value = this[key],
compare = match[key];
- if (compare instanceof RegExp) {
+ if (value === undefined && key === 'type')
+ value = Base.hyphenate(this._class);
+ if (/^(constructor|class)$/.test(key)) {
+ if (!(this instanceof compare))
+ return false;
+ } else if (compare instanceof RegExp) {
if (!compare.test(value))
return false;
} else if (typeof compare === 'function') {
@@ -3314,39 +3453,38 @@
}
}
return true;
- }
-}, new function() {
- function getItems(item, match, list) {
- var children = item._children,
- items = list && [];
- for (var i = 0, l = children && children.length; i < l; i++) {
- var child = children[i];
- if (child.matches(match)) {
+ },
+
+ getItems: function(match) {
+ return Item._getItems(this._children, match, true);
+ },
+
+ getItem: function(match) {
+ return Item._getItems(this._children, match, false);
+ },
+
+ statics: {
+ _getItems: function _getItems(children, match, list) {
+ var items = list && [];
+ for (var i = 0, l = children && children.length; i < l; i++) {
+ var child = children[i];
+ if (child.matches(match)) {
+ if (list) {
+ items.push(child);
+ } else {
+ return child;
+ }
+ }
+ var res = _getItems(child._children, match, list);
if (list) {
- items.push(child);
- } else {
- return child;
- }
- }
- var res = getItems(child, match, list);
- if (list) {
- items.push.apply(items, res);
- } else if (res) {
- return res;
- }
- }
- return list ? items : null;
- }
-
- return {
- getItems: function(match) {
- return getItems(this, match, true);
- },
-
- getItem: function(match) {
- return getItems(this, match, false);
- }
- };
+ items.push.apply(items, res);
+ } else if (res) {
+ return res;
+ }
+ }
+ return list ? items : null;
+ }
+ }
}, {
importJSON: function(json) {
@@ -3369,26 +3507,31 @@
return this.insertChildren(this._children.length, items, _preserve);
},
- insertChildren: function(index, items, _preserve, _type) {
+ insertChildren: function(index, items, _preserve, _proto) {
var children = this._children;
if (children && items && items.length > 0) {
items = Array.prototype.slice.apply(items);
for (var i = items.length - 1; i >= 0; i--) {
var item = items[i];
- if (_type && item._type !== _type)
+ if (_proto && !(item instanceof _proto)) {
items.splice(i, 1);
- else
- item._remove(true);
+ } else {
+ item._remove(false, true);
+ }
}
Base.splice(children, items, index, 0);
+ var project = this._project,
+ notifySelf = project && project._changes;
for (var i = 0, l = items.length; i < l; i++) {
var item = items[i];
item._parent = this;
- item._setProject(this._project);
+ item._setProject(this._project, true);
if (item._name)
item.setName(item._name);
- }
- this._changed(7);
+ if (notifySelf)
+ this._changed(5);
+ }
+ this._changed(11);
} else {
items = null;
}
@@ -3400,7 +3543,7 @@
return null;
var index = item._index + (above ? 1 : 0);
if (item._parent === this._parent && index > this._index)
- index--;
+ index--;
return item._parent.insertChild(index, this, _preserve);
},
@@ -3409,8 +3552,8 @@
},
insertBelow: function(item, _preserve) {
- return this._insert(false, item, _preserve);
- },
+ return this._insert(false, item, _preserve);
+ },
sendToBack: function() {
return this._parent.insertChild(0, this);
@@ -3432,7 +3575,7 @@
reduce: function() {
if (this._children && this._children.length === 1) {
- var child = this._children[0];
+ var child = this._children[0].reduce();
child.insertAbove(this);
child.setStyle(this._style);
this.remove();
@@ -3442,33 +3585,41 @@
},
_removeNamed: function() {
- var children = this._parent._children,
- namedChildren = this._parent._namedChildren,
- name = this._name,
- namedArray = namedChildren[name],
- index = namedArray ? namedArray.indexOf(this) : -1;
- if (index == -1)
- return;
- if (children[name] == this)
- delete children[name];
- namedArray.splice(index, 1);
- if (namedArray.length) {
- children[name] = namedArray[namedArray.length - 1];
- } else {
- delete namedChildren[name];
- }
- },
-
- _remove: function(notify) {
- if (this._parent) {
+ var parent = this._parent;
+ if (parent) {
+ var children = parent._children,
+ namedChildren = parent._namedChildren,
+ name = this._name,
+ namedArray = namedChildren[name],
+ index = namedArray ? namedArray.indexOf(this) : -1;
+ if (index !== -1) {
+ if (children[name] == this)
+ delete children[name];
+ namedArray.splice(index, 1);
+ if (namedArray.length) {
+ children[name] = namedArray[namedArray.length - 1];
+ } else {
+ delete namedChildren[name];
+ }
+ }
+ }
+ },
+
+ _remove: function(notifySelf, notifyParent) {
+ var parent = this._parent;
+ if (parent) {
if (this._name)
this._removeNamed();
if (this._index != null)
- Base.splice(this._parent._children, null, this._index, 1);
- if (this.responds('frame'))
- this._animateItem(false);
- if (notify)
- this._parent._changed(7);
+ Base.splice(parent._children, null, this._index, 1);
+ this._installEvents(false);
+ if (notifySelf) {
+ var project = this._project;
+ if (project && project._changes)
+ this._changed(5);
+ }
+ if (notifyParent)
+ parent._changed(11);
this._parent = null;
return true;
}
@@ -3476,7 +3627,7 @@
},
remove: function() {
- return this._remove(true);
+ return this._remove(true, true);
},
removeChildren: function(from, to) {
@@ -3485,10 +3636,11 @@
from = from || 0;
to = Base.pick(to, this._children.length);
var removed = Base.splice(this._children, null, from, to - from);
- for (var i = removed.length - 1; i >= 0; i--)
- removed[i]._remove(false);
+ for (var i = removed.length - 1; i >= 0; i--) {
+ removed[i]._remove(true, false);
+ }
if (removed.length > 0)
- this._changed(7);
+ this._changed(11);
return removed;
},
@@ -3499,12 +3651,12 @@
this._children.reverse();
for (var i = 0, l = this._children.length; i < l; i++)
this._children[i]._index = i;
- this._changed(7);
+ this._changed(11);
}
},
isEmpty: function() {
- return !this._children || this._children.length == 0;
+ return !this._children || this._children.length === 0;
},
isEditable: function() {
@@ -3517,6 +3669,18 @@
return true;
},
+ hasFill: function() {
+ return this.getStyle().hasFill();
+ },
+
+ hasStroke: function() {
+ return this.getStyle().hasStroke();
+ },
+
+ hasShadow: function() {
+ return this.getStyle().hasShadow();
+ },
+
_getOrder: function(item) {
function getList(item) {
var list = [];
@@ -3539,6 +3703,10 @@
return this._children && this._children.length > 0;
},
+ isInserted: function() {
+ return this._parent ? this._parent.isInserted() : false;
+ },
+
isAbove: function(item) {
return this._getOrder(item) === -1;
},
@@ -3572,7 +3740,7 @@
var parent = this._parent;
while (parent) {
if (parent._parent
- && /^(group|layer|compound-path)$/.test(parent._type)
+ && /^(Group|Layer|CompoundPath)$/.test(parent._class)
&& item.isDescendant(parent))
return true;
parent = parent._parent;
@@ -3580,90 +3748,87 @@
return false;
},
- scale: function(hor, ver , center) {
- if (arguments.length < 2 || typeof ver === 'object') {
- center = ver;
- ver = hor;
- }
- return this.transform(new Matrix().scale(hor, ver,
- center || this.getPosition(true)));
- },
-
translate: function() {
var mx = new Matrix();
return this.transform(mx.translate.apply(mx, arguments));
},
- rotate: function(angle, center) {
+ rotate: function(angle ) {
return this.transform(new Matrix().rotate(angle,
+ Point.read(arguments, 1, { readNull: true })
+ || this.getPosition(true)));
+ }
+}, Base.each(['scale', 'shear', 'skew'], function(name) {
+ this[name] = function() {
+ var point = Point.read(arguments),
+ center = Point.read(arguments, 0, { readNull: true });
+ return this.transform(new Matrix()[name](point,
center || this.getPosition(true)));
- },
-
- shear: function(hor, ver, center) {
- if (arguments.length < 2 || typeof ver === 'object') {
- center = ver;
- ver = hor;
- }
- return this.transform(new Matrix().shear(hor, ver,
- center || this.getPosition(true)));
- },
-
- transform: function(matrix ) {
+ };
+}, {
+
+}), {
+ transform: function(matrix, _applyMatrix) {
+ if (matrix && matrix.isIdentity())
+ matrix = null;
+ var _matrix = this._matrix,
+ applyMatrix = (_applyMatrix || this._applyMatrix)
+ && (!_matrix.isIdentity() || matrix);
+ if (!matrix && !applyMatrix)
+ return this;
+ if (matrix)
+ _matrix.preConcatenate(matrix);
+ if (applyMatrix = applyMatrix && this._transformContent(_matrix)) {
+ var pivot = this._pivot,
+ style = this._style,
+ fillColor = style.getFillColor(true),
+ strokeColor = style.getStrokeColor(true);
+ if (pivot)
+ _matrix._transformPoint(pivot, pivot, true);
+ if (fillColor)
+ fillColor.transform(_matrix);
+ if (strokeColor)
+ strokeColor.transform(_matrix);
+ _matrix.reset(true);
+ }
var bounds = this._bounds,
position = this._position;
- this._matrix.preConcatenate(matrix);
- if (this._transformContent || arguments[1])
- this.applyMatrix(true);
- this._changed(5);
- if (bounds && matrix.getRotation() % 90 === 0) {
+ this._changed(9);
+ var decomp = bounds && matrix && matrix.decompose();
+ if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
for (var key in bounds) {
var rect = bounds[key];
- matrix._transformBounds(rect, rect);
+ if (applyMatrix || !rect._internal)
+ matrix._transformBounds(rect, rect);
}
var getter = this._boundsGetter,
rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
if (rect)
this._position = rect.getCenter(true);
this._bounds = bounds;
- } else if (position) {
+ } else if (matrix && position) {
this._position = matrix._transformPoint(position, position);
}
return this;
},
- _applyMatrix: function(matrix, applyMatrix) {
+ _transformContent: function(matrix) {
var children = this._children;
- if (children && children.length > 0) {
+ if (children) {
for (var i = 0, l = children.length; i < l; i++)
- children[i].transform(matrix, applyMatrix);
+ children[i].transform(matrix, true);
return true;
}
},
- applyMatrix: function(_dontNotify) {
- var matrix = this._matrix;
- if (this._applyMatrix(matrix, true)) {
- var style = this._style,
- fillColor = style.getFillColor(true),
- strokeColor = style.getStrokeColor(true);
- if (fillColor)
- fillColor.transform(matrix);
- if (strokeColor)
- strokeColor.transform(matrix);
- matrix.reset();
- }
- if (!_dontNotify)
- this._changed(5);
- },
-
globalToLocal: function() {
- var matrix = this.getGlobalMatrix();
- return matrix && matrix._transformPoint(Point.read(arguments));
+ return this.getGlobalMatrix(true)._inverseTransform(
+ Point.read(arguments));
},
localToGlobal: function() {
- var matrix = this.getGlobalMatrix();
- return matrix && matrix._inverseTransform(Point.read(arguments));
+ return this.getGlobalMatrix(true)._transformPoint(
+ Point.read(arguments));
},
fitBounds: function(rectangle, fill) {
@@ -3728,59 +3893,125 @@
}
},
- draw: function(ctx, param) {
+ draw: function(ctx, param, parentStrokeMatrix) {
+ var updateVersion = this._updateVersion = this._project._updateVersion;
if (!this._visible || this._opacity === 0)
return;
- this._drawCount = this._project._drawCount;
- var trackTransforms = param.trackTransforms,
- transforms = param.transforms,
- parentMatrix = transforms[transforms.length - 1],
- globalMatrix = parentMatrix.clone().concatenate(this._matrix);
- if (trackTransforms)
- transforms.push(this._globalMatrix = globalMatrix);
+ var matrices = param.matrices,
+ parentMatrix = matrices[matrices.length - 1],
+ viewMatrix = param.viewMatrix,
+ matrix = this._matrix,
+ globalMatrix = parentMatrix.chain(matrix);
+ if (!globalMatrix.isInvertible())
+ return;
+
+ function getViewMatrix(matrix) {
+ return viewMatrix ? viewMatrix.chain(matrix) : matrix;
+ }
+
+ matrices.push(globalMatrix);
+ if (param.updateMatrix) {
+ globalMatrix._updateVersion = updateVersion;
+ this._globalMatrix = globalMatrix;
+ }
+
var blendMode = this._blendMode,
opacity = this._opacity,
normalBlend = blendMode === 'normal',
nativeBlend = BlendMode.nativeModes[blendMode],
direct = normalBlend && opacity === 1
+ || param.dontStart
+ || param.clip
|| (nativeBlend || normalBlend && opacity < 1)
&& this._canComposite(),
+ pixelRatio = param.pixelRatio,
mainCtx, itemOffset, prevOffset;
if (!direct) {
- var bounds = this.getStrokeBounds(parentMatrix);
+ var bounds = this.getStrokeBounds(getViewMatrix(parentMatrix));
if (!bounds.width || !bounds.height)
return;
prevOffset = param.offset;
itemOffset = param.offset = bounds.getTopLeft().floor();
mainCtx = ctx;
- ctx = CanvasProvider.getContext(
- bounds.getSize().ceil().add(new Size(1, 1)), param.ratio);
+ ctx = CanvasProvider.getContext(bounds.getSize().ceil().add(1)
+ .multiply(pixelRatio));
+ if (pixelRatio !== 1)
+ ctx.scale(pixelRatio, pixelRatio);
}
ctx.save();
+ var strokeMatrix = parentStrokeMatrix
+ ? parentStrokeMatrix.chain(matrix)
+ : !this.getStrokeScaling(true) && getViewMatrix(globalMatrix),
+ clip = !direct && param.clipItem,
+ transform = !strokeMatrix || clip;
if (direct) {
ctx.globalAlpha = opacity;
if (nativeBlend)
ctx.globalCompositeOperation = blendMode;
- } else {
+ } else if (transform) {
ctx.translate(-itemOffset.x, -itemOffset.y);
}
- (direct ? this._matrix : globalMatrix).applyToContext(ctx);
- if (!direct && param.clipItem)
+ if (transform)
+ (direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
+ if (clip)
param.clipItem.draw(ctx, param.extend({ clip: true }));
- this._draw(ctx, param);
+ if (strokeMatrix) {
+ ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
+ var offset = param.offset;
+ if (offset)
+ ctx.translate(-offset.x, -offset.y);
+ }
+ this._draw(ctx, param, strokeMatrix);
ctx.restore();
- if (trackTransforms)
- transforms.pop();
- if (param.clip)
+ matrices.pop();
+ if (param.clip && !param.dontFinish)
ctx.clip();
if (!direct) {
BlendMode.process(blendMode, ctx, mainCtx, opacity,
- itemOffset.subtract(prevOffset).multiply(param.ratio));
+ itemOffset.subtract(prevOffset).multiply(pixelRatio));
CanvasProvider.release(ctx);
param.offset = prevOffset;
}
},
+ _isUpdated: function(updateVersion) {
+ var parent = this._parent;
+ if (parent instanceof CompoundPath)
+ return parent._isUpdated(updateVersion);
+ var updated = this._updateVersion === updateVersion;
+ if (!updated && parent && parent._visible
+ && parent._isUpdated(updateVersion)) {
+ this._updateVersion = updateVersion;
+ updated = true;
+ }
+ return updated;
+ },
+
+ _drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) {
+ if ((this._drawSelected || this._boundsSelected)
+ && this._isUpdated(updateVersion)) {
+ var color = this.getSelectedColor(true)
+ || this.getLayer().getSelectedColor(true),
+ mx = matrix.chain(this.getGlobalMatrix(true));
+ ctx.strokeStyle = ctx.fillStyle = color
+ ? color.toCanvasStyle(ctx) : '#009dec';
+ if (this._drawSelected)
+ this._drawSelected(ctx, mx, selectedItems);
+ if (this._boundsSelected) {
+ var half = size / 2;
+ coords = mx._transformCorners(this.getInternalBounds());
+ ctx.beginPath();
+ for (var i = 0; i < 8; i++)
+ ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
+ ctx.closePath();
+ ctx.stroke();
+ for (var i = 0; i < 8; i++)
+ ctx.fillRect(coords[i] - half, coords[++i] - half,
+ size, size);
+ }
+ }
+ },
+
_canComposite: function() {
return false;
}
@@ -3808,6 +4039,7 @@
var Group = Item.extend({
_class: 'Group',
+ _selectChildren: true,
_serializeFields: {
children: []
},
@@ -3821,24 +4053,25 @@
_changed: function _changed(flags) {
_changed.base.call(this, flags);
- if (flags & 2 && this._transformContent
- && !this._matrix.isIdentity()) {
- this.applyMatrix();
- }
- if (flags & (2 | 256)) {
- delete this._clipItem;
+ if (flags & 1026) {
+ this._clipItem = undefined;
}
},
_getClipItem: function() {
- if (this._clipItem !== undefined)
- return this._clipItem;
- for (var i = 0, l = this._children.length; i < l; i++) {
- var child = this._children[i];
- if (child._clipMask)
- return this._clipItem = child;
- }
- return this._clipItem = null;
+ var clipItem = this._clipItem;
+ if (clipItem === undefined) {
+ clipItem = null;
+ for (var i = 0, l = this._children.length; i < l; i++) {
+ var child = this._children[i];
+ if (child._clipMask) {
+ clipItem = child;
+ break;
+ }
+ }
+ this._clipItem = clipItem;
+ }
+ return clipItem;
},
isClipped: function() {
@@ -3852,15 +4085,31 @@
},
_draw: function(ctx, param) {
- var clipItem = param.clipItem = this._getClipItem();
- if (clipItem)
+ var clip = param.clip,
+ clipItem = !clip && this._getClipItem(),
+ draw = true;
+ param = param.extend({ clipItem: clipItem, clip: false });
+ if (clip) {
+ if (this._currentPath) {
+ ctx.currentPath = this._currentPath;
+ draw = false;
+ } else {
+ ctx.beginPath();
+ param.dontStart = param.dontFinish = true;
+ }
+ } else if (clipItem) {
clipItem.draw(ctx, param.extend({ clip: true }));
- for (var i = 0, l = this._children.length; i < l; i++) {
- var item = this._children[i];
- if (item !== clipItem)
- item.draw(ctx, param);
- }
- param.clipItem = null;
+ }
+ if (draw) {
+ for (var i = 0, l = this._children.length; i < l; i++) {
+ var item = this._children[i];
+ if (item !== clipItem)
+ item.draw(ctx, param);
+ }
+ }
+ if (clip) {
+ this._currentPath = ctx.currentPath;
+ }
}
});
@@ -3869,7 +4118,7 @@
initialize: function Layer(arg) {
var props = Base.isPlainObject(arg)
- ? new Base(arg)
+ ? new Base(arg)
: { children: Array.isArray(arg) ? arg : arguments },
insert = props.insert;
props.insert = false;
@@ -3888,13 +4137,14 @@
this._project.activeLayer = this.getNextSibling()
|| this.getPreviousSibling();
Base.splice(this._project.layers, null, this._index, 1);
- this._project._needsRedraw = true;
+ this._installEvents(false);
+ this._project._needsUpdate = true;
return true;
}
return false;
},
- getNextSibling: function getNextSibling() {
+ getNextSibling: function getNextSibling() {
return this._parent ? getNextSibling.base.call(this)
: this._project.layers[this._index + 1] || null;
},
@@ -3914,10 +4164,10 @@
_insert: function _insert(above, item, _preserve) {
if (item instanceof Layer && !item._parent) {
- this._remove(true);
+ this._remove(true, true);
Base.splice(item._project.layers, [this],
item._index + (above ? 1 : 0), 0);
- this._setProject(item._project);
+ this._setProject(item._project, true);
return this;
}
return _insert.base.call(this, above, item, _preserve);
@@ -3926,32 +4176,43 @@
var Shape = Item.extend({
_class: 'Shape',
- _transformContent: false,
+ _applyMatrix: false,
+ _canApplyMatrix: false,
_boundsSelected: true,
-
- initialize: function Shape(shape, center, size, radius, props) {
- this._shape = shape;
- this._size = size;
- this._radius = radius;
- this._initialize(props, center);
+ _serializeFields: {
+ type: null,
+ size: null,
+ radius: null
+ },
+
+ initialize: function Shape(props) {
+ this._initialize(props);
},
_equals: function(item) {
- return this._shape === item._shape
+ return this._type === item._type
&& this._size.equals(item._size)
&& Base.equals(this._radius, item._radius);
},
clone: function(insert) {
- return this._clone(new Shape(this._shape, this.getPosition(true),
- this._size.clone(),
- this._radius.clone ? this._radius.clone() : this._radius,
- { insert: false }), insert);
- },
-
- getShape: function() {
- return this._shape;
- },
+ var copy = new Shape(Item.NO_INSERT);
+ copy.setType(this._type);
+ copy.setSize(this._size);
+ copy.setRadius(this._radius);
+ return this._clone(copy, insert);
+ },
+
+ getType: function() {
+ return this._type;
+ },
+
+ setType: function(type) {
+ this._type = type;
+ },
+
+ getShape: '#getType',
+ setShape: '#setType',
getSize: function() {
var size = this._size;
@@ -3959,35 +4220,37 @@
},
setSize: function() {
- var shape = this._shape,
- size = Size.read(arguments);
- if (!this._size.equals(size)) {
- var width = size.width,
+ var size = Size.read(arguments);
+ if (!this._size) {
+ this._size = size.clone();
+ } else if (!this._size.equals(size)) {
+ var type = this._type,
+ width = size.width,
height = size.height;
- if (shape === 'rectangle') {
+ if (type === 'rectangle') {
var radius = Size.min(this._radius, size.divide(2));
this._radius.set(radius.width, radius.height);
- } else if (shape === 'circle') {
+ } else if (type === 'circle') {
width = height = (width + height) / 2;
this._radius = width / 2;
- } else if (shape === 'ellipse') {
+ } else if (type === 'ellipse') {
this._radius.set(width / 2, height / 2);
}
this._size.set(width, height);
- this._changed(5);
+ this._changed(9);
}
},
getRadius: function() {
var rad = this._radius;
- return this._shape === 'circle'
+ return this._type === 'circle'
? rad
: new LinkedSize(rad.width, rad.height, this, 'setRadius');
},
setRadius: function(radius) {
- var shape = this._shape;
- if (shape === 'circle') {
+ var type = this._type;
+ if (type === 'circle') {
if (radius === this._radius)
return;
var size = radius * 2;
@@ -3995,17 +4258,21 @@
this._size.set(size, size);
} else {
radius = Size.read(arguments);
- if (this._radius.equals(radius))
- return;
- this._radius.set(radius.width, radius.height);
- if (shape === 'rectangle') {
- var size = Size.max(this._size, radius.multiply(2));
- this._size.set(size.width, size.height);
- } else if (shape === 'ellipse') {
- this._size.set(radius.width * 2, radius.height * 2);
- }
- }
- this._changed(5);
+ if (!this._radius) {
+ this._radius = radius.clone();
+ } else {
+ if (this._radius.equals(radius))
+ return;
+ this._radius.set(radius.width, radius.height);
+ if (type === 'rectangle') {
+ var size = Size.max(this._size, radius.multiply(2));
+ this._size.set(size.width, size.height);
+ } else if (type === 'ellipse') {
+ this._size.set(radius.width * 2, radius.height * 2);
+ }
+ }
+ }
+ this._changed(9);
},
isEmpty: function() {
@@ -4013,7 +4280,7 @@
},
toPath: function(insert) {
- var path = new Path[Base.capitalize(this._shape)]({
+ var path = new Path[Base.capitalize(this._type)]({
center: new Point(),
size: this._size,
radius: this._radius,
@@ -4026,55 +4293,70 @@
return path;
},
- _draw: function(ctx, param) {
+ _draw: function(ctx, param, strokeMatrix) {
var style = this._style,
hasFill = style.hasFill(),
hasStroke = style.hasStroke(),
- clip = param.clip;
- if (hasFill || hasStroke || clip) {
- var radius = this._radius,
- shape = this._shape;
- ctx.beginPath();
- if (shape === 'circle') {
+ dontPaint = param.dontFinish || param.clip,
+ untransformed = !strokeMatrix;
+ if (hasFill || hasStroke || dontPaint) {
+ var type = this._type,
+ radius = this._radius,
+ isCircle = type === 'circle';
+ if (!param.dontStart)
+ ctx.beginPath();
+ if (untransformed && isCircle) {
ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
} else {
- var rx = radius.width,
- ry = radius.height,
- kappa = Numerical.KAPPA;
- if (shape === 'ellipse') {
- var cx = rx * kappa,
- cy = ry * kappa;
- ctx.moveTo(-rx, 0);
- ctx.bezierCurveTo(-rx, -cy, -cx, -ry, 0, -ry);
- ctx.bezierCurveTo(cx, -ry, rx, -cy, rx, 0);
- ctx.bezierCurveTo(rx, cy, cx, ry, 0, ry);
- ctx.bezierCurveTo(-cx, ry, -rx, cy, -rx, 0);
- } else {
- var size = this._size,
- width = size.width,
- height = size.height;
- if (rx === 0 && ry === 0) {
- ctx.rect(-width / 2, -height / 2, width, height);
- } else {
- kappa = 1 - kappa;
- var x = width / 2,
- y = height / 2,
- cx = rx * kappa,
- cy = ry * kappa;
- ctx.moveTo(-x, -y + ry);
- ctx.bezierCurveTo(-x, -y + cy, -x + cx, -y, -x + rx, -y);
- ctx.lineTo(x - rx, -y);
- ctx.bezierCurveTo(x - cx, -y, x, -y + cy, x, -y + ry);
- ctx.lineTo(x, y - ry);
- ctx.bezierCurveTo(x, y - cy, x - cx, y, x - rx, y);
- ctx.lineTo(-x + rx, y);
- ctx.bezierCurveTo(-x + cx, y, -x, y - cy, -x, y - ry);
- }
+ var rx = isCircle ? radius : radius.width,
+ ry = isCircle ? radius : radius.height,
+ size = this._size,
+ width = size.width,
+ height = size.height;
+ if (untransformed && type === 'rect' && rx === 0 && ry === 0) {
+ ctx.rect(-width / 2, -height / 2, width, height);
+ } else {
+ var x = width / 2,
+ y = height / 2,
+ kappa = 1 - 0.5522847498307936,
+ cx = rx * kappa,
+ cy = ry * kappa,
+ c = [
+ -x, -y + ry,
+ -x, -y + cy,
+ -x + cx, -y,
+ -x + rx, -y,
+ x - rx, -y,
+ x - cx, -y,
+ x, -y + cy,
+ x, -y + ry,
+ x, y - ry,
+ x, y - cy,
+ x - cx, y,
+ x - rx, y,
+ -x + rx, y,
+ -x + cx, y,
+ -x, y - cy,
+ -x, y - ry
+ ];
+ if (strokeMatrix)
+ strokeMatrix.transform(c, c, 32);
+ ctx.moveTo(c[0], c[1]);
+ ctx.bezierCurveTo(c[2], c[3], c[4], c[5], c[6], c[7]);
+ if (x !== rx)
+ ctx.lineTo(c[8], c[9]);
+ ctx.bezierCurveTo(c[10], c[11], c[12], c[13], c[14], c[15]);
+ if (y !== ry)
+ ctx.lineTo(c[16], c[17]);
+ ctx.bezierCurveTo(c[18], c[19], c[20], c[21], c[22], c[23]);
+ if (x !== rx)
+ ctx.lineTo(c[24], c[25]);
+ ctx.bezierCurveTo(c[26], c[27], c[28], c[29], c[30], c[31]);
}
}
ctx.closePath();
}
- if (!clip && (hasFill || hasStroke)) {
+ if (!dontPaint && (hasFill || hasStroke)) {
this._setStyles(ctx);
if (hasFill) {
ctx.fill(style.getWindingRule());
@@ -4096,7 +4378,7 @@
return matrix ? matrix._transformBounds(rect) : rect;
}
},
-new function() {
+new function() {
function getCornerCenter(that, point, expand) {
var radius = that._radius;
@@ -4124,7 +4406,7 @@
return {
_contains: function _contains(point) {
- if (this._shape === 'rectangle') {
+ if (this._type === 'rectangle') {
var center = getCornerCenter(this, point);
return center
? point.subtract(center).divide(this._radius)
@@ -4135,13 +4417,13 @@
}
},
- _hitTest: function _hitTest(point, options) {
+ _hitTestSelf: function _hitTestSelf(point, options) {
var hit = false;
if (this.hasStroke()) {
- var shape = this._shape,
+ var type = this._type,
radius = this._radius,
strokeWidth = this.getStrokeWidth() + 2 * options.tolerance;
- if (shape === 'rectangle') {
+ if (type === 'rectangle') {
var center = getCornerCenter(this, point, strokeWidth);
if (center) {
var pt = point.subtract(center);
@@ -4155,7 +4437,7 @@
&& !inner._containsPoint(point);
}
} else {
- if (shape === 'ellipse')
+ if (type === 'ellipse')
radius = getEllipseRadius(point, radius);
hit = 2 * Math.abs(point.getLength() - radius)
<= strokeWidth;
@@ -4163,14 +4445,18 @@
}
return hit
? new HitResult('stroke', this)
- : _hitTest.base.apply(this, arguments);
+ : _hitTestSelf.base.apply(this, arguments);
}
};
}, {
statics: new function() {
- function createShape(shape, point, size, radius, args) {
- return new Shape(shape, point, size, radius, Base.getNamed(args));
+ function createShape(type, point, size, radius, args) {
+ var item = new Shape(Base.getNamed(args));
+ item._type = type;
+ item._size = size;
+ item._radius = radius;
+ return item.translate(point);
}
return {
@@ -4190,7 +4476,7 @@
},
Ellipse: function() {
- var ellipse = Shape._readEllipse(arguments);
+ var ellipse = Shape._readEllipse(arguments),
radius = ellipse.radius;
return createShape('ellipse', ellipse.center, radius.multiply(2),
radius, arguments);
@@ -4214,7 +4500,8 @@
var Raster = Item.extend({
_class: 'Raster',
- _transformContent: false,
+ _applyMatrix: false,
+ _canApplyMatrix: false,
_boundsGetter: 'getBounds',
_boundsSelected: true,
_serializeFields: {
@@ -4239,15 +4526,17 @@
},
clone: function(insert) {
- var param = { insert: false },
- image = this._image;
+ var copy = new Raster(Item.NO_INSERT),
+ image = this._image,
+ canvas = this._canvas;
if (image) {
- param.image = image;
- } else if (this._canvas) {
- var canvas = param.canvas = CanvasProvider.getCanvas(this._size);
- canvas.getContext('2d').drawImage(this._canvas, 0, 0);
- }
- return this._clone(new Raster(param), insert);
+ copy.setImage(image);
+ } else if (canvas) {
+ var copyCanvas = CanvasProvider.getCanvas(this._size);
+ copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
+ copy.setCanvas(copyCanvas);
+ }
+ return this._clone(copy, insert);
},
getSize: function() {
@@ -4275,10 +4564,10 @@
},
isEmpty: function() {
- return this._size.width == 0 && this._size.height == 0;
- },
-
- getPpi: function() {
+ return this._size.width === 0 && this._size.height === 0;
+ },
+
+ getResolution: function() {
var matrix = this._matrix,
orig = new Point(0, 0).transform(matrix),
u = new Point(1, 0).transform(matrix).subtract(orig),
@@ -4289,6 +4578,8 @@
);
},
+ getPpi: '#getResolution',
+
getImage: function() {
return this._image;
},
@@ -4296,7 +4587,7 @@
setImage: function(image) {
if (this._canvas)
CanvasProvider.release(this._canvas);
- if (image.getContext) {
+ if (image && image.getContext) {
this._image = null;
this._canvas = image;
} else {
@@ -4304,10 +4595,10 @@
this._canvas = null;
}
this._size = new Size(
- image.naturalWidth || image.width,
- image.naturalHeight || image.height);
+ image ? image.naturalWidth || image.width : 0,
+ image ? image.naturalHeight || image.height : 0);
this._context = null;
- this._changed(5 | 129);
+ this._changed(521);
},
getCanvas: function() {
@@ -4326,12 +4617,12 @@
setCanvas: '#setImage',
- getContext: function() {
+ getContext: function(modify) {
if (!this._context)
this._context = this.getCanvas().getContext('2d');
- if (arguments[0]) {
+ if (modify) {
this._image = null;
- this._changed(129);
+ this._changed(513);
}
return this._context;
},
@@ -4346,25 +4637,25 @@
setSource: function(src) {
var that = this,
- image = document.getElementById(src) || new Image();
+ image;
function loaded() {
- var view = that._project.view;
- if (view)
+ var view = that.getView();
+ if (view) {
paper = view._scope;
- that.fire('load');
- if (view)
- view.draw(true);
- }
+ that.setImage(image);
+ that.fire('load');
+ view.update();
+ }
+ }
+
+ image = document.getElementById(src) || new Image();
if (image.naturalWidth && image.naturalHeight) {
setTimeout(loaded, 0);
} else {
DomEvent.add(image, {
- load: function() {
- that.setImage(image);
- loaded();
- }
+ load: loaded
});
if (!image.src)
image.src = src;
@@ -4374,22 +4665,22 @@
getElement: function() {
return this._canvas || this._image;
- },
-
- getSubCanvas: function(rect) {
- rect = Rectangle.read(arguments);
- var ctx = CanvasProvider.getContext(rect.getSize());
+ }
+}, {
+ beans: false,
+
+ getSubCanvas: function() {
+ var rect = Rectangle.read(arguments),
+ ctx = CanvasProvider.getContext(rect.getSize());
ctx.drawImage(this.getCanvas(), rect.x, rect.y,
rect.width, rect.height, 0, 0, rect.width, rect.height);
return ctx.canvas;
},
- getSubRaster: function(rect) {
- rect = Rectangle.read(arguments);
- var raster = new Raster({
- canvas: this.getSubCanvas(rect),
- insert: false
- });
+ getSubRaster: function() {
+ var rect = Rectangle.read(arguments),
+ raster = new Raster(Item.NO_INSERT);
+ raster.setCanvas(this.getSubCanvas(rect));
raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
raster._matrix.preConcatenate(this._matrix);
raster.insertAbove(this);
@@ -4404,8 +4695,8 @@
return canvas ? canvas.toDataURL() : null;
},
- drawImage: function(image, point) {
- point = Point.read(arguments, 1);
+ drawImage: function(image ) {
+ var point = Point.read(arguments, 1);
this.getContext(true).drawImage(image, point.x, point.y);
},
@@ -4437,7 +4728,7 @@
.translate(-bounds.x, -bounds.y);
matrix.applyToContext(ctx);
if (path)
- path.draw(ctx, new Base({ clip: true, transforms: [matrix] }));
+ path.draw(ctx, new Base({ clip: true, matrices: [matrix] }));
this._matrix.applyToContext(ctx);
ctx.drawImage(this.getElement(),
-this._size.width / 2, -this._size.height / 2);
@@ -4459,8 +4750,8 @@
return total ? Color.read(channels) : null;
},
- getPixel: function(point) {
- point = Point.read(arguments);
+ getPixel: function() {
+ var point = Point.read(arguments);
var data = this.getContext().getImageData(point.x, point.y, 1, 1).data;
return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],
data[3] / 255);
@@ -4481,21 +4772,21 @@
ctx.putImageData(imageData, point.x, point.y);
},
- createImageData: function(size) {
- size = Size.read(arguments);
+ createImageData: function() {
+ var size = Size.read(arguments);
return this.getContext().createImageData(size.width, size.height);
},
- getImageData: function(rect) {
- rect = Rectangle.read(arguments);
+ getImageData: function() {
+ var rect = Rectangle.read(arguments);
if (rect.isEmpty())
rect = new Rectangle(this._size);
return this.getContext().getImageData(rect.x, rect.y,
rect.width, rect.height);
},
- setImageData: function(data, point) {
- point = Point.read(arguments, 1);
+ setImageData: function(data ) {
+ var point = Point.read(arguments, 1);
this.getContext(true).putImageData(data, point.x, point.y);
},
@@ -4504,7 +4795,7 @@
return matrix ? matrix._transformBounds(rect) : rect;
},
- _hitTest: function(point) {
+ _hitTestSelf: function(point) {
if (this._contains(point)) {
var that = this;
return new HitResult('pixel', that, {
@@ -4534,7 +4825,8 @@
var PlacedSymbol = Item.extend({
_class: 'PlacedSymbol',
- _transformContent: false,
+ _applyMatrix: false,
+ _canApplyMatrix: false,
_boundsGetter: { getBounds: 'getStrokeBounds' },
_boundsSelected: true,
_serializeFields: {
@@ -4556,32 +4848,30 @@
},
setSymbol: function(symbol) {
- if (this._symbol)
- delete this._symbol._instances[this._id];
this._symbol = symbol;
- symbol._instances[this._id] = this;
+ this._changed(9);
},
clone: function(insert) {
- return this._clone(new PlacedSymbol({
- symbol: this.symbol,
- insert: false
- }), insert);
+ var copy = new PlacedSymbol(Item.NO_INSERT);
+ copy.setSymbol(this._symbol);
+ return this._clone(copy, insert);
},
isEmpty: function() {
return this._symbol._definition.isEmpty();
},
- _getBounds: function(getter, matrix) {
- return this.symbol._definition._getCachedBounds(getter, matrix);
- },
-
- _hitTest: function(point, options, matrix) {
- var result = this._symbol._definition._hitTest(point, options, matrix);
- if (result)
- result.item = this;
- return result;
+ _getBounds: function(getter, matrix, cacheItem) {
+ return this.symbol._definition._getCachedBounds(getter, matrix,
+ cacheItem);
+ },
+
+ _hitTestSelf: function(point, options) {
+ var res = this._symbol._definition._hitTest(point, options);
+ if (res)
+ res.item = this;
+ return res;
},
_draw: function(ctx, param) {
@@ -4604,9 +4894,9 @@
statics: {
getOptions: function(options) {
- return options && options._merged ? options : new Base({
+ return new Base({
type: null,
- tolerance: paper.project.options.hitTolerance || 2,
+ tolerance: paper.settings.hitTolerance,
fill: !options,
stroke: !options,
segments: !options,
@@ -4615,8 +4905,7 @@
center: false,
bounds: false,
guides: false,
- selected: false,
- _merged: true
+ selected: false
}, options);
}
}
@@ -4624,6 +4913,7 @@
var Segment = Base.extend({
_class: 'Segment',
+ beans: true,
initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
var count = arguments.length,
@@ -4643,7 +4933,7 @@
point = arg0;
handleIn = arg1;
handleOut = arg2;
- } else {
+ } else {
point = arg0 !== undefined ? [ arg0, arg1 ] : null;
handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
@@ -4655,31 +4945,35 @@
_serialize: function(options) {
return Base.serialize(this.isLinear() ? this._point
- : [this._point, this._handleIn, this._handleOut], options, true);
+ : [this._point, this._handleIn, this._handleOut],
+ options, true);
},
_changed: function(point) {
- if (!this._path)
+ var path = this._path;
+ if (!path)
return;
- var curve = this._path._curves && this.getCurve(),
- other;
- if (curve) {
- curve._changed();
- if (other = (curve[point == this._point
- || point == this._handleIn && curve._segment1 == this
- ? 'getPrevious' : 'getNext']())) {
- other._changed();
- }
- }
- this._path._changed(5);
+ var curves = path._curves,
+ index = this._index,
+ curveIn, curveOut;
+ if (curves) {
+ if ((!point || point === this._point || point === this._handleIn)
+ && (curveIn = curves[index - 1]
+ || path._closed && curves[curves.length - 1]))
+ curveIn._changed();
+ if ((!point || point === this._point || point === this._handleOut)
+ && (curveOut = curves[index]))
+ curveOut._changed();
+ }
+ path._changed(25);
},
getPoint: function() {
return this._point;
},
- setPoint: function(point) {
- point = Point.read(arguments);
+ setPoint: function() {
+ var point = Point.read(arguments);
this._point.set(point.x, point.y);
},
@@ -4687,8 +4981,8 @@
return this._handleIn;
},
- setHandleIn: function(point) {
- point = Point.read(arguments);
+ setHandleIn: function() {
+ var point = Point.read(arguments);
this._handleIn.set(point.x, point.y);
},
@@ -4696,8 +4990,8 @@
return this._handleOut;
},
- setHandleOut: function(point) {
- point = Point.read(arguments);
+ setHandleOut: function() {
+ var point = Point.read(arguments);
this._handleOut.set(point.x, point.y);
},
@@ -4705,9 +4999,12 @@
return this._handleIn.isZero() && this._handleOut.isZero();
},
- setLinear: function() {
- this._handleIn.set(0, 0);
- this._handleOut.set(0, 0);
+ setLinear: function(linear) {
+ if (linear) {
+ this._handleIn.set(0, 0);
+ this._handleOut.set(0, 0);
+ } else {
+ }
},
isColinear: function(segment) {
@@ -4732,7 +5029,7 @@
var next = this.getNext(),
handle1 = this._handleOut,
handle2 = next._handleIn,
- kappa = Numerical.KAPPA;
+ kappa = 0.5522847498307936;
if (handle1.isOrthogonal(handle2)) {
var from = this._point,
to = next._point,
@@ -4746,58 +5043,37 @@
return false;
},
- _isSelected: function(point) {
+ _selectionState: 0,
+
+ isSelected: function(_point) {
var state = this._selectionState;
- return point == this._point ? !!(state & 4)
- : point == this._handleIn ? !!(state & 1)
- : point == this._handleOut ? !!(state & 2)
+ return !_point ? !!(state & 7)
+ : _point === this._point ? !!(state & 4)
+ : _point === this._handleIn ? !!(state & 1)
+ : _point === this._handleOut ? !!(state & 2)
: false;
},
- _setSelected: function(point, selected) {
+ setSelected: function(selected, _point) {
var path = this._path,
- selected = !!selected,
- state = this._selectionState || 0,
- selection = [
- !!(state & 4),
- !!(state & 1),
- !!(state & 2)
- ];
- if (point === this._point) {
- if (selected) {
- selection[1] = selection[2] = false;
- } else {
- var previous = this.getPrevious(),
- next = this.getNext();
- selection[1] = previous && (previous._point.isSelected()
- || previous._handleOut.isSelected());
- selection[2] = next && (next._point.isSelected()
- || next._handleIn.isSelected());
- }
- selection[0] = selected;
+ selected = !!selected,
+ state = this._selectionState,
+ oldState = state,
+ flag = !_point ? 7
+ : _point === this._point ? 4
+ : _point === this._handleIn ? 1
+ : _point === this._handleOut ? 2
+ : 0;
+ if (selected) {
+ state |= flag;
} else {
- var index = point === this._handleIn ? 1 : 2;
- if (selection[index] != selected) {
- if (selected)
- selection[0] = false;
- selection[index] = selected;
- }
- }
- this._selectionState = (selection[0] ? 4 : 0)
- | (selection[1] ? 1 : 0)
- | (selection[2] ? 2 : 0);
- if (path && state != this._selectionState) {
- path._updateSelection(this, state, this._selectionState);
- path._changed(33);
- }
- },
-
- isSelected: function() {
- return this._isSelected(this._point);
- },
-
- setSelected: function(selected) {
- this._setSelected(this._point, selected);
+ state &= ~flag;
+ }
+ this._selectionState = state;
+ if (path && state !== oldState) {
+ path._updateSelection(this, oldState, state);
+ path._changed(129);
+ }
},
getIndex: function() {
@@ -4812,7 +5088,8 @@
var path = this._path,
index = this._index;
if (path) {
- if (!path._closed && index == path._segments.length - 1)
+ if (index > 0 && !path._closed
+ && index === path._segments.length - 1)
index--;
return path.getCurves()[index] || null;
}
@@ -4821,7 +5098,9 @@
getLocation: function() {
var curve = this.getCurve();
- return curve ? new CurveLocation(curve, curve.getNext() ? 0 : 1) : null;
+ return curve
+ ? new CurveLocation(curve, this === curve._segment1 ? 0 : 1)
+ : null;
},
getNext: function() {
@@ -4865,9 +5144,14 @@
return '{ ' + parts.join(', ') + ' }';
},
+ transform: function(matrix) {
+ this._transformCoordinates(matrix, new Array(6), true);
+ this._changed();
+ },
+
_transformCoordinates: function(matrix, coords, change) {
var point = this._point,
- handleIn = !change || !this._handleIn.isZero()
+ handleIn = !change || !this._handleIn.isZero()
? this._handleIn : null,
handleOut = !change || !this._handleOut.isZero()
? this._handleOut : null,
@@ -4885,7 +5169,7 @@
coords[i++] = handleOut._y + y;
}
if (matrix) {
- matrix._transformCoordinates(coords, 0, coords, 0, i / 2);
+ matrix._transformCoordinates(coords, coords, i / 2);
x = coords[0];
y = coords[1];
if (change) {
@@ -4920,15 +5204,16 @@
var x, y, selected;
if (!point) {
x = y = 0;
- } else if ((x = point[0]) !== undefined) {
+ } else if ((x = point[0]) !== undefined) {
y = point[1];
} else {
- if ((x = point.x) === undefined) {
- point = Point.read(arguments);
- x = point.x;
- }
- y = point.y;
- selected = point.selected;
+ var pt = point;
+ if ((x = pt.x) === undefined) {
+ pt = Point.read(arguments);
+ x = pt.x;
+ }
+ y = pt.y;
+ selected = pt.selected;
}
this._x = x;
this._y = y;
@@ -4977,11 +5262,11 @@
},
setSelected: function(selected) {
- this._owner._setSelected(this, selected);
+ this._owner.setSelected(selected, this);
},
isSelected: function() {
- return this._owner._isSelected(this);
+ return this._owner.isSelected(this);
}
});
@@ -5021,16 +5306,15 @@
},
_changed: function() {
- delete this._length;
- delete this._bounds;
+ this._length = this._bounds = undefined;
},
getPoint1: function() {
return this._segment1._point;
},
- setPoint1: function(point) {
- point = Point.read(arguments);
+ setPoint1: function() {
+ var point = Point.read(arguments);
this._segment1._point.set(point.x, point.y);
},
@@ -5038,8 +5322,8 @@
return this._segment2._point;
},
- setPoint2: function(point) {
- point = Point.read(arguments);
+ setPoint2: function() {
+ var point = Point.read(arguments);
this._segment2._point.set(point.x, point.y);
},
@@ -5047,8 +5331,8 @@
return this._segment1._handleOut;
},
- setHandle1: function(point) {
- point = Point.read(arguments);
+ setHandle1: function() {
+ var point = Point.read(arguments);
this._segment1._handleOut.set(point.x, point.y);
},
@@ -5056,8 +5340,8 @@
return this._segment2._handleIn;
},
- setHandle2: function(point) {
- point = Point.read(arguments);
+ setHandle2: function() {
+ var point = Point.read(arguments);
this._segment2._handleIn.set(point.x, point.y);
},
@@ -5090,16 +5374,21 @@
},
isSelected: function() {
- return this.getHandle1().isSelected() && this.getHandle2().isSelected();
+ return this.getPoint1().isSelected()
+ && this.getHandle2().isSelected()
+ && this.getHandle2().isSelected()
+ && this.getPoint2().isSelected();
},
setSelected: function(selected) {
+ this.getPoint1().setSelected(selected);
this.getHandle1().setSelected(selected);
this.getHandle2().setSelected(selected);
- },
-
- getValues: function() {
- return Curve.getValues(this._segment1, this._segment2);
+ this.getPoint2().setSelected(selected);
+ },
+
+ getValues: function(matrix) {
+ return Curve.getValues(this._segment1, this._segment2, matrix);
},
getPoints: function() {
@@ -5111,15 +5400,12 @@
},
getLength: function() {
- var from = arguments[0],
- to = arguments[1],
- fullLength = arguments.length === 0 || from === 0 && to === 1;
- if (fullLength && this._length != null)
- return this._length;
- var length = Curve.getLength(this.getValues(), from, to);
- if (fullLength)
- this._length = length;
- return length;
+ if (this._length == null) {
+ this._length = this.isLinear()
+ ? this._segment2._point.getDistance(this._segment1._point)
+ : Curve.getLength(this.getValues(), 0, 1);
+ }
+ return this._length;
},
getArea: function() {
@@ -5130,37 +5416,42 @@
return new Curve(Curve.getPart(this.getValues(), from, to));
},
+ getPartLength: function(from, to) {
+ return Curve.getLength(this.getValues(), from, to);
+ },
+
isLinear: function() {
return this._segment1._handleOut.isZero()
&& this._segment2._handleIn.isZero();
},
+ isHorizontal: function() {
+ return this.isLinear() && Numerical.isZero(
+ this._segment1._point._y - this._segment2._point._y);
+ },
+
getIntersections: function(curve) {
return Curve.getIntersections(this.getValues(), curve.getValues(),
this, curve, []);
},
- reverse: function() {
- return new Curve(this._segment2.reverse(), this._segment1.reverse());
- },
-
_getParameter: function(offset, isParameter) {
return isParameter
? offset
: offset && offset.curve === this
? offset.parameter
: offset === undefined && isParameter === undefined
- ? 0.5
+ ? 0.5
: this.getParameterAt(offset, 0);
},
- divide: function(offset, isParameter) {
+ divide: function(offset, isParameter, ignoreLinear) {
var parameter = this._getParameter(offset, isParameter),
tolerance = 0.00001,
res = null;
if (parameter > tolerance && parameter < 1 - tolerance) {
var parts = Curve.subdivide(this.getValues(), parameter),
- isLinear = this.isLinear(),
+ isLinear = ignoreLinear ? false : this.isLinear(),
left = parts[0],
right = parts[1];
@@ -5182,7 +5473,7 @@
} else {
this._path.insert(this._segment2._index, segment);
}
- res = this;
+ res = this;
} else {
var end = this._segment2;
this._segment2 = segment;
@@ -5199,6 +5490,22 @@
: null;
},
+ reverse: function() {
+ return new Curve(this._segment2.reverse(), this._segment1.reverse());
+ },
+
+ remove: function() {
+ var removed = false;
+ if (this._path) {
+ var segment2 = this._segment2,
+ handleOut = segment2._handleOut;
+ removed = segment2.remove();
+ if (removed)
+ this._segment1._handleOut.set(handleOut.x, handleOut.y);
+ }
+ return removed;
+ },
+
clone: function() {
return new Curve(this._segment1, this._segment2);
},
@@ -5214,17 +5521,20 @@
},
statics: {
- getValues: function(segment1, segment2) {
+ getValues: function(segment1, segment2, matrix) {
var p1 = segment1._point,
h1 = segment1._handleOut,
h2 = segment2._handleIn,
- p2 = segment2._point;
- return [
- p1._x, p1._y,
- p1._x + h1._x, p1._y + h1._y,
- p2._x + h2._x, p2._y + h2._y,
- p2._x, p2._y
- ];
+ p2 = segment2._point,
+ values = [
+ p1._x, p1._y,
+ p1._x + h1._x, p1._y + h1._y,
+ p2._x + h2._x, p2._y + h2._y,
+ p2._x, p2._y
+ ];
+ if (matrix)
+ matrix._transformCoordinates(values, values, 6);
+ return values;
},
evaluate: function(v, t, type) {
@@ -5232,11 +5542,13 @@
c1x = v[2], c1y = v[3],
c2x = v[4], c2y = v[5],
p2x = v[6], p2y = v[7],
+ tolerance = 0.00001,
x, y;
- if (type === 0 && (t === 0 || t === 1)) {
- x = t === 0 ? p1x : p2x;
- y = t === 0 ? p1y : p2y;
+ if (type === 0 && (t < tolerance || t > 1 - tolerance)) {
+ var isZero = t < tolerance;
+ x = isZero ? p1x : p2x;
+ y = isZero ? p1y : p2y;
} else {
var cx = 3 * (c1x - p1x),
bx = 3 * (c2x - c1x) - cx,
@@ -5249,11 +5561,16 @@
x = ((ax * t + bx) * t + cx) * t + p1x;
y = ((ay * t + by) * t + cy) * t + p1y;
} else {
- var tolerance = 0.00001;
if (t < tolerance && c1x === p1x && c1y === p1y
|| t > 1 - tolerance && c2x === p2x && c2y === p2y) {
x = p2x - p1x;
y = p2y - p1y;
+ } else if (t < tolerance) {
+ x = cx;
+ y = cy;
+ } else if (t > 1 - tolerance) {
+ x = 3 * (p2x - c2x);
+ y = 3 * (p2y - c2y);
} else {
x = (3 * ax * t + 2 * bx) * t + cx;
y = (3 * ay * t + 2 * by) * t + cy;
@@ -5265,7 +5582,7 @@
}
}
}
- return type == 2 ? new Point(y, -x) : new Point(x, y);
+ return type === 2 ? new Point(y, -x) : new Point(x, y);
},
subdivide: function(v, t) {
@@ -5283,8 +5600,8 @@
p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y,
p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y;
return [
- [p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y],
- [p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y]
+ [p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y],
+ [p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y]
];
},
@@ -5300,11 +5617,10 @@
},
getParameterOf: function(v, x, y) {
- if (Math.abs(v[0] - x) < 0.00001
- && Math.abs(v[1] - y) < 0.00001)
+ var tolerance = 0.00001;
+ if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance)
return 0;
- if (Math.abs(v[6] - x) < 0.00001
- && Math.abs(v[7] - y) < 0.00001)
+ if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance)
return 1;
var txs = [],
tys = [],
@@ -5317,7 +5633,7 @@
if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) {
if (sx == -1) tx = ty;
else if (sy == -1) ty = tx;
- if (Math.abs(tx - ty) < 0.00001)
+ if (Math.abs(tx - ty) < tolerance)
return (tx + ty) * 0.5;
}
}
@@ -5330,9 +5646,9 @@
getPart: function(v, from, to) {
if (from > 0)
- v = Curve.subdivide(v, from)[1];
+ v = Curve.subdivide(v, from)[1];
if (to < 1)
- v = Curve.subdivide(v, (to - from) / (1 - from))[0];
+ v = Curve.subdivide(v, (to - from) / (1 - from))[0];
return v;
},
@@ -5369,8 +5685,8 @@
},
getBounds: function(v) {
- var min = v.slice(0, 2),
- max = min.slice(),
+ var min = v.slice(0, 2),
+ max = min.slice(),
roots = [0, 0];
for (var i = 0; i < 2; i++)
Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6],
@@ -5404,87 +5720,6 @@
+ t * t * t * v3,
padding);
}
- },
-
- _getWinding: function(v, prev, x, y, roots1, roots2) {
- var tolerance = 0.00001,
- abs = Math.abs;
-
- function getDirection(v) {
- var y0 = v[1],
- y1 = v[7],
- dir = y0 > y1 ? -1 : 1;
- return dir === 1 && (y < y0 || y > y1)
- || dir === -1 && (y < y1 || y > y0)
- ? 0
- : dir;
- }
-
- if (Curve.isLinear(v)) {
- var dir = getDirection(v);
- if (!dir)
- return 0;
- var cross = (v[6] - v[0]) * (y - v[1]) - (v[7] - v[1]) * (x - v[0]);
- return (cross < -tolerance ? -1 : 1) == dir ? 0 : dir;
- }
-
- var y0 = v[1],
- y1 = v[3],
- y2 = v[5],
- y3 = v[7];
- var a = 3 * (y1 - y2) - y0 + y3,
- b = 2 * (y0 + y2) - 4 * y1,
- c = y1 - y0;
- var count = Numerical.solveQuadratic(a, b, c, roots1, tolerance,
- 1 - tolerance),
- part,
- rest = v,
- t1 = roots1[0],
- winding = 0;
- for (var i = 0; i <= count; i++) {
- if (i === count) {
- part = rest;
- } else {
- var curves = Curve.subdivide(rest, t1);
- part = curves[0];
- rest = curves[1];
- t1 = roots1[i];
- t1 = (roots1[i + 1] - t1) / (1 - t1);
- }
- if (i > 0)
- part[3] = part[1];
- if (i < count)
- part[5] = rest[1];
- var dir = getDirection(part);
- if (!dir)
- continue;
- var t2,
- px;
- if (Curve.solveCubic(part, 1, y, roots2, -tolerance, 1 + -tolerance)
- === 1) {
- t2 = roots2[0];
- px = Curve.evaluate(part, t2, 0).x;
- } else {
- var mid = (part[1] + part[7]) / 2;
- t2 = y < mid && dir > 0 ? 0 : 1;
- if (t2 === 1 && y == part[7])
- continue;
- px = t2 === 0 ? part[0] : part[6];
- }
- var slope = Curve.evaluate(part, t2, 1).y,
- stationary = abs(slope) < tolerance || t2 < tolerance
- && Curve.evaluate(prev, 1, 1).y * slope < 0;
- if (x >= px + (stationary ? -tolerance : tolerance * dir)
- && !(stationary && (abs(t2) < tolerance
- && abs(x - part[0]) > tolerance
- || abs(t2 - 1) < tolerance
- && abs(x - part[6]) > tolerance))) {
- winding += stationary && abs(t2 - (dir > 0 ? 1 : 0)) < tolerance
- ? -dir : dir;
- }
- prev = part;
- }
- return winding;
}
}}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
function(name) {
@@ -5513,13 +5748,15 @@
};
},
{
+ beans: false,
+
getParameterAt: function(offset, start) {
return Curve.getParameterAt(this.getValues(), offset,
start !== undefined ? start : offset < 0 ? 1 : 0);
},
- getParameterOf: function(point) {
- point = Point.read(arguments);
+ getParameterOf: function() {
+ var point = Point.read(arguments);
return Curve.getParameterOf(this.getValues(), point.x, point.y);
},
@@ -5529,17 +5766,21 @@
return new CurveLocation(this, offset);
},
- getLocationOf: function(point) {
- point = Point.read(arguments);
- var t = this.getParameterOf(point);
+ getLocationOf: function() {
+ var point = Point.read(arguments),
+ t = this.getParameterOf(point);
return t != null ? new CurveLocation(this, t) : null;
},
- getNearestLocation: function(point) {
- point = Point.read(arguments);
- var values = this.getValues(),
+ getOffsetOf: function() {
+ var loc = this.getLocationOf.apply(this, arguments);
+ return loc ? loc.getOffset() : null;
+ },
+
+ getNearestLocation: function() {
+ var point = Point.read(arguments),
+ values = this.getValues(),
count = 100,
- tolerance = Numerical.TOLERANCE,
minDist = Infinity,
minT = 0;
@@ -5559,7 +5800,7 @@
refine(i / count);
var step = 1 / (count * 2);
- while (step > tolerance) {
+ while (step > 0.00001) {
if (!refine(minT - step) && !refine(minT + step))
step /= 2;
}
@@ -5568,13 +5809,12 @@
point.getDistance(pt));
},
- getNearestPoint: function(point) {
- point = Point.read(arguments);
- return this.getNearestLocation(point).getPoint();
+ getNearestPoint: function() {
+ return this.getNearestLocation.apply(this, arguments).getPoint();
}
}),
-new function() {
+new function() {
function getLengthIntegrand(v) {
var p1x = v[0], p1y = v[1],
@@ -5610,11 +5850,12 @@
if (b === undefined)
b = 1;
var isZero = Numerical.isZero;
- if (isZero(v[0] - v[2]) && isZero(v[1] - v[3])
+ if (a === 0 && b === 1
+ && isZero(v[0] - v[2]) && isZero(v[1] - v[3])
&& isZero(v[6] - v[4]) && isZero(v[7] - v[5])) {
- var dx = v[6] - v[0],
- dy = v[7] - v[1];
- return (b - a) * Math.sqrt(dx * dx + dy * dy);
+ var dx = v[6] - v[0],
+ dy = v[7] - v[1];
+ return Math.sqrt(dx * dx + dy * dy);
}
var ds = getLengthIntegrand(v);
return Numerical.integrate(ds, a, b, getIterations(a, b));
@@ -5643,151 +5884,92 @@
return length - offset;
}
return Numerical.findRoot(f, ds,
- forward ? a + guess : b - guess,
+ forward ? a + guess : b - guess,
a, b, 16, 0.00001);
}
};
-}, new function() {
- function addLocation(locations, curve1, t1, point1, curve2, t2, point2) {
- var first = locations[0],
- last = locations[locations.length - 1];
- if ((!first || !point1.isClose(first._point, Numerical.EPSILON))
- && (!last || !point1.isClose(last._point, Numerical.EPSILON)))
- locations.push(
- new CurveLocation(curve1, t1, point1, curve2, t2, point2));
- }
-
- function addCurveIntersections(v1, v2, curve1, curve2, locations,
- range1, range2, recursion) {
- recursion = (recursion || 0) + 1;
+}, new function() {
+ function addLocation(locations, include, curve1, t1, point1, curve2, t2,
+ point2) {
+ var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2);
+ if (!include || include(loc))
+ locations.push(loc);
+ }
+
+ function addCurveIntersections(v1, v2, curve1, curve2, locations, include,
+ tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) {
if (recursion > 20)
return;
- range1 = range1 || [ 0, 1 ];
- range2 = range2 || [ 0, 1 ];
- var part1 = Curve.getPart(v1, range1[0], range1[1]),
- part2 = Curve.getPart(v2, range2[0], range2[1]),
- iteration = 0;
- while (iteration++ < 20) {
- var range,
- intersects1 = clipFatLine(part1, part2, range = range2.slice()),
- intersects2 = 0;
- if (intersects1 === 0)
- break;
- if (intersects1 > 0) {
- range2 = range;
- part2 = Curve.getPart(v2, range2[0], range2[1]);
- intersects2 = clipFatLine(part2, part1, range = range1.slice());
- if (intersects2 === 0)
- break;
- if (intersects1 > 0) {
- range1 = range;
- part1 = Curve.getPart(v1, range1[0], range1[1]);
- }
- }
- if (intersects1 < 0 || intersects2 < 0) {
- if (range1[1] - range1[0] > range2[1] - range2[0]) {
- var t = (range1[0] + range1[1]) / 2;
- addCurveIntersections(v1, v2, curve1, curve2, locations,
- [ range1[0], t ], range2, recursion);
- addCurveIntersections(v1, v2, curve1, curve2, locations,
- [ t, range1[1] ], range2, recursion);
- break;
- } else {
- var t = (range2[0] + range2[1]) / 2;
- addCurveIntersections(v1, v2, curve1, curve2, locations,
- range1, [ range2[0], t ], recursion);
- addCurveIntersections(v1, v2, curve1, curve2, locations,
- range1, [ t, range2[1] ], recursion);
- break;
- }
- }
- if (Math.abs(range1[1] - range1[0]) <= 0.00001 &&
- Math.abs(range2[1] - range2[0]) <= 0.00001) {
- var t1 = (range1[0] + range1[1]) / 2,
- t2 = (range2[0] + range2[1]) / 2;
- addLocation(locations,
+ var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],
+ tolerance = 0.00001,
+ hullEpsilon = 1e-9,
+ getSignedDistance = Line.getSignedDistance,
+ d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0,
+ d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0,
+ factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
+ dMin = factor * Math.min(0, d1, d2),
+ dMax = factor * Math.max(0, d1, d2),
+ dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]),
+ dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]),
+ dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]),
+ dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]),
+ tMinNew, tMaxNew, tDiff;
+ if (q0x === q3x && uMax - uMin <= hullEpsilon && recursion > 3) {
+ tMinNew = (tMax + tMin) / 2;
+ tMaxNew = tMinNew;
+ tDiff = 0;
+ } else {
+ var hull = getConvexHull(dp0, dp1, dp2, dp3),
+ top = hull[0],
+ bottom = hull[1],
+ tMinClip, tMaxClip;
+ tMinClip = clipConvexHull(top, bottom, dMin, dMax);
+ top.reverse();
+ bottom.reverse();
+ tMaxClip = clipConvexHull(top, bottom, dMin, dMax);
+ if (tMinClip == null || tMaxClip == null)
+ return false;
+ v1 = Curve.getPart(v1, tMinClip, tMaxClip);
+ tDiff = tMaxClip - tMinClip;
+ tMinNew = tMax * tMinClip + tMin * (1 - tMinClip);
+ tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip);
+ }
+ if (oldTDiff > 0.8 && tDiff > 0.8) {
+ if (tMaxNew - tMinNew > uMax - uMin) {
+ var parts = Curve.subdivide(v1, 0.5),
+ t = tMinNew + (tMaxNew - tMinNew) / 2;
+ addCurveIntersections(
+ v2, parts[0], curve2, curve1, locations, include,
+ uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion);
+ addCurveIntersections(
+ v2, parts[1], curve2, curve1, locations, include,
+ uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion);
+ } else {
+ var parts = Curve.subdivide(v2, 0.5),
+ t = uMin + (uMax - uMin) / 2;
+ addCurveIntersections(
+ parts[0], v1, curve2, curve1, locations, include,
+ uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
+ addCurveIntersections(
+ parts[1], v1, curve2, curve1, locations, include,
+ t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion);
+ }
+ } else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) {
+ var t1 = tMinNew + (tMaxNew - tMinNew) / 2,
+ t2 = uMin + (uMax - uMin) / 2;
+ if (reverse) {
+ addLocation(locations, include,
+ curve2, t2, Curve.evaluate(v2, t2, 0),
+ curve1, t1, Curve.evaluate(v1, t1, 0));
+ } else {
+ addLocation(locations, include,
curve1, t1, Curve.evaluate(v1, t1, 0),
curve2, t2, Curve.evaluate(v2, t2, 0));
- break;
- }
- }
- }
-
- function clipFatLine(v1, v2, range2) {
- var p0x = v1[0], p0y = v1[1], p1x = v1[2], p1y = v1[3],
- p2x = v1[4], p2y = v1[5], p3x = v1[6], p3y = v1[7],
- q0x = v2[0], q0y = v2[1], q1x = v2[2], q1y = v2[3],
- q2x = v2[4], q2y = v2[5], q3x = v2[6], q3y = v2[7],
- getSignedDistance = Line.getSignedDistance,
- d1 = getSignedDistance(p0x, p0y, p3x, p3y, p1x, p1y) || 0,
- d2 = getSignedDistance(p0x, p0y, p3x, p3y, p2x, p2y) || 0,
- factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
- dmin = factor * Math.min(0, d1, d2),
- dmax = factor * Math.max(0, d1, d2),
- dq0 = getSignedDistance(p0x, p0y, p3x, p3y, q0x, q0y),
- dq1 = getSignedDistance(p0x, p0y, p3x, p3y, q1x, q1y),
- dq2 = getSignedDistance(p0x, p0y, p3x, p3y, q2x, q2y),
- dq3 = getSignedDistance(p0x, p0y, p3x, p3y, q3x, q3y);
- if (dmin > Math.max(dq0, dq1, dq2, dq3)
- || dmax < Math.min(dq0, dq1, dq2, dq3))
- return 0;
- var hull = getConvexHull(dq0, dq1, dq2, dq3),
- swap;
- if (dq3 < dq0) {
- swap = dmin;
- dmin = dmax;
- dmax = swap;
- }
- var tmaxdmin = -Infinity,
- tmin = Infinity,
- tmax = -Infinity;
- for (var i = 0, l = hull.length; i < l; i++) {
- var p1 = hull[i],
- p2 = hull[(i + 1) % l];
- if (p2[1] < p1[1]) {
- swap = p2;
- p2 = p1;
- p1 = swap;
- }
- var x1 = p1[0],
- y1 = p1[1],
- x2 = p2[0],
- y2 = p2[1];
- var inv = (y2 - y1) / (x2 - x1);
- if (dmin >= y1 && dmin <= y2) {
- var ixdx = x1 + (dmin - y1) / inv;
- if (ixdx < tmin)
- tmin = ixdx;
- if (ixdx > tmaxdmin)
- tmaxdmin = ixdx;
- }
- if (dmax >= y1 && dmax <= y2) {
- var ixdx = x1 + (dmax - y1) / inv;
- if (ixdx > tmax)
- tmax = ixdx;
- if (ixdx < tmin)
- tmin = 0;
- }
- }
- if (tmin !== Infinity && tmax !== -Infinity) {
- var min = Math.min(dmin, dmax),
- max = Math.max(dmin, dmax);
- if (dq3 > min && dq3 < max)
- tmax = 1;
- if (dq0 > min && dq0 < max)
- tmin = 0;
- if (tmaxdmin > tmax)
- tmax = 1;
- var v2tmin = range2[0],
- tdiff = range2[1] - v2tmin;
- range2[0] = v2tmin + tmin * tdiff;
- range2[1] = v2tmin + tmax * tdiff;
- if ((tdiff - (range2[1] - range2[0])) / tdiff >= 0.2)
- return 1;
- }
- if (Curve.getBounds(v1).touches(Curve.getBounds(v2)))
- return -1;
- return 0;
+ }
+ } else {
+ addCurveIntersections(v2, v1, curve2, curve1, locations, include,
+ uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
+ }
}
function getConvexHull(dq0, dq1, dq2, dq3) {
@@ -5797,26 +5979,74 @@
p3 = [ 1, dq3 ],
getSignedDistance = Line.getSignedDistance,
dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1),
- dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2);
+ dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2),
+ flip = false,
+ hull;
if (dist1 * dist2 < 0) {
- return [ p0, p1, p3, p2 ];
- }
- var pmax, cross;
- if (Math.abs(dist1) > Math.abs(dist2)) {
- pmax = p1;
- cross = (dq3 - dq2 - (dq3 - dq0) / 3)
- * (2 * (dq3 - dq2) - dq3 + dq1) / 3;
+ hull = [[p0, p1, p3], [p0, p2, p3]];
+ flip = dist1 < 0;
} else {
- pmax = p2;
- cross = (dq1 - dq0 + (dq0 - dq3) / 3)
- * (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
- }
- return cross < 0
- ? [ p0, pmax, p3 ]
- : [ p0, p1, p2, p3 ];
- }
-
- function addCurveLineIntersections(v1, v2, curve1, curve2, locations) {
+ var pmax, cross = 0,
+ distZero = dist1 === 0 || dist2 === 0;
+ if (Math.abs(dist1) > Math.abs(dist2)) {
+ pmax = p1;
+ cross = (dq3 - dq2 - (dq3 - dq0) / 3)
+ * (2 * (dq3 - dq2) - dq3 + dq1) / 3;
+ } else {
+ pmax = p2;
+ cross = (dq1 - dq0 + (dq0 - dq3) / 3)
+ * (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
+ }
+ hull = cross < 0 || distZero
+ ? [[p0, pmax, p3], [p0, p3]]
+ : [[p0, p1, p2, p3], [p0, p3]];
+ flip = dist1 ? dist1 < 0 : dist2 < 0;
+ }
+ return flip ? hull.reverse() : hull;
+ }
+
+ function clipConvexHull(hullTop, hullBottom, dMin, dMax) {
+ var tProxy,
+ tVal = null,
+ px, py,
+ qx, qy;
+ for (var i = 0, l = hullBottom.length - 1; i < l; i++) {
+ py = hullBottom[i][1];
+ qy = hullBottom[i + 1][1];
+ if (py < qy) {
+ tProxy = null;
+ } else if (qy <= dMax) {
+ px = hullBottom[i][0];
+ qx = hullBottom[i + 1][0];
+ tProxy = px + (dMax - py) * (qx - px) / (qy - py);
+ } else {
+ continue;
+ }
+ break;
+ }
+ if (hullTop[0][1] <= dMax)
+ tProxy = hullTop[0][0];
+ for (var i = 0, l = hullTop.length - 1; i < l; i++) {
+ py = hullTop[i][1];
+ qy = hullTop[i + 1][1];
+ if (py >= dMin) {
+ tVal = tProxy;
+ } else if (py > qy) {
+ tVal = null;
+ } else if (qy >= dMin) {
+ px = hullTop[i][0];
+ qx = hullTop[i + 1][0];
+ tVal = px + (dMin - py) * (qx - px) / (qy - py);
+ } else {
+ continue;
+ }
+ break;
+ }
+ return tVal;
+ }
+
+ function addCurveLineIntersections(v1, v2, curve1, curve2, locations,
+ include) {
var flip = Curve.isLinear(v1),
vc = flip ? v2 : v1,
vl = flip ? v1 : v2,
@@ -5846,43 +6076,37 @@
var tl = Curve.getParameterOf(rvl, x, 0),
t1 = flip ? tl : tc,
t2 = flip ? tc : tl;
- addLocation(locations,
+ addLocation(locations, include,
curve1, t1, Curve.evaluate(v1, t1, 0),
curve2, t2, Curve.evaluate(v2, t2, 0));
}
}
}
- function addLineIntersection(v1, v2, curve1, curve2, locations) {
+ function addLineIntersection(v1, v2, curve1, curve2, locations, include) {
var point = Line.intersect(
v1[0], v1[1], v1[6], v1[7],
v2[0], v2[1], v2[6], v2[7]);
- if (point)
- addLocation(locations, curve1, null, point, curve2);
+ if (point) {
+ var x = point.x,
+ y = point.y;
+ addLocation(locations, include,
+ curve1, Curve.getParameterOf(v1, x, y), point,
+ curve2, Curve.getParameterOf(v2, x, y), point);
+ }
}
return { statics: {
- getIntersections: function(v1, v2, curve1, curve2, locations) {
+ getIntersections: function(v1, v2, curve1, curve2, locations, include) {
var linear1 = Curve.isLinear(v1),
- linear2 = Curve.isLinear(v2),
- c1p1 = curve1.getPoint1(),
- c1p2 = curve1.getPoint2(),
- c2p1 = curve2.getPoint1(),
- c2p2 = curve2.getPoint2(),
- tolerance = 0.00001;
- if (c1p1.isClose(c2p1, tolerance))
- addLocation(locations, curve1, 0, c1p1, curve2, 0, c1p1);
- if (c1p1.isClose(c2p2, tolerance))
- addLocation(locations, curve1, 0, c1p1, curve2, 1, c1p1);
+ linear2 = Curve.isLinear(v2);
(linear1 && linear2
? addLineIntersection
: linear1 || linear2
? addCurveLineIntersections
- : addCurveIntersections)(v1, v2, curve1, curve2, locations);
- if (c1p2.isClose(c2p1, tolerance))
- addLocation(locations, curve1, 1, c1p2, curve2, 0, c1p2);
- if (c1p2.isClose(c2p2, tolerance))
- addLocation(locations, curve1, 1, c1p2, curve2, 1, c1p2);
+ : addCurveIntersections)(
+ v1, v2, curve1, curve2, locations, include,
+ 0, 1, 0, 1, 0, false, 0);
return locations;
}
}};
@@ -5890,6 +6114,8 @@
var CurveLocation = Base.extend({
_class: 'CurveLocation',
+ beans: true,
+
initialize: function CurveLocation(curve, parameter, point, _curve2,
_parameter2, _point2, _distance) {
this._id = CurveLocation._id = (CurveLocation._id || 0) + 1;
@@ -5904,19 +6130,19 @@
this._distance = _distance;
},
- getSegment: function() {
+ getSegment: function(_preferFirst) {
if (!this._segment) {
var curve = this.getCurve(),
parameter = this.getParameter();
if (parameter === 1) {
this._segment = curve._segment2;
- } else if (parameter === 0 || arguments[0]) {
+ } else if (parameter === 0 || _preferFirst) {
this._segment = curve._segment1;
} else if (parameter == null) {
return null;
} else {
- this._segment = curve.getLength(0, parameter)
- < curve.getLength(parameter, 1)
+ this._segment = curve.getPartLength(0, parameter)
+ < curve.getPartLength(parameter, 1)
? curve._segment1
: curve._segment2;
}
@@ -5924,8 +6150,8 @@
return this._segment;
},
- getCurve: function() {
- if (!this._curve || arguments[0]) {
+ getCurve: function(_uncached) {
+ if (!this._curve || _uncached) {
this._curve = this._segment1.getCurve();
if (this._curve.getParameterOf(this._point) == null)
this._curve = this._segment2.getPrevious().getCurve();
@@ -5956,43 +6182,31 @@
getOffset: function() {
var path = this.getPath();
- return path && path._getOffset(this);
+ return path ? path._getOffset(this) : this.getCurveOffset();
},
getCurveOffset: function() {
var curve = this.getCurve(),
parameter = this.getParameter();
- return parameter != null && curve && curve.getLength(0, parameter);
- },
-
- getParameter: function() {
- if ((this._parameter == null || arguments[0]) && this._point) {
- var curve = this.getCurve(arguments[0] && this._point);
+ return parameter != null && curve && curve.getPartLength(0, parameter);
+ },
+
+ getParameter: function(_uncached) {
+ if ((this._parameter == null || _uncached) && this._point) {
+ var curve = this.getCurve(_uncached && this._point);
this._parameter = curve && curve.getParameterOf(this._point);
}
return this._parameter;
},
- getPoint: function() {
- if ((!this._point || arguments[0]) && this._parameter != null) {
+ getPoint: function(_uncached) {
+ if ((!this._point || _uncached) && this._parameter != null) {
var curve = this.getCurve();
this._point = curve && curve.getPointAt(this._parameter, true);
}
return this._point;
},
- getTangent: function() {
- var parameter = this.getParameter(),
- curve = this.getCurve();
- return parameter != null && curve && curve.getTangentAt(parameter, true);
- },
-
- getNormal: function() {
- var parameter = this.getParameter(),
- curve = this.getCurve();
- return parameter != null && curve && curve.getNormalAt(parameter, true);
- },
-
getDistance: function() {
return this._distance;
},
@@ -6007,6 +6221,17 @@
return curve && curve.split(this.getParameter(true), true);
},
+ equals: function(loc) {
+ var isZero = Numerical.isZero;
+ return this === loc
+ || loc
+ && this._curve === loc._curve
+ && this._curve2 === loc._curve2
+ && isZero(this._parameter - loc._parameter)
+ && isZero(this._parameter2 - loc._parameter2)
+ || false;
+ },
+
toString: function() {
var parts = [],
point = this.getPoint(),
@@ -6023,7 +6248,16 @@
parts.push('distance: ' + f.number(this._distance));
return '{ ' + parts.join(', ') + ' }';
}
-});
+}, Base.each(['Tangent', 'Normal', 'Curvature'],
+ function(name) {
+ var get = 'get' + name + 'At';
+ this['get' + name] = function() {
+ var parameter = this.getParameter(),
+ curve = this.getCurve();
+ return parameter != null && curve && curve[get](parameter, true);
+ };
+ }, {}
+));
var PathItem = Item.extend({
_class: 'PathItem',
@@ -6031,22 +6265,95 @@
initialize: function PathItem() {
},
- getIntersections: function(path) {
- if (!this.getBounds().touches(path.getBounds()))
+ getIntersections: function(path, _expand) {
+ if (this === path)
+ path = null;
+ if (path && !this.getBounds().touches(path.getBounds()))
return [];
var locations = [],
curves1 = this.getCurves(),
- curves2 = path.getCurves(),
- length2 = curves2.length,
- values2 = [];
+ curves2 = path ? path.getCurves() : curves1,
+ matrix1 = this._matrix.orNullIfIdentity(),
+ matrix2 = path ? path._matrix.orNullIfIdentity() : matrix1,
+ length1 = curves1.length,
+ length2 = path ? curves2.length : length1,
+ values2 = [],
+ MIN = 1e-11,
+ MAX = 1 - 1e-11;
for (var i = 0; i < length2; i++)
- values2[i] = curves2[i].getValues();
- for (var i = 0, l = curves1.length; i < l; i++) {
+ values2[i] = curves2[i].getValues(matrix2);
+ for (var i = 0; i < length1; i++) {
var curve1 = curves1[i],
- values1 = curve1.getValues();
- for (var j = 0; j < length2; j++)
- Curve.getIntersections(values1, values2[j], curve1, curves2[j],
- locations);
+ values1 = path ? curve1.getValues(matrix1) : values2[i];
+ if (!path) {
+ var seg1 = curve1.getSegment1(),
+ seg2 = curve1.getSegment2(),
+ h1 = seg1._handleOut,
+ h2 = seg2._handleIn;
+ if (new Line(seg1._point.subtract(h1), h1.multiply(2), true)
+ .intersect(new Line(seg2._point.subtract(h2),
+ h2.multiply(2), true), false)) {
+ var parts = Curve.subdivide(values1);
+ Curve.getIntersections(
+ parts[0], parts[1], curve1, curve1, locations,
+ function(loc) {
+ if (loc._parameter <= MAX) {
+ loc._parameter /= 2;
+ loc._parameter2 = 0.5 + loc._parameter2 / 2;
+ return true;
+ }
+ }
+ );
+ }
+ }
+ for (var j = path ? 0 : i + 1; j < length2; j++) {
+ Curve.getIntersections(
+ values1, values2[j], curve1, curves2[j], locations,
+ !path && (j === i + 1 || j === length2 - 1 && i === 0)
+ && function(loc) {
+ var t = loc._parameter;
+ return t >= MIN && t <= MAX;
+ }
+ );
+ }
+ }
+ var last = locations.length - 1;
+ for (var i = last; i >= 0; i--) {
+ var loc = locations[i],
+ next = loc._curve.getNext(),
+ next2 = loc._curve2.getNext();
+ if (next && loc._parameter >= MAX) {
+ loc._parameter = 0;
+ loc._curve = next;
+ }
+ if (next2 && loc._parameter2 >= MAX) {
+ loc._parameter2 = 0;
+ loc._curve2 = next2;
+ }
+ }
+
+ function compare(loc1, loc2) {
+ var path1 = loc1.getPath(),
+ path2 = loc2.getPath();
+ return path1 === path2
+ ? (loc1.getIndex() + loc1.getParameter())
+ - (loc2.getIndex() + loc2.getParameter())
+ : path1._id - path2._id;
+ }
+
+ if (last > 0) {
+ locations.sort(compare);
+ for (var i = last; i >= 1; i--) {
+ if (locations[i].equals(locations[i === 0 ? last : i - 1])) {
+ locations.splice(i, 1);
+ last--;
+ }
+ }
+ }
+ if (_expand) {
+ for (var i = last; i >= 0; i--)
+ locations.push(locations[i].getIntersection());
+ locations.sort(compare);
}
return locations;
},
@@ -6056,22 +6363,22 @@
var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
coords,
relative = false,
+ previous,
control,
- current = new Point();
-
- function getCoord(index, coord, isCurrent) {
- var val = parseFloat(coords[index]);
+ current = new Point(),
+ start = new Point();
+
+ function getCoord(index, coord) {
+ var val = +coords[index];
if (relative)
val += current[coord];
- if (isCurrent)
- current[coord] = val;
return val;
}
- function getPoint(index, isCurrent) {
+ function getPoint(index) {
return new Point(
- getCoord(index, 'x', isCurrent),
- getCoord(index + 1, 'y', isCurrent)
+ getCoord(index, 'x'),
+ getCoord(index + 1, 'y')
);
}
@@ -6079,24 +6386,31 @@
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i],
- cmd = part[0],
- lower = cmd.toLowerCase();
+ command = part[0],
+ lower = command.toLowerCase();
coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
var length = coords && coords.length;
- relative = cmd === lower;
+ relative = command === lower;
+ if (previous === 'z' && !/[mz]/.test(lower))
+ this.moveTo(current = start);
switch (lower) {
case 'm':
case 'l':
+ var move = lower === 'm';
+ if (move && previous && previous !== 'z')
+ this.closePath(true);
for (var j = 0; j < length; j += 2)
- this[j === 0 && lower === 'm' ? 'moveTo' : 'lineTo'](
- getPoint(j, true));
+ this[j === 0 && move ? 'moveTo' : 'lineTo'](
+ current = getPoint(j));
control = current;
+ if (move)
+ start = current;
break;
case 'h':
case 'v':
- var coord = lower == 'h' ? 'x' : 'y';
+ var coord = lower === 'h' ? 'x' : 'y';
for (var j = 0; j < length; j++) {
- getCoord(j, coord, true);
+ current[coord] = getCoord(j, coord);
this.lineTo(current);
}
control = current;
@@ -6106,37 +6420,49 @@
this.cubicCurveTo(
getPoint(j),
control = getPoint(j + 2),
- getPoint(j + 4, true));
+ current = getPoint(j + 4));
}
break;
case 's':
for (var j = 0; j < length; j += 4) {
this.cubicCurveTo(
- current.multiply(2).subtract(control),
+ /[cs]/.test(previous)
+ ? current.multiply(2).subtract(control)
+ : current,
control = getPoint(j),
- getPoint(j + 2, true));
+ current = getPoint(j + 2));
+ previous = lower;
}
break;
case 'q':
for (var j = 0; j < length; j += 4) {
this.quadraticCurveTo(
control = getPoint(j),
- getPoint(j + 2, true));
+ current = getPoint(j + 2));
}
break;
case 't':
for (var j = 0; j < length; j += 2) {
this.quadraticCurveTo(
- control = current.multiply(2).subtract(control),
- getPoint(j, true));
+ control = (/[qt]/.test(previous)
+ ? current.multiply(2).subtract(control)
+ : current),
+ current = getPoint(j));
+ previous = lower;
}
break;
case 'a':
+ for (var j = 0; j < length; j += 7) {
+ this.arcTo(current = getPoint(j + 5),
+ new Size(+coords[0], +coords[1]),
+ +coords[2], +coords[4], +coords[3]);
+ }
break;
case 'z':
- this.closePath();
+ this.closePath(true);
break;
}
+ previous = lower;
}
},
@@ -6145,7 +6471,7 @@
},
_contains: function(point) {
- var winding = this._getWinding(point);
+ var winding = this._getWinding(point, false, true);
return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding);
}
@@ -6165,11 +6491,20 @@
? typeof arg[0] === 'object'
? arg
: arguments
- : arg && (arg.point !== undefined && arg.size === undefined
- || arg.x !== undefined)
+ : arg && (arg.size === undefined && (arg.x !== undefined
+ || arg.point !== undefined))
? arguments
: null;
- this.setSegments(segments || []);
+ if (segments && segments.length > 0) {
+ this.setSegments(segments);
+ } else {
+ this._curves = undefined;
+ this._selectedSegmentState = 0;
+ if (!segments && typeof arg === 'string') {
+ this.setPathData(arg);
+ arg = null;
+ }
+ }
this._initialize(!segments && arg);
},
@@ -6178,28 +6513,34 @@
},
clone: function(insert) {
- var copy = this._clone(new Path({
- segments: this._segments,
- insert: false
- }), insert);
+ var copy = new Path(Item.NO_INSERT);
+ copy.setSegments(this._segments);
copy._closed = this._closed;
if (this._clockwise !== undefined)
copy._clockwise = this._clockwise;
- return copy;
+ return this._clone(copy, insert);
},
_changed: function _changed(flags) {
_changed.base.call(this, flags);
- if (flags & 4) {
- delete this._length;
- delete this._clockwise;
- if (this._curves) {
+ if (flags & 8) {
+ var parent = this._parent;
+ if (parent)
+ parent._currentPath = undefined;
+ this._length = this._clockwise = undefined;
+ if (this._curves && !(flags & 16)) {
for (var i = 0, l = this._curves.length; i < l; i++)
- this._curves[i]._changed(5);
- }
- } else if (flags & 8) {
- delete this._bounds;
- }
+ this._curves[i]._changed();
+ }
+ this._monoCurves = undefined;
+ } else if (flags & 32) {
+ this._bounds = undefined;
+ }
+ },
+
+ getStyle: function() {
+ var parent = this._parent;
+ return (parent instanceof CompoundPath ? parent : this)._style;
},
getSegments: function() {
@@ -6210,8 +6551,9 @@
var fullySelected = this.isFullySelected();
this._segments.length = 0;
this._selectedSegmentState = 0;
- delete this._curves;
- this._add(Segment.readAll(segments));
+ this._curves = undefined;
+ if (segments && segments.length > 0)
+ this._add(Segment.readAll(segments));
if (fullySelected)
this.setFullySelected(true);
},
@@ -6246,41 +6588,6 @@
return curves[curves.length - 1];
},
- getPathData: function() {
- var segments = this._segments,
- precision = arguments[0],
- f = Formatter.instance,
- parts = [];
-
- function addCurve(seg1, seg2, skipLine) {
- var point1 = seg1._point,
- point2 = seg2._point,
- handle1 = seg1._handleOut,
- handle2 = seg2._handleIn;
- if (handle1.isZero() && handle2.isZero()) {
- if (!skipLine) {
- parts.push('L' + f.point(point2, precision));
- }
- } else {
- var end = point2.subtract(point1);
- parts.push('c' + f.point(handle1, precision)
- + ' ' + f.point(end.add(handle2), precision)
- + ' ' + f.point(end, precision));
- }
- }
-
- if (segments.length === 0)
- return '';
- parts.push('M' + f.point(segments[0]._point));
- for (var i = 0, l = segments.length - 1; i < l; i++)
- addCurve(segments[i], segments[i + 1], false);
- if (this._closed) {
- addCurve(segments[segments.length - 1], segments[0], true);
- parts.push('z');
- }
- return parts.join('');
- },
-
isClosed: function() {
return this._closed;
},
@@ -6294,9 +6601,62 @@
this._curves[length - 1] = new Curve(this,
this._segments[length - 1], this._segments[0]);
}
- this._changed(5);
- }
- },
+ this._changed(25);
+ }
+ }
+}, {
+ beans: true,
+
+ getPathData: function(_matrix, _precision) {
+ var segments = this._segments,
+ length = segments.length,
+ f = new Formatter(_precision),
+ coords = new Array(6),
+ first = true,
+ curX, curY,
+ prevX, prevY,
+ inX, inY,
+ outX, outY,
+ parts = [];
+
+ function addSegment(segment, skipLine) {
+ segment._transformCoordinates(_matrix, coords, false);
+ curX = coords[0];
+ curY = coords[1];
+ if (first) {
+ parts.push('M' + f.pair(curX, curY));
+ first = false;
+ } else {
+ inX = coords[2];
+ inY = coords[3];
+ if (inX === curX && inY === curY
+ && outX === prevX && outY === prevY) {
+ if (!skipLine)
+ parts.push('l' + f.pair(curX - prevX, curY - prevY));
+ } else {
+ parts.push('c' + f.pair(outX - prevX, outY - prevY)
+ + ' ' + f.pair(inX - prevX, inY - prevY)
+ + ' ' + f.pair(curX - prevX, curY - prevY));
+ }
+ }
+ prevX = curX;
+ prevY = curY;
+ outX = coords[4];
+ outY = coords[5];
+ }
+
+ if (length === 0)
+ return '';
+
+ for (var i = 0; i < length; i++)
+ addSegment(segments[i]);
+ if (this._closed && length > 0) {
+ addSegment(segments[0], true);
+ parts.push('z');
+ }
+ return parts.join('');
+ }
+}, {
isEmpty: function() {
return this._segments.length === 0;
@@ -6310,7 +6670,7 @@
return true;
},
- _applyMatrix: function(matrix) {
+ _transformContent: function(matrix) {
var coords = new Array(6);
for (var i = 0, l = this._segments.length; i < l; i++)
this._segments[i]._transformCoordinates(matrix, coords, true);
@@ -6353,7 +6713,7 @@
curves.splice(i, 0, new Curve(this, null, null));
this._adjustCurves(from, to);
}
- this._changed(5);
+ this._changed(25);
return segs;
},
@@ -6366,12 +6726,17 @@
curve._path = this;
curve._segment1 = segments[i];
curve._segment2 = segments[i + 1] || segments[0];
+ curve._changed();
}
if (curve = curves[this._closed && from === 0 ? segments.length - 1
- : from - 1])
+ : from - 1]) {
curve._segment2 = segments[from] || segments[0];
- if (curve = curves[to])
+ curve._changed();
+ }
+ if (curve = curves[to]) {
curve._segment1 = segments[to];
+ curve._changed();
+ }
},
_countCurves: function() {
@@ -6411,12 +6776,12 @@
return this.removeSegments(index, index + 1)[0] || null;
},
- removeSegments: function(from, to) {
+ removeSegments: function(from, to, _includeCurves) {
from = from || 0;
to = Base.pick(to, this._segments.length);
var segments = this._segments,
curves = this._curves,
- count = segments.length,
+ count = segments.length,
removed = segments.splice(from, to - from),
amount = removed.length;
if (!amount)
@@ -6425,8 +6790,7 @@
var segment = removed[i];
if (segment._selectionState)
this._updateSelection(segment, segment._selectionState, 0);
- delete segment._index;
- delete segment._path;
+ segment._index = segment._path = null;
}
for (var i = from, l = segments.length; i < l; i++)
segments[i]._index = i;
@@ -6435,20 +6799,38 @@
? from - 1
: from,
curves = curves.splice(index, amount);
- if (arguments[2])
+ if (_includeCurves)
removed._curves = curves.slice(1);
this._adjustCurves(index, index);
}
- this._changed(5);
+ this._changed(25);
return removed;
},
clear: '#removeSegments',
+ getLength: function() {
+ if (this._length == null) {
+ var curves = this.getCurves();
+ this._length = 0;
+ for (var i = 0, l = curves.length; i < l; i++)
+ this._length += curves[i].getLength();
+ }
+ return this._length;
+ },
+
+ getArea: function() {
+ var curves = this.getCurves();
+ var area = 0;
+ for (var i = 0, l = curves.length; i < l; i++)
+ area += curves[i].getArea();
+ return area;
+ },
+
isFullySelected: function() {
var length = this._segments.length;
return this._selected && length > 0 && this._selectedSegmentState
- === length * 4;
+ === length * 7;
},
setFullySelected: function(selected) {
@@ -6466,10 +6848,10 @@
_selectSegments: function(selected) {
var length = this._segments.length;
this._selectedSegmentState = selected
- ? length * 4 : 0;
+ ? length * 7 : 0;
for (var i = 0; i < length; i++)
this._segments[i]._selectionState = selected
- ? 4 : 0;
+ ? 7 : 0;
},
_updateSelection: function(segment, oldState, newState) {
@@ -6492,6 +6874,16 @@
this.setSegments(segments);
},
+ reduce: function() {
+ var curves = this.getCurves();
+ for (var i = curves.length - 1; i >= 0; i--) {
+ var curve = curves[i];
+ if (curve.isLinear() && curve.getLength() === 0)
+ curve.remove();
+ }
+ return this;
+ },
+
simplify: function(tolerance) {
if (this._segments.length > 2) {
var fitter = new PathFitter(this, tolerance || 2.5);
@@ -6509,13 +6901,14 @@
index = arg.index;
parameter = arg.parameter;
}
- if (parameter >= 1) {
+ var tolerance = 0.00001;
+ if (parameter >= 1 - tolerance) {
index++;
parameter--;
}
var curves = this.getCurves();
if (index >= 0 && index < curves.length) {
- if (parameter > 0) {
+ if (parameter > tolerance) {
curves[index++].divide(parameter, true);
}
var segs = this.removeSegments(index, this._segments.length, true),
@@ -6554,9 +6947,10 @@
segment._handleOut = handleIn;
segment._index = i;
}
- delete this._curves;
+ this._curves = null;
if (this._clockwise !== undefined)
this._clockwise = !this._clockwise;
+ this._changed(9);
},
join: function(path) {
@@ -6586,120 +6980,14 @@
if (path.closed)
this._add([segments[0]]);
path.remove();
- first1 = this.getFirstSegment();
- last1 = this.getLastSegment();
- if (last1._point.equals(first1._point)) {
- first1.setHandleIn(last1._handleIn);
- last1.remove();
- this.setClosed(true);
- }
- this._changed(5);
- return true;
- }
- return false;
- },
-
- getLength: function() {
- if (this._length == null) {
- var curves = this.getCurves();
- this._length = 0;
- for (var i = 0, l = curves.length; i < l; i++)
- this._length += curves[i].getLength();
- }
- return this._length;
- },
-
- getArea: function() {
- var curves = this.getCurves();
- var area = 0;
- for (var i = 0, l = curves.length; i < l; i++)
- area += curves[i].getArea();
- return area;
- },
-
- _getOffset: function(location) {
- var index = location && location.getIndex();
- if (index != null) {
- var curves = this.getCurves(),
- offset = 0;
- for (var i = 0; i < index; i++)
- offset += curves[i].getLength();
- var curve = curves[index];
- return offset + curve.getLength(0, location.getParameter());
- }
- return null;
- },
-
- getLocationOf: function(point) {
- point = Point.read(arguments);
- var curves = this.getCurves();
- for (var i = 0, l = curves.length; i < l; i++) {
- var loc = curves[i].getLocationOf(point);
- if (loc)
- return loc;
- }
- return null;
- },
-
- getLocationAt: function(offset, isParameter) {
- var curves = this.getCurves(),
- length = 0;
- if (isParameter) {
- var index = ~~offset;
- return curves[index].getLocationAt(offset - index, true);
- }
- for (var i = 0, l = curves.length; i < l; i++) {
- var start = length,
- curve = curves[i];
- length += curve.getLength();
- if (length >= offset) {
- return curve.getLocationAt(offset - start);
- }
- }
- if (offset <= this.getLength())
- return new CurveLocation(curves[curves.length - 1], 1);
- return null;
- },
-
- getPointAt: function(offset, isParameter) {
- var loc = this.getLocationAt(offset, isParameter);
- return loc && loc.getPoint();
- },
-
- getTangentAt: function(offset, isParameter) {
- var loc = this.getLocationAt(offset, isParameter);
- return loc && loc.getTangent();
- },
-
- getNormalAt: function(offset, isParameter) {
- var loc = this.getLocationAt(offset, isParameter);
- return loc && loc.getNormal();
- },
-
- getNearestLocation: function(point) {
- point = Point.read(arguments);
- var curves = this.getCurves(),
- minDist = Infinity,
- minLoc = null;
- for (var i = 0, l = curves.length; i < l; i++) {
- var loc = curves[i].getNearestLocation(point);
- if (loc._distance < minDist) {
- minDist = loc._distance;
- minLoc = loc;
- }
- }
- return minLoc;
- },
-
- getNearestPoint: function(point) {
- point = Point.read(arguments);
- return this.getNearestLocation(point).getPoint();
- },
-
- getStyle: function() {
- var parent = this._parent;
- return (parent && parent._type === 'compound-path'
- ? parent : this)._style;
+ }
+ var first = this.getFirstSegment(),
+ last = this.getLastSegment();
+ if (first !== last && first._point.equals(last._point)) {
+ first.setHandleIn(last._handleIn);
+ last.remove();
+ this.setClosed(true);
+ }
},
toShape: function(insert) {
@@ -6769,139 +7057,108 @@
return null;
},
- _getWinding: function(point) {
- var closed = this._closed;
- if (!closed && !this.hasFill()
- || !this._getBounds('getRoughBounds')._containsPoint(point))
- return 0;
- var curves = this.getCurves(),
+ _hitTestSelf: function(point, options) {
+ var that = this,
+ style = this.getStyle(),
segments = this._segments,
- winding = 0,
- roots1 = [],
- roots2 = [],
- last = (closed
- ? curves[curves.length - 1]
- : new Curve(segments[segments.length - 1]._point,
- segments[0]._point)).getValues(),
- previous = last;
- for (var i = 0, l = curves.length; i < l; i++) {
- var curve = curves[i].getValues(),
- x = curve[0],
- y = curve[1];
- if (!(x === curve[2] && y === curve[3] && x === curve[4]
- && y === curve[5] && x === curve[6] && y === curve[7])) {
- winding += Curve._getWinding(curve, previous, point.x, point.y,
- roots1, roots2);
- previous = curve;
- }
- }
- if (!closed) {
- winding += Curve._getWinding(last, previous, point.x, point.y,
- roots1, roots2);
- }
- return winding;
- },
-
- _hitTest: function(point, options) {
- var style = this.getStyle(),
- segments = this._segments,
+ numSegments = segments.length,
closed = this._closed,
- tolerance = options.tolerance,
- radius = 0, join, cap, miterLimit,
- that = this,
- area, loc, res;
-
- if (options.stroke) {
- radius = style.getStrokeWidth() / 2;
+ tolerancePadding = options._tolerancePadding,
+ strokePadding = tolerancePadding,
+ join, cap, miterLimit,
+ area, loc, res,
+ hitStroke = options.stroke && style.hasStroke(),
+ hitFill = options.fill && style.hasFill(),
+ hitCurves = options.curves,
+ radius = hitStroke
+ ? style.getStrokeWidth() / 2
+ : hitFill && options.tolerance > 0 || hitCurves
+ ? 0 : null;
+ if (radius !== null) {
if (radius > 0) {
join = style.getStrokeJoin();
cap = style.getStrokeCap();
miterLimit = radius * style.getMiterLimit();
+ strokePadding = tolerancePadding.add(new Point(radius, radius));
} else {
join = cap = 'round';
}
- radius += tolerance;
- }
-
- function checkPoint(seg, pt, name) {
- if (point.getDistance(pt) < tolerance)
- return new HitResult(name, that, { segment: seg, point: pt });
+ }
+
+ function isCloseEnough(pt, padding) {
+ return point.subtract(pt).divide(padding).length <= 1;
+ }
+
+ function checkSegmentPoint(seg, pt, name) {
+ if (!options.selected || pt.isSelected()) {
+ var anchor = seg._point;
+ if (pt !== anchor)
+ pt = pt.add(anchor);
+ if (isCloseEnough(pt, strokePadding)) {
+ return new HitResult(name, that, {
+ segment: seg,
+ point: pt
+ });
+ }
+ }
}
function checkSegmentPoints(seg, ends) {
- var pt = seg._point;
- return (ends || options.segments) && checkPoint(seg, pt, 'segment')
+ return (ends || options.segments)
+ && checkSegmentPoint(seg, seg._point, 'segment')
|| (!ends && options.handles) && (
- checkPoint(seg, pt.add(seg._handleIn), 'handle-in') ||
- checkPoint(seg, pt.add(seg._handleOut), 'handle-out'));
- }
-
- function addAreaPoint(point) {
- area.push(point);
- }
-
- function getAreaCurve(index) {
- var p1 = area[index],
- p2 = area[(index + 1) % area.length];
- return [p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p2.x ,p2.y];
- }
-
- function isInArea(point) {
- var length = area.length,
- previous = getAreaCurve(length - 1),
- roots1 = [],
- roots2 = [],
- winding = 0;
- for (var i = 0; i < length; i++) {
- var curve = getAreaCurve(i);
- winding += Curve._getWinding(curve, previous, point.x, point.y,
- roots1, roots2);
- previous = curve;
- }
- return !!winding;
+ checkSegmentPoint(seg, seg._handleIn, 'handle-in') ||
+ checkSegmentPoint(seg, seg._handleOut, 'handle-out'));
+ }
+
+ function addToArea(point) {
+ area.add(point);
}
function checkSegmentStroke(segment) {
if (join !== 'round' || cap !== 'round') {
- area = [];
+ area = new Path({ internal: true, closed: true });
if (closed || segment._index > 0
- && segment._index < segments.length - 1) {
- if (join !== 'round' && (segment._handleIn.isZero()
+ && segment._index < numSegments - 1) {
+ if (join !== 'round' && (segment._handleIn.isZero()
|| segment._handleOut.isZero()))
- Path._addSquareJoin(segment, join, radius, miterLimit,
- addAreaPoint, true);
+ Path._addBevelJoin(segment, join, radius, miterLimit,
+ addToArea, true);
} else if (cap !== 'round') {
- Path._addSquareCap(segment, cap, radius, addAreaPoint, true);
- }
- if (area.length > 0)
- return isInArea(point);
- }
- return point.getDistance(segment._point) <= radius;
+ Path._addSquareCap(segment, cap, radius, addToArea, true);
+ }
+ if (!area.isEmpty()) {
+ var loc;
+ return area.contains(point)
+ || (loc = area.getNearestLocation(point))
+ && isCloseEnough(loc.getPoint(), tolerancePadding);
+ }
+ }
+ return isCloseEnough(segment._point, strokePadding);
}
if (options.ends && !options.segments && !closed) {
if (res = checkSegmentPoints(segments[0], true)
- || checkSegmentPoints(segments[segments.length - 1], true))
+ || checkSegmentPoints(segments[numSegments - 1], true))
return res;
} else if (options.segments || options.handles) {
- for (var i = 0, l = segments.length; i < l; i++) {
+ for (var i = 0; i < numSegments; i++)
if (res = checkSegmentPoints(segments[i]))
return res;
- }
- }
- if (radius > 0) {
+ }
+ if (radius !== null) {
loc = this.getNearestLocation(point);
if (loc) {
var parameter = loc.getParameter();
- if (parameter === 0 || parameter === 1) {
+ if (parameter === 0 || parameter === 1 && numSegments > 1) {
if (!checkSegmentStroke(loc.getSegment()))
loc = null;
- } else if (loc._distance > radius) {
+ } else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
loc = null;
}
}
- if (!loc && join === 'miter') {
- for (var i = 0, l = segments.length; i < l; i++) {
+ if (!loc && join === 'miter' && numSegments > 1) {
+ for (var i = 0; i < numSegments; i++) {
var segment = segments[i];
if (point.getDistance(segment._point) <= miterLimit
&& checkSegmentStroke(segment)) {
@@ -6911,14 +7168,106 @@
}
}
}
- return !loc && options.fill && this.hasFill() && this._contains(point)
- ? new HitResult('fill', this)
- : loc
- ? new HitResult('stroke', this, { location: loc })
- : null;
- }
-
-}, new function() {
+ return !loc && hitFill && this._contains(point)
+ || loc && !hitStroke && !hitCurves
+ ? new HitResult('fill', this)
+ : loc
+ ? new HitResult(hitStroke ? 'stroke' : 'curve', this, {
+ location: loc,
+ point: loc.getPoint()
+ })
+ : null;
+ }
+
+}, {
+ beans: false,
+
+ _getOffset: function(location) {
+ var index = location && location.getIndex();
+ if (index != null) {
+ var curves = this.getCurves(),
+ offset = 0;
+ for (var i = 0; i < index; i++)
+ offset += curves[i].getLength();
+ var curve = curves[index],
+ parameter = location.getParameter();
+ if (parameter > 0)
+ offset += curve.getPartLength(0, parameter);
+ return offset;
+ }
+ return null;
+ },
+
+ getLocationOf: function() {
+ var point = Point.read(arguments),
+ curves = this.getCurves();
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var loc = curves[i].getLocationOf(point);
+ if (loc)
+ return loc;
+ }
+ return null;
+ },
+
+ getOffsetOf: function() {
+ var loc = this.getLocationOf.apply(this, arguments);
+ return loc ? loc.getOffset() : null;
+ },
+
+ getLocationAt: function(offset, isParameter) {
+ var curves = this.getCurves(),
+ length = 0;
+ if (isParameter) {
+ var index = ~~offset;
+ return curves[index].getLocationAt(offset - index, true);
+ }
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var start = length,
+ curve = curves[i];
+ length += curve.getLength();
+ if (length > offset) {
+ return curve.getLocationAt(offset - start);
+ }
+ }
+ if (offset <= this.getLength())
+ return new CurveLocation(curves[curves.length - 1], 1);
+ return null;
+ },
+
+ getPointAt: function(offset, isParameter) {
+ var loc = this.getLocationAt(offset, isParameter);
+ return loc && loc.getPoint();
+ },
+
+ getTangentAt: function(offset, isParameter) {
+ var loc = this.getLocationAt(offset, isParameter);
+ return loc && loc.getTangent();
+ },
+
+ getNormalAt: function(offset, isParameter) {
+ var loc = this.getLocationAt(offset, isParameter);
+ return loc && loc.getNormal();
+ },
+
+ getNearestLocation: function() {
+ var point = Point.read(arguments),
+ curves = this.getCurves(),
+ minDist = Infinity,
+ minLoc = null;
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var loc = curves[i].getNearestLocation(point);
+ if (loc._distance < minDist) {
+ minDist = loc._distance;
+ minLoc = loc;
+ }
+ }
+ return minLoc;
+ },
+
+ getNearestPoint: function() {
+ return this.getNearestLocation.apply(this, arguments).getPoint();
+ }
+}, new function() {
function drawHandles(ctx, segments, matrix, size) {
var half = size / 2;
@@ -6942,24 +7291,19 @@
var segment = segments[i];
segment._transformCoordinates(matrix, coords, false);
var state = segment._selectionState,
- selected = state & 4,
pX = coords[0],
pY = coords[1];
- if (selected || (state & 1))
+ if (state & 1)
drawHandle(2);
- if (selected || (state & 2))
+ if (state & 2)
drawHandle(4);
- ctx.save();
- ctx.beginPath();
- ctx.rect(pX - half, pY - half, size, size);
- ctx.fill();
- if (!selected) {
- ctx.beginPath();
- ctx.rect(pX - half + 1, pY - half + 1, size - 2, size - 2);
+ ctx.fillRect(pX - half, pY - half, size, size);
+ if (!(state & 4)) {
+ var fillStyle = ctx.fillStyle;
ctx.fillStyle = '#ffffff';
- ctx.fill();
- }
- ctx.restore();
+ ctx.fillRect(pX - half + 1, pY - half + 1, size - 2, size - 2);
+ ctx.fillStyle = fillStyle;
+ }
}
}
@@ -6973,8 +7317,7 @@
inX, inY,
outX, outY;
- function drawSegment(i) {
- var segment = segments[i];
+ function drawSegment(segment) {
if (matrix) {
segment._transformCoordinates(matrix, coords, false);
curX = coords[0];
@@ -6996,7 +7339,8 @@
inX = curX + handle._x;
inY = curY + handle._y;
}
- if (inX == curX && inY == curY && outX == prevX && outY == prevY) {
+ if (inX === curX && inY === curY
+ && outX === prevX && outY === prevY) {
ctx.lineTo(curX, curY);
} else {
ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
@@ -7015,35 +7359,40 @@
}
for (var i = 0; i < length; i++)
- drawSegment(i);
- if (path._closed && length > 1)
- drawSegment(0);
+ drawSegment(segments[i]);
+ if (path._closed && length > 0)
+ drawSegment(segments[0]);
}
return {
- _draw: function(ctx, param) {
- var clip = param.clip,
- compound = param.compound;
- if (!compound)
- ctx.beginPath();
-
- var style = this.getStyle(),
+ _draw: function(ctx, param, strokeMatrix) {
+ var dontStart = param.dontStart,
+ dontPaint = param.dontFinish || param.clip,
+ style = this.getStyle(),
hasFill = style.hasFill(),
hasStroke = style.hasStroke(),
dashArray = style.getDashArray(),
dashLength = !paper.support.nativeDash && hasStroke
&& dashArray && dashArray.length;
+ if (!dontStart)
+ ctx.beginPath();
+
+ if (!dontStart && this._currentPath) {
+ ctx.currentPath = this._currentPath;
+ } else if (hasFill || hasStroke && !dashLength || dontPaint) {
+ drawSegments(ctx, this, strokeMatrix);
+ if (this._closed)
+ ctx.closePath();
+ if (!dontStart)
+ this._currentPath = ctx.currentPath;
+ }
+
function getOffset(i) {
return dashArray[((i % dashLength) + dashLength) % dashLength];
}
- if (hasFill || hasStroke && !dashLength || compound || clip)
- drawSegments(ctx, this);
- if (this._closed)
- ctx.closePath();
-
- if (!clip && !compound && (hasFill || hasStroke)) {
+ if (!dontPaint && (hasFill || hasStroke)) {
this._setStyles(ctx);
if (hasFill) {
ctx.fill(style.getWindingRule());
@@ -7051,8 +7400,9 @@
}
if (hasStroke) {
if (dashLength) {
- ctx.beginPath();
- var flattener = new PathFlattener(this),
+ if (!dontStart)
+ ctx.beginPath();
+ var flattener = new PathFlattener(this, strokeMatrix),
length = flattener.length,
from = -style.getDashOffset(), to,
i = 0;
@@ -7077,16 +7427,15 @@
ctx.beginPath();
drawSegments(ctx, this, matrix);
ctx.stroke();
- drawHandles(ctx, this._segments, matrix,
- this._project.options.handleSize || 4);
+ drawHandles(ctx, this._segments, matrix, paper.settings.handleSize);
}
};
-}, new function() {
+}, new function() {
function getFirstControlPoints(rhs) {
var n = rhs.length,
- x = [],
- tmp = [],
+ x = [],
+ tmp = [],
b = 2;
x[0] = rhs[0] / b;
for (var i = 1; i < n; i++) {
@@ -7174,19 +7523,20 @@
}
}
};
-}, new function() {
+}, new function() {
function getCurrentSegment(that) {
var segments = that._segments;
- if (segments.length == 0)
+ if (segments.length === 0)
throw new Error('Use a moveTo() command first');
return segments[segments.length - 1];
}
return {
moveTo: function() {
- if (this._segments.length === 1)
+ var segments = this._segments;
+ if (segments.length === 1)
this.removeSegment(0);
- if (!this._segments.length)
+ if (!segments.length)
this._add([ new Segment(Point.read(arguments)) ]);
},
@@ -7235,52 +7585,115 @@
arcTo: function() {
var current = getCurrentSegment(this),
from = current._point,
+ to = Point.read(arguments),
through,
- to = Point.read(arguments),
- clockwise = Base.pick(Base.peek(arguments), true);
+ peek = Base.peek(arguments),
+ clockwise = Base.pick(peek, true),
+ center, extent, vector, matrix;
if (typeof clockwise === 'boolean') {
var middle = from.add(to).divide(2),
through = middle.add(middle.subtract(from).rotate(
clockwise ? -90 : 90));
- } else {
+ } else if (Base.remain(arguments) <= 2) {
through = to;
to = Point.read(arguments);
- }
- var l1 = new Line(from.add(through).divide(2),
- through.subtract(from).rotate(90), true),
- l2 = new Line(through.add(to).divide(2),
- to.subtract(through).rotate(90), true),
- center = l1.intersect(l2, true),
- line = new Line(from, to),
- throughSide = line.getSide(through);
- if (!center) {
- if (!throughSide)
+ } else {
+ var radius = Size.read(arguments);
+ if (radius.isZero())
return this.lineTo(to);
- throw new Error('Cannot put an arc through the given points: '
- + [from, through, to]);
- }
- var vector = from.subtract(center),
- extent = vector.getDirectedAngle(to.subtract(center)),
- centerSide = line.getSide(center);
- if (centerSide == 0) {
- extent = throughSide * Math.abs(extent);
- } else if (throughSide == centerSide) {
- extent -= 360 * (extent < 0 ? -1 : 1);
+ var rotation = Base.read(arguments),
+ clockwise = !!Base.read(arguments),
+ large = !!Base.read(arguments),
+ middle = from.add(to).divide(2),
+ pt = from.subtract(middle).rotate(-rotation),
+ x = pt.x,
+ y = pt.y,
+ abs = Math.abs,
+ EPSILON = 1e-11,
+ rx = abs(radius.width),
+ ry = abs(radius.height),
+ rxSq = rx * rx,
+ rySq = ry * ry,
+ xSq = x * x,
+ ySq = y * y;
+ var factor = Math.sqrt(xSq / rxSq + ySq / rySq);
+ if (factor > 1) {
+ rx *= factor;
+ ry *= factor;
+ rxSq = rx * rx;
+ rySq = ry * ry;
+ }
+ factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /
+ (rxSq * ySq + rySq * xSq);
+ if (abs(factor) < EPSILON)
+ factor = 0;
+ if (factor < 0)
+ throw new Error(
+ 'Cannot create an arc with the given arguments');
+ center = new Point(rx * y / ry, -ry * x / rx)
+ .multiply((large === clockwise ? -1 : 1)
+ * Math.sqrt(factor))
+ .rotate(rotation).add(middle);
+ matrix = new Matrix().translate(center).rotate(rotation)
+ .scale(rx, ry);
+ vector = matrix._inverseTransform(from);
+ extent = vector.getDirectedAngle(matrix._inverseTransform(to));
+ if (!clockwise && extent > 0)
+ extent -= 360;
+ else if (clockwise && extent < 0)
+ extent += 360;
+ }
+ if (through) {
+ var l1 = new Line(from.add(through).divide(2),
+ through.subtract(from).rotate(90), true),
+ l2 = new Line(through.add(to).divide(2),
+ to.subtract(through).rotate(90), true),
+ line = new Line(from, to),
+ throughSide = line.getSide(through);
+ center = l1.intersect(l2, true);
+ if (!center) {
+ if (!throughSide)
+ return this.lineTo(to);
+ throw new Error(
+ 'Cannot create an arc with the given arguments');
+ }
+ vector = from.subtract(center);
+ extent = vector.getDirectedAngle(to.subtract(center));
+ var centerSide = line.getSide(center);
+ if (centerSide === 0) {
+ extent = throughSide * Math.abs(extent);
+ } else if (throughSide === centerSide) {
+ extent += extent < 0 ? 360 : -360;
+ }
}
var ext = Math.abs(extent),
- count = ext >= 360 ? 4 : Math.ceil(ext / 90),
+ count = ext >= 360 ? 4 : Math.ceil(ext / 90),
inc = extent / count,
half = inc * Math.PI / 360,
z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
segments = [];
for (var i = 0; i <= count; i++) {
- var pt = i < count ? center.add(vector) : to;
- var out = i < count ? vector.rotate(90).multiply(z) : null;
- if (i == 0) {
+ var pt = to,
+ out = null;
+ if (i < count) {
+ out = vector.rotate(90).multiply(z);
+ if (matrix) {
+ pt = matrix._transformPoint(vector);
+ out = matrix._transformPoint(vector.add(out))
+ .subtract(pt);
+ } else {
+ pt = center.add(vector);
+ }
+ }
+ if (i === 0) {
current.setHandleOut(out);
} else {
- segments.push(
- new Segment(pt, vector.rotate(-90).multiply(z), out));
+ var _in = vector.rotate(-90).multiply(z);
+ if (matrix) {
+ _in = matrix._transformPoint(vector.add(_in))
+ .subtract(pt);
+ }
+ segments.push(new Segment(pt, _in, out));
}
vector = vector.rotate(inc);
}
@@ -7328,17 +7741,13 @@
}
},
- closePath: function() {
- var first = this.getFirstSegment(),
- last = this.getLastSegment();
- if (first._point.equals(last._point)) {
- first.setHandleIn(last._handleIn);
- last.remove();
- }
+ closePath: function(join) {
this.setClosed(true);
+ if (join)
+ this.join();
}
};
-}, {
+}, {
_getBounds: function(getter, matrix) {
return Path[getter](this._segments, this._closed, this.getStyle(),
@@ -7363,18 +7772,18 @@
return new Rectangle();
var coords = new Array(6),
prevCoords = first._transformCoordinates(matrix, new Array(6), false),
- min = prevCoords.slice(0, 2),
- max = min.slice(),
+ min = prevCoords.slice(0, 2),
+ max = min.slice(),
roots = new Array(2);
function processSegment(segment) {
segment._transformCoordinates(matrix, coords, false);
for (var i = 0; i < 2; i++) {
Curve._addBounds(
- prevCoords[i],
- prevCoords[i + 4],
- coords[i + 2],
- coords[i],
+ prevCoords[i],
+ prevCoords[i + 4],
+ coords[i + 2],
+ coords[i],
i, strokePadding ? strokePadding[i] : 0, min, max, roots);
}
var tmp = prevCoords;
@@ -7390,29 +7799,11 @@
},
getStrokeBounds: function(segments, closed, style, matrix) {
- function getPenPadding(radius, matrix) {
- if (!matrix)
- return [radius, radius];
- var mx = matrix.shiftless(),
- hor = mx.transform(new Point(radius, 0)),
- ver = mx.transform(new Point(0, radius)),
- phi = hor.getAngleInRadians(),
- a = hor.getLength(),
- b = ver.getLength();
- var sin = Math.sin(phi),
- cos = Math.cos(phi),
- tan = Math.tan(phi),
- tx = -Math.atan(b * tan / a),
- ty = Math.atan(b / (tan * a));
- return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
- Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
- }
-
if (!style.hasStroke())
return Path.getBounds(segments, closed, style, matrix);
var length = segments.length - (closed ? 0 : 1),
radius = style.getStrokeWidth() / 2,
- padding = getPenPadding(radius, matrix),
+ padding = Path._getPenPadding(radius, matrix),
bounds = Path.getBounds(segments, closed, style, matrix, padding),
join = style.getStrokeJoin(),
cap = style.getStrokeCap(),
@@ -7424,25 +7815,27 @@
? matrix._transformPoint(point, point) : point);
}
+ function addRound(segment) {
+ bounds = bounds.unite(joinBounds.setCenter(matrix
+ ? matrix._transformPoint(segment._point) : segment._point));
+ }
+
function addJoin(segment, join) {
- if (join === 'round' || !segment._handleIn.isZero()
- && !segment._handleOut.isZero()) {
- bounds = bounds.unite(joinBounds.setCenter(matrix
- ? matrix._transformPoint(segment._point) : segment._point));
+ var handleIn = segment._handleIn,
+ handleOut = segment._handleOut;
+ if (join === 'round' || !handleIn.isZero() && !handleOut.isZero()
+ && handleIn.isColinear(handleOut)) {
+ addRound(segment);
} else {
- Path._addSquareJoin(segment, join, radius, miterLimit, add);
+ Path._addBevelJoin(segment, join, radius, miterLimit, add);
}
}
function addCap(segment, cap) {
- switch (cap) {
- case 'round':
- addJoin(segment, cap);
- break;
- case 'butt':
- case 'square':
- Path._addSquareCap(segment, cap, radius, add);
- break;
+ if (cap === 'round') {
+ addRound(segment);
+ } else {
+ Path._addSquareCap(segment, cap, radius, add);
}
}
@@ -7450,14 +7843,32 @@
addJoin(segments[i], join);
if (closed) {
addJoin(segments[0], join);
- } else {
+ } else if (length > 0) {
addCap(segments[0], cap);
addCap(segments[segments.length - 1], cap);
}
return bounds;
},
- _addSquareJoin: function(segment, join, radius, miterLimit, addPoint, area) {
+ _getPenPadding: function(radius, matrix) {
+ if (!matrix)
+ return [radius, radius];
+ var mx = matrix.shiftless(),
+ hor = mx.transform(new Point(radius, 0)),
+ ver = mx.transform(new Point(0, radius)),
+ phi = hor.getAngleInRadians(),
+ a = hor.getLength(),
+ b = ver.getLength();
+ var sin = Math.sin(phi),
+ cos = Math.cos(phi),
+ tan = Math.tan(phi),
+ tx = -Math.atan(b * tan / a),
+ ty = Math.atan(b / (tan * a));
+ return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
+ Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
+ },
+
+ _addBevelJoin: function(segment, join, radius, miterLimit, addPoint, area) {
var curve2 = segment.getCurve(),
curve1 = curve2.getPrevious(),
point = curve2.getPointAt(0, true),
@@ -7498,7 +7909,7 @@
addPoint(point.add(normal));
}
if (cap === 'square')
- point = point.add(normal.rotate(loc.getParameter() == 0 ? -90 : 90));
+ point = point.add(normal.rotate(loc.getParameter() === 0 ? -90 : 90));
addPoint(point.add(normal));
addPoint(point.subtract(normal));
},
@@ -7510,19 +7921,19 @@
x2 = -x1,
y1 = x1,
y2 = x2;
- strokePadding = strokePadding / 2 || 0;
- joinPadding = joinPadding / 2 || 0;
for (var i = 0, l = segments.length; i < l; i++) {
var segment = segments[i];
segment._transformCoordinates(matrix, coords, false);
for (var j = 0; j < 6; j += 2) {
- var padding = j == 0 ? joinPadding : strokePadding,
+ var padding = j === 0 ? joinPadding : strokePadding,
+ paddingX = padding ? padding[0] : 0,
+ paddingY = padding ? padding[1] : 0,
x = coords[j],
y = coords[j + 1],
- xn = x - padding,
- xx = x + padding,
- yn = y - padding,
- yx = y + padding;
+ xn = x - paddingX,
+ xx = x + paddingX,
+ yn = y - paddingY,
+ yx = y + paddingY;
if (xn < x1) x1 = xn;
if (xx > x2) x2 = xx;
if (yn < y1) y1 = yn;
@@ -7533,24 +7944,23 @@
},
getRoughBounds: function(segments, closed, style, matrix) {
- var strokeWidth = style.getStrokeColor() ? style.getStrokeWidth() : 0,
- joinWidth = strokeWidth;
- if (strokeWidth === 0) {
- strokeWidth = 0.00001;
- } else {
+ var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0,
+ joinRadius = strokeRadius;
+ if (strokeRadius > 0) {
if (style.getStrokeJoin() === 'miter')
- joinWidth = strokeWidth * style.getMiterLimit();
+ joinRadius = strokeRadius * style.getMiterLimit();
if (style.getStrokeCap() === 'square')
- joinWidth = Math.max(joinWidth, strokeWidth * Math.sqrt(2));
+ joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2));
}
return Path.getHandleBounds(segments, closed, style, matrix,
- strokeWidth, joinWidth);
+ Path._getPenPadding(strokeRadius, matrix),
+ Path._getPenPadding(joinRadius, matrix));
}
}});
Path.inject({ statics: new function() {
- var kappa = Numerical.KAPPA,
+ var kappa = 0.5522847498307936,
ellipseSegments = [
new Segment([-1, 0], [0, kappa ], [0, -kappa]),
new Segment([0, -1], [-kappa, 0], [kappa, 0 ]),
@@ -7558,9 +7968,16 @@
new Segment([0, 1], [kappa, 0 ], [-kappa, 0])
];
+ function createPath(segments, closed, args) {
+ var props = Base.getNamed(args),
+ path = new Path(props && props.insert === false && Item.NO_INSERT);
+ path._add(segments);
+ path._closed = closed;
+ return path.set(props);
+ }
+
function createEllipse(center, radius, args) {
- var path = new Path(),
- segments = new Array(4);
+ var segments = new Array(4);
for (var i = 0; i < 4; i++) {
var segment = ellipseSegments[i];
segments[i] = new Segment(
@@ -7569,17 +7986,15 @@
segment._handleOut.multiply(radius)
);
}
- path._add(segments);
- path._closed = true;
- return path.set(Base.getNamed(args));
+ return createPath(segments, true, args);
}
return {
Line: function() {
- return new Path(
- Point.readNamed(arguments, 'from'),
- Point.readNamed(arguments, 'to')
- ).set(Base.getNamed(arguments));
+ return createPath([
+ new Segment(Point.readNamed(arguments, 'from')),
+ new Segment(Point.readNamed(arguments, 'to'))
+ ], false, arguments);
},
Circle: function() {
@@ -7590,27 +8005,27 @@
Rectangle: function() {
var rect = Rectangle.readNamed(arguments, 'rectangle'),
- radius = Size.readNamed(arguments, 'radius', 0, 0,
+ radius = Size.readNamed(arguments, 'radius', 0,
{ readNull: true }),
bl = rect.getBottomLeft(true),
tl = rect.getTopLeft(true),
tr = rect.getTopRight(true),
- br = rect.getBottomRight(true);
- path = new Path();
+ br = rect.getBottomRight(true),
+ segments;
if (!radius || radius.isZero()) {
- path._add([
+ segments = [
new Segment(bl),
new Segment(tl),
new Segment(tr),
new Segment(br)
- ]);
+ ];
} else {
radius = Size.min(radius, rect.getSize(true).divide(2));
var rx = radius.width,
ry = radius.height,
hx = rx * kappa,
hy = ry * kappa;
- path._add([
+ segments = [
new Segment(bl.add(rx, 0), null, [-hx, 0]),
new Segment(bl.subtract(0, ry), [0, hy]),
new Segment(tl.add(0, ry), null, [0, -hy]),
@@ -7619,10 +8034,9 @@
new Segment(tr.add(0, ry), [0, -hy], null),
new Segment(br.subtract(0, ry), null, [0, hy]),
new Segment(br.subtract(rx, 0), [hx, 0])
- ]);
- }
- path._closed = true;
- return path.set(Base.getNamed(arguments));
+ ];
+ }
+ return createPath(segments, true, arguments);
},
RoundRectangle: '#Rectangle',
@@ -7638,29 +8052,27 @@
var from = Point.readNamed(arguments, 'from'),
through = Point.readNamed(arguments, 'through'),
to = Point.readNamed(arguments, 'to'),
- path = new Path();
+ props = Base.getNamed(arguments),
+ path = new Path(props && props.insert === false
+ && Item.NO_INSERT);
path.moveTo(from);
path.arcTo(through, to);
- return path.set(Base.getNamed(arguments));
+ return path.set(props);
},
RegularPolygon: function() {
var center = Point.readNamed(arguments, 'center'),
sides = Base.readNamed(arguments, 'sides'),
radius = Base.readNamed(arguments, 'radius'),
- path = new Path(),
step = 360 / sides,
three = !(sides % 3),
vector = new Point(0, three ? -radius : radius),
offset = three ? -1 : 0.5,
segments = new Array(sides);
- for (var i = 0; i < sides; i++) {
+ for (var i = 0; i < sides; i++)
segments[i] = new Segment(center.add(
vector.rotate((i + offset) * step)));
- }
- path._add(segments);
- path._closed = true;
- return path.set(Base.getNamed(arguments));
+ return createPath(segments, true, arguments);
},
Star: function() {
@@ -7668,17 +8080,13 @@
points = Base.readNamed(arguments, 'points') * 2,
radius1 = Base.readNamed(arguments, 'radius1'),
radius2 = Base.readNamed(arguments, 'radius2'),
- path = new Path(),
step = 360 / points,
vector = new Point(0, -1),
segments = new Array(points);
- for (var i = 0; i < points; i++) {
- segments[i] = new Segment(center.add(
- vector.rotate(step * i).multiply(i % 2 ? radius2 : radius1)));
- }
- path._add(segments);
- path._closed = true;
- return path.set(Base.getNamed(arguments));
+ for (var i = 0; i < points; i++)
+ segments[i] = new Segment(center.add(vector.rotate(step * i)
+ .multiply(i % 2 ? radius2 : radius1)));
+ return createPath(segments, true, arguments);
}
};
}});
@@ -7692,12 +8100,17 @@
initialize: function CompoundPath(arg) {
this._children = [];
this._namedChildren = {};
- if (!this._initialize(arg))
- this.addChildren(Array.isArray(arg) ? arg : arguments);
+ if (!this._initialize(arg)) {
+ if (typeof arg === 'string') {
+ this.setPathData(arg);
+ } else {
+ this.addChildren(Array.isArray(arg) ? arg : arguments);
+ }
+ }
},
insertChildren: function insertChildren(index, items, _preserve) {
- items = insertChildren.base.call(this, index, items, _preserve, 'path');
+ items = insertChildren.base.call(this, index, items, _preserve, Path);
for (var i = 0, l = !_preserve && items && items.length; i < l; i++) {
var item = items[i];
if (item._clockwise === undefined)
@@ -7723,7 +8136,7 @@
},
setClockwise: function(clockwise) {
- if (this.isClockwise() != !!clockwise)
+ if (this.isClockwise() !== !!clockwise)
this.reverse();
},
@@ -7741,7 +8154,7 @@
var children = this._children,
curves = [];
for (var i = 0, l = children.length; i < l; i++)
- curves = curves.concat(children[i].getCurves());
+ curves.push.apply(curves, children[i].getCurves());
return curves;
},
@@ -7761,49 +8174,42 @@
for (var i = 0, l = children.length; i < l; i++)
area += children[i].getArea();
return area;
- },
-
- getPathData: function() {
+ }
+}, {
+ beans: true,
+
+ getPathData: function(_matrix, _precision) {
var children = this._children,
paths = [];
- for (var i = 0, l = children.length; i < l; i++)
- paths.push(children[i].getPathData(arguments[0]));
+ for (var i = 0, l = children.length; i < l; i++) {
+ var child = children[i],
+ mx = child._matrix;
+ paths.push(child.getPathData(_matrix && !mx.isIdentity()
+ ? _matrix.chain(mx) : mx, _precision));
+ }
return paths.join(' ');
- },
-
- _getWinding: function(point) {
- var children = this._children,
- winding = 0;
- for (var i = 0, l = children.length; i < l; i++)
- winding += children[i]._getWinding(point);
- return winding;
- },
-
- _hitTest : function _hitTest(point, options) {
- var res = _hitTest.base.call(this, point,
- new Base(options, { fill: false }));
- if (!res) {
- if (options.compoundChildren) {
- var children = this._children;
- for (var i = children.length - 1; i >= 0 && !res; i--)
- res = children[i]._hitTest(point, options);
- } else if (options.fill && this.hasFill()
- && this._contains(point)) {
- res = new HitResult('fill', this);
- }
- }
- return res;
- },
-
- _draw: function(ctx, param) {
+ }
+}, {
+ _getChildHitTestOptions: function(options) {
+ return options.class === Path || options.type === 'path'
+ ? options
+ : new Base(options, { fill: false });
+ },
+
+ _draw: function(ctx, param, strokeMatrix) {
var children = this._children;
if (children.length === 0)
return;
- ctx.beginPath();
- param = param.extend({ compound: true });
- for (var i = 0, l = children.length; i < l; i++)
- children[i].draw(ctx, param);
+ if (this._currentPath) {
+ ctx.currentPath = this._currentPath;
+ } else {
+ param = param.extend({ dontStart: true, dontFinish: true });
+ ctx.beginPath();
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].draw(ctx, param, strokeMatrix);
+ this._currentPath = ctx.currentPath;
+ }
if (!param.clip) {
this._setStyles(ctx);
@@ -7815,28 +8221,44 @@
if (style.hasStroke())
ctx.stroke();
}
- }
-}, new function() {
- function getCurrentPath(that) {
- if (!that._children.length)
+ },
+
+ _drawSelected: function(ctx, matrix, selectedItems) {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++) {
+ var child = children[i],
+ mx = child._matrix;
+ if (!selectedItems[child._id])
+ child._drawSelected(ctx, mx.isIdentity() ? matrix
+ : matrix.chain(mx));
+ }
+ }
+}, new function() {
+ function getCurrentPath(that, check) {
+ var children = that._children;
+ if (check && children.length === 0)
throw new Error('Use a moveTo() command first');
- return that._children[that._children.length - 1];
+ return children[children.length - 1];
}
var fields = {
moveTo: function() {
- var path = new Path();
- this.addChild(path);
+ var current = getCurrentPath(this),
+ path = current && current.isEmpty() ? current : new Path();
+ if (path !== current)
+ this.addChild(path);
path.moveTo.apply(path, arguments);
},
moveBy: function() {
- this.moveTo(getCurrentPath(this).getLastSegment()._point.add(
- Point.read(arguments)));
- },
-
- closePath: function() {
- getCurrentPath(this).closePath();
+ var current = getCurrentPath(this, true),
+ last = current && current.getLastSegment(),
+ point = Point.read(arguments);
+ this.moveTo(last ? point.add(last._point) : point);
+ },
+
+ closePath: function(join) {
+ getCurrentPath(this, true).closePath(join);
}
};
@@ -7844,7 +8266,7 @@
'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
function(key) {
fields[key] = function() {
- var path = getCurrentPath(this);
+ var path = getCurrentPath(this, true);
path[key].apply(path, arguments);
};
}
@@ -7853,11 +8275,468 @@
return fields;
});
+PathItem.inject(new function() {
+ function computeBoolean(path1, path2, operator, subtract) {
+ function preparePath(path) {
+ return path.clone(false).reduce().reorient().transform(null, true);
+ }
+
+ var _path1 = preparePath(path1),
+ _path2 = path2 && path1 !== path2 && preparePath(path2);
+ if (!_path1.isClockwise())
+ _path1.reverse();
+ if (_path2 && !(subtract ^ _path2.isClockwise()))
+ _path2.reverse();
+ splitPath(_path1.getIntersections(_path2, true));
+
+ var chain = [],
+ windings = [],
+ lengths = [],
+ segments = [],
+ monoCurves = [];
+
+ function collect(paths) {
+ for (var i = 0, l = paths.length; i < l; i++) {
+ var path = paths[i];
+ segments.push.apply(segments, path._segments);
+ monoCurves.push.apply(monoCurves, path._getMonoCurves());
+ }
+ }
+
+ collect(_path1._children || [_path1]);
+ if (_path2)
+ collect(_path2._children || [_path2]);
+ segments.sort(function(a, b) {
+ var _a = a._intersection,
+ _b = b._intersection;
+ return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1;
+ });
+ for (var i = 0, l = segments.length; i < l; i++) {
+ var segment = segments[i];
+ if (segment._winding != null)
+ continue;
+ chain.length = windings.length = lengths.length = 0;
+ var totalLength = 0,
+ startSeg = segment;
+ do {
+ chain.push(segment);
+ lengths.push(totalLength += segment.getCurve().getLength());
+ segment = segment.getNext();
+ } while (segment && !segment._intersection && segment !== startSeg);
+ for (var j = 0; j < 3; j++) {
+ var length = totalLength * Math.random(),
+ amount = lengths.length,
+ k = 0;
+ do {
+ if (lengths[k] >= length) {
+ if (k > 0)
+ length -= lengths[k - 1];
+ break;
+ }
+ } while (++k < amount);
+ var curve = chain[k].getCurve(),
+ point = curve.getPointAt(length),
+ hor = curve.isHorizontal(),
+ path = curve._path;
+ if (path._parent instanceof CompoundPath)
+ path = path._parent;
+ windings[j] = subtract && _path2
+ && (path === _path1 && _path2._getWinding(point, hor)
+ || path === _path2 && !_path1._getWinding(point, hor))
+ ? 0
+ : getWinding(point, monoCurves, hor);
+ }
+ windings.sort();
+ var winding = windings[1];
+ for (var j = chain.length - 1; j >= 0; j--)
+ chain[j]._winding = winding;
+ }
+ var result = new CompoundPath();
+ result.addChildren(tracePaths(segments, operator), true);
+ _path1.remove();
+ if (_path2)
+ _path2.remove();
+ result = result.reduce();
+ result.setStyle(path1._style);
+ return result;
+ }
+
+ function splitPath(intersections) {
+ var TOLERANCE = 0.00001,
+ linearSegments;
+
+ function resetLinear() {
+ for (var i = 0, l = linearSegments.length; i < l; i++) {
+ var segment = linearSegments[i];
+ segment._handleOut.set(0, 0);
+ segment._handleIn.set(0, 0);
+ }
+ }
+
+ for (var i = intersections.length - 1, curve, prevLoc; i >= 0; i--) {
+ var loc = intersections[i],
+ t = loc._parameter;
+ if (prevLoc && prevLoc._curve === loc._curve
+ && prevLoc._parameter > 0) {
+ t /= prevLoc._parameter;
+ } else {
+ if (linearSegments)
+ resetLinear();
+ curve = loc._curve;
+ linearSegments = curve.isLinear() && [];
+ }
+ var newCurve,
+ segment;
+ if (newCurve = curve.divide(t, true, true)) {
+ segment = newCurve._segment1;
+ curve = newCurve.getPrevious();
+ } else {
+ segment = t < TOLERANCE
+ ? curve._segment1
+ : t > 1 - TOLERANCE
+ ? curve._segment2
+ : curve.getPartLength(0, t) < curve.getPartLength(t, 1)
+ ? curve._segment1
+ : curve._segment2;
+ }
+ segment._intersection = loc.getIntersection();
+ loc._segment = segment;
+ if (linearSegments)
+ linearSegments.push(segment);
+ prevLoc = loc;
+ }
+ if (linearSegments)
+ resetLinear();
+ }
+
+ function getWinding(point, curves, horizontal, testContains) {
+ var TOLERANCE = 0.00001,
+ x = point.x,
+ y = point.y,
+ windLeft = 0,
+ windRight = 0,
+ roots = [],
+ abs = Math.abs,
+ MAX = 1 - TOLERANCE;
+ if (horizontal) {
+ var yTop = -Infinity,
+ yBottom = Infinity,
+ yBefore = y - TOLERANCE,
+ yAfter = y + TOLERANCE;
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var values = curves[i].values;
+ if (Curve.solveCubic(values, 0, x, roots, 0, 1) > 0) {
+ for (var j = roots.length - 1; j >= 0; j--) {
+ var y0 = Curve.evaluate(values, roots[j], 0).y;
+ if (y0 < yBefore && y0 > yTop) {
+ yTop = y0;
+ } else if (y0 > yAfter && y0 < yBottom) {
+ yBottom = y0;
+ }
+ }
+ }
+ }
+ yTop = (yTop + y) / 2;
+ yBottom = (yBottom + y) / 2;
+ if (yTop > -Infinity)
+ windLeft = getWinding(new Point(x, yTop), curves);
+ if (yBottom < Infinity)
+ windRight = getWinding(new Point(x, yBottom), curves);
+ } else {
+ var xBefore = x - TOLERANCE,
+ xAfter = x + TOLERANCE;
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var curve = curves[i],
+ values = curve.values,
+ winding = curve.winding,
+ next = curve.next;
+ if (winding && (winding === 1
+ && y >= values[1] && y <= values[7]
+ || y >= values[7] && y <= values[1])
+ && Curve.solveCubic(values, 1, y, roots, 0,
+ !next.winding && next.values[1] === y ? 1 : MAX) === 1){
+ var t = roots[0],
+ x0 = Curve.evaluate(values, t, 0).x,
+ slope = Curve.evaluate(values, t, 1).y;
+ if (abs(slope) < TOLERANCE && !Curve.isLinear(values)
+ || t < TOLERANCE && slope * Curve.evaluate(
+ curve.previous.values, t, 1).y < 0) {
+ if (testContains && x0 >= xBefore && x0 <= xAfter) {
+ ++windLeft;
+ ++windRight;
+ }
+ } else if (x0 <= xBefore) {
+ windLeft += winding;
+ } else if (x0 >= xAfter) {
+ windRight += winding;
+ }
+ }
+ }
+ }
+ return Math.max(abs(windLeft), abs(windRight));
+ }
+
+ function tracePaths(segments, operator, selfOp) {
+ operator = operator || function() {
+ return true;
+ };
+ var paths = [],
+ ZERO = 1e-3,
+ ONE = 1 - 1e-3;
+ for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) {
+ seg = startSeg = segments[i];
+ if (seg._visited || !operator(seg._winding))
+ continue;
+ var path = new Path(Item.NO_INSERT),
+ inter = seg._intersection,
+ startInterSeg = inter && inter._segment,
+ added = false,
+ dir = 1;
+ do {
+ var handleIn = dir > 0 ? seg._handleIn : seg._handleOut,
+ handleOut = dir > 0 ? seg._handleOut : seg._handleIn,
+ interSeg;
+ if (added && (!operator(seg._winding) || selfOp)
+ && (inter = seg._intersection)
+ && (interSeg = inter._segment)
+ && interSeg !== startSeg) {
+ if (selfOp) {
+ seg._visited = interSeg._visited;
+ seg = interSeg;
+ dir = 1;
+ } else {
+ var c1 = seg.getCurve();
+ if (dir > 0)
+ c1 = c1.getPrevious();
+ var t1 = c1.getTangentAt(dir < 1 ? ZERO : ONE, true),
+ c4 = interSeg.getCurve(),
+ c3 = c4.getPrevious(),
+ t3 = c3.getTangentAt(ONE, true),
+ t4 = c4.getTangentAt(ZERO, true),
+ w3 = t1.cross(t3),
+ w4 = t1.cross(t4);
+ if (w3 * w4 !== 0) {
+ var curve = w3 < w4 ? c3 : c4,
+ nextCurve = operator(curve._segment1._winding)
+ ? curve
+ : w3 < w4 ? c4 : c3,
+ nextSeg = nextCurve._segment1;
+ dir = nextCurve === c3 ? -1 : 1;
+ if (nextSeg._visited && seg._path !== nextSeg._path
+ || !operator(nextSeg._winding)) {
+ dir = 1;
+ } else {
+ seg._visited = interSeg._visited;
+ seg = interSeg;
+ if (nextSeg._visited)
+ dir = 1;
+ }
+ } else {
+ dir = 1;
+ }
+ }
+ handleOut = dir > 0 ? seg._handleOut : seg._handleIn;
+ }
+ path.add(new Segment(seg._point, added && handleIn, handleOut));
+ added = true;
+ seg._visited = true;
+ seg = dir > 0 ? seg.getNext() : seg. getPrevious();
+ } while (seg && !seg._visited
+ && seg !== startSeg && seg !== startInterSeg
+ && (seg._intersection || operator(seg._winding)));
+ if (seg && (seg === startSeg || seg === startInterSeg)) {
+ path.firstSegment.setHandleIn((seg === startInterSeg
+ ? startInterSeg : seg)._handleIn);
+ path.setClosed(true);
+ } else {
+ path.lastSegment._handleOut.set(0, 0);
+ }
+ if (path._segments.length >
+ (path._closed ? path.isPolygon() ? 2 : 0 : 1))
+ paths.push(path);
+ }
+ return paths;
+ }
+
+ return {
+ _getWinding: function(point, horizontal, testContains) {
+ return getWinding(point, this._getMonoCurves(),
+ horizontal, testContains);
+ },
+
+ unite: function(path) {
+ return computeBoolean(this, path, function(w) {
+ return w === 1 || w === 0;
+ }, false);
+ },
+
+ intersect: function(path) {
+ return computeBoolean(this, path, function(w) {
+ return w === 2;
+ }, false);
+ },
+
+ subtract: function(path) {
+ return computeBoolean(this, path, function(w) {
+ return w === 1;
+ }, true);
+ },
+
+ exclude: function(path) {
+ return new Group([this.subtract(path), path.subtract(this)]);
+ },
+
+ divide: function(path) {
+ return new Group([this.subtract(path), this.intersect(path)]);
+ }
+ };
+});
+
+Path.inject({
+ _getMonoCurves: function() {
+ var monoCurves = this._monoCurves,
+ prevCurve;
+
+ function insertCurve(v) {
+ var y0 = v[1],
+ y1 = v[7],
+ curve = {
+ values: v,
+ winding: y0 === y1
+ ? 0
+ : y0 > y1
+ ? -1
+ : 1,
+ previous: prevCurve,
+ next: null
+ };
+ if (prevCurve)
+ prevCurve.next = curve;
+ monoCurves.push(curve);
+ prevCurve = curve;
+ }
+
+ function handleCurve(v) {
+ if (Curve.getLength(v) === 0)
+ return;
+ var y0 = v[1],
+ y1 = v[3],
+ y2 = v[5],
+ y3 = v[7];
+ if (Curve.isLinear(v)) {
+ insertCurve(v);
+ } else {
+ var a = 3 * (y1 - y2) - y0 + y3,
+ b = 2 * (y0 + y2) - 4 * y1,
+ c = y1 - y0,
+ TOLERANCE = 0.00001,
+ roots = [];
+ var count = Numerical.solveQuadratic(a, b, c, roots, TOLERANCE,
+ 1 - TOLERANCE);
+ if (count === 0) {
+ insertCurve(v);
+ } else {
+ roots.sort();
+ var t = roots[0],
+ parts = Curve.subdivide(v, t);
+ insertCurve(parts[0]);
+ if (count > 1) {
+ t = (roots[1] - t) / (1 - t);
+ parts = Curve.subdivide(parts[1], t);
+ insertCurve(parts[0]);
+ }
+ insertCurve(parts[1]);
+ }
+ }
+ }
+
+ if (!monoCurves) {
+ monoCurves = this._monoCurves = [];
+ var curves = this.getCurves(),
+ segments = this._segments;
+ for (var i = 0, l = curves.length; i < l; i++)
+ handleCurve(curves[i].getValues());
+ if (!this._closed && segments.length > 1) {
+ var p1 = segments[segments.length - 1]._point,
+ p2 = segments[0]._point,
+ p1x = p1._x, p1y = p1._y,
+ p2x = p2._x, p2y = p2._y;
+ handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]);
+ }
+ if (monoCurves.length > 0) {
+ var first = monoCurves[0],
+ last = monoCurves[monoCurves.length - 1];
+ first.previous = last;
+ last.next = first;
+ }
+ }
+ return monoCurves;
+ },
+
+ getInteriorPoint: function() {
+ var bounds = this.getBounds(),
+ point = bounds.getCenter(true);
+ if (!this.contains(point)) {
+ var curves = this._getMonoCurves(),
+ roots = [],
+ y = point.y,
+ xIntercepts = [];
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var values = curves[i].values;
+ if ((curves[i].winding === 1
+ && y >= values[1] && y <= values[7]
+ || y >= values[7] && y <= values[1])
+ && Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
+ for (var j = roots.length - 1; j >= 0; j--)
+ xIntercepts.push(Curve.evaluate(values, roots[j], 0).x);
+ }
+ if (xIntercepts.length > 1)
+ break;
+ }
+ point.x = (xIntercepts[0] + xIntercepts[1]) / 2;
+ }
+ return point;
+ },
+
+ reorient: function() {
+ this.setClockwise(true);
+ return this;
+ }
+});
+
+CompoundPath.inject({
+ _getMonoCurves: function() {
+ var children = this._children,
+ monoCurves = [];
+ for (var i = 0, l = children.length; i < l; i++)
+ monoCurves.push.apply(monoCurves, children[i]._getMonoCurves());
+ return monoCurves;
+ },
+
+ reorient: function() {
+ var children = this.removeChildren().sort(function(a, b) {
+ return b.getBounds().getArea() - a.getBounds().getArea();
+ });
+ this.addChildren(children);
+ var clockwise = children[0].isClockwise();
+ for (var i = 1, l = children.length; i < l; i++) {
+ var point = children[i].getInteriorPoint(),
+ counters = 0;
+ for (var j = i - 1; j >= 0; j--) {
+ if (children[j].contains(point))
+ counters++;
+ }
+ children[i].setClockwise(counters % 2 === 0 && clockwise);
+ }
+ return this;
+ }
+});
+
var PathFlattener = Base.extend({
- initialize: function(path) {
- this.curves = [];
- this.parts = [];
- this.length = 0;
+ initialize: function(path, matrix) {
+ this.curves = [];
+ this.parts = [];
+ this.length = 0;
this.index = 0;
var segments = path._segments,
@@ -7866,7 +8745,7 @@
that = this;
function addCurve(segment1, segment2) {
- var curve = Curve.getValues(segment1, segment2);
+ var curve = Curve.getValues(segment1, segment2, matrix);
that.curves.push(curve);
that._computeParts(curve, segment1._index, 0, 1);
}
@@ -7917,7 +8796,7 @@
prevLen = prev ? prev.offset : 0;
return {
value: prevVal + (part.value - prevVal)
- * (offset - prevLen) / (part.offset - prevLen),
+ * (offset - prevLen) / (part.offset - prevLen),
index: part.index
};
}
@@ -8044,8 +8923,8 @@
var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
alpha1, alpha2;
if (Math.abs(detC0C1) > epsilon) {
- var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
- detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
+ var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
+ detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
alpha1 = detXC1 / detC0C1;
alpha2 = detC0X / detC0C1;
} else {
@@ -8123,7 +9002,7 @@
for (var i = first + 1; i < last; i++) {
var P = this.evaluate(3, curve, u[i - first]);
var v = P.subtract(this.points[i]);
- var dist = v.x * v.x + v.y * v.y;
+ var dist = v.x * v.x + v.y * v.y;
if (dist >= maxDist) {
maxDist = dist;
index = i;
@@ -8136,186 +9015,11 @@
}
});
-PathItem.inject(new function() {
-
- function splitPath(intersections, collectOthers) {
- intersections.sort(function(loc1, loc2) {
- var path1 = loc1.getPath(),
- path2 = loc2.getPath();
- return path1 === path2
- ? (loc1.getIndex() + loc1.getParameter())
- - (loc2.getIndex() + loc2.getParameter())
- : path1._id - path2._id;
- });
- var others = collectOthers && [];
- for (var i = intersections.length - 1; i >= 0; i--) {
- var loc = intersections[i],
- other = loc.getIntersection(),
- curve = loc.divide(),
- segment = curve && curve.getSegment1() || loc.getSegment();
- if (others)
- others.push(other);
- segment._intersection = other;
- loc._segment = segment;
- }
- return others;
- }
-
- function reorientPath(path) {
- if (path instanceof CompoundPath) {
- var children = path.removeChildren(),
- length = children.length,
- bounds = new Array(length),
- counters = new Array(length),
- clockwise;
- children.sort(function(a, b){
- var b1 = a.getBounds(), b2 = b.getBounds();
- return b1._width * b1._height < b2._width * b2._height;
- });
- path.addChildren(children);
- clockwise = children[0].isClockwise();
- for (var i = 0; i < length; i++) {
- bounds[i] = children[i].getBounds();
- counters[i] = 0;
- }
- for (var i = 0; i < length; i++) {
- for (var j = 1; j < length; j++) {
- if (i !== j && bounds[i].contains(bounds[j]))
- counters[j]++;
- }
- if (i > 0 && counters[i] % 2 === 0)
- children[i].setClockwise(clockwise);
- }
- }
- return path;
- }
-
- function computeBoolean(path1, path2, operator, subtract) {
- path1 = reorientPath(path1.clone(false));
- path2 = reorientPath(path2.clone(false));
- var path1Clockwise = path1.isClockwise(),
- path2Clockwise = path2.isClockwise(),
- intersections = path1.getIntersections(path2);
- splitPath(splitPath(intersections, true));
- if (!path1Clockwise)
- path1.reverse();
- if (!(subtract ^ path2Clockwise))
- path2.reverse();
- path1Clockwise = true;
- path2Clockwise = !subtract;
- var paths = []
- .concat(path1._children || [path1])
- .concat(path2._children || [path2]),
- segments = [],
- result = new CompoundPath();
- for (var i = 0, l = paths.length; i < l; i++) {
- var path = paths[i],
- parent = path._parent,
- clockwise = path.isClockwise(),
- segs = path._segments;
- path = parent instanceof CompoundPath ? parent : path;
- for (var j = segs.length - 1; j >= 0; j--) {
- var segment = segs[j],
- midPoint = segment.getCurve().getPoint(0.5),
- insidePath1 = path !== path1 && path1.contains(midPoint)
- && (clockwise === path1Clockwise || subtract
- || !testOnCurve(path1, midPoint)),
- insidePath2 = path !== path2 && path2.contains(midPoint)
- && (clockwise === path2Clockwise
- || !testOnCurve(path2, midPoint));
- if (operator(path === path1, insidePath1, insidePath2)) {
- segment._invalid = true;
- } else {
- segments.push(segment);
- }
- }
- }
- for (var i = 0, l = segments.length; i < l; i++) {
- var segment = segments[i];
- if (segment._visited)
- continue;
- var path = new Path(),
- loc = segment._intersection,
- intersection = loc && loc.getSegment(true);
- if (segment.getPrevious()._invalid)
- segment.setHandleIn(intersection
- ? intersection._handleIn
- : new Point(0, 0));
- do {
- segment._visited = true;
- if (segment._invalid && segment._intersection) {
- var inter = segment._intersection.getSegment(true);
- path.add(new Segment(segment._point, segment._handleIn,
- inter._handleOut));
- inter._visited = true;
- segment = inter;
- } else {
- path.add(segment.clone());
- }
- segment = segment.getNext();
- } while (segment && !segment._visited && segment !== intersection);
- var amount = path._segments.length;
- if (amount > 1 && (amount > 2 || !path.isPolygon())) {
- path.setClosed(true);
- result.addChild(path, true);
- } else {
- path.remove();
- }
- }
- path1.remove();
- path2.remove();
- return result.reduce();
- }
-
- function testOnCurve(path, point) {
- var curves = path.getCurves(),
- bounds = path.getBounds();
- if (bounds.contains(point)) {
- for (var i = 0, l = curves.length; i < l; i++) {
- var curve = curves[i];
- if (curve.getBounds().contains(point)
- && curve.getParameterOf(point))
- return true;
- }
- }
- return false;
- }
-
- return {
- unite: function(path) {
- return computeBoolean(this, path,
- function(isPath1, isInPath1, isInPath2) {
- return isInPath1 || isInPath2;
- });
- },
-
- intersect: function(path) {
- return computeBoolean(this, path,
- function(isPath1, isInPath1, isInPath2) {
- return !(isInPath1 || isInPath2);
- });
- },
-
- subtract: function(path) {
- return computeBoolean(this, path,
- function(isPath1, isInPath1, isInPath2) {
- return isPath1 && isInPath2 || !isPath1 && !isInPath1;
- }, true);
- },
-
- exclude: function(path) {
- return new Group([this.subtract(path), path.subtract(this)]);
- },
-
- divide: function(path) {
- return new Group([this.subtract(path), this.intersect(path)]);
- }
- };
-});
-
var TextItem = Item.extend({
_class: 'TextItem',
_boundsSelected: true,
+ _applyMatrix: false,
+ _canApplyMatrix: false,
_serializeFields: {
content: null
},
@@ -8345,7 +9049,7 @@
setContent: function(content) {
this._content = '' + content;
this._lines = this._content.split(/\r\n|\n|\r/mg);
- this._changed(69);
+ this._changed(265);
},
isEmpty: function() {
@@ -8367,7 +9071,7 @@
},
clone: function(insert) {
- return this._clone(new PointText({ insert: false }), insert);
+ return this._clone(new PointText(Item.NO_INSERT), insert);
},
getPoint: function() {
@@ -8375,8 +9079,8 @@
return new LinkedPoint(point.x, point.y, this, 'setPoint');
},
- setPoint: function(point) {
- point = Point.read(arguments);
+ setPoint: function() {
+ var point = Point.read(arguments);
this.translate(point.subtract(this._matrix.getTranslation()));
},
@@ -8388,7 +9092,6 @@
lines = this._lines,
leading = style.getLeading(),
shadowColor = ctx.shadowColor;
-
ctx.font = style.getFontStyle();
ctx.textAlign = style.getJustification();
for (var i = 0, l = lines.length; i < l; i++) {
@@ -8402,36 +9105,26 @@
ctx.strokeText(line, 0, 0);
ctx.translate(0, leading);
}
- }
-}, new function() {
- var measureCtx = null;
-
- return {
- _getBounds: function(getter, matrix) {
- if (!measureCtx)
- measureCtx = CanvasProvider.getContext(1, 1);
- var style = this._style,
- lines = this._lines,
- count = lines.length,
- justification = style.getJustification(),
- leading = style.getLeading(),
- x = 0;
- measureCtx.font = style.getFontStyle();
- var width = 0;
- for (var i = 0; i < count; i++)
- width = Math.max(width, measureCtx.measureText(lines[i]).width);
- if (justification !== 'left')
- x -= width / (justification === 'center' ? 2: 1);
- var bounds = new Rectangle(x,
- count ? - 0.75 * leading : 0,
- width, count * leading);
- return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
- }
- };
+ },
+
+ _getBounds: function(getter, matrix) {
+ var style = this._style,
+ lines = this._lines,
+ numLines = lines.length,
+ justification = style.getJustification(),
+ leading = style.getLeading(),
+ width = this.getView().getTextWidth(style.getFontStyle(), lines),
+ x = 0;
+ if (justification !== 'left')
+ x -= width / (justification === 'center' ? 2: 1);
+ var bounds = new Rectangle(x,
+ numLines ? - 0.75 * leading : 0,
+ width, numLines * leading);
+ return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
+ }
});
var Color = Base.extend(new function() {
-
var types = {
gray: ['gray'],
rgb: ['red', 'green', 'blue'],
@@ -8457,7 +9150,7 @@
} else if (match = string.match(/^rgba?\((.*)\)$/)) {
components = match[1].split(',');
for (var i = 0, l = components.length; i < l; i++) {
- var value = parseFloat(components[i]);
+ var value = +components[i];
components[i] = i < 3 ? value / 255 : value;
}
} else {
@@ -8474,7 +9167,7 @@
cached = colorCache[string] = [
data[0] / 255,
data[1] / 255,
- data[2] / 255
+ data[2] / 255
];
}
components = cached.slice();
@@ -8483,12 +9176,12 @@
}
var hsbIndices = [
- [0, 3, 1],
- [2, 0, 1],
- [1, 0, 3],
- [1, 2, 0],
- [3, 1, 0],
- [0, 1, 2]
+ [0, 3, 1],
+ [2, 0, 1],
+ [1, 0, 3],
+ [1, 2, 0],
+ [3, 1, 0],
+ [0, 1, 2]
];
var converters = {
@@ -8497,22 +9190,22 @@
min = Math.min(r, g, b),
delta = max - min,
h = delta === 0 ? 0
- : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
+ : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
: max == g ? (b - r) / delta + 2
- : (r - g) / delta + 4) * 60;
+ : (r - g) / delta + 4) * 60;
return [h, max === 0 ? 0 : delta / max, max];
},
'hsb-rgb': function(h, s, b) {
- var h = (h / 60) % 6,
- i = Math.floor(h),
+ h = (((h / 60) % 6) + 6) % 6;
+ var i = Math.floor(h),
f = h - i,
i = hsbIndices[i],
v = [
- b,
- b * (1 - s),
- b * (1 - s * f),
- b * (1 - s * (1 - f))
+ b,
+ b * (1 - s),
+ b * (1 - s * f),
+ b * (1 - s * (1 - f))
];
return [v[i[0]], v[i[1]], v[i[2]]];
},
@@ -8523,9 +9216,9 @@
delta = max - min,
achromatic = delta === 0,
h = achromatic ? 0
- : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
+ : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
: max == g ? (b - r) / delta + 2
- : (r - g) / delta + 4) * 60,
+ : (r - g) / delta + 4) * 60,
l = (max + min) / 2,
s = achromatic ? 0 : l < 0.5
? delta / (max + min)
@@ -8534,7 +9227,7 @@
},
'hsl-rgb': function(h, s, l) {
- h /= 360;
+ h = (((h / 360) % 1) + 1) % 1;
if (s === 0)
return [l, l, l];
var t3s = [ h + 1 / 3, h, h - 1 / 3 ],
@@ -8590,9 +9283,8 @@
parser = componentParsers[type][index] = name === 'gradient'
? function(value) {
var current = this._components[0];
- value = Gradient.read(
- Array.isArray(value) ? value : arguments,
- 0, 0, { readNull: true });
+ value = Gradient.read(Array.isArray(value) ? value
+ : arguments, 0, { readNull: true });
if (current !== value) {
if (current)
current._removeOwner(this);
@@ -8601,22 +9293,16 @@
}
return value;
}
- : name === 'hue'
- ? function(value) {
- return isNaN(value) ? 0
- : ((value % 360) + 360) % 360;
+ : type === 'gradient'
+ ? function() {
+ return Point.read(arguments, 0, {
+ readNull: name === 'highlight',
+ clone: true
+ });
}
- : type === 'gradient'
- ? function() {
- return Point.read(arguments, 0, 0, {
- readNull: name === 'highlight',
- clone: true
- });
- }
- : function(value) {
- return isNaN(value) ? 0
- : Math.min(Math.max(value, 0), 1);
- };
+ : function(value) {
+ return value == null || isNaN(value) ? 0 : value;
+ };
this['get' + part] = function() {
return this._type === type
@@ -8647,7 +9333,6 @@
var slice = Array.prototype.slice,
args = arguments,
read = 0,
- parse = true,
type,
components,
alpha,
@@ -8665,13 +9350,12 @@
alpha = args[2];
} else {
if (this.__read)
- read = 1;
+ read = 1;
args = slice.call(args, 1);
argType = typeof arg;
}
}
if (!components) {
- parse = !(this.__options && this.__options.dontParse);
values = argType === 'number'
? args
: argType === 'object' && arg.length != null
@@ -8724,7 +9408,7 @@
? 'gray'
: 'rgb';
var properties = types[type];
- parsers = parse && componentParsers[type];
+ parsers = componentParsers[type];
this._components = components = [];
for (var i = 0, l = properties.length; i < l; i++) {
var value = arg[properties[i]];
@@ -8735,8 +9419,7 @@
radial: arg.radial
};
}
- if (parse)
- value = parsers[i].call(this, value);
+ value = parsers[i].call(this, value);
if (value != null)
components[i] = value;
}
@@ -8753,9 +9436,7 @@
this._components = components = [];
var parsers = componentParsers[this._type];
for (var i = 0, l = parsers.length; i < l; i++) {
- var value = values && values[i];
- if (parse)
- value = parsers[i].call(this, value);
+ var value = parsers[i].call(this, values && values[i]);
if (value != null)
components[i] = value;
}
@@ -8779,7 +9460,7 @@
_changed: function() {
this._canvasStyle = null;
if (this._owner)
- this._owner._changed(17);
+ this._owner._changed(65);
},
_convert: function(type) {
@@ -8790,7 +9471,7 @@
? converter.apply(this, this._components)
: converters['rgb-' + type].apply(this,
converters[this._type + '-rgb'].apply(this,
- this._components));
+ this._components));
},
convert: function(type) {
@@ -8828,12 +9509,13 @@
},
equals: function(color) {
- if (Base.isPlainValue(color))
- color = Color.read(arguments);
- return color === this || color && this._class === color._class
- && this._type === color._type
- && this._alpha === color._alpha
- && Base.equals(this._components, color._components)
+ var col = Base.isPlainValue(color, true)
+ ? Color.read(arguments)
+ : color;
+ return col === this || col && this._class === col._class
+ && this._type === col._type
+ && this._alpha === col._alpha
+ && Base.equals(this._components, col._components)
|| false;
},
@@ -8856,13 +9538,16 @@
toCSS: function(hex) {
var components = this._convert('rgb'),
alpha = hex || this._alpha == null ? 1 : this._alpha;
+ function convert(val) {
+ return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);
+ }
components = [
- Math.round(components[0] * 255),
- Math.round(components[1] * 255),
- Math.round(components[2] * 255)
+ convert(components[0]),
+ convert(components[1]),
+ convert(components[2])
];
if (alpha < 1)
- components.push(alpha);
+ components.push(alpha < 0 ? 0 : alpha);
return hex
? '#' + ((1 << 24) + (components[0] << 16)
+ (components[1] << 8)
@@ -8926,46 +9611,32 @@
}
});
}, new function() {
- function clamp(value, hue) {
- return value < 0
- ? 0
- : hue && value > 360
- ? 360
- : !hue && value > 1
- ? 1
- : value;
- }
-
var operators = {
- add: function(a, b, hue) {
- return clamp(a + b, hue);
- },
-
- subtract: function(a, b, hue) {
- return clamp(a - b, hue);
- },
-
- multiply: function(a, b, hue) {
- return clamp(a * b, hue);
- },
-
- divide: function(a, b, hue) {
- return clamp(a / b, hue);
+ add: function(a, b) {
+ return a + b;
+ },
+
+ subtract: function(a, b) {
+ return a - b;
+ },
+
+ multiply: function(a, b) {
+ return a * b;
+ },
+
+ divide: function(a, b) {
+ return a / b;
}
};
return Base.each(operators, function(operator, name) {
- var options = { dontParse: /^(multiply|divide)$/.test(name) };
-
this[name] = function(color) {
- color = Color.read(arguments, 0, 0, options);
+ color = Color.read(arguments);
var type = this._type,
- properties = this._properties,
components1 = this._components,
components2 = color._convert(type);
for (var i = 0, l = components1.length; i < l; i++)
- components2[i] = operator(components1[i], components2[i],
- properties[i] === 'hue');
+ components2[i] = operator(components1[i], components2[i]);
return new Color(type, components2,
this._alpha != null
? operator(this._alpha, color.getAlpha())
@@ -9030,7 +9701,7 @@
if (index != -1) {
this._owners.splice(index, 1);
if (this._owners.length === 0)
- delete this._owners;
+ this._owners = undefined;
}
},
@@ -9048,12 +9719,12 @@
setStops: function(stops) {
if (this.stops) {
for (var i = 0, l = this._stops.length; i < l; i++)
- delete this._stops[i]._owner;
+ this._stops[i]._owner = undefined;
}
if (stops.length < 2)
throw new Error(
'Gradient stop list needs to contain at least two stops.');
- this._stops = GradientStop.readAll(stops, 0, false, true);
+ this._stops = GradientStop.readAll(stops, 0, { clone: true });
for (var i = 0, l = this._stops.length; i < l; i++) {
var stop = this._stops[i];
stop._owner = this;
@@ -9075,7 +9746,7 @@
equals: function(gradient) {
if (gradient === this)
return true;
- if (gradient && this._class === gradient._class
+ if (gradient && this._class === gradient._class
&& this._stops.length === gradient._stops.length) {
for (var i = 0, l = this._stops.length; i < l; i++) {
if (!this._stops[i].equals(gradient._stops[i]))
@@ -9113,13 +9784,13 @@
},
_serialize: function(options, dictionary) {
- return Base.serialize([this._color, this._rampPoint], options, true,
+ return Base.serialize([this._color, this._rampPoint], options, true,
dictionary);
},
_changed: function() {
if (this._owner)
- this._owner._changed(17);
+ this._owner._changed(65);
},
getRampPoint: function() {
@@ -9159,6 +9830,7 @@
strokeWidth: 1,
strokeCap: 'butt',
strokeJoin: 'miter',
+ strokeScaling: true,
miterLimit: 10,
dashOffset: 0,
dashArray: [],
@@ -9167,29 +9839,35 @@
shadowBlur: 0,
shadowOffset: new Point(),
selectedColor: undefined,
+ fontFamily: 'sans-serif',
+ fontWeight: 'normal',
+ fontSize: 12,
font: 'sans-serif',
- fontSize: 12,
leading: null,
justification: 'left'
};
var flags = {
- strokeWidth: 25,
- strokeCap: 25,
- strokeJoin: 25,
- miterLimit: 25,
- font: 5,
- fontSize: 5,
- leading: 5,
- justification: 5
+ strokeWidth: 97,
+ strokeCap: 97,
+ strokeJoin: 97,
+ strokeScaling: 105,
+ miterLimit: 97,
+ fontFamily: 9,
+ fontWeight: 9,
+ fontSize: 9,
+ font: 9,
+ leading: 9,
+ justification: 9
};
- var item = {},
+ var item = { beans: true },
fields = {
_defaults: defaults,
_textDefaults: new Base(defaults, {
- fillColor: new Color()
- })
+ fillColor: new Color()
+ }),
+ beans: true
};
Base.each(defaults, function(value, key) {
@@ -9200,9 +9878,10 @@
get = 'get' + part;
fields[set] = function(value) {
- var children = this._item && this._item._children;
+ var owner = this._owner,
+ children = owner && owner._children;
if (children && children.length > 0
- && this._item._type !== 'compound-path') {
+ && !(owner instanceof CompoundPath)) {
for (var i = 0, l = children.length; i < l; i++)
children[i]._style[set](value);
} else {
@@ -9210,25 +9889,26 @@
if (old != value) {
if (isColor) {
if (old)
- delete old._owner;
+ old._owner = undefined;
if (value && value.constructor === Color) {
if (value._owner)
value = value.clone();
- value._owner = this._item;
+ value._owner = owner;
}
}
this._values[key] = value;
- if (this._item)
- this._item._changed(flag || 17);
+ if (owner)
+ owner._changed(flag || 65);
}
}
};
- fields[get] = function() {
- var value,
- children = this._item && this._item._children;
- if (!children || children.length === 0 || arguments[0]
- || this._item._type === 'compound-path') {
+ fields[get] = function(_dontMerge) {
+ var owner = this._owner,
+ children = owner && owner._children,
+ value;
+ if (!children || children.length === 0 || _dontMerge
+ || owner instanceof CompoundPath) {
var value = this._values[key];
if (value === undefined) {
value = this._defaults[key];
@@ -9236,10 +9916,10 @@
value = value.clone();
this._values[key] = value;
} else if (isColor && !(value && value.constructor === Color)) {
- this._values[key] = value = Color.read(
- [value], 0, 0, { readNull: true, clone: true });
+ this._values[key] = value = Color.read([value], 0,
+ { readNull: true, clone: true });
if (value)
- value._owner = this._item;
+ value._owner = owner;
}
return value;
}
@@ -9254,8 +9934,8 @@
return value;
};
- item[get] = function() {
- return this._style[get]();
+ item[get] = function(_dontMerge) {
+ return this._style[get](_dontMerge);
};
item[set] = function(value) {
@@ -9268,10 +9948,11 @@
}, {
_class: 'Style',
- initialize: function Style(style, _item) {
+ initialize: function Style(style, _owner, _project) {
this._values = {};
- this._item = _item;
- if (_item instanceof TextItem)
+ this._owner = _owner;
+ this._project = _owner && _owner._project || _project || paper.project;
+ if (_owner instanceof TextItem)
this._defaults = this._textDefaults;
if (style)
this.set(style);
@@ -9309,14 +9990,26 @@
return !!this.getShadowColor() && this.getShadowBlur() > 0;
},
- getLeading: function getLeading() {
- var leading = getLeading.base.call(this);
- return leading != null ? leading : this.getFontSize() * 1.2;
+ getView: function() {
+ return this._project.getView();
},
getFontStyle: function() {
- var size = this.getFontSize();
- return size + (/[a-z]/i.test(size + '') ? ' ' : 'px ') + this.getFont();
+ var fontSize = this.getFontSize();
+ return this.getFontWeight()
+ + ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ')
+ + this.getFontFamily();
+ },
+
+ getFont: '#getFontFamily',
+ setFont: '#setFontFamily',
+
+ getLeading: function getLeading() {
+ var leading = getLeading.base.call(this),
+ fontSize = this.getFontSize();
+ if (/pt|em|%|px/.test(fontSize))
+ fontSize = this.getView().getPixelSize(fontSize);
+ return leading != null ? leading : fontSize * 1.2;
}
});
@@ -9347,6 +10040,23 @@
return res;
}
+ function handlePrefix(el, name, set, value) {
+ var prefixes = ['', 'webkit', 'moz', 'Moz', 'ms', 'o'],
+ suffix = name[0].toUpperCase() + name.substring(1);
+ for (var i = 0; i < 6; i++) {
+ var prefix = prefixes[i],
+ key = prefix ? prefix + suffix : name;
+ if (key in el) {
+ if (set) {
+ el[key] = value;
+ } else {
+ return el[key];
+ }
+ break;
+ }
+ }
+ }
+
return {
create: function(nodes, parent) {
var isArray = Array.isArray(nodes),
@@ -9452,7 +10162,7 @@
rect = { left: 0, top: 0, width: 0, height: 0 };
}
var x = rect.left - (html.clientLeft || body.clientLeft || 0),
- y = rect.top - (html.clientTop || body.clientTop || 0);
+ y = rect.top - (html.clientTop || body.clientTop || 0);
if (!viewport) {
var view = doc.defaultView;
x += view.pageXOffset || html.scrollLeft || body.scrollLeft;
@@ -9465,7 +10175,7 @@
var doc = el.ownerDocument,
view = doc.defaultView,
html = doc.documentElement;
- return new Rectangle(0, 0,
+ return new Rectangle(0, 0,
view.innerWidth || html.clientWidth,
view.innerHeight || html.clientHeight
);
@@ -9488,13 +10198,17 @@
this.getBounds(el, true));
},
- getPrefixValue: function(el, name) {
- var value = el[name],
- prefixes = ['webkit', 'moz', 'ms', 'o'],
- suffix = name[0].toUpperCase() + name.substring(1);
- for (var i = 0; i < 4 && value == null; i++)
- value = el[prefixes[i] + suffix];
- return value;
+ getPrefixed: function(el, name) {
+ return handlePrefix(el, name);
+ },
+
+ setPrefixed: function(el, name, value) {
+ if (typeof name === 'object') {
+ for (var key in name)
+ handlePrefix(el, key, true, name[key]);
+ } else {
+ handlePrefix(el, name, true, value);
+ }
}
};
};
@@ -9502,25 +10216,19 @@
var DomEvent = {
add: function(el, events) {
for (var type in events) {
- var func = events[type];
- if (el.addEventListener) {
- el.addEventListener(type, func, false);
- } else if (el.attachEvent) {
- el.attachEvent('on' + type, func.bound = function() {
- func.call(el, window.event);
- });
- }
+ var func = events[type],
+ parts = type.split(/[\s,]+/g);
+ for (var i = 0, l = parts.length; i < l; i++)
+ el.addEventListener(parts[i], func, false);
}
},
remove: function(el, events) {
for (var type in events) {
- var func = events[type];
- if (el.removeEventListener) {
- el.removeEventListener(type, func, false);
- } else if (el.detachEvent) {
- el.detachEvent('on' + type, func.bound);
- }
+ var func = events[type],
+ parts = type.split(/[\s,]+/g);
+ for (var i = 0, l = parts.length; i < l; i++)
+ el.removeEventListener(parts[i], func, false);
}
},
@@ -9540,36 +10248,23 @@
return event.target || event.srcElement;
},
+ getRelatedTarget: function(event) {
+ return event.relatedTarget || event.toElement;
+ },
+
getOffset: function(event, target) {
return DomEvent.getPoint(event).subtract(DomElement.getOffset(
target || DomEvent.getTarget(event)));
},
- preventDefault: function(event) {
- if (event.preventDefault) {
- event.preventDefault();
- } else {
- event.returnValue = false;
- }
- },
-
- stopPropagation: function(event) {
- if (event.stopPropagation) {
- event.stopPropagation();
- } else {
- event.cancelBubble = true;
- }
- },
-
stop: function(event) {
- DomEvent.stopPropagation(event);
- DomEvent.preventDefault(event);
+ event.stopPropagation();
+ event.preventDefault();
}
};
DomEvent.requestAnimationFrame = new function() {
- var nativeRequest = DomElement.getPrefixValue(window,
- 'requestAnimationFrame'),
+ var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
requested = false,
callbacks = [],
focused = true,
@@ -9620,21 +10315,32 @@
var View = Base.extend(Callback, {
_class: 'View',
- initialize: function View(element) {
- this._scope = paper;
- this._project = paper.project;
+ initialize: function View(project, element) {
+ this._project = project;
+ this._scope = project._scope;
this._element = element;
var size;
+ if (!this._pixelRatio)
+ this._pixelRatio = window.devicePixelRatio || 1;
this._id = element.getAttribute('id');
if (this._id == null)
element.setAttribute('id', this._id = 'view-' + View._id++);
- DomEvent.add(element, this._viewHandlers);
+ DomEvent.add(element, this._viewEvents);
+ var none = 'none';
+ DomElement.setPrefixed(element.style, {
+ userSelect: none,
+ touchAction: none,
+ touchCallout: none,
+ contentZooming: none,
+ userDrag: none,
+ tapHighlightColor: 'rgba(0,0,0,0)'
+ });
if (PaperScope.hasAttribute(element, 'resize')) {
var offset = DomElement.getOffset(element, true),
that = this;
size = DomElement.getViewportBounds(element)
.getSize().subtract(offset);
- this._windowHandlers = {
+ this._windowEvents = {
resize: function() {
if (!DomElement.isInvisible(element))
offset = DomElement.getOffset(element, true);
@@ -9642,12 +10348,16 @@
.getSize().subtract(offset));
}
};
- DomEvent.add(window, this._windowHandlers);
+ DomEvent.add(window, this._windowEvents);
} else {
size = DomElement.getSize(element);
- if (size.isNaN() || size.isZero())
- size = new Size(parseInt(element.getAttribute('width'), 10),
- parseInt(element.getAttribute('height'), 10));
+ if (size.isNaN() || size.isZero()) {
+ var getSize = function(name) {
+ return element[name]
+ || parseInt(element.getAttribute(name), 10);
+ };
+ size = new Size(getSize('width'), getSize('height'));
+ }
}
this._setViewSize(size);
if (PaperScope.hasAttribute(element, 'stats')
@@ -9675,16 +10385,17 @@
remove: function() {
if (!this._project)
return false;
- if (View._focused == this)
+ if (View._focused === this)
View._focused = null;
View._views.splice(View._views.indexOf(this), 1);
delete View._viewsById[this._id];
- if (this._project.view == this)
- this._project.view = null;
- DomEvent.remove(this._element, this._viewHandlers);
- DomEvent.remove(window, this._windowHandlers);
+ if (this._project._view === this)
+ this._project._view = null;
+ DomEvent.remove(this._element, this._viewEvents);
+ DomEvent.remove(window, this._windowEvents);
this._element = this._project = null;
this.detach('frame');
+ this._animate = false;
this._frameItems = {};
return true;
},
@@ -9692,13 +10403,11 @@
_events: {
onFrame: {
install: function() {
- this._animate = true;
- if (!this._requested)
- this._requestFrame();
+ this.play();
},
uninstall: function() {
- this._animate = false;
+ this.pause();
}
},
@@ -9735,7 +10444,7 @@
if (this._stats)
this._stats.update();
this._handlingFrame = false;
- this.draw(true);
+ this.update();
},
_animateItem: function(item, animate) {
@@ -9766,50 +10475,58 @@
}
},
- _redraw: function() {
- this._project._needsRedraw = true;
+ _update: function() {
+ this._project._needsUpdate = true;
if (this._handlingFrame)
return;
if (this._animate) {
this._handleFrame();
} else {
- this.draw();
+ this.update();
}
},
_changed: function(flags) {
if (flags & 1)
- this._project._needsRedraw = true;
+ this._project._needsUpdate = true;
},
_transform: function(matrix) {
this._matrix.concatenate(matrix);
this._bounds = null;
- this._redraw();
+ this._update();
},
getElement: function() {
return this._element;
},
+ getPixelRatio: function() {
+ return this._pixelRatio;
+ },
+
+ getResolution: function() {
+ return this._pixelRatio * 72;
+ },
+
getViewSize: function() {
var size = this._viewSize;
return new LinkedSize(size.width, size.height, this, 'setViewSize');
},
- setViewSize: function(size) {
- size = Size.read(arguments);
- var delta = size.subtract(this._viewSize);
+ setViewSize: function() {
+ var size = Size.read(arguments),
+ delta = size.subtract(this._viewSize);
if (delta.isZero())
return;
this._viewSize.set(size.width, size.height);
this._setViewSize(size);
- this._bounds = null;
+ this._bounds = null;
this.fire('resize', {
size: size,
delta: delta
});
- this._redraw();
+ this._update();
},
_setViewSize: function(size) {
@@ -9826,15 +10543,15 @@
},
getSize: function() {
- return this.getBounds().getSize(arguments[0]);
+ return this.getBounds().getSize();
},
getCenter: function() {
- return this.getBounds().getCenter(arguments[0]);
- },
-
- setCenter: function(center) {
- center = Point.read(arguments);
+ return this.getBounds().getCenter();
+ },
+
+ setCenter: function() {
+ var center = Point.read(arguments);
this.scrollBy(center.subtract(this.getCenter()));
},
@@ -9856,13 +10573,27 @@
this._transform(new Matrix().translate(Point.read(arguments).negate()));
},
+ play: function() {
+ this._animate = true;
+ if (!this._requested)
+ this._requestFrame();
+ },
+
+ pause: function() {
+ this._animate = false;
+ },
+
+ draw: function() {
+ this.update();
+ },
+
projectToView: function() {
return this._matrix._transformPoint(Point.read(arguments));
},
viewToProject: function() {
return this._matrix._inverseTransform(Point.read(arguments));
- },
+ }
}, {
statics: {
@@ -9870,10 +10601,10 @@
_viewsById: {},
_id: 0,
- create: function(element) {
+ create: function(project, element) {
if (typeof element === 'string')
element = document.getElementById(element);
- return new CanvasView(element);
+ return new CanvasView(project, element);
}
}
}, new function() {
@@ -9903,80 +10634,106 @@
}
}
- function mousedown(event) {
+ function handleMouseMove(view, point, event) {
+ view._handleEvent('mousemove', point, event);
+ var tool = view._scope.tool;
+ if (tool) {
+ tool._handleEvent(dragging && tool.responds('mousedrag')
+ ? 'mousedrag' : 'mousemove', point, event);
+ }
+ view.update();
+ return tool;
+ }
+
+ var navigator = window.navigator,
+ mousedown, mousemove, mouseup;
+ if (navigator.pointerEnabled || navigator.msPointerEnabled) {
+ mousedown = 'pointerdown MSPointerDown';
+ mousemove = 'pointermove MSPointerMove';
+ mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
+ } else {
+ mousedown = 'touchstart';
+ mousemove = 'touchmove';
+ mouseup = 'touchend touchcancel';
+ if (!('ontouchstart' in window && navigator.userAgent.match(
+ /mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
+ mousedown += ' mousedown';
+ mousemove += ' mousemove';
+ mouseup += ' mouseup';
+ }
+ }
+
+ var viewEvents = {
+ 'selectstart dragstart': function(event) {
+ if (dragging)
+ event.preventDefault();
+ }
+ };
+
+ var docEvents = {
+ mouseout: function(event) {
+ var view = View._focused,
+ target = DomEvent.getRelatedTarget(event);
+ if (view && (!target || target.nodeName === 'HTML'))
+ handleMouseMove(view, viewToProject(view, event), event);
+ },
+
+ scroll: updateFocus
+ };
+
+ viewEvents[mousedown] = function(event) {
var view = View._focused = getView(event),
point = viewToProject(view, event);
dragging = true;
- if (view._onMouseDown)
- view._onMouseDown(event, point);
- if (tool = view._scope._tool)
- tool._onHandleEvent('mousedown', point, event);
- view.draw(true);
- }
-
- function mousemove(event) {
- var view;
+ view._handleEvent('mousedown', point, event);
+ if (tool = view._scope.tool)
+ tool._handleEvent('mousedown', point, event);
+ view.update();
+ };
+
+ docEvents[mousemove] = function(event) {
+ var view = View._focused;
if (!dragging) {
- view = getView(event);
- if (view) {
- prevFocus = View._focused;
- View._focused = tempFocus = view;
- } else if (tempFocus && tempFocus == View._focused) {
- View._focused = prevFocus;
+ var target = getView(event);
+ if (target) {
+ if (view !== target)
+ handleMouseMove(view, viewToProject(view, event), event);
+ prevFocus = view;
+ view = View._focused = tempFocus = target;
+ } else if (tempFocus && tempFocus === view) {
+ view = View._focused = prevFocus;
updateFocus();
}
}
- if (!(view = view || View._focused))
- return;
- var point = event && viewToProject(view, event);
- if (view._onMouseMove)
- view._onMouseMove(event, point);
- if (tool = view._scope._tool) {
- if (tool._onHandleEvent(dragging && tool.responds('mousedrag')
- ? 'mousedrag' : 'mousemove', point, event))
- DomEvent.stop(event);
- }
- view.draw(true);
- }
-
- function mouseup(event) {
+ if (view) {
+ var point = viewToProject(view, event);
+ if (dragging || view.getBounds().contains(point))
+ tool = handleMouseMove(view, point, event);
+ }
+ };
+
+ docEvents[mouseup] = function(event) {
var view = View._focused;
if (!view || !dragging)
return;
var point = viewToProject(view, event);
- curPoint = null;
dragging = false;
- if (view._onMouseUp)
- view._onMouseUp(event, point);
- if (tool && tool._onHandleEvent('mouseup', point, event))
- DomEvent.stop(event);
- view.draw(true);
- }
-
- function selectstart(event) {
- if (dragging)
- DomEvent.stop(event);
- }
-
- DomEvent.add(document, {
- mousemove: mousemove,
- mouseup: mouseup,
- touchmove: mousemove,
- touchend: mouseup,
- selectstart: selectstart,
- scroll: updateFocus
- });
+ view._handleEvent('mouseup', point, event);
+ if (tool)
+ tool._handleEvent('mouseup', point, event);
+ view.update();
+ };
+
+ DomEvent.add(document, docEvents);
DomEvent.add(window, {
load: updateFocus
});
return {
- _viewHandlers: {
- mousedown: mousedown,
- touchstart: mousedown,
- selectstart: selectstart
- },
+ _viewEvents: viewEvents,
+
+ _handleEvent: function() {},
statics: {
updateFocus: updateFocus
@@ -9987,53 +10744,74 @@
var CanvasView = View.extend({
_class: 'CanvasView',
- initialize: function CanvasView(canvas) {
+ initialize: function CanvasView(project, canvas) {
if (!(canvas instanceof HTMLCanvasElement)) {
var size = Size.read(arguments);
if (size.isZero())
throw new Error(
'Cannot create CanvasView with the provided argument: '
- + canvas);
+ + [].slice.call(arguments, 1));
canvas = CanvasProvider.getCanvas(size);
}
this._context = canvas.getContext('2d');
this._eventCounters = {};
- this._ratio = 1;
+ this._pixelRatio = 1;
if (PaperScope.getAttribute(canvas, 'hidpi') !== 'off') {
var deviceRatio = window.devicePixelRatio || 1,
- backingStoreRatio = DomElement.getPrefixValue(this._context,
+ backingStoreRatio = DomElement.getPrefixed(this._context,
'backingStorePixelRatio') || 1;
- this._ratio = deviceRatio / backingStoreRatio;
- }
- View.call(this, canvas);
+ this._pixelRatio = deviceRatio / backingStoreRatio;
+ }
+ View.call(this, project, canvas);
},
_setViewSize: function(size) {
var width = size.width,
height = size.height,
- ratio = this._ratio,
+ pixelRatio = this._pixelRatio,
element = this._element,
style = element.style;
- element.width = width * ratio;
- element.height = height * ratio;
- if (ratio !== 1) {
+ element.width = width * pixelRatio;
+ element.height = height * pixelRatio;
+ if (pixelRatio !== 1) {
style.width = width + 'px';
style.height = height + 'px';
- this._context.scale(ratio, ratio);
- }
- },
-
- draw: function(checkRedraw) {
- if (checkRedraw && !this._project._needsRedraw)
+ this._context.scale(pixelRatio, pixelRatio);
+ }
+ },
+
+ getPixelSize: function(size) {
+ var ctx = this._context,
+ prevFont = ctx.font;
+ ctx.font = size + ' serif';
+ size = parseFloat(ctx.font);
+ ctx.font = prevFont;
+ return size;
+ },
+
+ getTextWidth: function(font, lines) {
+ var ctx = this._context,
+ prevFont = ctx.font,
+ width = 0;
+ ctx.font = font;
+ for (var i = 0, l = lines.length; i < l; i++)
+ width = Math.max(width, ctx.measureText(lines[i]).width);
+ ctx.font = prevFont;
+ return width;
+ },
+
+ update: function() {
+ var project = this._project;
+ if (!project || !project._needsUpdate)
return false;
var ctx = this._context,
size = this._viewSize;
ctx.clearRect(0, 0, size.width + 1, size.height + 1);
- this._project.draw(ctx, this._matrix, this._ratio);
- this._project._needsRedraw = false;
+ project.draw(ctx, this._matrix, this._pixelRatio);
+ project._needsUpdate = false;
return true;
}
-}, new function() {
+}, new function() {
var downPoint,
lastPoint,
@@ -10041,86 +10819,98 @@
downItem,
lastItem,
overItem,
- hasDrag,
- doubleClick,
+ dragItem,
+ dblClick,
clickTime;
- function callEvent(type, event, point, target, lastPoint, bubble) {
+ function callEvent(view, type, event, point, target, lastPoint) {
var item = target,
mouseEvent;
- while (item) {
- if (item.responds(type)) {
- if (!mouseEvent)
+
+ function call(obj) {
+ if (obj.responds(type)) {
+ if (!mouseEvent) {
mouseEvent = new MouseEvent(type, event, point, target,
lastPoint ? point.subtract(lastPoint) : null);
- if (item.fire(type, mouseEvent)
- && (!bubble || mouseEvent._stopped))
- return false;
- }
+ }
+ if (obj.fire(type, mouseEvent) && mouseEvent.isStopped) {
+ event.preventDefault();
+ return true;
+ }
+ }
+ }
+
+ while (item) {
+ if (call(item))
+ return true;
item = item.getParent();
}
- return true;
- }
-
- function handleEvent(view, type, event, point, lastPoint) {
- if (view._eventCounters[type]) {
- var project = view._project,
+ if (call(view))
+ return true;
+ return false;
+ }
+
+ return {
+ _handleEvent: function(type, point, event) {
+ if (!this._eventCounters[type])
+ return;
+ var project = this._project,
hit = project.hitTest(point, {
- tolerance: project.options.hitTolerance || 0,
+ tolerance: 0,
fill: true,
stroke: true
}),
- item = hit && hit.item;
- if (item) {
- if (type === 'mousemove' && item != overItem)
- lastPoint = point;
- if (type !== 'mousemove' || !hasDrag)
- callEvent(type, event, point, item, lastPoint);
- return item;
- }
- }
- }
-
- return {
- _onMouseDown: function(event, point) {
- var item = handleEvent(this, 'mousedown', event, point);
- doubleClick = lastItem == item && (Date.now() - clickTime < 300);
- downItem = lastItem = item;
- downPoint = lastPoint = overPoint = point;
- hasDrag = downItem && downItem.responds('mousedrag');
- },
-
- _onMouseUp: function(event, point) {
- var item = handleEvent(this, 'mouseup', event, point);
- if (hasDrag) {
- if (lastPoint && !lastPoint.equals(point))
- callEvent('mousedrag', event, point, downItem, lastPoint);
- if (item != downItem) {
- overPoint = point;
- callEvent('mousemove', event, point, item, overPoint);
- }
- }
- if (item === downItem) {
- clickTime = Date.now();
- if (!doubleClick
- || callEvent('doubleclick', event, downPoint, item))
- callEvent('click', event, downPoint, item);
- doubleClick = false;
- }
- downItem = null;
- hasDrag = false;
- },
-
- _onMouseMove: function(event, point) {
- if (downItem)
- callEvent('mousedrag', event, point, downItem, lastPoint);
- var item = handleEvent(this, 'mousemove', event, point, overPoint);
- lastPoint = overPoint = point;
- if (item !== overItem) {
- callEvent('mouseleave', event, point, overItem);
- overItem = item;
- callEvent('mouseenter', event, point, item);
- }
+ item = hit && hit.item,
+ stopped = false;
+ switch (type) {
+ case 'mousedown':
+ stopped = callEvent(this, type, event, point, item);
+ dblClick = lastItem == item && (Date.now() - clickTime < 300);
+ downItem = lastItem = item;
+ downPoint = lastPoint = overPoint = point;
+ dragItem = !stopped && item;
+ while (dragItem && !dragItem.responds('mousedrag'))
+ dragItem = dragItem._parent;
+ break;
+ case 'mouseup':
+ stopped = callEvent(this, type, event, point, item, downPoint);
+ if (dragItem) {
+ if (lastPoint && !lastPoint.equals(point))
+ callEvent(this, 'mousedrag', event, point, dragItem,
+ lastPoint);
+ if (item !== dragItem) {
+ overPoint = point;
+ callEvent(this, 'mousemove', event, point, item,
+ overPoint);
+ }
+ }
+ if (!stopped && item && item === downItem) {
+ clickTime = Date.now();
+ callEvent(this, dblClick && downItem.responds('doubleclick')
+ ? 'doubleclick' : 'click', event, downPoint, item);
+ dblClick = false;
+ }
+ downItem = dragItem = null;
+ break;
+ case 'mousemove':
+ if (dragItem)
+ stopped = callEvent(this, 'mousedrag', event, point,
+ dragItem, lastPoint);
+ if (!stopped) {
+ if (item !== overItem)
+ overPoint = point;
+ stopped = callEvent(this, type, event, point, item,
+ overPoint);
+ }
+ lastPoint = overPoint = point;
+ if (item !== overItem) {
+ callEvent(this, 'mouseleave', event, point, overItem);
+ overItem = item;
+ callEvent(this, 'mouseenter', event, point, item);
+ }
+ break;
+ }
+ return stopped;
}
};
});
@@ -10132,14 +10922,17 @@
this.event = event;
},
+ isPrevented: false,
+ isStopped: false,
+
preventDefault: function() {
- this._prevented = true;
- DomEvent.preventDefault(this.event);
+ this.isPrevented = true;
+ this.event.preventDefault();
},
stopPropagation: function() {
- this._stopped = true;
- DomEvent.stopPropagation(this.event);
+ this.isStopped = true;
+ this.event.stopPropagation();
},
stop: function() {
@@ -10192,14 +10985,14 @@
40: 'down',
46: 'delete',
91: 'command',
- 93: 'command',
- 224: 'command'
+ 93: 'command',
+ 224: 'command'
},
specialChars = {
- 9: true,
- 13: true,
- 32: true
+ 9: true,
+ 13: true,
+ 32: true
},
modifiers = new Base({
@@ -10211,9 +11004,9 @@
space: false
}),
- charCodeMap = {},
- keyMap = {},
- downCode;
+ charCodeMap = {},
+ keyMap = {},
+ downCode;
function handleKey(down, keyCode, charCode, event) {
var character = charCode ? String.fromCharCode(charCode) : '',
@@ -10222,7 +11015,7 @@
type = down ? 'keydown' : 'keyup',
view = View._focused,
scope = view && view.isVisible() && view._scope,
- tool = scope && scope._tool,
+ tool = scope && scope.tool,
name;
keyMap[key] = down;
if (specialKey && (name = Base.camelize(specialKey)) in modifiers)
@@ -10236,15 +11029,17 @@
paper = scope;
tool.fire(type, new KeyEvent(down, key, character, event));
if (view)
- view.draw(true);
+ view.update();
}
}
DomEvent.add(document, {
keydown: function(event) {
var code = event.which || event.keyCode;
- if (code in specialKeys) {
- handleKey(true, code, code in specialChars ? code : 0, event);
+ if (code in specialKeys || modifiers.command) {
+ handleKey(true, code,
+ code in specialChars || modifiers.command ? code : 0,
+ event);
} else {
downCode = code;
}
@@ -10591,7 +11386,7 @@
getDelta: function() {
return !this._delta && this.tool._lastPoint
- ? this.tool._point.subtract(this.tool._lastPoint)
+ ? this.tool._point.subtract(this.tool._lastPoint)
: this._delta;
},
@@ -10616,7 +11411,7 @@
if (result) {
var item = result.item,
parent = item._parent;
- while (/^(group|compound-path)$/.test(parent._type)) {
+ while (/^(Group|CompoundPath)$/.test(parent._class)) {
item = parent;
parent = parent._parent;
}
@@ -10625,6 +11420,7 @@
}
return this._item;
},
+
setItem: function(item) {
this._item = item;
},
@@ -10641,7 +11437,7 @@
var Tool = PaperScopeItem.extend({
_class: 'Tool',
_list: 'tools',
- _reference: '_tool',
+ _reference: 'tool',
_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
'onKeyDown', 'onKeyUp' ],
@@ -10748,7 +11544,7 @@
&& this.fire(type, new ToolEvent(this, type, event));
},
- _onHandleEvent: function(type, point, event) {
+ _handleEvent: function(type, point, event) {
paper = this._scope;
var called = false;
switch (type) {
@@ -10786,6 +11582,8 @@
}
break;
}
+ if (called)
+ event.preventDefault();
return called;
}
@@ -10816,20 +11614,13 @@
var CanvasProvider = {
canvases: [],
- getCanvas: function(width, height, ratio) {
+ getCanvas: function(width, height) {
var canvas,
- init = true;
+ clear = true;
if (typeof width === 'object') {
- ratio = height;
height = width.height;
width = width.width;
}
- if (!ratio) {
- ratio = 1;
- } else if (ratio !== 1) {
- width *= ratio;
- height *= ratio;
- }
if (this.canvases.length) {
canvas = this.canvases.pop();
} else {
@@ -10837,15 +11628,13 @@
}
var ctx = canvas.getContext('2d');
if (canvas.width === width && canvas.height === height) {
- if (init)
+ if (clear)
ctx.clearRect(0, 0, width + 1, height + 1);
} else {
canvas.width = width;
canvas.height = height;
}
ctx.save();
- if (ratio !== 1)
- ctx.scale(ratio, ratio);
return canvas;
},
@@ -10864,9 +11653,9 @@
var min = Math.min,
max = Math.max,
abs = Math.abs,
- sr, sg, sb, sa,
- br, bg, bb, ba,
- dr, dg, db;
+ sr, sg, sb, sa,
+ br, bg, bb, ba,
+ dr, dg, db;
function getLum(r, g, b) {
return 0.2989 * r + 0.587 * g + 0.114 * b;
@@ -10901,9 +11690,9 @@
function setSat(r, g, b, s) {
var col = [r, g, b],
- mx = max(r, g, b),
- mn = min(r, g, b),
- md;
+ mx = max(r, g, b),
+ mn = min(r, g, b),
+ md;
mn = mn === r ? 0 : mn === g ? 1 : 2;
mx = mx === r ? 0 : mx === g ? 1 : 2;
md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0;
@@ -11048,19 +11837,21 @@
var ctx = CanvasProvider.getContext(1, 1);
Base.each(modes, function(func, mode) {
- ctx.save();
var darken = mode === 'darken',
ok = false;
- ctx.fillStyle = darken ? '#300' : '#a00';
- ctx.fillRect(0, 0, 1, 1);
- ctx.globalCompositeOperation = mode;
- if (ctx.globalCompositeOperation === mode) {
- ctx.fillStyle = darken ? '#a00' : '#300';
+ ctx.save();
+ try {
+ ctx.fillStyle = darken ? '#300' : '#a00';
ctx.fillRect(0, 0, 1, 1);
- ok = ctx.getImageData(0, 0, 1, 1).data[0] !== (darken ? 170 : 51);
- }
- nativeModes[mode] = ok;
+ ctx.globalCompositeOperation = mode;
+ if (ctx.globalCompositeOperation === mode) {
+ ctx.fillStyle = darken ? '#a00' : '#300';
+ ctx.fillRect(0, 0, 1, 1);
+ ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51;
+ }
+ } catch (e) {}
ctx.restore();
+ nativeModes[mode] = ok;
});
CanvasProvider.release(ctx);
@@ -11074,15 +11865,15 @@
if (!normal)
dstContext.globalCompositeOperation = mode;
dstContext.drawImage(srcCanvas, offset.x, offset.y);
- dstContext.restore();
+ dstContext.restore();
} else {
var process = modes[mode];
if (!process)
return;
var dstData = dstContext.getImageData(offset.x, offset.y,
srcCanvas.width, srcCanvas.height),
- dst = dstData.data,
- src = srcContext.getImageData(0, 0,
+ dst = dstData.data,
+ src = srcContext.getImageData(0, 0,
srcCanvas.width, srcCanvas.height).data;
for (var i = 0, l = dst.length; i < l; i += 4) {
sr = src[i];
@@ -11112,10 +11903,20 @@
strokeWidth: ['stroke-width', 'number'],
strokeCap: ['stroke-linecap', 'string'],
strokeJoin: ['stroke-linejoin', 'string'],
+ strokeScaling: ['vector-effect', 'lookup', {
+ true: 'none',
+ false: 'non-scaling-stroke'
+ }, function(item, value) {
+ return !value
+ && (item instanceof PathItem
+ || item instanceof Shape
+ || item instanceof TextItem);
+ }],
miterLimit: ['stroke-miterlimit', 'number'],
dashArray: ['stroke-dasharray', 'array'],
dashOffset: ['stroke-dashoffset', 'number'],
- font: ['font-family', 'string'],
+ fontFamily: ['font-family', 'string'],
+ fontWeight: ['font-weight', 'string'],
fontSize: ['font-size', 'number'],
justification: ['text-anchor', 'lookup', {
left: 'start',
@@ -11135,6 +11936,7 @@
fromSVG: lookup && Base.each(lookup, function(value, name) {
this[value] = name;
}, {}),
+ exportFilter: entry[3],
get: 'get' + part,
set: 'set' + part
};
@@ -11168,10 +11970,9 @@
document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
}
- function getTransform(item, coordinates, center) {
- var matrix = item._matrix,
- trans = matrix.getTranslation(),
- attrs = {};
+ function getTransform(matrix, coordinates, center) {
+ var attrs = new Base(),
+ trans = matrix.getTranslation();
if (coordinates) {
matrix = matrix.shiftless();
var point = matrix._inverseTransform(trans);
@@ -11179,28 +11980,29 @@
attrs[center ? 'cy' : 'y'] = point.y;
trans = null;
}
- if (matrix.isIdentity())
- return attrs;
- var decomposed = matrix.decompose();
- if (decomposed && !decomposed.shearing) {
- var parts = [],
- angle = decomposed.rotation,
- scale = decomposed.scaling;
- if (trans && !trans.isZero())
- parts.push('translate(' + formatter.point(trans) + ')');
- if (!Numerical.isZero(scale.x - 1) || !Numerical.isZero(scale.y - 1))
- parts.push('scale(' + formatter.point(scale) +')');
- if (angle)
- parts.push('rotate(' + formatter.number(angle) + ')');
- attrs.transform = parts.join(' ');
- } else {
- attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
+ if (!matrix.isIdentity()) {
+ var decomposed = matrix.decompose();
+ if (decomposed && !decomposed.shearing) {
+ var parts = [],
+ angle = decomposed.rotation,
+ scale = decomposed.scaling;
+ if (trans && !trans.isZero())
+ parts.push('translate(' + formatter.point(trans) + ')');
+ if (angle)
+ parts.push('rotate(' + formatter.number(angle) + ')');
+ if (!Numerical.isZero(scale.x - 1)
+ || !Numerical.isZero(scale.y - 1))
+ parts.push('scale(' + formatter.point(scale) +')');
+ attrs.transform = parts.join(' ');
+ } else {
+ attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
+ }
}
return attrs;
}
function exportGroup(item, options) {
- var attrs = getTransform(item),
+ var attrs = getTransform(item._matrix),
children = item._children;
var node = createElement('g', attrs);
for (var i = 0, l = children.length; i < l; i++) {
@@ -11223,7 +12025,7 @@
}
function exportRaster(item) {
- var attrs = getTransform(item, true),
+ var attrs = getTransform(item._matrix, true),
size = item.getSize();
attrs.x -= size.width / 2;
attrs.y -= size.height / 2;
@@ -11241,7 +12043,7 @@
}
var segments = item._segments,
type,
- attrs;
+ attrs = getTransform(item._matrix);
if (segments.length === 0)
return null;
if (item.isPolygon()) {
@@ -11250,34 +12052,31 @@
var parts = [];
for(i = 0, l = segments.length; i < l; i++)
parts.push(formatter.point(segments[i]._point));
- attrs = {
- points: parts.join(' ')
- };
+ attrs.points = parts.join(' ');
} else {
type = 'line';
var first = segments[0]._point,
last = segments[segments.length - 1]._point;
- attrs = {
+ attrs.set({
x1: first.x,
y1: first.y,
x2: last.x,
y2: last.y
- };
+ });
}
} else {
type = 'path';
- var data = item.getPathData();
- attrs = data && { d: data };
+ attrs.d = item.getPathData(null, options.precision);
}
return createElement(type, attrs);
}
function exportShape(item) {
- var shape = item._shape,
+ var type = item._type,
radius = item._radius,
- attrs = getTransform(item, true, shape !== 'rectangle');
- if (shape === 'rectangle') {
- shape = 'rect';
+ attrs = getTransform(item._matrix, true, type !== 'rectangle');
+ if (type === 'rectangle') {
+ type = 'rect';
var size = item._size,
width = size.width,
height = size.height;
@@ -11289,26 +12088,26 @@
radius = null;
}
if (radius) {
- if (shape === 'circle') {
+ if (type === 'circle') {
attrs.r = radius;
} else {
attrs.rx = radius.width;
attrs.ry = radius.height;
}
}
- return createElement(shape, attrs);
- }
-
- function exportCompoundPath(item) {
- var attrs = getTransform(item, true);
- var data = item.getPathData();
+ return createElement(type, attrs);
+ }
+
+ function exportCompoundPath(item, options) {
+ var attrs = getTransform(item._matrix);
+ var data = item.getPathData(null, options.precision);
if (data)
attrs.d = data;
return createElement('path', attrs);
}
function exportPlacedSymbol(item, options) {
- var attrs = getTransform(item, true),
+ var attrs = getTransform(item._matrix, true),
symbol = item.getSymbol(),
symbolNode = getDefinition(symbol, 'symbol'),
definition = symbol.getDefinition(),
@@ -11378,25 +12177,25 @@
}
function exportText(item) {
- var node = createElement('text', getTransform(item, true));
+ var node = createElement('text', getTransform(item._matrix, true));
node.textContent = item._content;
return node;
}
var exporters = {
- group: exportGroup,
- layer: exportGroup,
- raster: exportRaster,
- path: exportPath,
- shape: exportShape,
- 'compound-path': exportCompoundPath,
- 'placed-symbol': exportPlacedSymbol,
- 'point-text': exportText
+ Group: exportGroup,
+ Layer: exportGroup,
+ Raster: exportRaster,
+ Path: exportPath,
+ Shape: exportShape,
+ CompoundPath: exportCompoundPath,
+ PlacedSymbol: exportPlacedSymbol,
+ PointText: exportText
};
- function applyStyle(item, node) {
+ function applyStyle(item, node, isRoot) {
var attrs = {},
- parent = item.getParent();
+ parent = !isRoot && item.getParent();
if (item._name != null)
attrs.id = item._name;
@@ -11405,7 +12204,9 @@
var get = entry.get,
type = entry.type,
value = item[get]();
- if (!parent || !Base.equals(parent[get](), value)) {
+ if (entry.exportFilter
+ ? entry.exportFilter(item, value)
+ : !parent || !Base.equals(parent[get](), value)) {
if (type === 'color' && value != null) {
var alpha = value.getAlpha();
if (alpha < 1)
@@ -11430,7 +12231,7 @@
if (attrs.opacity === 1)
delete attrs.opacity;
- if (item._visibility != null && !item._visibility)
+ if (!item._visible)
attrs.visibility = 'hidden';
return setAttributes(node, attrs);
@@ -11474,12 +12275,18 @@
: svg;
}
- function exportSVG(item, options) {
- var exporter = exporters[item._type],
+ function exportSVG(item, options, isRoot) {
+ var exporter = exporters[item._class],
node = exporter && exporter(item, options);
- if (node && item._data)
- node.setAttribute('data-paper-data', JSON.stringify(item._data));
- return node && applyStyle(item, node);
+ if (node) {
+ var onExport = options.onExport;
+ if (onExport)
+ node = onExport(item, node, options) || node;
+ var data = JSON.stringify(item._data);
+ if (data && data !== '{}')
+ node.setAttribute('data-paper-data', data);
+ }
+ return node && applyStyle(item, node, isRoot);
}
function setOptions(options) {
@@ -11492,7 +12299,7 @@
Item.inject({
exportSVG: function(options) {
options = setOptions(options);
- return exportDefinitions(exportSVG(this, options), options);
+ return exportDefinitions(exportSVG(this, options, true), options);
}
});
@@ -11500,7 +12307,8 @@
exportSVG: function(options) {
options = setOptions(options);
var layers = this.layers,
- size = this.view.getSize(),
+ view = this.getView(),
+ size = view.getViewSize(),
node = createElement('svg', {
x: 0,
y: 0,
@@ -11509,9 +12317,14 @@
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink'
- });
+ }),
+ parent = node,
+ matrix = view._matrix;
+ if (!matrix.isIdentity())
+ parent = node.appendChild(
+ createElement('g', getTransform(matrix)));
for (var i = 0, l = layers.length; i < l; i++)
- node.appendChild(exportSVG(layers[i], options));
+ parent.appendChild(exportSVG(layers[i], options, true));
return exportDefinitions(node, options);
}
});
@@ -11565,7 +12378,7 @@
: value;
}
- function importGroup(node, type, isRoot, options) {
+ function importGroup(node, type, options, isRoot) {
var nodes = node.childNodes,
isClip = type === 'clippath',
item = new Group(),
@@ -11573,7 +12386,6 @@
currentStyle = project._currentStyle,
children = [];
if (!isClip) {
- item._transformContent = false;
item = applyAttributes(item, node, isRoot);
project._currentStyle = item._style.clone();
}
@@ -11581,7 +12393,7 @@
var childNode = nodes[i],
child;
if (childNode.nodeType === 1
- && (child = importSVG(childNode, false, options))
+ && (child = importSVG(childNode, options, false))
&& !(child instanceof Symbol))
children.push(child);
}
@@ -11597,11 +12409,14 @@
}
function importPoly(node, type) {
- var path = new Path(),
- points = node.points;
- path.moveTo(points.getItem(0));
- for (var i = 1, l = points.numberOfItems; i < l; i++)
- path.lineTo(points.getItem(i));
+ var coords = node.getAttribute('points').match(
+ /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g),
+ points = [];
+ for (var i = 0, l = coords.length; i < l; i += 2)
+ points.push(new Point(
+ parseFloat(coords[i]),
+ parseFloat(coords[i + 1])));
+ var path = new Path(points);
if (type === 'polygon')
path.closePath();
return path;
@@ -11609,24 +12424,29 @@
function importPath(node) {
var data = node.getAttribute('d'),
- path = data.match(/m/gi).length > 1
- ? new CompoundPath()
- : new Path();
- path.setPathData(data);
- return path;
+ param = { pathData: data };
+ return data.match(/m/gi).length > 1 || /z\S+/i.test(data)
+ ? new CompoundPath(param)
+ : new Path(param);
}
function importGradient(node, type) {
- var nodes = node.childNodes,
- stops = [];
- for (var i = 0, l = nodes.length; i < l; i++) {
- var child = nodes[i];
- if (child.nodeType === 1)
- stops.push(applyAttributes(new GradientStop(), child));
- }
- var isRadial = type === 'radialgradient',
- gradient = new Gradient(stops, isRadial),
- origin, destination, highlight;
+ var id = (getValue(node, 'href', true) || '').substring(1),
+ isRadial = type === 'radialgradient',
+ gradient;
+ if (id) {
+ gradient = definitions[id].getGradient();
+ } else {
+ var nodes = node.childNodes,
+ stops = [];
+ for (var i = 0, l = nodes.length; i < l; i++) {
+ var child = nodes[i];
+ if (child.nodeType === 1)
+ stops.push(applyAttributes(new GradientStop(), child));
+ }
+ gradient = new Gradient(stops, isRadial);
+ }
+ var origin, destination, highlight;
if (isRadial) {
origin = getPoint(node, 'cx', 'cy');
destination = origin.add(getValue(node, 'r'), 0);
@@ -11641,14 +12461,14 @@
}
var importers = {
- '#document': function (node, type, isRoot, options) {
+ '#document': function (node, type, options, isRoot) {
var nodes = node.childNodes;
for (var i = 0, l = nodes.length; i < l; i++) {
var child = nodes[i];
if (child.nodeType === 1) {
var next = child.nextSibling;
document.body.appendChild(child);
- var item = importSVG(child, isRoot, options);
+ var item = importSVG(child, options, isRoot);
if (next) {
node.insertBefore(child, next);
} else {
@@ -11679,8 +12499,8 @@
return raster;
},
- symbol: function(node, type, isRoot, options) {
- return new Symbol(importGroup(node, type, isRoot, options), true);
+ symbol: function(node, type, options, isRoot) {
+ return new Symbol(importGroup(node, type, options, isRoot), true);
},
defs: importGroup,
@@ -11755,10 +12575,10 @@
matrix.scale(v);
break;
case 'skewX':
+ matrix.skew(v[0], 0);
+ break;
case 'skewY':
- var value = Math.tan(v[0] * Math.PI / 180),
- isX = command == 'skewX';
- matrix.shear(isX ? value : 0, isX ? 0 : value);
+ matrix.skew(0, v[0]);
break;
}
}
@@ -11812,6 +12632,10 @@
item.setVisible(value === 'visible');
},
+ display: function(item, value) {
+ item.setVisible(value !== null);
+ },
+
'stop-color': function(item, value) {
if (item.setColor)
item.setColor(value);
@@ -11851,6 +12675,7 @@
});
function getAttribute(node, name, styles) {
+ ppp = node;
var attr = node.attributes[name],
value = attr && attr.value;
if (!value) {
@@ -11885,7 +12710,7 @@
return match && definitions[match[1]];
}
- function importSVG(source, isRoot, options) {
+ function importSVG(source, options, isRoot) {
if (!source)
return null;
if (!options) {
@@ -11899,12 +12724,12 @@
function onLoadCallback(svg) {
paper = scope;
- var item = importSVG(svg, isRoot, options),
+ var item = importSVG(svg, options, isRoot),
onLoad = options.onLoad,
- view = scope.project && scope.project.view;
+ view = scope.project && scope.getView();
if (onLoad)
onLoad.call(this, item);
- view.draw(true);
+ view.update();
}
if (isRoot) {
@@ -11930,11 +12755,19 @@
throw new Error('Unsupported SVG source: ' + source);
var type = node.nodeName.toLowerCase(),
importer = importers[type],
- item = importer && importer(node, type, isRoot, options) || null,
- data = node.getAttribute && node.getAttribute('data-paper-data');
+ item,
+ data = node.getAttribute && node.getAttribute('data-paper-data'),
+ settings = scope.settings,
+ prevApplyMatrix = settings.applyMatrix;
+ settings.applyMatrix = false;
+ item = importer && importer(node, type, options, isRoot) || null;
+ settings.applyMatrix = prevApplyMatrix;
if (item) {
- if (!(item instanceof Group))
+ if (type !== '#document' && !(item instanceof Group))
item = applyAttributes(item, node, isRoot);
+ var onImport = options.onImport;
+ if (onImport)
+ item = onImport(node, item, options) || item;
if (options.expandShapes && item instanceof Shape) {
item.remove();
item = item.toPath();
@@ -11949,69 +12782,70 @@
Item.inject({
importSVG: function(node, options) {
- return this.addChild(importSVG(node, true, options));
+ return this.addChild(importSVG(node, options, true));
}
});
Project.inject({
importSVG: function(node, options) {
this.activate();
- return importSVG(node, true, options);
+ return importSVG(node, options, true);
}
});
};
-paper = new (PaperScope.inject(new Base(Base.exports, {
- enumerable: true,
- Base: Base,
- Numerical: Numerical,
- DomElement: DomElement,
- DomEvent: DomEvent,
- Http: Http,
- Key: Key
-})))();
-
-if (typeof define === 'function' && define.amd)
- define('paper', paper);
-
-return paper;
-};
-
-paper.PaperScope.prototype.PaperScript = (function(root) {
- var Base = paper.Base,
- PaperScope = paper.PaperScope,
- PaperScript,
- exports, define,
+Base.exports.PaperScript = (function() {
+ var exports, define,
scope = this;
!function(e,r){return"object"==typeof exports&&"object"==typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):(r(e.acorn||(e.acorn={})),void 0)}(this,function(e){"use strict";function r(e){fr=e||{};for(var r in hr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=hr[r]);mr=fr.sourceFile||null}function t(e,r){var t=vr(pr,e);r+=" ("+t.line+":"+t.column+")";var n=new SyntaxError(r);throw n.pos=e,n.loc=t,n.raisedAt=br,n}function n(e){function r(e){if(1==e.length)return t+="return str === "+JSON.stringify(e[0])+";";t+="switch(str){";for(var r=0;r<e.length;++r)t+="case "+JSON.stringify(e[r])+":";t+="return true}return false;"}e=e.split(" ");var t="",n=[];e:for(var a=0;a<e.length;++a){for(var o=0;o<n.length;++o)if(n[o][0].length==e[a].length){n[o].push(e[a]);continue e}n.push([e[a]])}if(n.length>3){n.sort(function(e,r){return r.length-e.length}),t+="switch(str.length){";for(var a=0;a<n.length;++a){var i=n[a];t+="case "+i[0].length+":",r(i)}t+="}"}else r(e);return new Function("str",t)}function a(){this.line=Ar,this.column=br-Sr}function o(){Ar=1,br=Sr=0,Er=!0,u()}function i(e,r){gr=br,fr.locations&&(kr=new a),wr=e,u(),Cr=r,Er=e.beforeExpr}function s(){var e=fr.onComment&&fr.locations&&new a,r=br,n=pr.indexOf("*/",br+=2);if(-1===n&&t(br-2,"Unterminated comment"),br=n+2,fr.locations){Kt.lastIndex=r;for(var o;(o=Kt.exec(pr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,pr.slice(r+2,n),r,br,e,fr.locations&&new a)}function c(){for(var e=br,r=fr.onComment&&fr.locations&&new a,t=pr.charCodeAt(br+=2);dr>br&&10!==t&&13!==t&&8232!==t&&8329!==t;)++br,t=pr.charCodeAt(br);fr.onComment&&fr.onComment(!1,pr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;dr>br;){var e=pr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=pr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e)++br,++Ar,Sr=br;else if(14>e&&e>8)++br;else if(47===e){var r=pr.charCodeAt(br+1);if(42===r)s();else{if(47!==r)break;c()}}else if(160===e)++br;else{if(!(e>=5760&&Jt.test(String.fromCharCode(e))))break;++br}}}function l(){var e=pr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=pr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function p(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Ft,1)}function d(e){var r=pr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Vt,1)}function m(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Tt,1)}function h(e){var r=pr.charCodeAt(br+1);return r===e?x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=pr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===pr.charCodeAt(br+2)?3:2,61===pr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):(61===r&&(t=61===pr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=pr.charCodeAt(br+1);return 61===r?x(qt,61===pr.charCodeAt(br+2)?3:2):x(61===e?Ct:It,1)}function y(e){switch(e){case 46:return l();case 40:return++br,i(ht);case 41:return++br,i(vt);case 59:return++br,i(yt);case 44:return++br,i(bt);case 91:return++br,i(ft);case 93:return++br,i(pt);case 123:return++br,i(dt);case 125:return++br,i(mt);case 58:return++br,i(gt);case 63:return++br,i(kt);case 48:var r=pr.charCodeAt(br+1);if(120===r||88===r)return C();case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:return A(e);case 47:return f(e);case 37:case 42:return p();case 124:case 38:return d(e);case 94:return m();case 43:case 45:return h(e);case 60:case 62:return v(e);case 61:case 33:return b(e);case 126:return x(It,1)}return!1}function g(e){if(e?br=yr+1:yr=br,fr.locations&&(xr=new a),e)return k();if(br>=dr)return i(Br);var r=pr.charCodeAt(br);if(Qt(r)||92===r)return L();var n=y(r);if(n===!1){var o=String.fromCharCode(r);if("\\"===o||$t.test(o))return L();t(br,"Unexpected character '"+o+"'")}return n}function x(e,r){var t=pr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=dr&&t(a,"Unterminated regular expression");var o=pr.charAt(br);if(Gt.test(o)&&t(a,"Unterminated regular expression"),e)e=!1;else{if("["===o)r=!0;else if("]"===o&&r)r=!1;else if("/"===o&&!r)break;e="\\"===o}++br}var n=pr.slice(a,br);++br;var s=I();return s&&!/^[gmsiy]*$/.test(s)&&t(a,"Invalid regexp flag"),i(jr,new RegExp(n,s))}function w(e,r){for(var t=br,n=0,a=0,o=null==r?1/0:r;o>a;++a){var i,s=pr.charCodeAt(br);if(i=s>=97?s-97+10:s>=65?s-65+10:s>=48&&57>=s?s-48:1/0,i>=e)break;++br,n=n*e+i}return br===t||null!=r&&br-t!==r?null:n}function C(){br+=2;var e=w(16);return null==e&&t(yr+2,"Expected hexadecimal number"),Qt(pr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===pr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===pr.charCodeAt(br)&&(++br,w(10),n=!0);var o=pr.charCodeAt(br);(69===o||101===o)&&(o=pr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(pr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=pr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Vr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=dr&&t(yr,"Unterminated string constant");var n=pr.charCodeAt(br);if(n===e)return++br,i(Fr,r);if(92===n){n=pr.charCodeAt(++br);var a=/^[0-7]+/.exec(pr.slice(br,br+3));for(a&&(a=a[0]);a&&parseInt(a,8)>255;)a=a.slice(0,a.length-1);if("0"===a&&(a=null),++br,a)Vr&&t(br-2,"Octal literal in strict mode"),r+=String.fromCharCode(parseInt(a,8)),br+=a.length-1;else switch(n){case 110:r+="\n";break;case 114:r+="\r";break;case 120:r+=String.fromCharCode(S(2));break;case 117:r+=String.fromCharCode(S(4));break;case 85:r+=String.fromCharCode(S(8));break;case 116:r+=" ";break;case 98:r+="\b";break;case 118:r+="";break;case 102:r+="\f";break;case 48:r+="\0";break;case 13:10===pr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8329===n)&&t(yr,"Unterminated string constant"),r+=String.fromCharCode(n),++br}}function S(e){var r=w(16,e);return null===r&&t(yr,"Bad character escape sequence"),r}function I(){Bt=!1;for(var e,r=!0,n=br;;){var a=pr.charCodeAt(br);if(Yt(a))Bt&&(e+=pr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=pr.slice(n,br)),Bt=!0,117!=pr.charCodeAt(++br)&&t(br,"Expecting Unicode escape sequence \\uXXXX"),++br;var o=S(4),i=String.fromCharCode(o);i||t(br-1,"Invalid Unicode escape"),(r?Qt(o):Yt(o))||t(br-4,"Invalid Unicode escape"),e+=i}r=!1}return Bt?e:pr.slice(n,br)}function L(){var e=I(),r=Dr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Vr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){for(Vr=e,br=Lr;Sr>br;)Sr=pr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function T(){this.type=null,this.start=yr,this.end=null}function V(){this.start=xr,this.end=null,null!==mr&&(this.source=mr)}function q(){var e=new T;return fr.locations&&(e.loc=new V),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new T;return r.start=e.start,fr.locations&&(r.loc=new V,r.loc.start=e.loc.start),fr.ranges&&(r.range=[e.range[0],0]),r}function j(e,r){return e.type=r,e.end=Lr,fr.locations&&(e.loc.end=Ur),fr.ranges&&(e.range[1]=Lr),e}function F(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function D(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===mt||Gt.test(pr.slice(Lr,yr)))}function M(){D(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Vr&&"Identifier"===e.type&&Nt(e.name)&&t(e.start,"Assigning to "+e.name+" in strict mode")}function W(e){Ir=Lr=br,fr.locations&&(Ur=new a),Rr=Vr=null,Tr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&F(n)&&R(!0),t=!1}return j(r,"Program")}function J(){wr===wt&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;D(yt)||B()?r.label=null:wr!==Dr?X():(r.label=lr(),M());for(var a=0;a<Tr.length;++a){var o=Tr[a];if(null==r.label||o.name===r.label.name){if(null!=o.kind&&(n||"loop"===o.kind))break;if(r.label&&n)break}}return a===Tr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Tr.push(Zt),r.body=J(),Tr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Tr.push(Zt),z(ht),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),1===i.declarations.length&&D(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return D(ut)?(N(i),_(r,i)):$(r,i);case Gr:return U(),cr(r,!0);case Kr:return U(),r.test=P(),r.consequent=J(),r.alternate=D(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),D(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(dt),Tr.push(en);for(var s,c;wr!=mt;)if(wr===zr||wr===Jr){var u=wr===zr;s&&j(s,"SwitchCase"),r.cases.push(s=q()),s.consequent=[],U(),u?s.test=K():(c&&t(Ir,"Multiple default clauses"),c=!0,s.test=null),z(gt)}else s||X(),s.consequent.push(J());return s&&j(s,"SwitchCase"),U(),Tr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(pr.slice(Lr,yr))&&t(Lr,"Illegal newline after throw"),r.argument=K(),M(),j(r,"ThrowStatement");case et:if(U(),r.block=H(),r.handler=null,wr===Xr){var l=q();U(),z(ht),l.param=lr(),Vr&&Nt(l.param.name)&&t(l.param.start,"Binding "+l.param.name+" in strict mode"),z(vt),l.guard=null,l.body=H(),r.handler=j(l,"CatchClause")}return r.guardedHandlers=qr,r.finalizer=D($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),r=G(r),M(),r;case tt:return U(),r.test=P(),Tr.push(Zt),r.body=J(),Tr.pop(),j(r,"WhileStatement");case nt:return Vr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case dt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,p=K();if(e===Dr&&"Identifier"===p.type&&D(gt)){for(var a=0;a<Tr.length;++a)Tr[a].name===f&&t(p.start,"Label '"+f+"' is already declared");var d=wr.isLoop?"loop":wr===Yr?"switch":null;return Tr.push({name:f,kind:d}),r.body=J(),Tr.pop(),r.label=p,j(r,"LabeledStatement")}return r.expression=p,M(),j(r,"ExpressionStatement")}}function P(){z(ht);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(dt);!D(mt);){var o=J();t.body.push(o),n&&e&&F(o)&&(r=a,R(a=!0)),n=!1}return a&&!r&&R(!1),j(t,"BlockStatement")}function $(e,r){return e.init=r,z(yt),e.test=wr===yt?null:K(),z(yt),e.update=wr===vt?null:K(),z(vt),e.body=J(),Tr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Tr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Vr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=D(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!D(bt))break}return j(e,"VariableDeclaration")}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];D(bt);)n.expressions.push(Q(r));return j(n,"SequenceExpression")}return t}function Q(e){var r=Y(e);if(wr.isAssign){var t=O(r);return t.operator=Cr,t.left=r,U(),t.right=Q(e),N(r),j(t,"AssignmentExpression")}return r}function Y(e){var r=Z(e);if(D(kt)){var t=O(r);return t.test=r,t.consequent=K(!0),z(gt),t.alternate=K(!0,e),j(t,"ConditionalExpression")}return r}function Z(e){return er(rr(),-1,e)}function er(e,r,t){var n=wr.binop;if(null!=n&&(!t||wr!==ut)&&n>r){var a=O(e);a.left=e,a.operator=Cr,U(),a.right=er(rr(),n,t);var a=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(a,r,t)}return e}function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,U(),e.argument=rr(),r?N(e.argument):Vr&&"delete"===e.operator&&"Identifier"===e.argument.type&&t(e.start,"Deleting local variable in strict mode"),j(e,r?"UpdateExpression":"UnaryExpression")}for(var n=tr();wr.postfix&&!B();){var e=O(n);e.operator=Cr,e.prefix=!1,e.argument=n,N(n),U(),n=j(e,"UpdateExpression")}return n}function tr(){return nr(ar())}function nr(e,r){if(D(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(D(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(pt),nr(j(t,"MemberExpression"),r)}if(!r&&D(ht)){var t=O(e);return t.callee=e,t.arguments=ur(vt,!1),nr(j(t,"CallExpression"),r)}return e}function ar(){switch(wr){case ot:var e=q();return U(),j(e,"ThisExpression");case Dr:return lr();case Or:case Fr:case jr:var e=q();return e.value=Cr,e.raw=pr.slice(yr,gr),U(),j(e,"Literal");case it:case st:case ct:var e=q();return e.value=wr.atomValue,e.raw=wr.keyword,U(),j(e,"Literal");case ht:var r=xr,t=yr;U();var n=K();return n.start=t,n.end=gr,fr.locations&&(n.loc.start=r,n.loc.end=kr),fr.ranges&&(n.range=[t,gr]),z(vt),n;case ft:var e=q();return U(),e.elements=ur(pt,!0,!0),j(e,"ArrayExpression");case dt:return ir();case Gr:var e=q();return U(),cr(e,!1);case at:return or();default:X()}}function or(){var e=q();return U(),e.callee=nr(ar(),!0),e.arguments=D(ht)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!D(mt);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&D(mt))break;var a,o={key:sr()},i=!1;if(D(gt)?(o.value=K(!0),a=o.kind="init"):fr.ecmaVersion>=5&&"Identifier"===o.key.type&&("get"===o.key.name||"set"===o.key.name)?(i=n=!0,a=o.kind=o.key.name,o.key=sr(),wr!==ht&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Vr||n))for(var s=0;s<e.properties.length;++s){var c=e.properties[s];if(c.key.name===o.key.name){var u=a==c.kind||i&&"init"===c.kind||"init"===a&&("get"===c.kind||"set"===c.kind);u&&!Vr&&"init"===a&&"init"===c.kind&&(u=!1),u&&t(o.key.start,"Redefinition of property")}}e.properties.push(o)}return j(e,"ObjectExpression")}function sr(){return wr===Or||wr===Fr?ar():lr(!0)}function cr(e,r){wr===Dr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(ht);!D(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Tr;if(Rr=!0,Tr=[],e.body=H(!0),Rr=a,Tr=o,Vr||e.body.body.length&&F(e.body.body[0]))for(var i=e.id?-1:0;i<e.params.length;++i){var s=0>i?e.id:e.params[i];if((Xt(s.name)||Nt(s.name))&&t(s.start,"Defining '"+s.name+"' in strict mode"),i>=0)for(var c=0;i>c;++c)s.name===e.params[c].name&&t(s.start,"Argument name clash in strict mode")}return j(e,r?"FunctionDeclaration":"FunctionExpression")}function ur(e,r,t){for(var n=[],a=!0;!D(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&D(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Dr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),U(),j(r,"Identifier")}e.version="0.3.2";var fr,pr,dr,mr;e.parse=function(e,t){return pr=String(e),dr=pr.length,r(t),o(),W(fr.program)};var hr=e.defaultOptions={ecmaVersion:5,strictSemicolons:!1,allowTrailingCommas:!0,forbidReserved:!1,locations:!1,onComment:null,ranges:!1,program:null,sourceFile:null},vr=e.getLineInfo=function(e,r){for(var t=1,n=0;;){Kt.lastIndex=n;var a=Kt.exec(e);if(!(a&&a.index<r))break;++t,n=a.index+a[0].length}return{line:t,column:r-n}};e.tokenize=function(e,t){function n(e){return g(e),a.start=yr,a.end=gr,a.startLoc=xr,a.endLoc=kr,a.type=wr,a.value=Cr,a}pr=String(e),dr=pr.length,r(t),o();var a={};return n.jumpTo=function(e,r){if(br=e,fr.locations){Ar=1,Sr=Kt.lastIndex=0;for(var t;(t=Kt.exec(pr))&&t.index<e;)++Ar,Sr=t.index+t[0].length}Er=r,u()},n};var br,yr,gr,xr,kr,wr,Cr,Er,Ar,Sr,Ir,Lr,Ur,Rr,Tr,Vr,qr=[],Or={type:"num"},jr={type:"regexp"},Fr={type:"string"},Dr={type:"name"},Br={type:"eof"},Mr={keyword:"break"},zr={keyword:"case",beforeExpr:!0},Xr={keyword:"catch"},Nr={keyword:"continue"},Wr={keyword:"debugger"},Jr={keyword:"default"},Pr={keyword:"do",isLoop:!0},Hr={keyword:"else",beforeExpr:!0},$r={keyword:"finally"},_r={keyword:"for",isLoop:!0},Gr={keyword:"function"},Kr={keyword:"if"},Qr={keyword:"return",beforeExpr:!0},Yr={keyword:"switch"},Zr={keyword:"throw",beforeExpr:!0},et={keyword:"try"},rt={keyword:"var"},tt={keyword:"while",isLoop:!0},nt={keyword:"with"},at={keyword:"new",beforeExpr:!0},ot={keyword:"this"},it={keyword:"null",atomValue:null},st={keyword:"true",atomValue:!0},ct={keyword:"false",atomValue:!1},ut={keyword:"in",binop:7,beforeExpr:!0},lt={"break":Mr,"case":zr,"catch":Xr,"continue":Nr,"debugger":Wr,"default":Jr,"do":Pr,"else":Hr,"finally":$r,"for":_r,"function":Gr,"if":Kr,"return":Qr,"switch":Yr,"throw":Zr,"try":et,"var":rt,"while":tt,"with":nt,"null":it,"true":st,"false":ct,"new":at,"in":ut,"instanceof":{keyword:"instanceof",binop:7,beforeExpr:!0},"this":ot,"typeof":{keyword:"typeof",prefix:!0,beforeExpr:!0},"void":{keyword:"void",prefix:!0,beforeExpr:!0},"delete":{keyword:"delete",prefix:!0,beforeExpr:!0}},ft={type:"[",beforeExpr:!0},pt={type:"]"},dt={type:"{",beforeExpr:!0},mt={type:"}"},ht={type:"(",beforeExpr:!0},vt={type:")"},bt={type:",",beforeExpr:!0},yt={type:";",beforeExpr:!0},gt={type:":",beforeExpr:!0},xt={type:"."},kt={type:"?",beforeExpr:!0},wt={binop:10,beforeExpr:!0},Ct={isAssign:!0,beforeExpr:!0},Et={isAssign:!0,beforeExpr:!0},At={binop:9,prefix:!0,beforeExpr:!0},St={postfix:!0,prefix:!0,isUpdate:!0},It={prefix:!0,beforeExpr:!0},Lt={binop:1,beforeExpr:!0},Ut={binop:2,beforeExpr:!0},Rt={binop:3,beforeExpr:!0},Tt={binop:4,beforeExpr:!0},Vt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Ft={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:pt,braceL:dt,braceR:mt,parenL:ht,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Dr,eof:Br,num:Or,regexp:jr,string:Fr};for(var Dt in lt)e.tokTypes["_"+Dt]=lt[Dt];var Bt,Mt=n("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),zt=n("class enum extends super const export import"),Xt=n("implements interface let package private protected public static yield"),Nt=n("eval arguments"),Wt=n("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"),Jt=/[\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/,Pt="\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc",Ht="\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f",$t=new RegExp("["+Pt+"]"),_t=new RegExp("["+Pt+Ht+"]"),Gt=/[\n\r\u2028\u2029]/,Kt=/\r\n|[\n\r\u2028\u2029]/g,Qt=e.isIdentifierStart=function(e){return 65>e?36===e:91>e?!0:97>e?95===e:123>e?!0:e>=170&&$t.test(String.fromCharCode(e))},Yt=e.isIdentifierChar=function(e){return 48>e?36===e:58>e?!0:65>e?!1:91>e?!0:97>e?95===e:123>e?!0:e>=170&&_t.test(String.fromCharCode(e))},Zt={kind:"loop"},en={kind:"switch"}});
+ var ua = navigator.userAgent,
+ browser = {};
+ ua.toLowerCase().replace(
+ /(opera|chrome|safari|webkit|firefox|msie|trident)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:([.\d]+))?/g,
+ function(all, n, v1, v2, rv) {
+ if (!browser.chrome) {
+ var v = n === 'opera' ? v2 : v1;
+ if (n === 'trident') {
+ v = rv;
+ n = 'msie';
+ }
+ browser.version = parseFloat(v);
+ browser.name = n;
+ browser[n] = true;
+ if (browser.chrome)
+ delete browser.webkit;
+ }
+ }
+ );
+
var binaryOperators = {
- '+': '_add',
- '-': '_subtract',
- '*': '_multiply',
- '/': '_divide',
- '%': '_modulo',
+ '+': '__add',
+ '-': '__subtract',
+ '*': '__multiply',
+ '/': '__divide',
+ '%': '__modulo',
'==': 'equals',
'!=': 'equals'
};
var unaryOperators = {
- '-': '_negate',
+ '-': '__negate',
'+': null
};
var fields = Base.each(
['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'],
function(name) {
- this['_' + name] = '#' + name;
- },
+ this['__' + name] = '#' + name;
+ },
{}
);
- paper.Point.inject(fields);
- paper.Size.inject(fields);
- paper.Color.inject(fields);
-
- function _$_(left, operator, right) {
+ Point.inject(fields);
+ Size.inject(fields);
+ Color.inject(fields);
+
+ function __$__(left, operator, right) {
var handler = binaryOperators[operator];
if (left && left[handler]) {
var res = left[handler](right);
@@ -12028,7 +12862,7 @@
}
}
- function $_(operator, value) {
+ function $__(operator, value) {
var handler = unaryOperators[operator];
if (handler && value && value[handler])
return value[handler]();
@@ -12038,7 +12872,15 @@
}
}
- function compile(code) {
+ function parse(code, options) {
+ return scope.acorn.parse(code, options);
+ }
+
+ function compile(code, url, options) {
+ if (!code)
+ return '';
+ options = options || {};
+ url = url || '';
var insertions = [];
@@ -12059,8 +12901,8 @@
function replaceCode(node, str) {
var start = getOffset(node.range[0]),
- end = getOffset(node.range[1]);
- var insert = 0;
+ end = getOffset(node.range[1]),
+ insert = 0;
for (var i = insertions.length - 1; i >= 0; i--) {
if (start > insertions[i][0]) {
insert = i + 1;
@@ -12075,7 +12917,7 @@
if (!node)
return;
for (var key in node) {
- if (key === 'range')
+ if (key === 'range' || key === 'loc')
continue;
var value = node[key];
if (Array.isArray(value)) {
@@ -12085,134 +12927,222 @@
walkAST(value, node);
}
}
- switch (node && node.type) {
+ switch (node.type) {
+ case 'UnaryExpression':
+ if (node.operator in unaryOperators
+ && node.argument.type !== 'Literal') {
+ var arg = getCode(node.argument);
+ replaceCode(node, '$__("' + node.operator + '", '
+ + arg + ')');
+ }
+ break;
case 'BinaryExpression':
if (node.operator in binaryOperators
&& node.left.type !== 'Literal') {
var left = getCode(node.left),
right = getCode(node.right);
- replaceCode(node, '_$_(' + left + ', "' + node.operator
+ replaceCode(node, '__$__(' + left + ', "' + node.operator
+ '", ' + right + ')');
}
break;
- case 'AssignmentExpression':
- if (/^.=$/.test(node.operator)
- && node.left.type !== 'Literal') {
- var left = getCode(node.left),
- right = getCode(node.right);
- replaceCode(node, left + ' = _$_(' + left + ', "'
- + node.operator[0] + '", ' + right + ')');
- }
- break;
case 'UpdateExpression':
- if (!node.prefix && !(parent && (
- parent.type === 'BinaryExpression'
+ case 'AssignmentExpression':
+ var parentType = parent && parent.type;
+ if (!(
+ parentType === 'ForStatement'
+ || parentType === 'BinaryExpression'
&& /^[=!<>]/.test(parent.operator)
- || parent.type === 'MemberExpression'
- && parent.computed))) {
- var arg = getCode(node.argument);
- replaceCode(node, arg + ' = _$_(' + arg + ', "'
- + node.operator[0] + '", 1)');
- }
- break;
- case 'UnaryExpression':
- if (node.operator in unaryOperators
- && node.argument.type !== 'Literal') {
- var arg = getCode(node.argument);
- replaceCode(node, '$_("' + node.operator + '", '
- + arg + ')');
+ || parentType === 'MemberExpression' && parent.computed
+ )) {
+ if (node.type === 'UpdateExpression') {
+ var arg = getCode(node.argument);
+ var str = arg + ' = __$__(' + arg
+ + ', "' + node.operator[0] + '", 1)';
+ if (!node.prefix
+ && (parentType === 'AssignmentExpression'
+ || parentType === 'VariableDeclarator'))
+ str = arg + '; ' + str;
+ replaceCode(node, str);
+ } else {
+ if (/^.=$/.test(node.operator)
+ && node.left.type !== 'Literal') {
+ var left = getCode(node.left),
+ right = getCode(node.right);
+ replaceCode(node, left + ' = __$__(' + left + ', "'
+ + node.operator[0] + '", ' + right + ')');
+ }
+ }
}
break;
}
}
- walkAST(scope.acorn.parse(code, { ranges: true }));
+ var sourceMap = null,
+ version = browser.version,
+ lineBreaks = /\r\n|\n|\r/mg;
+ if (browser.chrome && version >= 30
+ || browser.webkit && version >= 537.76
+ || browser.firefox && version >= 23) {
+ var offset = 0;
+ if (window.location.href.indexOf(url) === 0) {
+ var html = document.getElementsByTagName('html')[0].innerHTML;
+ offset = html.substr(0, html.indexOf(code) + 1).match(
+ lineBreaks).length + 1;
+ }
+ var mappings = ['AAAA'];
+ mappings.length = (code.match(lineBreaks) || []).length + 1 + offset;
+ sourceMap = {
+ version: 3,
+ file: url,
+ names:[],
+ mappings: mappings.join(';AACA'),
+ sourceRoot: '',
+ sources: [url]
+ };
+ var source = options.source || !url && code;
+ if (source)
+ sourceMap.sourcesContent = [source];
+ }
+ walkAST(parse(code, { ranges: true }));
+ if (sourceMap) {
+ code = new Array(offset + 1).join('\n') + code
+ + "\n//# sourceMappingURL=data:application/json;base64,"
+ + (btoa(unescape(encodeURIComponent(
+ JSON.stringify(sourceMap)))))
+ + "\n//# sourceURL=" + (url || 'paperscript');
+ }
return code;
}
- function evaluate(code, scope) {
+ function execute(code, scope, url, options) {
paper = scope;
- var view = scope.project && scope.project.view,
- res;
- with (scope) {
- (function() {
- var onActivate, onDeactivate, onEditOptions,
- onMouseDown, onMouseUp, onMouseDrag, onMouseMove,
- onKeyDown, onKeyUp, onFrame, onResize;
- code = compile(code);
- if (root.InstallTrigger) {
- var handle = PaperScript.handleException;
- if (!handle) {
- handle = PaperScript.handleException = function(e) {
- throw e.lineNumber >= lineNumber
- ? new Error(e.message, e.fileName,
- e.lineNumber - lineNumber)
- : e;
- }
- var lineNumber = new Error().lineNumber;
- lineNumber += (new Error().lineNumber - lineNumber) * 3;
- }
- try {
- res = eval(';' + code);
- } catch (e) {
- handle(e);
- }
- } else {
- res = eval(code);
- }
- if (/on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)) {
- Base.each(paper.Tool.prototype._events, function(key) {
- var value = eval(key);
- if (value) {
- scope.getTool()[key] = value;
- }
- });
- }
- if (view) {
- view.setOnResize(onResize);
- view.fire('resize', {
- size: view.size,
- delta: new Point()
- });
- if (onFrame)
- view.setOnFrame(onFrame);
- view.draw();
- }
- }).call(scope);
- }
- return res;
- }
-
- function load() {
- Base.each(document.getElementsByTagName('script'), function(script) {
- if (/^text\/(?:x-|)paperscript$/.test(script.type)
- && !script.getAttribute('data-paper-ignore')) {
- var canvas = PaperScope.getAttribute(script, 'canvas'),
- scope = PaperScope.get(canvas)
- || new PaperScope(script).setup(canvas),
- src = script.src;
- if (src) {
- paper.Http.request('get', src, function(code) {
- evaluate(code, scope);
- });
- } else {
- evaluate(script.innerHTML, scope);
- }
- script.setAttribute('data-paper-ignore', true);
- }
- }, this);
+ var view = scope.getView(),
+ tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/.test(code)
+ ? new Tool()
+ : null,
+ toolHandlers = tool ? tool._events : [],
+ handlers = ['onFrame', 'onResize'].concat(toolHandlers),
+ params = [],
+ args = [],
+ func;
+ code = compile(code, url, options);
+ function expose(scope, hidden) {
+ for (var key in scope) {
+ if ((hidden || !/^_/.test(key)) && new RegExp('([\\b\\s\\W]|^)'
+ + key.replace(/\$/g, '\\$') + '\\b').test(code)) {
+ params.push(key);
+ args.push(scope[key]);
+ }
+ }
+ }
+ expose({ __$__: __$__, $__: $__, paper: scope, view: view, tool: tool },
+ true);
+ expose(scope);
+ handlers = Base.each(handlers, function(key) {
+ if (new RegExp('\\s+' + key + '\\b').test(code)) {
+ params.push(key);
+ this.push(key + ': ' + key);
+ }
+ }, []).join(', ');
+ if (handlers)
+ code += '\nreturn { ' + handlers + ' };';
+ if (browser.chrome || browser.firefox) {
+ var script = document.createElement('script'),
+ head = document.head;
+ if (browser.firefox)
+ code = '\n' + code;
+ script.appendChild(document.createTextNode(
+ 'paper._execute = function(' + params + ') {' + code + '\n}'
+ ));
+ head.appendChild(script);
+ func = paper._execute;
+ delete paper._execute;
+ head.removeChild(script);
+ } else {
+ func = Function(params, code);
+ }
+ var res = func.apply(scope, args) || {};
+ Base.each(toolHandlers, function(key) {
+ var value = res[key];
+ if (value)
+ tool[key] = value;
+ });
+ if (view) {
+ if (res.onResize)
+ view.setOnResize(res.onResize);
+ view.fire('resize', {
+ size: view.size,
+ delta: new Point()
+ });
+ if (res.onFrame)
+ view.setOnFrame(res.onFrame);
+ view.update();
+ }
+ }
+
+ function loadScript(script) {
+ if (/^text\/(?:x-|)paperscript$/.test(script.type)
+ && PaperScope.getAttribute(script, 'ignore') !== 'true') {
+ var canvasId = PaperScope.getAttribute(script, 'canvas'),
+ canvas = document.getElementById(canvasId),
+ src = script.src,
+ scopeAttribute = 'data-paper-scope';
+ if (!canvas)
+ throw new Error('Unable to find canvas with id "'
+ + canvasId + '"');
+ var scope = PaperScope.get(canvas.getAttribute(scopeAttribute))
+ || new PaperScope().setup(canvas);
+ canvas.setAttribute(scopeAttribute, scope._id);
+ if (src) {
+ Http.request('get', src, function(code) {
+ execute(code, scope, src);
+ });
+ } else {
+ execute(script.innerHTML, scope, script.baseURI);
+ }
+ script.setAttribute('data-paper-ignore', 'true');
+ return scope;
+ }
+ }
+
+ function loadAll() {
+ Base.each(document.getElementsByTagName('script'), loadScript);
+ }
+
+ function load(script) {
+ return script ? loadScript(script) : loadAll();
}
if (document.readyState === 'complete') {
- setTimeout(load);
+ setTimeout(loadAll);
} else {
- paper.DomEvent.add(window, { load: load });
- }
-
- return PaperScript = {
+ DomEvent.add(window, { load: loadAll });
+ }
+
+ return {
compile: compile,
- evaluate: evaluate,
+ execute: execute,
load: load,
- lineNumberBase: 0
+ parse: parse
};
-})(this);
+}).call(this);
+
+paper = new (PaperScope.inject(Base.exports, {
+ enumerable: true,
+ Base: Base,
+ Numerical: Numerical,
+ DomElement: DomElement,
+ DomEvent: DomEvent,
+ Http: Http,
+ Key: Key
+}))();
+
+if (typeof define === 'function' && define.amd) {
+ define('paper', paper);
+} else if (typeof module === 'object' && module
+ && typeof module.exports === 'object') {
+ module.exports = paper;
+}
+
+return paper;
+};