diff -r fd7bfe49155a -r 444b80998255 server/php/basic/public_html/static/lib/paper/paper-full.js --- a/server/php/basic/public_html/static/lib/paper/paper-full.js Fri Aug 28 23:04:21 2015 +0200 +++ b/server/php/basic/public_html/static/lib/paper/paper-full.js Mon Sep 07 16:34:30 2015 +0200 @@ -1,5 +1,5 @@ /*! - * Paper.js v0.9.22 - The Swiss Army Knife of Vector Graphics Scripting. + * Paper.js v0.9.24 - The Swiss Army Knife of Vector Graphics Scripting. * http://paperjs.org/ * * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey @@ -9,7 +9,7 @@ * * All rights reserved. * - * Date: Sat Feb 28 19:20:48 2015 +0100 + * Date: Fri Aug 21 16:39:41 2015 +0200 * *** * @@ -88,7 +88,7 @@ val = dest[val.substring(1)] || val; var isFunc = typeof val === 'function', res = val, - prev = preserve || isFunc + prev = preserve || isFunc && !val.base ? (val && val.get ? name in dest : dest[name]) : null, bean; @@ -161,19 +161,22 @@ extend: function() { var base = this, - ctor; + ctor, + proto; for (var i = 0, l = arguments.length; i < l; i++) if (ctor = arguments[i].initialize) break; ctor = ctor || function() { base.apply(this, arguments); }; - ctor.prototype = create(this.prototype); - ctor.base = base; - define(ctor.prototype, 'constructor', + proto = ctor.prototype = create(this.prototype); + define(proto, 'constructor', { value: ctor, writable: true, configurable: true }); inject(ctor, this, true); - return arguments.length ? this.inject.apply(ctor, arguments) : ctor; + if (arguments.length) + this.inject.apply(ctor, arguments); + ctor.base = base; + return ctor; } }, true).inject({ inject: function() { @@ -259,9 +262,10 @@ _set: function(props, exclude, dontCheck) { if (props && (dontCheck || Base.isPlainObject(props))) { - var orig = props._filtering || props; - for (var key in orig) { - if (orig.hasOwnProperty(key) && !(exclude && exclude[key])) { + var keys = Object.keys(props._filtering || props); + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + if (!(exclude && exclude[key])) { var value = props[key]; if (value !== undefined) this[key] = value; @@ -286,35 +290,33 @@ }, equals: function(obj1, obj2) { - function checkKeys(o1, o2) { - for (var i in o1) - if (o1.hasOwnProperty(i) && !o2.hasOwnProperty(i)) - return false; - return true; - } if (obj1 === obj2) return true; if (obj1 && obj1.equals) return obj1.equals(obj2); if (obj2 && obj2.equals) return obj2.equals(obj1); - if (Array.isArray(obj1) && Array.isArray(obj2)) { - if (obj1.length !== obj2.length) - return false; - for (var i = 0, l = obj1.length; i < l; i++) { - if (!Base.equals(obj1[i], obj2[i])) + if (obj1 && obj2 + && typeof obj1 === 'object' && typeof obj2 === 'object') { + if (Array.isArray(obj1) && Array.isArray(obj2)) { + var length = obj1.length; + if (length !== obj2.length) return false; - } - return true; - } - if (obj1 && typeof obj1 === 'object' - && obj2 && typeof obj2 === 'object') { - if (!checkKeys(obj1, obj2) || !checkKeys(obj2, obj1)) - return false; - for (var i in obj1) { - if (obj1.hasOwnProperty(i) - && !Base.equals(obj1[i], obj2[i])) + while (length--) { + if (!Base.equals(obj1[length], obj2[length])) + return false; + } + } else { + var keys = Object.keys(obj1), + length = keys.length; + if (length !== Object.keys(obj2).length) return false; + while (length--) { + var key = keys[length]; + if (!(obj2.hasOwnProperty(key) + && Base.equals(obj1[key], obj2[key]))) + return false; + } } return true; } @@ -443,10 +445,12 @@ res._compact = true; } else if (Base.isPlainObject(obj)) { res = {}; - for (var i in obj) - if (obj.hasOwnProperty(i)) - res[i] = Base.serialize(obj[i], options, compact, - dictionary); + var keys = Object.keys(obj); + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + res[key] = Base.serialize(obj[key], options, compact, + dictionary); + } } else if (typeof obj === 'number') { res = options.formatter.number(obj, options.precision); } else { @@ -457,24 +461,23 @@ : res; }, - deserialize: function(json, create, _data) { + deserialize: function(json, create, _data, _isDictionary) { var res = json, isRoot = !_data; _data = _data || {}; if (Array.isArray(json)) { var type = json[0], isDictionary = type === 'dictionary'; - if (!isDictionary) { - if (_data.dictionary && json.length == 1 && /^#/.test(type)) - return _data.dictionary[type]; - type = Base.exports[type]; - } + if (json.length == 1 && /^#/.test(type)) + return _data.dictionary[type]; + type = Base.exports[type]; res = []; + if (_isDictionary) + _data.dictionary = res; for (var i = type ? 1 : 0, l = json.length; i < l; i++) - res.push(Base.deserialize(json[i], create, _data)); - if (isDictionary) { - _data.dictionary = res[0]; - } else if (type) { + res.push(Base.deserialize(json[i], create, _data, + isDictionary)); + if (type) { var args = res; if (create) { res = create(type, args); @@ -485,6 +488,8 @@ } } else if (Base.isPlainObject(json)) { res = {}; + if (_isDictionary) + _data.dictionary = res; for (var key in json) res[key] = Base.deserialize(json[key], create, _data); } @@ -570,15 +575,14 @@ this.on(key, value); }, this); } else { - var entry = this._eventTypes[type]; - if (entry) { - var handlers = this._callbacks = this._callbacks || {}; - handlers = handlers[type] = handlers[type] || []; - if (handlers.indexOf(func) === -1) { - handlers.push(func); - if (entry.install && handlers.length == 1) - entry.install.call(this, type); - } + var types = this._eventTypes, + entry = types && types[type], + handlers = this._callbacks = this._callbacks || {}; + handlers = handlers[type] = handlers[type] || []; + if (handlers.indexOf(func) === -1) { + handlers.push(func); + if (entry && entry.install && handlers.length == 1) + entry.install.call(this, type); } } return this; @@ -591,13 +595,14 @@ }, this); return; } - var entry = this._eventTypes[type], + var types = this._eventTypes, + entry = types && types[type], handlers = this._callbacks && this._callbacks[type], index; - if (entry && handlers) { + if (handlers) { if (!func || (index = handlers.indexOf(func)) !== -1 && handlers.length === 1) { - if (entry.uninstall) + if (entry && entry.uninstall) entry.uninstall.call(this, type); delete this._callbacks[type]; } else if (index !== -1) { @@ -619,10 +624,11 @@ if (!handlers) return false; var args = [].slice.call(arguments, 1); + handlers = handlers.slice(); for (var i = 0, l = handlers.length; i < l; i++) { - if (handlers[i].apply(this, args) === false - && event && event.stop) { - event.stop(); + if (handlers[i].apply(this, args) === false) { + if (event && event.stop) + event.stop(); break; } } @@ -642,8 +648,9 @@ key = install ? 'install' : 'uninstall'; for (var type in handlers) { if (handlers[type].length > 0) { - var entry = this._eventTypes[type], - func = entry[key]; + var types = this._eventTypes, + entry = types && types[type], + func = entry && entry[key]; if (func) func.call(this, type); } @@ -708,8 +715,15 @@ } if (!this.browser) { - var browser = proto.browser = {}; - navigator.userAgent.toLowerCase().replace( + var agent = navigator.userAgent.toLowerCase(), + platform = (/(win)/.exec(agent) + || /(mac)/.exec(agent) + || /(linux)/.exec(agent) + || [])[0], + browser = proto.browser = { platform: platform }; + if (platform) + browser[platform] = true; + agent.replace( /(opera|chrome|safari|webkit|firefox|msie|trident|atom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:([.\d]+))?/g, function(all, n, v1, v2, rv) { if (!browser.chrome) { @@ -732,7 +746,7 @@ } }, - version: '0.9.22', + version: '0.9.24', getView: function() { return this.project && this.project.getView(); @@ -969,16 +983,9 @@ D; b /= 2; D = b * b - a * c; - if (abs(D) < MACHINE_EPSILON) { - var pow = Math.pow, - gmC = pow(abs(a*b*c), 1/3); + if (D !== 0 && abs(D) < MACHINE_EPSILON) { + var gmC = pow(abs(a * b * c), 1 / 3); if (gmC < 1e-8) { - /* - * we multiply with a factor to normalize the - * coefficients. The factor is just the nearest exponent - * of 10, big enough to raise all the coefficients to - * nearly [-1, +1] range. - */ var mult = pow(10, abs( Math.floor(Math.log(gmC) * Math.LOG10E))); if (!isFinite(mult)) @@ -989,9 +996,9 @@ D = b * b - a * c; } } - if (abs(a) < MACHINE_EPSILON) { - if (abs(B) < MACHINE_EPSILON) - return abs(c) < MACHINE_EPSILON ? -1 : 0; + if (abs(a) < EPSILON) { + if (abs(B) < EPSILON) + return abs(c) < EPSILON ? -1 : 0; x1 = -c / B; } else { if (D >= -MACHINE_EPSILON) { @@ -1016,13 +1023,14 @@ }, solveCubic: function(a, b, c, d, roots, min, max) { - var x, b1, c2, count = 0; - if (a === 0) { + var count = 0, + x, b1, c2; + if (abs(a) < EPSILON) { a = b; b1 = c; c2 = d; x = Infinity; - } else if (d === 0) { + } else if (abs(d) < EPSILON) { b1 = b; c2 = c; x = 0; @@ -1070,6 +1078,23 @@ }; }; +var UID = { + _id: 1, + _pools: {}, + + get: function(ctor) { + if (ctor) { + var name = ctor._class, + pool = this._pools[name]; + if (!pool) + pool = this._pools[name] = { _id: 1 }; + return pool._id++; + } else { + return this._id++; + } + } +}; + var Point = Base.extend({ _class: 'Point', _readIndex: true, @@ -1284,12 +1309,14 @@ return this.getDistance(point) < tolerance; }, - isColinear: function(point) { - return Math.abs(this.cross(point)) < 1e-12; - }, + isCollinear: function(point) { + return Math.abs(this.cross(point)) < 0.000001; + }, + + isColinear: '#isCollinear', isOrthogonal: function(point) { - return Math.abs(this.dot(point)) < 1e-12; + return Math.abs(this.dot(point)) < 0.000001; }, isZero: function() { @@ -2638,7 +2665,7 @@ _class: 'Symbol', initialize: function Symbol(item, dontCenter) { - this._id = Symbol._id = (Symbol._id || 0) + 1; + this._id = UID.get(); this.project = paper.project; this.project.symbols.push(this); if (item) @@ -2735,7 +2762,7 @@ matrix = this._matrix = new Matrix(), project = hasProps && props.project || paper.project; if (!internal) - this._id = Item._id = (Item._id || 0) + 1; + this._id = UID.get(); this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix; if (point) matrix.translate(point); @@ -2751,7 +2778,8 @@ } } if (hasProps && props !== Item.NO_INSERT) - this._set(props, { insert: true, parent: true }, true); + this._set(props, { insert: true, project: true, parent: true }, + true); return hasProps; }, @@ -3046,14 +3074,11 @@ }, setPivot: function() { - this._pivot = Point.read(arguments); + this._pivot = Point.read(arguments, 0, { clone: true, readNull: true }); this._position = undefined; }, _pivot: null, - - getRegistration: '#getPivot', - setRegistration: '#setPivot' }, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds', 'internalBounds', 'internalRoughBounds'], function(key) { @@ -3080,6 +3105,7 @@ var children = this._children; if (!children || children.length == 0) return new Rectangle(); + Item._updateBoundsCache(this, cacheItem); var x1 = Infinity, x2 = -x1, y1 = x1, @@ -3120,18 +3146,7 @@ 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; - } - } + Item._updateBoundsCache(this._parent || this._parentSymbol, cacheItem); if (cache && this._bounds && this._bounds[cache]) return this._bounds[cache].clone(); var bounds = this._getBounds(internalGetter || getter, @@ -3146,11 +3161,25 @@ }, statics: { + _updateBoundsCache: function(parent, item) { + if (parent) { + var id = item._id, + ref = parent._boundsCache = parent._boundsCache || { + ids: {}, + list: [] + }; + if (!ref.ids[id]) { + ref.list.push(item); + ref.ids[id] = item; + } + } + }, + _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++) { + 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; @@ -3206,8 +3235,9 @@ return this._matrix; }, - setMatrix: function(matrix) { - this._matrix.initialize(matrix); + setMatrix: function() { + var matrix = this._matrix; + matrix.initialize.apply(matrix, arguments); if (this._applyMatrix) { this.transform(null, true); } else { @@ -3340,24 +3370,27 @@ return this._clone(new this.constructor(Item.NO_INSERT), insert); }, - _clone: function(copy, insert) { + _clone: function(copy, insert, includeMatrix) { + var keys = ['_locked', '_visible', '_blendMode', '_opacity', + '_clipMask', '_guide'], + children = this._children; copy.setStyle(this._style); - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) - copy.addChild(this._children[i].clone(false), true); - } - if (insert || insert === undefined) - copy.insertAbove(this); - var keys = ['_locked', '_visible', '_blendMode', '_opacity', - '_clipMask', '_guide', '_applyMatrix']; + for (var i = 0, l = children && children.length; i < l; i++) { + copy.addChild(children[i].clone(false), true); + } for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; if (this.hasOwnProperty(key)) copy[key] = this[key]; } - copy._matrix.initialize(this._matrix); + if (includeMatrix !== false) + copy._matrix.initialize(this._matrix); + copy.setApplyMatrix(this._applyMatrix); + copy.setPivot(this._pivot); + copy.setSelected(this._selected); copy._data = this._data ? Base.clone(this._data) : null; - copy.setSelected(this._selected); + if (insert || insert === undefined) + copy.insertAbove(this); if (this._name) copy.setName(this._name, true); return copy; @@ -3517,11 +3550,14 @@ } return true; } - if (typeof name === 'object') { + var type = typeof name; + if (type === 'object') { for (var key in name) { if (name.hasOwnProperty(key) && !this.matches(key, name[key])) return false; } + } else if (type === 'function') { + return name(this); } else { var value = /^(empty|editable)$/.test(name) ? this['is' + Base.capitalize(name)]() @@ -3559,15 +3595,17 @@ statics: { _getItems: function _getItems(children, match, matrix, param, firstOnly) { - if (!param) { + if (!param && typeof match === 'object') { var overlapping = match.overlapping, inside = match.inside, bounds = overlapping || inside, rect = bounds && Rectangle.read([bounds]); param = { items: [], - inside: rect, - overlapping: overlapping && new Path.Rectangle({ + inside: !!inside, + overlapping: !!overlapping, + rect: rect, + path: overlapping && new Path.Rectangle({ rectangle: rect, insert: false }) @@ -3576,20 +3614,20 @@ match = Base.set({}, match, { inside: true, overlapping: true }); } - var items = param.items, - inside = param.inside, - overlapping = param.overlapping; - matrix = inside && (matrix || new Matrix()); + var items = param && param.items, + rect = param && param.rect; + matrix = rect && (matrix || new Matrix()); for (var i = 0, l = children && children.length; i < l; i++) { var child = children[i], childMatrix = matrix && matrix.chain(child._matrix), add = true; - if (inside) { + if (rect) { var bounds = child.getBounds(childMatrix); - if (!inside.intersects(bounds)) + if (!rect.intersects(bounds)) continue; - if (!(inside && inside.contains(bounds)) && !(overlapping - && overlapping.intersects(child, childMatrix))) + if (!(param.inside && rect.contains(bounds)) + && !(param.overlapping && (bounds.contains(rect) + || param.path.intersects(child, childMatrix)))) add = false; } if (add && child.matches(match)) { @@ -4066,7 +4104,7 @@ || param.clip || (nativeBlend || normalBlend && opacity < 1) && this._canComposite(), - pixelRatio = param.pixelRatio, + pixelRatio = param.pixelRatio || 1, mainCtx, itemOffset, prevOffset; if (!direct) { var bounds = this.getStrokeBounds(getViewMatrix(globalMatrix)); @@ -4422,16 +4460,14 @@ }, toPath: function(insert) { - var path = new Path[Base.capitalize(this._type)]({ + var path = this._clone(new Path[Base.capitalize(this._type)]({ center: new Point(), size: this._size, radius: this._radius, insert: false - }); - path.setStyle(this._style); - path.transform(this._matrix); - if (insert || insert === undefined) - path.insertAbove(this); + }), insert); + if (paper.settings.applyMatrix) + path.setApplyMatrix(true); return path; }, @@ -4455,7 +4491,7 @@ size = this._size, width = size.width, height = size.height; - if (untransformed && type === 'rect' && rx === 0 && ry === 0) { + if (untransformed && type === 'rectangle' && rx === 0 && ry === 0) { ctx.rect(-width / 2, -height / 2, width, height); } else { var x = width / 2, @@ -4647,6 +4683,7 @@ _boundsGetter: 'getBounds', _boundsSelected: true, _serializeFields: { + crossOrigin: null, source: null }, @@ -4680,6 +4717,7 @@ copyCanvas.getContext('2d').drawImage(canvas, 0, 0); copy.setImage(copyCanvas); } + copy._crossOrigin = this._crossOrigin; return this._clone(copy, insert); }, @@ -4799,6 +4837,7 @@ setSource: function(src) { var that = this, + crossOrigin = this._crossOrigin, image; function loaded() { @@ -4811,20 +4850,29 @@ } } - image = document.getElementById(src) || new Image(); - + image = document.getElementById(src) || new Image(); + if (crossOrigin) + image.crossOrigin = crossOrigin; if (image.naturalWidth && image.naturalHeight) { setTimeout(loaded, 0); } else { - DomEvent.add(image, { - load: loaded - }); + DomEvent.add(image, { load: loaded }); if (!image.src) image.src = src; } this.setImage(image); }, + getCrossOrigin: function() { + return this._image && this._image.crossOrigin || this._crossOrigin || ''; + }, + + setCrossOrigin: function(crossOrigin) { + this._crossOrigin = crossOrigin; + if (this._image) + this._image.crossOrigin = crossOrigin; + }, + getElement: function() { return this._canvas || this._loaded && this._image; } @@ -5109,7 +5157,7 @@ }, _serialize: function(options) { - return Base.serialize(this.isLinear() ? this._point + return Base.serialize(this.isStraight() ? this._point : [this._point, this._handleIn, this._handleOut], options, true); }, @@ -5160,53 +5208,34 @@ this._handleOut.set(point.x, point.y); }, - isLinear: function() { + hasHandles: function() { + return !this.isStraight(); + }, + + isStraight: function() { return this._handleIn.isZero() && this._handleOut.isZero(); }, - setLinear: function(linear) { - if (linear) { - this._handleIn.set(0, 0); - this._handleOut.set(0, 0); - } else { - } - }, - - isColinear: function(segment) { - var next1 = this.getNext(), - next2 = segment.getNext(); - return this._handleOut.isZero() && next1._handleIn.isZero() - && segment._handleOut.isZero() && next2._handleIn.isZero() - && next1._point.subtract(this._point).isColinear( - next2._point.subtract(segment._point)); - }, + isLinear: function() { + return Segment.isLinear(this, this.getNext()); + }, + + isCollinear: function(segment) { + return Segment.isCollinear(this, this.getNext(), + segment, segment.getNext()); + }, + + isColinear: '#isCollinear', isOrthogonal: function() { - var prev = this.getPrevious(), - next = this.getNext(); - return prev._handleOut.isZero() && this._handleIn.isZero() - && this._handleOut.isZero() && next._handleIn.isZero() - && this._point.subtract(prev._point).isOrthogonal( - next._point.subtract(this._point)); - }, - - isArc: function() { - var next = this.getNext(), - handle1 = this._handleOut, - handle2 = next._handleIn, - kappa = 0.5522847498307936; - if (handle1.isOrthogonal(handle2)) { - var from = this._point, - to = next._point, - corner = new Line(from, handle1, true).intersect( - new Line(to, handle2, true), true); - return corner && Numerical.isZero(handle1.getLength() / - corner.subtract(from).getLength() - kappa) - && Numerical.isZero(handle2.getLength() / - corner.subtract(to).getLength() - kappa); - } - return false; - }, + return Segment.isOrthogonal(this.getPrevious(), this, this.getNext()); + }, + + isOrthogonalArc: function() { + return Segment.isOrthogonalArc(this, this.getNext()); + }, + + isArc: '#isOrthogonalArc', _selectionState: 0, @@ -5361,6 +5390,46 @@ } } return coords; + }, + + statics: { + + isLinear: function(seg1, seg2) { + var l = seg2._point.subtract(seg1._point); + return l.isCollinear(seg1._handleOut) + && l.isCollinear(seg2._handleIn); + }, + + isCollinear: function(seg1, seg2, seg3, seg4) { + return seg1._handleOut.isZero() && seg2._handleIn.isZero() + && seg3._handleOut.isZero() && seg4._handleIn.isZero() + && seg2._point.subtract(seg1._point).isCollinear( + seg4._point.subtract(seg3._point)); + }, + + isOrthogonal: function(seg1, seg2, seg3) { + return seg1._handleOut.isZero() && seg2._handleIn.isZero() + && seg2._handleOut.isZero() && seg3._handleIn.isZero() + && seg2._point.subtract(seg1._point).isOrthogonal( + seg3._point.subtract(seg2._point)); + }, + + isOrthogonalArc: function(seg1, seg2) { + var handle1 = seg1._handleOut, + handle2 = seg2._handleIn, + kappa = 0.5522847498307936; + if (handle1.isOrthogonal(handle2)) { + var pt1 = seg1._point, + pt2 = seg2._point, + corner = new Line(pt1, handle1, true).intersect( + new Line(pt2, handle2, true), true); + return corner && Numerical.isZero(handle1.getLength() / + corner.subtract(pt1).getLength() - kappa) + && Numerical.isZero(handle2.getLength() / + corner.subtract(pt2).getLength() - kappa); + } + return false; + }, } }); @@ -5586,9 +5655,22 @@ return Curve.getLength(this.getValues(), from, to); }, + hasHandles: function() { + return !this._segment1._handleOut.isZero() + || !this._segment2._handleIn.isZero(); + }, + isLinear: function() { - return this._segment1._handleOut.isZero() - && this._segment2._handleIn.isZero(); + return Segment.isLinear(this._segment1, this._segment2); + }, + + isCollinear: function(curve) { + return Ssegment.isCollinear(this._segment1, this._segment2, + curve._segment1, curve._segment2); + }, + + isOrthogonalArc: function() { + return Segment.isOrthogonalArc(this._segment1, this._segment2); }, getIntersections: function(curve) { @@ -5698,54 +5780,6 @@ return values; }, - evaluate: function(v, t, type) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7], - tolerance = 0.000001, - x, y; - - 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, - ax = p2x - p1x - cx - bx, - - cy = 3 * (c1y - p1y), - by = 3 * (c2y - c1y) - cy, - ay = p2y - p1y - cy - by; - if (type === 0) { - x = ((ax * t + bx) * t + cx) * t + p1x; - y = ((ay * t + by) * t + cy) * t + p1y; - } else { - if (t < tolerance && c1x === p1x && c1y === p1y - || t > 1 - tolerance && c2x === p2x && c2y === p2y) { - x = c2x - c1x; - y = c2y - c1y; - } 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; - } - if (type === 3) { - var x2 = 6 * ax * t + 2 * bx, - y2 = 6 * ay * t + 2 * by; - return (x * y2 - y * x2) / Math.pow(x * x + y * y, 3 / 2); - } - } - } - return type === 2 ? new Point(y, -x) : new Point(x, y); - }, - subdivide: function(v, t) { var p1x = v[0], p1y = v[1], c1x = v[2], c1y = v[3], @@ -5773,10 +5807,7 @@ p2 = v[coord + 6], c = 3 * (c1 - p1), b = 3 * (c2 - c1) - c, - a = p2 - p1 - c - b, - isZero = Numerical.isZero; - if (isZero(a) && isZero(b)) - a = b = 0; + a = p2 - p1 - c - b; return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max); }, @@ -5791,17 +5822,20 @@ sx = Curve.solveCubic(v, 0, x, txs, 0, 1), sy = Curve.solveCubic(v, 1, y, tys, 0, 1), tx, ty; - for (var cx = 0; sx == -1 || cx < sx;) { - if (sx == -1 || (tx = txs[cx++]) >= 0 && tx <= 1) { - for (var cy = 0; sy == -1 || cy < sy;) { - if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) { - if (sx == -1) tx = ty; - else if (sy == -1) ty = tx; + for (var cx = 0; sx === -1 || cx < sx;) { + if (sx === -1 || (tx = txs[cx++]) > 0 && tx < 1) { + for (var cy = 0; sy === -1 || cy < sy;) { + 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) < tolerance) return (tx + ty) * 0.5; } } - if (sx == -1) + if (sx === -1) break; } } @@ -5816,10 +5850,18 @@ return v; }, - isLinear: function(v) { + hasHandles: function(v) { var isZero = Numerical.isZero; - return isZero(v[0] - v[2]) && isZero(v[1] - v[3]) - && isZero(v[4] - v[6]) && isZero(v[5] - v[7]); + return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3]) + && isZero(v[4] - v[6]) && isZero(v[5] - v[7])); + }, + + isLinear: function(v) { + var p1x = v[0], p1y = v[1], + p2x = v[6], p2y = v[7], + l = new Point(p2x - p1x, p2y - p1y); + return l.isCollinear(new Point(v[2] - p1x, v[3] - p1y)) + && l.isCollinear(new Point(v[4] - p2x, v[5] - p2y)); }, isFlatEnough: function(v, tolerance) { @@ -5891,7 +5933,8 @@ padding); } } -}}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'], +}}, Base.each( + ['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'], function(name) { this[name] = function() { if (!this._bounds) @@ -5906,18 +5949,7 @@ }, { -}), Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'], - function(name, index) { - this[name + 'At'] = function(offset, isParameter) { - var values = this.getValues(); - return Curve.evaluate(values, isParameter - ? offset : Curve.getParameterAt(values, offset, 0), index); - }; - this[name] = function(parameter) { - return Curve.evaluate(this.getValues(), parameter, index); - }; - }, -{ +}), { beans: false, getParameterAt: function(offset, start) { @@ -5930,9 +5962,10 @@ }, getLocationAt: function(offset, isParameter) { - if (!isParameter) - offset = this.getParameterAt(offset); - return offset >= 0 && offset <= 1 && new CurveLocation(this, offset); + var t = isParameter ? offset : this.getParameterAt(offset); + return t != null && t >= 0 && t <= 1 + ? new CurveLocation(this, t) + : null; }, getLocationOf: function() { @@ -5954,8 +5987,7 @@ function refine(t) { if (t >= 0 && t <= 1) { - var dist = point.getDistance( - Curve.evaluate(values, t, 0), true); + var dist = point.getDistance(Curve.getPoint(values, t), true); if (dist < minDist) { minDist = dist; minT = t; @@ -5972,7 +6004,7 @@ if (!refine(minT - step) && !refine(minT + step)) step /= 2; } - var pt = Curve.evaluate(values, minT, 0); + var pt = Curve.getPoint(values, minT); return new CurveLocation(this, minT, pt, null, null, null, point.getDistance(pt)); }, @@ -5981,7 +6013,23 @@ return this.getNearestLocation.apply(this, arguments).getPoint(); } -}), +}, +new function() { + var methods = ['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent', + 'getWeightedNormal', 'getCurvature']; + return Base.each(methods, + function(name) { + this[name + 'At'] = function(offset, isParameter) { + var values = this.getValues(); + return Curve[name](values, isParameter ? offset + : Curve.getParameterAt(values, offset, 0)); + }; + }, { + statics: { + evaluateMethods: methods + } + }) +}, new function() { function getLengthIntegrand(v) { @@ -6009,6 +6057,64 @@ return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32))); } + function evaluate(v, t, type, normalized) { + if (t == null || t < 0 || t > 1) + return null; + var p1x = v[0], p1y = v[1], + c1x = v[2], c1y = v[3], + c2x = v[4], c2y = v[5], + p2x = v[6], p2y = v[7], + tolerance = 0.000001, + x, y; + + 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, + ax = p2x - p1x - cx - bx, + + cy = 3 * (c1y - p1y), + by = 3 * (c2y - c1y) - cy, + ay = p2y - p1y - cy - by; + if (type === 0) { + x = ((ax * t + bx) * t + cx) * t + p1x; + y = ((ay * t + by) * t + cy) * t + 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; + } + if (normalized) { + if (x === 0 && y === 0 + && (t < tolerance || t > 1 - tolerance)) { + x = c2x - c1x; + y = c2y - c1y; + } + var len = Math.sqrt(x * x + y * y); + x /= len; + y /= len; + } + if (type === 3) { + var x2 = 6 * ax * t + 2 * bx, + y2 = 6 * ay * t + 2 * by, + d = Math.pow(x * x + y * y, 3 / 2); + x = d !== 0 ? (x * y2 - y * x2) / d : 0; + y = 0; + } + } + } + return type === 2 ? new Point(y, -x) : new Point(x, y); + } + return { statics: true, @@ -6034,14 +6140,19 @@ start = offset < 0 ? 1 : 0 if (offset === 0) return start; - var forward = offset > 0, + var tolerance = 0.000001, + abs = Math.abs, + forward = offset > 0, a = forward ? start : 0, b = forward ? 1 : start, ds = getLengthIntegrand(v), rangeLength = Numerical.integrate(ds, a, b, getIterations(a, b)); - if (Math.abs(offset) >= rangeLength) + if (abs(offset - rangeLength) < tolerance) { return forward ? b : a; + } else if (abs(offset) > rangeLength) { + return null; + } var guess = offset / rangeLength, length = 0; function f(t) { @@ -6051,7 +6162,31 @@ return length - offset; } return Numerical.findRoot(f, ds, start + guess, a, b, 16, - 0.000001); + tolerance); + }, + + getPoint: function(v, t) { + return evaluate(v, t, 0, false); + }, + + getTangent: function(v, t) { + return evaluate(v, t, 1, true); + }, + + getWeightedTangent: function(v, t) { + return evaluate(v, t, 1, false); + }, + + getNormal: function(v, t) { + return evaluate(v, t, 2, true); + }, + + getWeightedNormal: function(v, t) { + return evaluate(v, t, 2, false); + }, + + getCurvature: function(v, t) { + return evaluate(v, t, 3, false).x; } }; }, new function() { @@ -6079,7 +6214,7 @@ 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 <= tolerance && recursion > 3) { + if (q0x === q3x && uMax - uMin < tolerance && recursion > 3) { tMaxNew = tMinNew = (tMax + tMin) / 2; tDiff = 0; } else { @@ -6123,12 +6258,12 @@ t2 = uMin + (uMax - uMin) / 2; if (reverse) { addLocation(locations, include, - curve2, t2, Curve.evaluate(v2, t2, 0), - curve1, t1, Curve.evaluate(v1, t1, 0)); + curve2, t2, Curve.getPoint(v2, t2), + curve1, t1, Curve.getPoint(v1, t1)); } else { addLocation(locations, include, - curve1, t1, Curve.evaluate(v1, t1, 0), - curve2, t2, Curve.evaluate(v2, t2, 0)); + curve1, t1, Curve.getPoint(v1, t1), + curve2, t2, Curve.getPoint(v2, t2)); } } else if (tDiff > 0) { addCurveIntersections(v2, v1, curve2, curve1, locations, include, @@ -6219,14 +6354,14 @@ count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1); for (var i = 0; i < count; i++) { var tc = roots[i], - x = Curve.evaluate(rvc, tc, 0).x; + x = Curve.getPoint(rvc, tc).x; if (x >= 0 && x <= rlx2) { var tl = Curve.getParameterOf(rvl, x, 0), t1 = flip ? tl : tc, t2 = flip ? tc : tl; addLocation(locations, include, - curve1, t1, Curve.evaluate(v1, t1, 0), - curve2, t2, Curve.evaluate(v2, t2, 0)); + curve1, t1, Curve.getPoint(v1, t1), + curve2, t2, Curve.getPoint(v2, t2)); } } } @@ -6322,16 +6457,18 @@ initialize: function CurveLocation(curve, parameter, point, _curve2, _parameter2, _point2, _distance) { - this._id = CurveLocation._id = (CurveLocation._id || 0) + 1; + this._id = UID.get(CurveLocation); + var path = curve._path; + this._version = path ? path._version : 0; this._curve = curve; - this._segment1 = curve._segment1; - this._segment2 = curve._segment2; this._parameter = parameter; - this._point = point; + this._point = point || curve.getPointAt(parameter, true); this._curve2 = _curve2; this._parameter2 = _parameter2; this._point2 = _point2; this._distance = _distance; + this._segment1 = curve._segment1; + this._segment2 = curve._segment2; }, getSegment: function(_preferFirst) { @@ -6354,24 +6491,22 @@ return this._segment; }, - 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(); - } - return this._curve; - }, - - getIntersection: function() { - var intersection = this._intersection; - if (!intersection && this._curve2) { - var param = this._parameter2; - this._intersection = intersection = new CurveLocation( - this._curve2, param, this._point2 || this._point, this); - intersection._intersection = this; - } - return intersection; + getCurve: function() { + var curve = this._curve, + path = curve && curve._path; + if (path && path._version !== this._version) { + curve = null; + this._parameter = null; + } + if (!curve) { + curve = this._segment1.getCurve(); + if (curve.getParameterOf(this._point) == null) + curve = this._segment2.getPrevious().getCurve(); + this._curve = curve; + path = curve._path; + this._version = path ? path._version : 0; + } + return curve; }, getPath: function() { @@ -6384,6 +6519,18 @@ return curve && curve.getIndex(); }, + getParameter: function() { + var curve = this.getCurve(), + parameter = this._parameter; + return curve && parameter == null + ? this._parameter = curve.getParameterOf(this._point) + : parameter; + }, + + getPoint: function() { + return this._point; + }, + getOffset: function() { var path = this.getPath(); return path ? path._getOffset(this) : this.getCurveOffset(); @@ -6395,20 +6542,14 @@ return parameter != null && curve && curve.getPartLength(0, parameter); }, - getParameter: function(_uncached) { - if ((this._parameter == null || _uncached) && this._point) { - var curve = this.getCurve(_uncached); - this._parameter = curve && curve.getParameterOf(this._point); - } - return this._parameter; - }, - - getPoint: function(_uncached) { - if ((!this._point || _uncached) && this._parameter != null) { - var curve = this.getCurve(_uncached); - this._point = curve && curve.getPointAt(this._parameter, true); - } - return this._point; + getIntersection: function() { + var intersection = this._intersection; + if (!intersection && this._curve2) { + this._intersection = intersection = new CurveLocation(this._curve2, + this._parameter2, this._point2 || this._point, this); + intersection._intersection = this; + } + return intersection; }, getDistance: function() { @@ -6416,24 +6557,24 @@ }, divide: function() { - var curve = this.getCurve(true); - return curve && curve.divide(this.getParameter(true), true); + var curve = this.getCurve(); + return curve && curve.divide(this.getParameter(), true); }, split: function() { - var curve = this.getCurve(true); - return curve && curve.split(this.getParameter(true), true); + var curve = this.getCurve(); + return curve && curve.split(this.getParameter(), true); }, equals: function(loc) { var abs = Math.abs, tolerance = 0.000001; return this === loc - || loc - && this._curve === loc._curve + || loc instanceof CurveLocation + && this.getCurve() === loc.getCurve() + && abs(this.getParameter() - loc.getParameter()) < tolerance && this._curve2 === loc._curve2 - && abs(this._parameter - loc._parameter) <= tolerance - && abs(this._parameter2 - loc._parameter2) <= tolerance + && abs(this._parameter2 - loc._parameter2) < tolerance || false; }, @@ -6453,13 +6594,15 @@ parts.push('distance: ' + f.number(this._distance)); return '{ ' + parts.join(', ') + ' }'; } -}, Base.each(['getTangent', 'getNormal', 'getCurvature'], function(name) { - var get = name + 'At'; - this[name] = function() { - var parameter = this.getParameter(), - curve = this.getCurve(); - return parameter != null && curve && curve[get](parameter, true); - }; +}, Base.each(Curve.evaluateMethods, function(name) { + if (name !== 'getPoint') { + var get = name + 'At'; + this[name] = function() { + var parameter = this.getParameter(), + curve = this.getCurve(); + return parameter != null && curve && curve[get](parameter, true); + }; + } }, {})); var PathItem = Item.extend({ @@ -6567,8 +6710,6 @@ 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 && move ? 'moveTo' : 'lineTo']( current = getPoint(j)); @@ -6657,6 +6798,7 @@ initialize: function Path(arg) { this._closed = false; this._segments = []; + this._version = 0; var segments = Array.isArray(arg) ? typeof arg[0] === 'object' ? arg @@ -6699,8 +6841,10 @@ 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++) + if (flags & 16) { + this._version++; + } else if (this._curves) { + for (var i = 0, l = this._curves.length; i < l; i++) this._curves[i]._changed(); } this._monoCurves = undefined; @@ -6833,14 +6977,24 @@ return this._segments.length === 0; }, - isPolygon: function() { - for (var i = 0, l = this._segments.length; i < l; i++) { - if (!this._segments[i].isLinear()) + isLinear: function() { + var segments = this._segments; + for (var i = 0, l = segments.length; i < l; i++) { + if (!segments[i].isLinear()) return false; } return true; }, + hasHandles: function() { + var segments = this._segments; + for (var i = 0, l = segments.length; i < l; i++) { + if (segments[i].hasHandles()) + return true; + } + return false; + }, + _transformContent: function(matrix) { var coords = new Array(6); for (var i = 0, l = this._segments.length; i < l; i++) @@ -7039,7 +7193,7 @@ end = iterator.length + (this._closed ? -step : step) / 2; var segments = []; while (pos <= end) { - segments.push(new Segment(iterator.evaluate(pos, 0))); + segments.push(new Segment(iterator.getPointAt(pos))); pos += step; } this.setSegments(segments); @@ -7175,8 +7329,8 @@ radius, topCenter; - function isColinear(i, j) { - return segments[i].isColinear(segments[j]); + function isCollinear(i, j) { + return segments[i].isCollinear(segments[j]); } function isOrthogonal(i) { @@ -7184,20 +7338,20 @@ } function isArc(i) { - return segments[i].isArc(); + return segments[i].isOrthogonalArc(); } function getDistance(i, j) { return segments[i]._point.getDistance(segments[j]._point); } - if (this.isPolygon() && segments.length === 4 - && isColinear(0, 2) && isColinear(1, 3) && isOrthogonal(1)) { + if (!this.hasHandles() && segments.length === 4 + && isCollinear(0, 2) && isCollinear(1, 3) && isOrthogonal(1)) { type = Shape.Rectangle; size = new Size(getDistance(0, 3), getDistance(0, 1)); topCenter = segments[1]._point.add(segments[2]._point).divide(2); } else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4) - && isArc(6) && isColinear(1, 5) && isColinear(3, 7)) { + && isArc(6) && isCollinear(1, 5) && isCollinear(3, 7)) { type = Shape.Rectangle; size = new Size(getDistance(1, 6), getDistance(0, 3)); radius = size.subtract(new Size(getDistance(0, 7), @@ -7217,16 +7371,13 @@ if (type) { var center = this.getPosition(true), - shape = new type({ + shape = this._clone(new type({ center: center, size: size, radius: radius, insert: false - }); + }), insert, false); shape.rotate(topCenter.subtract(center).getAngle() + 90); - shape.setStyle(this._style); - if (insert || insert === undefined) - shape.insertAbove(this); return shape; } return null; @@ -7354,7 +7505,7 @@ : null; } -}, Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'], +}, Base.each(Curve.evaluateMethods, function(name) { this[name + 'At'] = function(offset, isParameter) { var loc = this.getLocationAt(offset, isParameter); @@ -7400,8 +7551,9 @@ var curves = this.getCurves(), length = 0; if (isParameter) { - var index = ~~offset; - return curves[index].getLocationAt(offset - index, true); + var index = ~~offset, + curve = curves[index]; + return curve ? curve.getLocationAt(offset - index, true) : null; } for (var i = 0, l = curves.length; i < l; i++) { var start = length, @@ -7989,7 +8141,7 @@ var handleIn = segment._handleIn, handleOut = segment._handleOut; if (join === 'round' || !handleIn.isZero() && !handleOut.isZero() - && handleIn.isColinear(handleOut)) { + && handleIn.isCollinear(handleOut)) { addRound(segment); } else { Path._addBevelJoin(segment, join, radius, miterLimit, add); @@ -8068,7 +8220,7 @@ _addSquareCap: function(segment, cap, radius, addPoint, area) { var point = segment._point, loc = segment.getLocation(), - normal = loc.getNormal().normalize(radius); + normal = loc.getNormal().multiply(radius); if (area) { addPoint(point.subtract(normal)); addPoint(point.add(normal)); @@ -8522,17 +8674,17 @@ } while (segment && !segment._intersection && segment !== startSeg); for (var j = 0; j < 3; j++) { var length = totalLength * (j + 1) / 4; - for (k = 0, m = chain.length; k < m; k++) { + for (var k = 0, m = chain.length; k < m; k++) { var node = chain[k], curveLength = node.length; if (length <= curveLength) { - if (length <= tolerance - || curveLength - length <= tolerance) + if (length < tolerance + || curveLength - length < tolerance) length = curveLength / 2; var curve = node.segment.getCurve(), pt = curve.getPointAt(length), hor = curve.isLinear() && Math.abs(curve - .getTangentAt(0.5, true).y) <= tolerance, + .getTangentAt(0.5, true).y) < tolerance, path = curve._path; if (path._parent instanceof CompoundPath) path = path._parent; @@ -8625,7 +8777,7 @@ var values = curves[i].values; if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) { for (var j = roots.length - 1; j >= 0; j--) { - var y = Curve.evaluate(values, roots[j], 0).y; + var y = Curve.getPoint(values, roots[j]).y; if (y < yBefore && y > yTop) { yTop = y; } else if (y > yAfter && y < yBottom) { @@ -8643,43 +8795,47 @@ } else { var xBefore = px - tolerance, xAfter = px + tolerance; + var startCounted = false, + prevCurve, + prevT; for (var i = 0, l = curves.length; i < l; i++) { var curve = curves[i], values = curve.values, - winding = curve.winding, - prevT, - prevX; + winding = curve.winding; if (winding && (winding === 1 && py >= values[1] && py <= values[7] || py >= values[7] && py <= values[1]) && Curve.solveCubic(values, 1, py, roots, 0, 1) === 1) { - var t = roots[0], - x = Curve.evaluate(values, t, 0).x, - slope = Curve.evaluate(values, t, 1).y; - if (!(t > tMax - && (i === l - 1 || curve.next !== curves[i + 1]) - && abs(Curve.evaluate(curve.next.values, 0, 0).x -x) - <= tolerance - || i > 0 && curve.previous === curves[i - 1] - && abs(prevX - x) < tolerance - && prevT > tMax && t < tMin)) { + var t = roots[0]; + if (!( + t > tMax && startCounted && curve.next !== curves[i + 1] + || t < tMin && prevT > tMax + && curve.previous === prevCurve)) { + var x = Curve.getPoint(values, t).x, + slope = Curve.getTangent(values, t).y, + counted = false; if (Numerical.isZero(slope) && !Curve.isLinear(values) - || t < tMin && slope * Curve.evaluate( - curve.previous.values, 1, 1).y < 0 - || t > tMax && slope * Curve.evaluate( - curve.next.values, 0, 1).y < 0) { + || t < tMin && slope * Curve.getTangent( + curve.previous.values, 1).y < 0 + || t > tMax && slope * Curve.getTangent( + curve.next.values, 0).y < 0) { if (testContains && x >= xBefore && x <= xAfter) { ++windLeft; ++windRight; + counted = true; } } else if (x <= xBefore) { windLeft += winding; + counted = true; } else if (x >= xAfter) { windRight += winding; + counted = true; } + if (curve.previous !== curves[i - 1]) + startCounted = t < tMin && counted; } + prevCurve = curve; prevT = t; - prevX = x; } } } @@ -8759,7 +8915,7 @@ path.lastSegment._handleOut.set(0, 0); } if (path._segments.length > - (path._closed ? path.isPolygon() ? 2 : 0 : 1)) + (path._closed ? path.isLinear() ? 2 : 0 : 1)) paths.push(path); } return paths; @@ -8889,7 +9045,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); + xIntercepts.push(Curve.getPoint(values, roots[j]).x); } if (xIntercepts.length > 1) break; @@ -9017,11 +9173,6 @@ }; }, - evaluate: function(offset, type) { - var param = this.getParameterAt(offset); - return Curve.evaluate(this.curves[param.index], param.value, type); - }, - drawPart: function(ctx, from, to) { from = this.getParameterAt(from); to = this.getParameterAt(to); @@ -9034,10 +9185,11 @@ ctx.bezierCurveTo.apply(ctx, curve.slice(2)); } } -}, Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'], - function(name, index) { - this[name + 'At'] = function(offset) { - return this.evaluate(offset, index); +}, Base.each(Curve.evaluateMethods, + function(name) { + this[name + 'At'] = function(offset, weighted) { + var param = this.getParameterAt(offset); + return Curve[name](this.curves[param.index], param.value, weighted); }; }, {}) ); @@ -9093,18 +9245,19 @@ } var uPrime = this.chordLengthParameterize(first, last), maxError = Math.max(this.error, this.error * this.error), - split; + split, + parametersInOrder = true; for (var i = 0; i <= 4; i++) { var curve = this.generateBezier(first, last, uPrime, tan1, tan2); var max = this.findMaxError(first, last, curve, uPrime); - if (max.error < this.error) { + if (max.error < this.error && parametersInOrder) { this.addCurve(curve); return; } split = max.index; if (max.error >= maxError) break; - this.reparameterize(first, last, uPrime, curve); + parametersInOrder = this.reparameterize(first, last, uPrime, curve); maxError = max.error; } var V1 = this.points[split - 1].subtract(this.points[split]), @@ -9168,20 +9321,35 @@ } } - var segLength = pt2.getDistance(pt1); - epsilon *= segLength; - if (alpha1 < epsilon || alpha2 < epsilon) { + var segLength = pt2.getDistance(pt1), + eps = epsilon * segLength, + handle1, + handle2; + if (alpha1 < eps || alpha2 < eps) { alpha1 = alpha2 = segLength / 3; - } - - return [pt1, pt1.add(tan1.normalize(alpha1)), - pt2.add(tan2.normalize(alpha2)), pt2]; + } else { + var line = pt2.subtract(pt1); + handle1 = tan1.normalize(alpha1); + handle2 = tan2.normalize(alpha2); + if (handle1.dot(line) - handle2.dot(line) > segLength * segLength) { + alpha1 = alpha2 = segLength / 3; + handle1 = handle2 = null; + } + } + + return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)), + pt2.add(handle2 || tan2.normalize(alpha2)), pt2]; }, reparameterize: function(first, last, u, curve) { for (var i = first; i <= last; i++) { u[i - first] = this.findRoot(curve, this.points[i], u[i - first]); } + for (var i = 1, l = u.length; i < l; i++) { + if (u[i] <= u[i - 1]) + return false; + } + return true; }, findRoot: function(curve, point, u) { @@ -9266,9 +9434,9 @@ return this._content === item._content; }, - _clone: function _clone(copy, insert) { + _clone: function _clone(copy, insert, includeMatrix) { copy.setContent(this._content); - return _clone.base.call(this, copy, insert); + return _clone.base.call(this, copy, insert, includeMatrix); }, getContent: function() { @@ -9547,11 +9715,8 @@ this._properties = types[type]; this._type = type; } - value = parser.call(this, value); - if (value != null) { - this._components[index] = value; - this._changed(); - } + this._components[index] = parser.call(this, value); + this._changed(); }; }, this); }, { @@ -9659,8 +9824,7 @@ read = 1; } this._type = type || 'rgb'; - if (type === 'gradient') - this._id = Color._id = (Color._id || 0) + 1; + this._id = UID.get(Color); if (!components) { this._components = components = []; var parsers = componentParsers[this._type]; @@ -9897,7 +10061,7 @@ _class: 'Gradient', initialize: function Gradient(stops, radial) { - this._id = Gradient._id = (Gradient._id || 0) + 1; + this._id = UID.get(); if (stops && this._set(stops)) stops = radial = null; if (!this._stops) @@ -9938,7 +10102,7 @@ var stops = []; for (var i = 0, l = this._stops.length; i < l; i++) stops[i] = this._stops[i].clone(); - return new Gradient(stops); + return new Gradient(stops, this._radial); }, getStops: function() { @@ -10900,12 +11064,23 @@ }, getPixelSize: function(size) { - var ctx = this._context, - prevFont = ctx.font; - ctx.font = size + ' serif'; - size = parseFloat(ctx.font); - ctx.font = prevFont; - return size; + var browser = paper.browser, + pixels; + if (browser && browser.firefox) { + var parent = this._element.parentNode, + temp = document.createElement('div'); + temp.style.fontSize = size; + parent.appendChild(temp); + pixels = parseFloat(DomElement.getStyles(temp).fontSize); + parent.removeChild(temp); + } else { + var ctx = this._context, + prevFont = ctx.font; + ctx.font = size + ' serif'; + pixels = parseFloat(ctx.font); + ctx.font = prevFont; + } + return pixels; }, getTextWidth: function(font, lines) { @@ -10919,9 +11094,9 @@ return width; }, - update: function() { + update: function(force) { var project = this._project; - if (!project || !project._needsUpdate) + if (!project || !force && !project._needsUpdate) return false; var ctx = this._context, size = this._viewSize; @@ -11125,6 +11300,7 @@ charCodeMap = {}, keyMap = {}, + commandFixMap, downCode; function handleKey(down, keyCode, charCode, event) { @@ -11137,13 +11313,28 @@ tool = scope && scope.tool, name; keyMap[key] = down; - if (specialKey && (name = Base.camelize(specialKey)) in modifiers) - modifiers[name] = down; if (down) { charCodeMap[keyCode] = charCode; } else { delete charCodeMap[keyCode]; } + if (specialKey && (name = Base.camelize(specialKey)) in modifiers) { + modifiers[name] = down; + var browser = paper.browser; + if (name === 'command' && browser && browser.mac) { + if (down) { + commandFixMap = {}; + } else { + for (var code in commandFixMap) { + if (code in charCodeMap) + handleKey(false, code, commandFixMap[code], event); + } + commandFixMap = null; + } + } + } else if (down && commandFixMap) { + commandFixMap[keyCode] = charCode; + } if (tool && tool.responds(type)) { paper = scope; tool.emit(type, new KeyEvent(down, key, character, event)); @@ -11336,9 +11527,9 @@ setMinDistance: function(minDistance) { this._minDistance = minDistance; - if (this._minDistance != null && this._maxDistance != null - && this._minDistance > this._maxDistance) { - this._maxDistance = this._minDistance; + if (minDistance != null && this._maxDistance != null + && minDistance > this._maxDistance) { + this._maxDistance = minDistance; } }, @@ -11348,8 +11539,8 @@ setMaxDistance: function(maxDistance) { this._maxDistance = maxDistance; - if (this._minDistance != null && this._maxDistance != null - && this._maxDistance < this._minDistance) { + if (this._minDistance != null && maxDistance != null + && maxDistance < this._minDistance) { this._minDistance = maxDistance; } }, @@ -11373,10 +11564,9 @@ distance = vector.getLength(); if (distance < minDist) return false; - var maxDist = maxDistance != null ? maxDistance : 0; - if (maxDist != 0) { - if (distance > maxDist) { - point = this._point.add(vector.normalize(maxDist)); + if (maxDistance != null && maxDistance != 0) { + if (distance > maxDistance) { + point = this._point.add(vector.normalize(maxDistance)); } else if (matchMaxDistance) { return false; } @@ -11470,10 +11660,11 @@ }); var Http = { - request: function(method, url, callback) { + request: function(method, url, callback, async) { + async = (async === undefined) ? true : async; var xhr = new (window.ActiveXObject || XMLHttpRequest)( 'Microsoft.XMLHTTP'); - xhr.open(method.toUpperCase(), url, true); + xhr.open(method.toUpperCase(), url, async); if ('overrideMimeType' in xhr) xhr.overrideMimeType('text/plain'); xhr.onreadystatechange = function() { @@ -11904,19 +12095,22 @@ return node; } - function exportRaster(item) { + function exportRaster(item, options) { var attrs = getTransform(item._matrix, true), - size = item.getSize(); + size = item.getSize(), + image = item.getImage(); attrs.x -= size.width / 2; attrs.y -= size.height / 2; attrs.width = size.width; attrs.height = size.height; - attrs.href = item.toDataURL(); + attrs.href = options.embedImages === false && image && image.src + || item.toDataURL(); return createElement('image', attrs); } function exportPath(item, options) { - if (options.matchShapes) { + var matchShapes = options.matchShapes; + if (matchShapes) { var shape = item.toShape(false); if (shape) return exportShape(shape, options); @@ -11926,11 +12120,11 @@ attrs = getTransform(item._matrix); if (segments.length === 0) return null; - if (item.isPolygon()) { + if (matchShapes && !item.hasHandles()) { if (segments.length >= 3) { type = item._closed ? 'polygon' : 'polyline'; var parts = []; - for(i = 0, l = segments.length; i < l; i++) + for(var i = 0, l = segments.length; i < l; i++) parts.push(formatter.point(segments[i]._point)); attrs.points = parts.join(' '); } else { @@ -12270,10 +12464,17 @@ item = applyAttributes(item, node, isRoot); project._currentStyle = item._style.clone(); } + if (isRoot) { + var defs = node.querySelectorAll('defs'); + for (var i = 0, l = defs.length; i < l; i++) { + importSVG(defs[i], options, false); + } + } for (var i = 0, l = nodes.length; i < l; i++) { var childNode = nodes[i], child; if (childNode.nodeType === 1 + && childNode.nodeName.toLowerCase() !== 'defs' && (child = importSVG(childNode, options, false)) && !(child instanceof Symbol)) children.push(child); @@ -12473,7 +12674,7 @@ color.setAlpha(parseFloat(value)); } - var attributes = Base.each(SVGStyles, function(entry) { + var attributes = Base.set(Base.each(SVGStyles, function(entry) { this[entry.attribute] = function(item, value) { item[entry.set](convertValue(value, entry.type, entry.fromSVG)); if (entry.type === 'color' && item instanceof Shape) { @@ -12483,7 +12684,7 @@ item.getPosition(true).negate())); } }; - }, { + }, {}), { id: function(item, value) { definitions[value] = item; if (item.setName) @@ -12657,7 +12858,7 @@ } if (isRoot) { definitions = {}; - if (applyMatrix && item) + if (item && Base.pick(options.applyMatrix, applyMatrix)) item.matrix.apply(true, true); } return item; @@ -12827,13 +13028,17 @@ || parentType === 'MemberExpression' && parent.computed )) { if (node.type === 'UpdateExpression') { - var arg = getCode(node.argument); - var str = arg + ' = __$__(' + arg - + ', "' + node.operator[0] + '", 1)'; + var arg = getCode(node.argument), + exp = '__$__(' + arg + ', "' + node.operator[0] + + '", 1)', + str = arg + ' = ' + exp; if (!node.prefix && (parentType === 'AssignmentExpression' - || parentType === 'VariableDeclarator')) + || parentType === 'VariableDeclarator')) { + if (getCode(parent.left || parent.id) === arg) + str = exp; str = arg + '; ' + str; + } replaceCode(node, str); } else { if (/^.=$/.test(node.operator) @@ -12958,7 +13163,8 @@ && PaperScope.getAttribute(script, 'ignore') !== 'true') { var canvasId = PaperScope.getAttribute(script, 'canvas'), canvas = document.getElementById(canvasId), - src = script.src, + src = script.src || script.getAttribute('data-src'), + async = PaperScope.hasAttribute(script, 'async'), scopeAttribute = 'data-paper-scope'; if (!canvas) throw new Error('Unable to find canvas with id "' @@ -12969,7 +13175,7 @@ if (src) { Http.request('get', src, function(code) { execute(code, scope, src); - }); + }, async); } else { execute(script.innerHTML, scope, script.baseURI); }