|
1 /*! |
|
2 * Paper.js v0.9.15 - The Swiss Army Knife of Vector Graphics Scripting. |
|
3 * http://paperjs.org/ |
|
4 * |
|
5 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey |
|
6 * http://lehni.org/ & http://jonathanpuckey.com/ |
|
7 * |
|
8 * Distributed under the MIT license. See LICENSE file for details. |
|
9 * |
|
10 * All rights reserved. |
|
11 * |
|
12 * Date: Sun Dec 1 23:54:52 2013 +0100 |
|
13 * |
|
14 *** |
|
15 * |
|
16 * straps.js - Class inheritance library with support for bean-style accessors |
|
17 * |
|
18 * Copyright (c) 2006 - 2013 Juerg Lehni |
|
19 * http://lehni.org/ |
|
20 * |
|
21 * Distributed under the MIT license. |
|
22 * |
|
23 *** |
|
24 * |
|
25 * acorn.js |
|
26 * http://marijnhaverbeke.nl/acorn/ |
|
27 * |
|
28 * Acorn is a tiny, fast JavaScript parser written in JavaScript, |
|
29 * created by Marijn Haverbeke and released under an MIT license. |
|
30 * |
|
31 */ |
|
32 |
|
33 var paper = new function(undefined) { |
|
34 |
|
35 var Base = new function() { |
|
36 var hidden = /^(statics|generics|preserve|enumerable|prototype|toString|valueOf)$/, |
|
37 slice = [].slice, |
|
38 |
|
39 forEach = [].forEach || function(iter, bind) { |
|
40 for (var i = 0, l = this.length; i < l; i++) |
|
41 iter.call(bind, this[i], i, this); |
|
42 }, |
|
43 |
|
44 forIn = function(iter, bind) { |
|
45 for (var i in this) |
|
46 if (this.hasOwnProperty(i)) |
|
47 iter.call(bind, this[i], i, this); |
|
48 }, |
|
49 |
|
50 create = Object.create || function(proto) { |
|
51 return { __proto__: proto }; |
|
52 }, |
|
53 |
|
54 describe = Object.getOwnPropertyDescriptor || function(obj, name) { |
|
55 var get = obj.__lookupGetter__ && obj.__lookupGetter__(name); |
|
56 return get |
|
57 ? { get: get, set: obj.__lookupSetter__(name), |
|
58 enumerable: true, configurable: true } |
|
59 : obj.hasOwnProperty(name) |
|
60 ? { value: obj[name], enumerable: true, |
|
61 configurable: true, writable: true } |
|
62 : null; |
|
63 }, |
|
64 |
|
65 _define = Object.defineProperty || function(obj, name, desc) { |
|
66 if ((desc.get || desc.set) && obj.__defineGetter__) { |
|
67 if (desc.get) |
|
68 obj.__defineGetter__(name, desc.get); |
|
69 if (desc.set) |
|
70 obj.__defineSetter__(name, desc.set); |
|
71 } else { |
|
72 obj[name] = desc.value; |
|
73 } |
|
74 return obj; |
|
75 }, |
|
76 |
|
77 define = function(obj, name, desc) { |
|
78 delete obj[name]; |
|
79 return _define(obj, name, desc); |
|
80 }; |
|
81 |
|
82 function inject(dest, src, enumerable, base, preserve, generics) { |
|
83 var beans; |
|
84 |
|
85 function field(name, val, dontCheck, generics) { |
|
86 var val = val || (val = describe(src, name)) |
|
87 && (val.get ? val : val.value); |
|
88 if (typeof val === 'string' && val[0] === '#') |
|
89 val = dest[val.substring(1)] || val; |
|
90 var isFunc = typeof val === 'function', |
|
91 res = val, |
|
92 prev = preserve || isFunc |
|
93 ? (val && val.get ? name in dest : dest[name]) : null, |
|
94 bean; |
|
95 if ((dontCheck || val !== undefined && src.hasOwnProperty(name)) |
|
96 && (!preserve || !prev)) { |
|
97 if (isFunc && prev) |
|
98 val.base = prev; |
|
99 if (isFunc && beans && val.length === 0 |
|
100 && (bean = name.match(/^(get|is)(([A-Z])(.*))$/))) |
|
101 beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]); |
|
102 if (!res || isFunc || !res.get || typeof res.get !== 'function' |
|
103 || res.get.length !== 0) |
|
104 res = { value: res, writable: true }; |
|
105 if ((describe(dest, name) |
|
106 || { configurable: true }).configurable) { |
|
107 res.configurable = true; |
|
108 res.enumerable = enumerable; |
|
109 } |
|
110 define(dest, name, res); |
|
111 } |
|
112 if (generics && isFunc && (!preserve || !generics[name])) { |
|
113 generics[name] = function(bind) { |
|
114 return bind && dest[name].apply(bind, |
|
115 slice.call(arguments, 1)); |
|
116 }; |
|
117 } |
|
118 } |
|
119 if (src) { |
|
120 beans = []; |
|
121 for (var name in src) |
|
122 if (src.hasOwnProperty(name) && !hidden.test(name)) |
|
123 field(name, null, true, generics); |
|
124 field('toString'); |
|
125 field('valueOf'); |
|
126 for (var i = 0, l = beans.length; i < l; i++) { |
|
127 var bean = beans[i], |
|
128 part = bean[1]; |
|
129 field(bean[0], { |
|
130 get: dest['get' + part] || dest['is' + part], |
|
131 set: dest['set' + part] |
|
132 }, true); |
|
133 } |
|
134 } |
|
135 return dest; |
|
136 } |
|
137 |
|
138 function each(obj, iter, bind) { |
|
139 if (obj) |
|
140 ('length' in obj && !obj.getLength |
|
141 && typeof obj.length === 'number' |
|
142 ? forEach |
|
143 : forIn).call(obj, iter, bind = bind || obj); |
|
144 return bind; |
|
145 } |
|
146 |
|
147 function copy(dest, source) { |
|
148 for (var i in source) |
|
149 if (source.hasOwnProperty(i)) |
|
150 dest[i] = source[i]; |
|
151 return dest; |
|
152 } |
|
153 |
|
154 function clone(obj) { |
|
155 return copy(new obj.constructor(), obj); |
|
156 } |
|
157 |
|
158 return inject(function Base() { |
|
159 for (var i = 0, l = arguments.length; i < l; i++) |
|
160 copy(this, arguments[i]); |
|
161 }, { |
|
162 inject: function(src) { |
|
163 if (src) { |
|
164 var proto = this.prototype, |
|
165 base = Object.getPrototypeOf(proto).constructor, |
|
166 statics = src.statics === true ? src : src.statics; |
|
167 if (statics != src) |
|
168 inject(proto, src, src.enumerable, base && base.prototype, |
|
169 src.preserve, src.generics && this); |
|
170 inject(this, statics, true, base, src.preserve); |
|
171 } |
|
172 for (var i = 1, l = arguments.length; i < l; i++) |
|
173 this.inject(arguments[i]); |
|
174 return this; |
|
175 }, |
|
176 |
|
177 extend: function() { |
|
178 var base = this, |
|
179 ctor; |
|
180 for (var i = 0, l = arguments.length; i < l; i++) |
|
181 if (ctor = arguments[i].initialize) |
|
182 break; |
|
183 ctor = ctor || function() { |
|
184 base.apply(this, arguments); |
|
185 }; |
|
186 ctor.prototype = create(this.prototype); |
|
187 define(ctor.prototype, 'constructor', |
|
188 { value: ctor, writable: true, configurable: true }); |
|
189 inject(ctor, this, true); |
|
190 return arguments.length ? this.inject.apply(ctor, arguments) : ctor; |
|
191 } |
|
192 }, true).inject({ |
|
193 inject: function() { |
|
194 for (var i = 0, l = arguments.length; i < l; i++) |
|
195 inject(this, arguments[i], arguments[i].enumerable); |
|
196 return this; |
|
197 }, |
|
198 |
|
199 extend: function() { |
|
200 var res = create(this); |
|
201 return res.inject.apply(res, arguments); |
|
202 }, |
|
203 |
|
204 each: function(iter, bind) { |
|
205 return each(this, iter, bind); |
|
206 }, |
|
207 |
|
208 clone: function() { |
|
209 return new this.constructor(this); |
|
210 }, |
|
211 |
|
212 statics: { |
|
213 each: each, |
|
214 create: create, |
|
215 define: define, |
|
216 describe: describe, |
|
217 copy: copy, |
|
218 |
|
219 clone: function(obj) { |
|
220 return copy(new obj.constructor(), obj); |
|
221 }, |
|
222 |
|
223 isPlainObject: function(obj) { |
|
224 var ctor = obj != null && obj.constructor; |
|
225 return ctor && (ctor === Object || ctor === Base |
|
226 || ctor.name === 'Object'); |
|
227 }, |
|
228 |
|
229 pick: function() { |
|
230 for (var i = 0, l = arguments.length; i < l; i++) |
|
231 if (arguments[i] !== undefined) |
|
232 return arguments[i]; |
|
233 return null; |
|
234 } |
|
235 } |
|
236 }); |
|
237 }; |
|
238 |
|
239 if (typeof module !== 'undefined') |
|
240 module.exports = Base; |
|
241 |
|
242 Base.inject({ |
|
243 generics: true, |
|
244 |
|
245 toString: function() { |
|
246 return this._id != null |
|
247 ? (this._class || 'Object') + (this._name |
|
248 ? " '" + this._name + "'" |
|
249 : ' @' + this._id) |
|
250 : '{ ' + Base.each(this, function(value, key) { |
|
251 if (!/^_/.test(key)) { |
|
252 var type = typeof value; |
|
253 this.push(key + ': ' + (type === 'number' |
|
254 ? Formatter.instance.number(value) |
|
255 : type === 'string' ? "'" + value + "'" : value)); |
|
256 } |
|
257 }, []).join(', ') + ' }'; |
|
258 }, |
|
259 |
|
260 exportJSON: function(options) { |
|
261 return Base.exportJSON(this, options); |
|
262 }, |
|
263 |
|
264 toJSON: function() { |
|
265 return Base.serialize(this); |
|
266 }, |
|
267 |
|
268 _set: function(props, exclude) { |
|
269 if (props && Base.isPlainObject(props)) { |
|
270 var orig = props._filtering || props; |
|
271 for (var key in orig) { |
|
272 if (key in this && orig.hasOwnProperty(key) |
|
273 && (!exclude || !exclude[key])) { |
|
274 var value = props[key]; |
|
275 if (value !== undefined) |
|
276 this[key] = value; |
|
277 } |
|
278 } |
|
279 return true; |
|
280 } |
|
281 }, |
|
282 |
|
283 statics: { |
|
284 |
|
285 exports: {}, |
|
286 |
|
287 extend: function extend() { |
|
288 var res = extend.base.apply(this, arguments), |
|
289 name = res.prototype._class; |
|
290 if (name && !Base.exports[name]) |
|
291 Base.exports[name] = res; |
|
292 return res; |
|
293 }, |
|
294 |
|
295 equals: function(obj1, obj2) { |
|
296 function checkKeys(o1, o2) { |
|
297 for (var i in o1) |
|
298 if (o1.hasOwnProperty(i) && !o2.hasOwnProperty(i)) |
|
299 return false; |
|
300 return true; |
|
301 } |
|
302 if (obj1 === obj2) |
|
303 return true; |
|
304 if (obj1 && obj1.equals) |
|
305 return obj1.equals(obj2); |
|
306 if (obj2 && obj2.equals) |
|
307 return obj2.equals(obj1); |
|
308 if (Array.isArray(obj1) && Array.isArray(obj2)) { |
|
309 if (obj1.length !== obj2.length) |
|
310 return false; |
|
311 for (var i = 0, l = obj1.length; i < l; i++) { |
|
312 if (!Base.equals(obj1[i], obj2[i])) |
|
313 return false; |
|
314 } |
|
315 return true; |
|
316 } |
|
317 if (obj1 && typeof obj1 === 'object' |
|
318 && obj2 && typeof obj2 === 'object') { |
|
319 if (!checkKeys(obj1, obj2) || !checkKeys(obj2, obj1)) |
|
320 return false; |
|
321 for (var i in obj1) { |
|
322 if (obj1.hasOwnProperty(i) && !Base.equals(obj1[i], obj2[i])) |
|
323 return false; |
|
324 } |
|
325 return true; |
|
326 } |
|
327 return false; |
|
328 }, |
|
329 |
|
330 read: function(list, start, length, options) { |
|
331 if (this === Base) { |
|
332 var value = this.peek(list, start); |
|
333 list._index++; |
|
334 list.__read = 1; |
|
335 return value; |
|
336 } |
|
337 var proto = this.prototype, |
|
338 readIndex = proto._readIndex, |
|
339 index = start || readIndex && list._index || 0; |
|
340 if (!length) |
|
341 length = list.length - index; |
|
342 var obj = list[index]; |
|
343 if (obj instanceof this |
|
344 || options && options.readNull && obj == null && length <= 1) { |
|
345 if (readIndex) |
|
346 list._index = index + 1; |
|
347 return obj && options && options.clone ? obj.clone() : obj; |
|
348 } |
|
349 obj = Base.create(this.prototype); |
|
350 if (readIndex) |
|
351 obj.__read = true; |
|
352 if (options) |
|
353 obj.__options = options; |
|
354 obj = obj.initialize.apply(obj, index > 0 || length < list.length |
|
355 ? Array.prototype.slice.call(list, index, index + length) |
|
356 : list) || obj; |
|
357 if (readIndex) { |
|
358 list._index = index + obj.__read; |
|
359 list.__read = obj.__read; |
|
360 delete obj.__read; |
|
361 if (options) |
|
362 delete obj.__options; |
|
363 } |
|
364 return obj; |
|
365 }, |
|
366 |
|
367 peek: function(list, start) { |
|
368 return list[list._index = start || list._index || 0]; |
|
369 }, |
|
370 |
|
371 readAll: function(list, start, options) { |
|
372 var res = [], entry; |
|
373 for (var i = start || 0, l = list.length; i < l; i++) { |
|
374 res.push(Array.isArray(entry = list[i]) |
|
375 ? this.read(entry, 0, 0, options) |
|
376 : this.read(list, i, 1, options)); |
|
377 } |
|
378 return res; |
|
379 }, |
|
380 |
|
381 readNamed: function(list, name, start, length, options) { |
|
382 var value = this.getNamed(list, name), |
|
383 hasObject = value !== undefined; |
|
384 if (hasObject) { |
|
385 var filtered = list._filtered; |
|
386 if (!filtered) { |
|
387 filtered = list._filtered = Base.create(list[0]); |
|
388 filtered._filtering = list[0]; |
|
389 } |
|
390 filtered[name] = undefined; |
|
391 } |
|
392 return this.read(hasObject ? [value] : list, start, length, options); |
|
393 }, |
|
394 |
|
395 getNamed: function(list, name) { |
|
396 var arg = list[0]; |
|
397 if (list._hasObject === undefined) |
|
398 list._hasObject = list.length === 1 && Base.isPlainObject(arg); |
|
399 if (list._hasObject) |
|
400 return name ? arg[name] : list._filtered || arg; |
|
401 }, |
|
402 |
|
403 hasNamed: function(list, name) { |
|
404 return !!this.getNamed(list, name); |
|
405 }, |
|
406 |
|
407 isPlainValue: function(obj) { |
|
408 return this.isPlainObject(obj) || Array.isArray(obj); |
|
409 }, |
|
410 |
|
411 serialize: function(obj, options, compact, dictionary) { |
|
412 options = options || {}; |
|
413 |
|
414 var root = !dictionary, |
|
415 res; |
|
416 if (root) { |
|
417 options.formatter = new Formatter(options.precision); |
|
418 dictionary = { |
|
419 length: 0, |
|
420 definitions: {}, |
|
421 references: {}, |
|
422 add: function(item, create) { |
|
423 var id = '#' + item._id, |
|
424 ref = this.references[id]; |
|
425 if (!ref) { |
|
426 this.length++; |
|
427 var res = create.call(item), |
|
428 name = item._class; |
|
429 if (name && res[0] !== name) |
|
430 res.unshift(name); |
|
431 this.definitions[id] = res; |
|
432 ref = this.references[id] = [id]; |
|
433 } |
|
434 return ref; |
|
435 } |
|
436 }; |
|
437 } |
|
438 if (obj && obj._serialize) { |
|
439 res = obj._serialize(options, dictionary); |
|
440 var name = obj._class; |
|
441 if (name && !compact && !res._compact && res[0] !== name) |
|
442 res.unshift(name); |
|
443 } else if (Array.isArray(obj)) { |
|
444 res = []; |
|
445 for (var i = 0, l = obj.length; i < l; i++) |
|
446 res[i] = Base.serialize(obj[i], options, compact, |
|
447 dictionary); |
|
448 if (compact) |
|
449 res._compact = true; |
|
450 } else if (Base.isPlainObject(obj)) { |
|
451 res = {}; |
|
452 for (var i in obj) |
|
453 if (obj.hasOwnProperty(i)) |
|
454 res[i] = Base.serialize(obj[i], options, compact, |
|
455 dictionary); |
|
456 } else if (typeof obj === 'number') { |
|
457 res = options.formatter.number(obj, options.precision); |
|
458 } else { |
|
459 res = obj; |
|
460 } |
|
461 return root && dictionary.length > 0 |
|
462 ? [['dictionary', dictionary.definitions], res] |
|
463 : res; |
|
464 }, |
|
465 |
|
466 deserialize: function(json, create, _data) { |
|
467 var res = json; |
|
468 _data = _data || {}; |
|
469 if (Array.isArray(json)) { |
|
470 var type = json[0], |
|
471 isDictionary = type === 'dictionary'; |
|
472 if (!isDictionary) { |
|
473 if (_data.dictionary && json.length == 1 && /^#/.test(type)) |
|
474 return _data.dictionary[type]; |
|
475 type = Base.exports[type]; |
|
476 } |
|
477 res = []; |
|
478 for (var i = type ? 1 : 0, l = json.length; i < l; i++) |
|
479 res.push(Base.deserialize(json[i], create, _data)); |
|
480 if (isDictionary) { |
|
481 _data.dictionary = res[0]; |
|
482 } else if (type) { |
|
483 var args = res; |
|
484 if (create) { |
|
485 res = create(type, args); |
|
486 } else { |
|
487 res = Base.create(type.prototype); |
|
488 type.apply(res, args); |
|
489 } |
|
490 } |
|
491 } else if (Base.isPlainObject(json)) { |
|
492 res = {}; |
|
493 for (var key in json) |
|
494 res[key] = Base.deserialize(json[key], create, _data); |
|
495 } |
|
496 return res; |
|
497 }, |
|
498 |
|
499 exportJSON: function(obj, options) { |
|
500 return JSON.stringify(Base.serialize(obj, options)); |
|
501 }, |
|
502 |
|
503 importJSON: function(json, target) { |
|
504 return Base.deserialize( |
|
505 typeof json === 'string' ? JSON.parse(json) : json, |
|
506 function(type, args) { |
|
507 var obj = target && target.constructor === type |
|
508 ? target |
|
509 : Base.create(type.prototype), |
|
510 isTarget = obj === target; |
|
511 if (args.length === 1 && obj instanceof Item |
|
512 && (!(obj instanceof Layer) || isTarget)) { |
|
513 var arg = args[0]; |
|
514 if (Base.isPlainObject(arg)) |
|
515 arg.insert = false; |
|
516 } |
|
517 type.apply(obj, args); |
|
518 if (isTarget) |
|
519 target = null; |
|
520 return obj; |
|
521 }); |
|
522 }, |
|
523 |
|
524 splice: function(list, items, index, remove) { |
|
525 var amount = items && items.length, |
|
526 append = index === undefined; |
|
527 index = append ? list.length : index; |
|
528 if (index > list.length) |
|
529 index = list.length; |
|
530 for (var i = 0; i < amount; i++) |
|
531 items[i]._index = index + i; |
|
532 if (append) { |
|
533 list.push.apply(list, items); |
|
534 return []; |
|
535 } else { |
|
536 var args = [index, remove]; |
|
537 if (items) |
|
538 args.push.apply(args, items); |
|
539 var removed = list.splice.apply(list, args); |
|
540 for (var i = 0, l = removed.length; i < l; i++) |
|
541 delete removed[i]._index; |
|
542 for (var i = index + amount, l = list.length; i < l; i++) |
|
543 list[i]._index = i; |
|
544 return removed; |
|
545 } |
|
546 }, |
|
547 |
|
548 capitalize: function(str) { |
|
549 return str.replace(/\b[a-z]/g, function(match) { |
|
550 return match.toUpperCase(); |
|
551 }); |
|
552 }, |
|
553 |
|
554 camelize: function(str) { |
|
555 return str.replace(/-(.)/g, function(all, chr) { |
|
556 return chr.toUpperCase(); |
|
557 }); |
|
558 }, |
|
559 |
|
560 hyphenate: function(str) { |
|
561 return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); |
|
562 } |
|
563 } |
|
564 }); |
|
565 |
|
566 var Callback = { |
|
567 attach: function(type, func) { |
|
568 if (typeof type !== 'string') { |
|
569 Base.each(type, function(value, key) { |
|
570 this.attach(key, value); |
|
571 }, this); |
|
572 return; |
|
573 } |
|
574 var entry = this._eventTypes[type]; |
|
575 if (entry) { |
|
576 var handlers = this._handlers = this._handlers || {}; |
|
577 handlers = handlers[type] = handlers[type] || []; |
|
578 if (handlers.indexOf(func) == -1) { |
|
579 handlers.push(func); |
|
580 if (entry.install && handlers.length == 1) |
|
581 entry.install.call(this, type); |
|
582 } |
|
583 } |
|
584 }, |
|
585 |
|
586 detach: function(type, func) { |
|
587 if (typeof type !== 'string') { |
|
588 Base.each(type, function(value, key) { |
|
589 this.detach(key, value); |
|
590 }, this); |
|
591 return; |
|
592 } |
|
593 var entry = this._eventTypes[type], |
|
594 handlers = this._handlers && this._handlers[type], |
|
595 index; |
|
596 if (entry && handlers) { |
|
597 if (!func || (index = handlers.indexOf(func)) != -1 |
|
598 && handlers.length == 1) { |
|
599 if (entry.uninstall) |
|
600 entry.uninstall.call(this, type); |
|
601 delete this._handlers[type]; |
|
602 } else if (index != -1) { |
|
603 handlers.splice(index, 1); |
|
604 } |
|
605 } |
|
606 }, |
|
607 |
|
608 once: function(type, func) { |
|
609 this.attach(type, function() { |
|
610 func.apply(this, arguments); |
|
611 this.detach(type, func); |
|
612 }); |
|
613 }, |
|
614 |
|
615 fire: function(type, event) { |
|
616 var handlers = this._handlers && this._handlers[type]; |
|
617 if (!handlers) |
|
618 return false; |
|
619 var args = [].slice.call(arguments, 1), |
|
620 PaperScript = paper.PaperScript, |
|
621 handleException = PaperScript && PaperScript.handleException, |
|
622 that = this; |
|
623 |
|
624 function callHandlers() { |
|
625 for (var i in handlers) { |
|
626 if (handlers[i].apply(that, args) === false |
|
627 && event && event.stop) |
|
628 event.stop(); |
|
629 } |
|
630 } |
|
631 |
|
632 if (handleException) { |
|
633 try { |
|
634 callHandlers(); |
|
635 } catch (e) { |
|
636 handleException(e); |
|
637 } |
|
638 } else { |
|
639 callHandlers(); |
|
640 } |
|
641 return true; |
|
642 }, |
|
643 |
|
644 responds: function(type) { |
|
645 return !!(this._handlers && this._handlers[type]); |
|
646 }, |
|
647 |
|
648 on: '#attach', |
|
649 off: '#detach', |
|
650 trigger: '#fire', |
|
651 |
|
652 statics: { |
|
653 inject: function inject() { |
|
654 for (var i = 0, l = arguments.length; i < l; i++) { |
|
655 var src = arguments[i], |
|
656 events = src._events; |
|
657 if (events) { |
|
658 var types = {}; |
|
659 Base.each(events, function(entry, key) { |
|
660 var isString = typeof entry === 'string', |
|
661 name = isString ? entry : key, |
|
662 part = Base.capitalize(name), |
|
663 type = name.substring(2).toLowerCase(); |
|
664 types[type] = isString ? {} : entry; |
|
665 name = '_' + name; |
|
666 src['get' + part] = function() { |
|
667 return this[name]; |
|
668 }; |
|
669 src['set' + part] = function(func) { |
|
670 if (func) { |
|
671 this.attach(type, func); |
|
672 } else if (this[name]) { |
|
673 this.detach(type, this[name]); |
|
674 } |
|
675 this[name] = func; |
|
676 }; |
|
677 }); |
|
678 src._eventTypes = types; |
|
679 } |
|
680 inject.base.call(this, src); |
|
681 } |
|
682 return this; |
|
683 } |
|
684 } |
|
685 }; |
|
686 |
|
687 var PaperScope = Base.extend({ |
|
688 _class: 'PaperScope', |
|
689 |
|
690 initialize: function PaperScope(script) { |
|
691 paper = this; |
|
692 this.project = null; |
|
693 this.projects = []; |
|
694 this.tools = []; |
|
695 this.palettes = []; |
|
696 this._id = script && (script.getAttribute('id') || script.src) |
|
697 || ('paperscope-' + (PaperScope._id++)); |
|
698 if (script) |
|
699 script.setAttribute('id', this._id); |
|
700 PaperScope._scopes[this._id] = this; |
|
701 if (!this.support) { |
|
702 var ctx = CanvasProvider.getContext(1, 1); |
|
703 PaperScope.prototype.support = { |
|
704 nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx, |
|
705 nativeBlendModes: BlendMode.nativeModes |
|
706 }; |
|
707 CanvasProvider.release(ctx); |
|
708 } |
|
709 }, |
|
710 |
|
711 version: '0.9.15', |
|
712 |
|
713 getView: function() { |
|
714 return this.project && this.project.view; |
|
715 }, |
|
716 |
|
717 getTool: function() { |
|
718 if (!this._tool) |
|
719 this._tool = new Tool(); |
|
720 return this._tool; |
|
721 }, |
|
722 |
|
723 getPaper: function() { |
|
724 return this; |
|
725 }, |
|
726 |
|
727 evaluate: function(code) { |
|
728 var res = paper.PaperScript.evaluate(code, this); |
|
729 View.updateFocus(); |
|
730 return res; |
|
731 }, |
|
732 |
|
733 install: function(scope) { |
|
734 var that = this; |
|
735 Base.each(['project', 'view', 'tool'], function(key) { |
|
736 Base.define(scope, key, { |
|
737 configurable: true, |
|
738 get: function() { |
|
739 return that[key]; |
|
740 } |
|
741 }); |
|
742 }); |
|
743 for (var key in this) { |
|
744 if (!/^(version|_id)/.test(key)) |
|
745 scope[key] = this[key]; |
|
746 } |
|
747 }, |
|
748 |
|
749 setup: function(canvas) { |
|
750 paper = this; |
|
751 this.project = new Project(canvas); |
|
752 return this; |
|
753 }, |
|
754 |
|
755 activate: function() { |
|
756 paper = this; |
|
757 }, |
|
758 |
|
759 clear: function() { |
|
760 for (var i = this.projects.length - 1; i >= 0; i--) |
|
761 this.projects[i].remove(); |
|
762 for (var i = this.tools.length - 1; i >= 0; i--) |
|
763 this.tools[i].remove(); |
|
764 for (var i = this.palettes.length - 1; i >= 0; i--) |
|
765 this.palettes[i].remove(); |
|
766 }, |
|
767 |
|
768 remove: function() { |
|
769 this.clear(); |
|
770 delete PaperScope._scopes[this._id]; |
|
771 }, |
|
772 |
|
773 statics: new function() { |
|
774 function handleAttribute(name) { |
|
775 name += 'Attribute'; |
|
776 return function(el, attr) { |
|
777 return el[name](attr) || el[name]('data-paper-' + attr); |
|
778 }; |
|
779 } |
|
780 |
|
781 return { |
|
782 _scopes: {}, |
|
783 _id: 0, |
|
784 |
|
785 get: function(id) { |
|
786 if (typeof id === 'object') |
|
787 id = id.getAttribute('id'); |
|
788 return this._scopes[id] || null; |
|
789 }, |
|
790 |
|
791 getAttribute: handleAttribute('get'), |
|
792 hasAttribute: handleAttribute('has') |
|
793 }; |
|
794 } |
|
795 }); |
|
796 |
|
797 var PaperScopeItem = Base.extend(Callback, { |
|
798 |
|
799 initialize: function(activate) { |
|
800 this._scope = paper; |
|
801 this._index = this._scope[this._list].push(this) - 1; |
|
802 if (activate || !this._scope[this._reference]) |
|
803 this.activate(); |
|
804 }, |
|
805 |
|
806 activate: function() { |
|
807 if (!this._scope) |
|
808 return false; |
|
809 var prev = this._scope[this._reference]; |
|
810 if (prev && prev !== this) |
|
811 prev.fire('deactivate'); |
|
812 this._scope[this._reference] = this; |
|
813 this.fire('activate', prev); |
|
814 return true; |
|
815 }, |
|
816 |
|
817 isActive: function() { |
|
818 return this._scope[this._reference] === this; |
|
819 }, |
|
820 |
|
821 remove: function() { |
|
822 if (this._index == null) |
|
823 return false; |
|
824 Base.splice(this._scope[this._list], null, this._index, 1); |
|
825 if (this._scope[this._reference] == this) |
|
826 this._scope[this._reference] = null; |
|
827 this._scope = null; |
|
828 return true; |
|
829 } |
|
830 }); |
|
831 |
|
832 var Formatter = Base.extend({ |
|
833 initialize: function(precision) { |
|
834 this.precision = precision || 5; |
|
835 this.multiplier = Math.pow(10, this.precision); |
|
836 }, |
|
837 |
|
838 number: function(val) { |
|
839 return Math.round(val * this.multiplier) / this.multiplier; |
|
840 }, |
|
841 |
|
842 point: function(val, separator) { |
|
843 return this.number(val.x) + (separator || ',') + this.number(val.y); |
|
844 }, |
|
845 |
|
846 size: function(val, separator) { |
|
847 return this.number(val.width) + (separator || ',') |
|
848 + this.number(val.height); |
|
849 }, |
|
850 |
|
851 rectangle: function(val, separator) { |
|
852 return this.point(val, separator) + (separator || ',') |
|
853 + this.size(val, separator); |
|
854 } |
|
855 }); |
|
856 |
|
857 Formatter.instance = new Formatter(); |
|
858 |
|
859 var Numerical = new function() { |
|
860 |
|
861 var abscissas = [ |
|
862 [ 0.5773502691896257645091488], |
|
863 [0,0.7745966692414833770358531], |
|
864 [ 0.3399810435848562648026658,0.8611363115940525752239465], |
|
865 [0,0.5384693101056830910363144,0.9061798459386639927976269], |
|
866 [ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016], |
|
867 [0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897], |
|
868 [ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609], |
|
869 [0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762], |
|
870 [ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640], |
|
871 [0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380], |
|
872 [ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491], |
|
873 [0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294], |
|
874 [ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973], |
|
875 [0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657], |
|
876 [ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542] |
|
877 ]; |
|
878 |
|
879 var weights = [ |
|
880 [1], |
|
881 [0.8888888888888888888888889,0.5555555555555555555555556], |
|
882 [0.6521451548625461426269361,0.3478548451374538573730639], |
|
883 [0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640], |
|
884 [0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961], |
|
885 [0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114], |
|
886 [0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314], |
|
887 [0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922], |
|
888 [0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688], |
|
889 [0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537], |
|
890 [0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160], |
|
891 [0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216], |
|
892 [0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329], |
|
893 [0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284], |
|
894 [0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806] |
|
895 ]; |
|
896 |
|
897 var abs = Math.abs, |
|
898 sqrt = Math.sqrt, |
|
899 pow = Math.pow, |
|
900 cos = Math.cos, |
|
901 PI = Math.PI; |
|
902 |
|
903 return { |
|
904 TOLERANCE: 10e-6, |
|
905 EPSILON: 10e-12, |
|
906 KAPPA: 4 * (sqrt(2) - 1) / 3, |
|
907 |
|
908 isZero: function(val) { |
|
909 return abs(val) <= Numerical.EPSILON; |
|
910 }, |
|
911 |
|
912 integrate: function(f, a, b, n) { |
|
913 var x = abscissas[n - 2], |
|
914 w = weights[n - 2], |
|
915 A = 0.5 * (b - a), |
|
916 B = A + a, |
|
917 i = 0, |
|
918 m = (n + 1) >> 1, |
|
919 sum = n & 1 ? w[i++] * f(B) : 0; |
|
920 while (i < m) { |
|
921 var Ax = A * x[i]; |
|
922 sum += w[i++] * (f(B + Ax) + f(B - Ax)); |
|
923 } |
|
924 return A * sum; |
|
925 }, |
|
926 |
|
927 findRoot: function(f, df, x, a, b, n, tolerance) { |
|
928 for (var i = 0; i < n; i++) { |
|
929 var fx = f(x), |
|
930 dx = fx / df(x); |
|
931 if (abs(dx) < tolerance) |
|
932 return x; |
|
933 var nx = x - dx; |
|
934 if (fx > 0) { |
|
935 b = x; |
|
936 x = nx <= a ? 0.5 * (a + b) : nx; |
|
937 } else { |
|
938 a = x; |
|
939 x = nx >= b ? 0.5 * (a + b) : nx; |
|
940 } |
|
941 } |
|
942 }, |
|
943 |
|
944 solveQuadratic: function(a, b, c, roots, min, max) { |
|
945 var epsilon = Numerical.EPSILON, |
|
946 unbound = min === undefined, |
|
947 minE = min - epsilon, |
|
948 maxE = max + epsilon, |
|
949 count = 0; |
|
950 |
|
951 function add(root) { |
|
952 if (unbound || root > minE && root < maxE) |
|
953 roots[count++] = root < min ? min : root > max ? max : root; |
|
954 return count; |
|
955 } |
|
956 |
|
957 if (abs(a) < epsilon) { |
|
958 if (abs(b) >= epsilon) |
|
959 return add(-c / b); |
|
960 return abs(c) < epsilon ? -1 : 0; |
|
961 } |
|
962 var p = b / (2 * a); |
|
963 var q = c / a; |
|
964 var p2 = p * p; |
|
965 if (p2 < q - epsilon) |
|
966 return 0; |
|
967 var s = p2 > q ? sqrt(p2 - q) : 0; |
|
968 add (s - p); |
|
969 if (s > 0) |
|
970 add(-s - p); |
|
971 return count; |
|
972 }, |
|
973 |
|
974 solveCubic: function(a, b, c, d, roots, min, max) { |
|
975 var epsilon = Numerical.EPSILON; |
|
976 if (abs(a) < epsilon) |
|
977 return Numerical.solveQuadratic(b, c, d, roots, min, max); |
|
978 |
|
979 var unbound = min === undefined, |
|
980 minE = min - epsilon, |
|
981 maxE = max + epsilon, |
|
982 count = 0; |
|
983 |
|
984 function add(root) { |
|
985 if (unbound || root > minE && root < maxE) |
|
986 roots[count++] = root < min ? min : root > max ? max : root; |
|
987 return count; |
|
988 } |
|
989 |
|
990 b /= a; |
|
991 c /= a; |
|
992 d /= a; |
|
993 var bb = b * b, |
|
994 p = (bb - 3 * c) / 9, |
|
995 q = (2 * bb * b - 9 * b * c + 27 * d) / 54, |
|
996 ppp = p * p * p, |
|
997 D = q * q - ppp; |
|
998 b /= 3; |
|
999 if (abs(D) < epsilon) { |
|
1000 if (abs(q) < epsilon) |
|
1001 return add(-b); |
|
1002 var sqp = sqrt(p), |
|
1003 snq = q > 0 ? 1 : -1; |
|
1004 add(-snq * 2 * sqp - b); |
|
1005 return add(snq * sqp - b); |
|
1006 } |
|
1007 if (D < 0) { |
|
1008 var sqp = sqrt(p), |
|
1009 phi = Math.acos(q / (sqp * sqp * sqp)) / 3, |
|
1010 t = -2 * sqp, |
|
1011 o = 2 * PI / 3; |
|
1012 add(t * cos(phi) - b); |
|
1013 add(t * cos(phi + o) - b); |
|
1014 return add(t * cos(phi - o) - b); |
|
1015 } |
|
1016 var A = (q > 0 ? -1 : 1) * pow(abs(q) + sqrt(D), 1 / 3); |
|
1017 return add(A + p / A - b); |
|
1018 } |
|
1019 }; |
|
1020 }; |
|
1021 |
|
1022 var Point = Base.extend({ |
|
1023 _class: 'Point', |
|
1024 _readIndex: true, |
|
1025 |
|
1026 initialize: function Point(arg0, arg1) { |
|
1027 var type = typeof arg0; |
|
1028 if (type === 'number') { |
|
1029 var hasY = typeof arg1 === 'number'; |
|
1030 this.x = arg0; |
|
1031 this.y = hasY ? arg1 : arg0; |
|
1032 if (this.__read) |
|
1033 this.__read = hasY ? 2 : 1; |
|
1034 } else if (type === 'undefined' || arg0 === null) { |
|
1035 this.x = this.y = 0; |
|
1036 if (this.__read) |
|
1037 this.__read = arg0 === null ? 1 : 0; |
|
1038 } else { |
|
1039 if (Array.isArray(arg0)) { |
|
1040 this.x = arg0[0]; |
|
1041 this.y = arg0.length > 1 ? arg0[1] : arg0[0]; |
|
1042 } else if (arg0.x != null) { |
|
1043 this.x = arg0.x; |
|
1044 this.y = arg0.y; |
|
1045 } else if (arg0.width != null) { |
|
1046 this.x = arg0.width; |
|
1047 this.y = arg0.height; |
|
1048 } else if (arg0.angle != null) { |
|
1049 this.x = arg0.length; |
|
1050 this.y = 0; |
|
1051 this.setAngle(arg0.angle); |
|
1052 } else { |
|
1053 this.x = this.y = 0; |
|
1054 if (this.__read) |
|
1055 this.__read = 0; |
|
1056 } |
|
1057 if (this.__read) |
|
1058 this.__read = 1; |
|
1059 } |
|
1060 }, |
|
1061 |
|
1062 set: function(x, y) { |
|
1063 this.x = x; |
|
1064 this.y = y; |
|
1065 return this; |
|
1066 }, |
|
1067 |
|
1068 equals: function(point) { |
|
1069 return point === this || point && (this.x === point.x |
|
1070 && this.y === point.y |
|
1071 || Array.isArray(point) && this.x === point[0] |
|
1072 && this.y === point[1]) || false; |
|
1073 }, |
|
1074 |
|
1075 clone: function() { |
|
1076 return new Point(this.x, this.y); |
|
1077 }, |
|
1078 |
|
1079 toString: function() { |
|
1080 var f = Formatter.instance; |
|
1081 return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }'; |
|
1082 }, |
|
1083 |
|
1084 _serialize: function(options) { |
|
1085 var f = options.formatter; |
|
1086 return [f.number(this.x), f.number(this.y)]; |
|
1087 }, |
|
1088 |
|
1089 add: function(point) { |
|
1090 point = Point.read(arguments); |
|
1091 return new Point(this.x + point.x, this.y + point.y); |
|
1092 }, |
|
1093 |
|
1094 subtract: function(point) { |
|
1095 point = Point.read(arguments); |
|
1096 return new Point(this.x - point.x, this.y - point.y); |
|
1097 }, |
|
1098 |
|
1099 multiply: function(point) { |
|
1100 point = Point.read(arguments); |
|
1101 return new Point(this.x * point.x, this.y * point.y); |
|
1102 }, |
|
1103 |
|
1104 divide: function(point) { |
|
1105 point = Point.read(arguments); |
|
1106 return new Point(this.x / point.x, this.y / point.y); |
|
1107 }, |
|
1108 |
|
1109 modulo: function(point) { |
|
1110 point = Point.read(arguments); |
|
1111 return new Point(this.x % point.x, this.y % point.y); |
|
1112 }, |
|
1113 |
|
1114 negate: function() { |
|
1115 return new Point(-this.x, -this.y); |
|
1116 }, |
|
1117 |
|
1118 transform: function(matrix) { |
|
1119 return matrix ? matrix._transformPoint(this) : this; |
|
1120 }, |
|
1121 |
|
1122 getDistance: function(point, squared) { |
|
1123 point = Point.read(arguments); |
|
1124 var x = point.x - this.x, |
|
1125 y = point.y - this.y, |
|
1126 d = x * x + y * y; |
|
1127 return squared ? d : Math.sqrt(d); |
|
1128 }, |
|
1129 |
|
1130 getLength: function() { |
|
1131 var length = this.x * this.x + this.y * this.y; |
|
1132 return arguments.length && arguments[0] ? length : Math.sqrt(length); |
|
1133 }, |
|
1134 |
|
1135 setLength: function(length) { |
|
1136 if (this.isZero()) { |
|
1137 var angle = this._angle || 0; |
|
1138 this.set( |
|
1139 Math.cos(angle) * length, |
|
1140 Math.sin(angle) * length |
|
1141 ); |
|
1142 } else { |
|
1143 var scale = length / this.getLength(); |
|
1144 if (Numerical.isZero(scale)) |
|
1145 this.getAngle(); |
|
1146 this.set( |
|
1147 this.x * scale, |
|
1148 this.y * scale |
|
1149 ); |
|
1150 } |
|
1151 return this; |
|
1152 }, |
|
1153 |
|
1154 normalize: function(length) { |
|
1155 if (length === undefined) |
|
1156 length = 1; |
|
1157 var current = this.getLength(), |
|
1158 scale = current !== 0 ? length / current : 0, |
|
1159 point = new Point(this.x * scale, this.y * scale); |
|
1160 point._angle = this._angle; |
|
1161 return point; |
|
1162 }, |
|
1163 |
|
1164 getAngle: function() { |
|
1165 return this.getAngleInRadians(arguments[0]) * 180 / Math.PI; |
|
1166 }, |
|
1167 |
|
1168 setAngle: function(angle) { |
|
1169 angle = this._angle = angle * Math.PI / 180; |
|
1170 if (!this.isZero()) { |
|
1171 var length = this.getLength(); |
|
1172 this.set( |
|
1173 Math.cos(angle) * length, |
|
1174 Math.sin(angle) * length |
|
1175 ); |
|
1176 } |
|
1177 return this; |
|
1178 }, |
|
1179 |
|
1180 getAngleInRadians: function() { |
|
1181 if (arguments[0] === undefined) { |
|
1182 return this.isZero() |
|
1183 ? this._angle || 0 |
|
1184 : this._angle = Math.atan2(this.y, this.x); |
|
1185 } else { |
|
1186 var point = Point.read(arguments), |
|
1187 div = this.getLength() * point.getLength(); |
|
1188 if (Numerical.isZero(div)) { |
|
1189 return NaN; |
|
1190 } else { |
|
1191 return Math.acos(this.dot(point) / div); |
|
1192 } |
|
1193 } |
|
1194 }, |
|
1195 |
|
1196 getAngleInDegrees: function() { |
|
1197 return this.getAngle(arguments[0]); |
|
1198 }, |
|
1199 |
|
1200 getQuadrant: function() { |
|
1201 return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; |
|
1202 }, |
|
1203 |
|
1204 getDirectedAngle: function(point) { |
|
1205 point = Point.read(arguments); |
|
1206 return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI; |
|
1207 }, |
|
1208 |
|
1209 rotate: function(angle, center) { |
|
1210 if (angle === 0) |
|
1211 return this.clone(); |
|
1212 angle = angle * Math.PI / 180; |
|
1213 var point = center ? this.subtract(center) : this, |
|
1214 s = Math.sin(angle), |
|
1215 c = Math.cos(angle); |
|
1216 point = new Point( |
|
1217 point.x * c - point.y * s, |
|
1218 point.y * c + point.x * s |
|
1219 ); |
|
1220 return center ? point.add(center) : point; |
|
1221 }, |
|
1222 |
|
1223 isInside: function(rect) { |
|
1224 return rect.contains(this); |
|
1225 }, |
|
1226 |
|
1227 isClose: function(point, tolerance) { |
|
1228 return this.getDistance(point) < tolerance; |
|
1229 }, |
|
1230 |
|
1231 isColinear: function(point) { |
|
1232 return this.cross(point) < 0.00001; |
|
1233 }, |
|
1234 |
|
1235 isOrthogonal: function(point) { |
|
1236 return this.dot(point) < 0.00001; |
|
1237 }, |
|
1238 |
|
1239 isZero: function() { |
|
1240 return Numerical.isZero(this.x) && Numerical.isZero(this.y); |
|
1241 }, |
|
1242 |
|
1243 isNaN: function() { |
|
1244 return isNaN(this.x) || isNaN(this.y); |
|
1245 }, |
|
1246 |
|
1247 dot: function(point) { |
|
1248 point = Point.read(arguments); |
|
1249 return this.x * point.x + this.y * point.y; |
|
1250 }, |
|
1251 |
|
1252 cross: function(point) { |
|
1253 point = Point.read(arguments); |
|
1254 return this.x * point.y - this.y * point.x; |
|
1255 }, |
|
1256 |
|
1257 project: function(point) { |
|
1258 point = Point.read(arguments); |
|
1259 if (point.isZero()) { |
|
1260 return new Point(0, 0); |
|
1261 } else { |
|
1262 var scale = this.dot(point) / point.dot(point); |
|
1263 return new Point( |
|
1264 point.x * scale, |
|
1265 point.y * scale |
|
1266 ); |
|
1267 } |
|
1268 }, |
|
1269 |
|
1270 statics: { |
|
1271 min: function() { |
|
1272 var point1 = Point.read(arguments); |
|
1273 point2 = Point.read(arguments); |
|
1274 return new Point( |
|
1275 Math.min(point1.x, point2.x), |
|
1276 Math.min(point1.y, point2.y) |
|
1277 ); |
|
1278 }, |
|
1279 |
|
1280 max: function() { |
|
1281 var point1 = Point.read(arguments); |
|
1282 point2 = Point.read(arguments); |
|
1283 return new Point( |
|
1284 Math.max(point1.x, point2.x), |
|
1285 Math.max(point1.y, point2.y) |
|
1286 ); |
|
1287 }, |
|
1288 |
|
1289 random: function() { |
|
1290 return new Point(Math.random(), Math.random()); |
|
1291 } |
|
1292 } |
|
1293 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { |
|
1294 var op = Math[name]; |
|
1295 this[name] = function() { |
|
1296 return new Point(op(this.x), op(this.y)); |
|
1297 }; |
|
1298 }, {})); |
|
1299 |
|
1300 var LinkedPoint = Point.extend({ |
|
1301 initialize: function Point(x, y, owner, setter) { |
|
1302 this._x = x; |
|
1303 this._y = y; |
|
1304 this._owner = owner; |
|
1305 this._setter = setter; |
|
1306 }, |
|
1307 |
|
1308 set: function(x, y, _dontNotify) { |
|
1309 this._x = x; |
|
1310 this._y = y; |
|
1311 if (!_dontNotify) |
|
1312 this._owner[this._setter](this); |
|
1313 return this; |
|
1314 }, |
|
1315 |
|
1316 getX: function() { |
|
1317 return this._x; |
|
1318 }, |
|
1319 |
|
1320 setX: function(x) { |
|
1321 this._x = x; |
|
1322 this._owner[this._setter](this); |
|
1323 }, |
|
1324 |
|
1325 getY: function() { |
|
1326 return this._y; |
|
1327 }, |
|
1328 |
|
1329 setY: function(y) { |
|
1330 this._y = y; |
|
1331 this._owner[this._setter](this); |
|
1332 } |
|
1333 }); |
|
1334 |
|
1335 var Size = Base.extend({ |
|
1336 _class: 'Size', |
|
1337 _readIndex: true, |
|
1338 |
|
1339 initialize: function Size(arg0, arg1) { |
|
1340 var type = typeof arg0; |
|
1341 if (type === 'number') { |
|
1342 var hasHeight = typeof arg1 === 'number'; |
|
1343 this.width = arg0; |
|
1344 this.height = hasHeight ? arg1 : arg0; |
|
1345 if (this.__read) |
|
1346 this.__read = hasHeight ? 2 : 1; |
|
1347 } else if (type === 'undefined' || arg0 === null) { |
|
1348 this.width = this.height = 0; |
|
1349 if (this.__read) |
|
1350 this.__read = arg0 === null ? 1 : 0; |
|
1351 } else { |
|
1352 if (Array.isArray(arg0)) { |
|
1353 this.width = arg0[0]; |
|
1354 this.height = arg0.length > 1 ? arg0[1] : arg0[0]; |
|
1355 } else if (arg0.width != null) { |
|
1356 this.width = arg0.width; |
|
1357 this.height = arg0.height; |
|
1358 } else if (arg0.x != null) { |
|
1359 this.width = arg0.x; |
|
1360 this.height = arg0.y; |
|
1361 } else { |
|
1362 this.width = this.height = 0; |
|
1363 if (this.__read) |
|
1364 this.__read = 0; |
|
1365 } |
|
1366 if (this.__read) |
|
1367 this.__read = 1; |
|
1368 } |
|
1369 }, |
|
1370 |
|
1371 set: function(width, height) { |
|
1372 this.width = width; |
|
1373 this.height = height; |
|
1374 return this; |
|
1375 }, |
|
1376 |
|
1377 equals: function(size) { |
|
1378 return size === this || size && (this.width === size.width |
|
1379 && this.height === size.height |
|
1380 || Array.isArray(size) && this.width === size[0] |
|
1381 && this.height === size[1]) || false; |
|
1382 }, |
|
1383 |
|
1384 clone: function() { |
|
1385 return new Size(this.width, this.height); |
|
1386 }, |
|
1387 |
|
1388 toString: function() { |
|
1389 var f = Formatter.instance; |
|
1390 return '{ width: ' + f.number(this.width) |
|
1391 + ', height: ' + f.number(this.height) + ' }'; |
|
1392 }, |
|
1393 |
|
1394 _serialize: function(options) { |
|
1395 var f = options.formatter; |
|
1396 return [f.number(this.width), |
|
1397 f.number(this.height)]; |
|
1398 }, |
|
1399 |
|
1400 add: function(size) { |
|
1401 size = Size.read(arguments); |
|
1402 return new Size(this.width + size.width, this.height + size.height); |
|
1403 }, |
|
1404 |
|
1405 subtract: function(size) { |
|
1406 size = Size.read(arguments); |
|
1407 return new Size(this.width - size.width, this.height - size.height); |
|
1408 }, |
|
1409 |
|
1410 multiply: function(size) { |
|
1411 size = Size.read(arguments); |
|
1412 return new Size(this.width * size.width, this.height * size.height); |
|
1413 }, |
|
1414 |
|
1415 divide: function(size) { |
|
1416 size = Size.read(arguments); |
|
1417 return new Size(this.width / size.width, this.height / size.height); |
|
1418 }, |
|
1419 |
|
1420 modulo: function(size) { |
|
1421 size = Size.read(arguments); |
|
1422 return new Size(this.width % size.width, this.height % size.height); |
|
1423 }, |
|
1424 |
|
1425 negate: function() { |
|
1426 return new Size(-this.width, -this.height); |
|
1427 }, |
|
1428 |
|
1429 isZero: function() { |
|
1430 return Numerical.isZero(this.width) && Numerical.isZero(this.height); |
|
1431 }, |
|
1432 |
|
1433 isNaN: function() { |
|
1434 return isNaN(this.width) || isNaN(this.height); |
|
1435 }, |
|
1436 |
|
1437 statics: { |
|
1438 min: function(size1, size2) { |
|
1439 return new Size( |
|
1440 Math.min(size1.width, size2.width), |
|
1441 Math.min(size1.height, size2.height)); |
|
1442 }, |
|
1443 |
|
1444 max: function(size1, size2) { |
|
1445 return new Size( |
|
1446 Math.max(size1.width, size2.width), |
|
1447 Math.max(size1.height, size2.height)); |
|
1448 }, |
|
1449 |
|
1450 random: function() { |
|
1451 return new Size(Math.random(), Math.random()); |
|
1452 } |
|
1453 } |
|
1454 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { |
|
1455 var op = Math[name]; |
|
1456 this[name] = function() { |
|
1457 return new Size(op(this.width), op(this.height)); |
|
1458 }; |
|
1459 }, {})); |
|
1460 |
|
1461 var LinkedSize = Size.extend({ |
|
1462 initialize: function Size(width, height, owner, setter) { |
|
1463 this._width = width; |
|
1464 this._height = height; |
|
1465 this._owner = owner; |
|
1466 this._setter = setter; |
|
1467 }, |
|
1468 |
|
1469 set: function(width, height, _dontNotify) { |
|
1470 this._width = width; |
|
1471 this._height = height; |
|
1472 if (!_dontNotify) |
|
1473 this._owner[this._setter](this); |
|
1474 return this; |
|
1475 }, |
|
1476 |
|
1477 getWidth: function() { |
|
1478 return this._width; |
|
1479 }, |
|
1480 |
|
1481 setWidth: function(width) { |
|
1482 this._width = width; |
|
1483 this._owner[this._setter](this); |
|
1484 }, |
|
1485 |
|
1486 getHeight: function() { |
|
1487 return this._height; |
|
1488 }, |
|
1489 |
|
1490 setHeight: function(height) { |
|
1491 this._height = height; |
|
1492 this._owner[this._setter](this); |
|
1493 } |
|
1494 }); |
|
1495 |
|
1496 var Rectangle = Base.extend({ |
|
1497 _class: 'Rectangle', |
|
1498 _readIndex: true, |
|
1499 |
|
1500 initialize: function Rectangle(arg0, arg1, arg2, arg3) { |
|
1501 var type = typeof arg0, |
|
1502 read = 0; |
|
1503 if (type === 'number') { |
|
1504 this.x = arg0; |
|
1505 this.y = arg1; |
|
1506 this.width = arg2; |
|
1507 this.height = arg3; |
|
1508 read = 4; |
|
1509 } else if (type === 'undefined' || arg0 === null) { |
|
1510 this.x = this.y = this.width = this.height = 0; |
|
1511 read = arg0 === null ? 1 : 0; |
|
1512 } else if (arguments.length === 1) { |
|
1513 if (Array.isArray(arg0)) { |
|
1514 this.x = arg0[0]; |
|
1515 this.y = arg0[1]; |
|
1516 this.width = arg0[2]; |
|
1517 this.height = arg0[3]; |
|
1518 read = 1; |
|
1519 } else if (arg0.x !== undefined || arg0.width !== undefined) { |
|
1520 this.x = arg0.x || 0; |
|
1521 this.y = arg0.y || 0; |
|
1522 this.width = arg0.width || 0; |
|
1523 this.height = arg0.height || 0; |
|
1524 read = 1; |
|
1525 } else if (arg0.from === undefined && arg0.to === undefined) { |
|
1526 this.x = this.y = this.width = this.height = 0; |
|
1527 this._set(arg0); |
|
1528 read = 1; |
|
1529 } |
|
1530 } |
|
1531 if (!read) { |
|
1532 var point = Point.readNamed(arguments, 'from'), |
|
1533 next = Base.peek(arguments); |
|
1534 this.x = point.x; |
|
1535 this.y = point.y; |
|
1536 if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) { |
|
1537 var to = Point.readNamed(arguments, 'to'); |
|
1538 this.width = to.x - point.x; |
|
1539 this.height = to.y - point.y; |
|
1540 if (this.width < 0) { |
|
1541 this.x = to.x; |
|
1542 this.width = -this.width; |
|
1543 } |
|
1544 if (this.height < 0) { |
|
1545 this.y = to.y; |
|
1546 this.height = -this.height; |
|
1547 } |
|
1548 } else { |
|
1549 var size = Size.read(arguments); |
|
1550 this.width = size.width; |
|
1551 this.height = size.height; |
|
1552 } |
|
1553 read = arguments._index; |
|
1554 } |
|
1555 if (this.__read) |
|
1556 this.__read = read; |
|
1557 }, |
|
1558 |
|
1559 set: function(x, y, width, height) { |
|
1560 this.x = x; |
|
1561 this.y = y; |
|
1562 this.width = width; |
|
1563 this.height = height; |
|
1564 return this; |
|
1565 }, |
|
1566 |
|
1567 clone: function() { |
|
1568 return new Rectangle(this.x, this.y, this.width, this.height); |
|
1569 }, |
|
1570 |
|
1571 equals: function(rect) { |
|
1572 if (Base.isPlainValue(rect)) |
|
1573 rect = Rectangle.read(arguments); |
|
1574 return rect === this |
|
1575 || rect && this.x === rect.x && this.y === rect.y |
|
1576 && this.width === rect.width && this.height === rect.height |
|
1577 || false; |
|
1578 }, |
|
1579 |
|
1580 toString: function() { |
|
1581 var f = Formatter.instance; |
|
1582 return '{ x: ' + f.number(this.x) |
|
1583 + ', y: ' + f.number(this.y) |
|
1584 + ', width: ' + f.number(this.width) |
|
1585 + ', height: ' + f.number(this.height) |
|
1586 + ' }'; |
|
1587 }, |
|
1588 |
|
1589 _serialize: function(options) { |
|
1590 var f = options.formatter; |
|
1591 return [f.number(this.x), |
|
1592 f.number(this.y), |
|
1593 f.number(this.width), |
|
1594 f.number(this.height)]; |
|
1595 }, |
|
1596 |
|
1597 getPoint: function() { |
|
1598 return new (arguments[0] ? Point : LinkedPoint) |
|
1599 (this.x, this.y, this, 'setPoint'); |
|
1600 }, |
|
1601 |
|
1602 setPoint: function(point) { |
|
1603 point = Point.read(arguments); |
|
1604 this.x = point.x; |
|
1605 this.y = point.y; |
|
1606 }, |
|
1607 |
|
1608 getSize: function() { |
|
1609 return new (arguments[0] ? Size : LinkedSize) |
|
1610 (this.width, this.height, this, 'setSize'); |
|
1611 }, |
|
1612 |
|
1613 setSize: function(size) { |
|
1614 size = Size.read(arguments); |
|
1615 if (this._fixX) |
|
1616 this.x += (this.width - size.width) * this._fixX; |
|
1617 if (this._fixY) |
|
1618 this.y += (this.height - size.height) * this._fixY; |
|
1619 this.width = size.width; |
|
1620 this.height = size.height; |
|
1621 this._fixW = 1; |
|
1622 this._fixH = 1; |
|
1623 }, |
|
1624 |
|
1625 getLeft: function() { |
|
1626 return this.x; |
|
1627 }, |
|
1628 |
|
1629 setLeft: function(left) { |
|
1630 if (!this._fixW) |
|
1631 this.width -= left - this.x; |
|
1632 this.x = left; |
|
1633 this._fixX = 0; |
|
1634 }, |
|
1635 |
|
1636 getTop: function() { |
|
1637 return this.y; |
|
1638 }, |
|
1639 |
|
1640 setTop: function(top) { |
|
1641 if (!this._fixH) |
|
1642 this.height -= top - this.y; |
|
1643 this.y = top; |
|
1644 this._fixY = 0; |
|
1645 }, |
|
1646 |
|
1647 getRight: function() { |
|
1648 return this.x + this.width; |
|
1649 }, |
|
1650 |
|
1651 setRight: function(right) { |
|
1652 if (this._fixX !== undefined && this._fixX !== 1) |
|
1653 this._fixW = 0; |
|
1654 if (this._fixW) |
|
1655 this.x = right - this.width; |
|
1656 else |
|
1657 this.width = right - this.x; |
|
1658 this._fixX = 1; |
|
1659 }, |
|
1660 |
|
1661 getBottom: function() { |
|
1662 return this.y + this.height; |
|
1663 }, |
|
1664 |
|
1665 setBottom: function(bottom) { |
|
1666 if (this._fixY !== undefined && this._fixY !== 1) |
|
1667 this._fixH = 0; |
|
1668 if (this._fixH) |
|
1669 this.y = bottom - this.height; |
|
1670 else |
|
1671 this.height = bottom - this.y; |
|
1672 this._fixY = 1; |
|
1673 }, |
|
1674 |
|
1675 getCenterX: function() { |
|
1676 return this.x + this.width * 0.5; |
|
1677 }, |
|
1678 |
|
1679 setCenterX: function(x) { |
|
1680 this.x = x - this.width * 0.5; |
|
1681 this._fixX = 0.5; |
|
1682 }, |
|
1683 |
|
1684 getCenterY: function() { |
|
1685 return this.y + this.height * 0.5; |
|
1686 }, |
|
1687 |
|
1688 setCenterY: function(y) { |
|
1689 this.y = y - this.height * 0.5; |
|
1690 this._fixY = 0.5; |
|
1691 }, |
|
1692 |
|
1693 getCenter: function() { |
|
1694 return new (arguments[0] ? Point : LinkedPoint) |
|
1695 (this.getCenterX(), this.getCenterY(), this, 'setCenter'); |
|
1696 }, |
|
1697 |
|
1698 setCenter: function(point) { |
|
1699 point = Point.read(arguments); |
|
1700 this.setCenterX(point.x); |
|
1701 this.setCenterY(point.y); |
|
1702 return this; |
|
1703 }, |
|
1704 |
|
1705 isEmpty: function() { |
|
1706 return this.width == 0 || this.height == 0; |
|
1707 }, |
|
1708 |
|
1709 contains: function(arg) { |
|
1710 return arg && arg.width !== undefined |
|
1711 || (Array.isArray(arg) ? arg : arguments).length == 4 |
|
1712 ? this._containsRectangle(Rectangle.read(arguments)) |
|
1713 : this._containsPoint(Point.read(arguments)); |
|
1714 }, |
|
1715 |
|
1716 _containsPoint: function(point) { |
|
1717 var x = point.x, |
|
1718 y = point.y; |
|
1719 return x >= this.x && y >= this.y |
|
1720 && x <= this.x + this.width |
|
1721 && y <= this.y + this.height; |
|
1722 }, |
|
1723 |
|
1724 _containsRectangle: function(rect) { |
|
1725 var x = rect.x, |
|
1726 y = rect.y; |
|
1727 return x >= this.x && y >= this.y |
|
1728 && x + rect.width <= this.x + this.width |
|
1729 && y + rect.height <= this.y + this.height; |
|
1730 }, |
|
1731 |
|
1732 intersects: function(rect) { |
|
1733 rect = Rectangle.read(arguments); |
|
1734 return rect.x + rect.width > this.x |
|
1735 && rect.y + rect.height > this.y |
|
1736 && rect.x < this.x + this.width |
|
1737 && rect.y < this.y + this.height; |
|
1738 }, |
|
1739 |
|
1740 touches: function(rect) { |
|
1741 rect = Rectangle.read(arguments); |
|
1742 return rect.x + rect.width >= this.x |
|
1743 && rect.y + rect.height >= this.y |
|
1744 && rect.x <= this.x + this.width |
|
1745 && rect.y <= this.y + this.height; |
|
1746 }, |
|
1747 |
|
1748 intersect: function(rect) { |
|
1749 rect = Rectangle.read(arguments); |
|
1750 var x1 = Math.max(this.x, rect.x), |
|
1751 y1 = Math.max(this.y, rect.y), |
|
1752 x2 = Math.min(this.x + this.width, rect.x + rect.width), |
|
1753 y2 = Math.min(this.y + this.height, rect.y + rect.height); |
|
1754 return new Rectangle(x1, y1, x2 - x1, y2 - y1); |
|
1755 }, |
|
1756 |
|
1757 unite: function(rect) { |
|
1758 rect = Rectangle.read(arguments); |
|
1759 var x1 = Math.min(this.x, rect.x), |
|
1760 y1 = Math.min(this.y, rect.y), |
|
1761 x2 = Math.max(this.x + this.width, rect.x + rect.width), |
|
1762 y2 = Math.max(this.y + this.height, rect.y + rect.height); |
|
1763 return new Rectangle(x1, y1, x2 - x1, y2 - y1); |
|
1764 }, |
|
1765 |
|
1766 include: function(point) { |
|
1767 point = Point.read(arguments); |
|
1768 var x1 = Math.min(this.x, point.x), |
|
1769 y1 = Math.min(this.y, point.y), |
|
1770 x2 = Math.max(this.x + this.width, point.x), |
|
1771 y2 = Math.max(this.y + this.height, point.y); |
|
1772 return new Rectangle(x1, y1, x2 - x1, y2 - y1); |
|
1773 }, |
|
1774 |
|
1775 expand: function(hor, ver) { |
|
1776 if (ver === undefined) |
|
1777 ver = hor; |
|
1778 return new Rectangle(this.x - hor / 2, this.y - ver / 2, |
|
1779 this.width + hor, this.height + ver); |
|
1780 }, |
|
1781 |
|
1782 scale: function(hor, ver) { |
|
1783 return this.expand(this.width * hor - this.width, |
|
1784 this.height * (ver === undefined ? hor : ver) - this.height); |
|
1785 } |
|
1786 }, new function() { |
|
1787 return Base.each([ |
|
1788 ['Top', 'Left'], ['Top', 'Right'], |
|
1789 ['Bottom', 'Left'], ['Bottom', 'Right'], |
|
1790 ['Left', 'Center'], ['Top', 'Center'], |
|
1791 ['Right', 'Center'], ['Bottom', 'Center'] |
|
1792 ], |
|
1793 function(parts, index) { |
|
1794 var part = parts.join(''); |
|
1795 var xFirst = /^[RL]/.test(part); |
|
1796 if (index >= 4) |
|
1797 parts[1] += xFirst ? 'Y' : 'X'; |
|
1798 var x = parts[xFirst ? 0 : 1], |
|
1799 y = parts[xFirst ? 1 : 0], |
|
1800 getX = 'get' + x, |
|
1801 getY = 'get' + y, |
|
1802 setX = 'set' + x, |
|
1803 setY = 'set' + y, |
|
1804 get = 'get' + part, |
|
1805 set = 'set' + part; |
|
1806 this[get] = function() { |
|
1807 return new (arguments[0] ? Point : LinkedPoint) |
|
1808 (this[getX](), this[getY](), this, set); |
|
1809 }; |
|
1810 this[set] = function(point) { |
|
1811 point = Point.read(arguments); |
|
1812 this[setX](point.x); |
|
1813 this[setY](point.y); |
|
1814 }; |
|
1815 }, {}); |
|
1816 }); |
|
1817 |
|
1818 var LinkedRectangle = Rectangle.extend({ |
|
1819 initialize: function Rectangle(x, y, width, height, owner, setter) { |
|
1820 this.set(x, y, width, height, true); |
|
1821 this._owner = owner; |
|
1822 this._setter = setter; |
|
1823 }, |
|
1824 |
|
1825 set: function(x, y, width, height, _dontNotify) { |
|
1826 this._x = x; |
|
1827 this._y = y; |
|
1828 this._width = width; |
|
1829 this._height = height; |
|
1830 if (!_dontNotify) |
|
1831 this._owner[this._setter](this); |
|
1832 return this; |
|
1833 } |
|
1834 }, new function() { |
|
1835 var proto = Rectangle.prototype; |
|
1836 |
|
1837 return Base.each(['x', 'y', 'width', 'height'], function(key) { |
|
1838 var part = Base.capitalize(key); |
|
1839 var internal = '_' + key; |
|
1840 this['get' + part] = function() { |
|
1841 return this[internal]; |
|
1842 }; |
|
1843 |
|
1844 this['set' + part] = function(value) { |
|
1845 this[internal] = value; |
|
1846 if (!this._dontNotify) |
|
1847 this._owner[this._setter](this); |
|
1848 }; |
|
1849 }, Base.each(['Point', 'Size', 'Center', |
|
1850 'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY', |
|
1851 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', |
|
1852 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], |
|
1853 function(key) { |
|
1854 var name = 'set' + key; |
|
1855 this[name] = function() { |
|
1856 this._dontNotify = true; |
|
1857 proto[name].apply(this, arguments); |
|
1858 delete this._dontNotify; |
|
1859 this._owner[this._setter](this); |
|
1860 }; |
|
1861 }, { |
|
1862 isSelected: function() { |
|
1863 return this._owner._boundsSelected; |
|
1864 }, |
|
1865 |
|
1866 setSelected: function(selected) { |
|
1867 var owner = this._owner; |
|
1868 if (owner.setSelected) { |
|
1869 owner._boundsSelected = selected; |
|
1870 owner.setSelected(selected || owner._selectedSegmentState > 0); |
|
1871 } |
|
1872 } |
|
1873 }) |
|
1874 ); |
|
1875 }); |
|
1876 |
|
1877 var Matrix = Base.extend({ |
|
1878 _class: 'Matrix', |
|
1879 |
|
1880 initialize: function Matrix(arg) { |
|
1881 var count = arguments.length, |
|
1882 ok = true; |
|
1883 if (count === 6) { |
|
1884 this.set.apply(this, arguments); |
|
1885 } else if (count === 1) { |
|
1886 if (arg instanceof Matrix) { |
|
1887 this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty); |
|
1888 } else if (Array.isArray(arg)) { |
|
1889 this.set.apply(this, arg); |
|
1890 } else { |
|
1891 ok = false; |
|
1892 } |
|
1893 } else if (count === 0) { |
|
1894 this.reset(); |
|
1895 } else { |
|
1896 ok = false; |
|
1897 } |
|
1898 if (!ok) |
|
1899 throw new Error('Unsupported matrix parameters'); |
|
1900 }, |
|
1901 |
|
1902 set: function(a, c, b, d, tx, ty, _dontNotify) { |
|
1903 this._a = a; |
|
1904 this._c = c; |
|
1905 this._b = b; |
|
1906 this._d = d; |
|
1907 this._tx = tx; |
|
1908 this._ty = ty; |
|
1909 if (!_dontNotify) |
|
1910 this._changed(); |
|
1911 return this; |
|
1912 }, |
|
1913 |
|
1914 _serialize: function(options) { |
|
1915 return Base.serialize(this.getValues(), options); |
|
1916 }, |
|
1917 |
|
1918 _changed: function() { |
|
1919 if (this._owner) |
|
1920 this._owner._changed(5); |
|
1921 }, |
|
1922 |
|
1923 clone: function() { |
|
1924 return new Matrix(this._a, this._c, this._b, this._d, |
|
1925 this._tx, this._ty); |
|
1926 }, |
|
1927 |
|
1928 equals: function(mx) { |
|
1929 return mx === this || mx && this._a === mx._a && this._b === mx._b |
|
1930 && this._c === mx._c && this._d === mx._d |
|
1931 && this._tx === mx._tx && this._ty === mx._ty |
|
1932 || false; |
|
1933 }, |
|
1934 |
|
1935 toString: function() { |
|
1936 var f = Formatter.instance; |
|
1937 return '[[' + [f.number(this._a), f.number(this._b), |
|
1938 f.number(this._tx)].join(', ') + '], [' |
|
1939 + [f.number(this._c), f.number(this._d), |
|
1940 f.number(this._ty)].join(', ') + ']]'; |
|
1941 }, |
|
1942 |
|
1943 reset: function() { |
|
1944 this._a = this._d = 1; |
|
1945 this._c = this._b = this._tx = this._ty = 0; |
|
1946 this._changed(); |
|
1947 return this; |
|
1948 }, |
|
1949 |
|
1950 scale: function() { |
|
1951 var scale = Point.read(arguments), |
|
1952 center = Point.read(arguments, 0, 0, { readNull: true }); |
|
1953 if (center) |
|
1954 this.translate(center); |
|
1955 this._a *= scale.x; |
|
1956 this._c *= scale.x; |
|
1957 this._b *= scale.y; |
|
1958 this._d *= scale.y; |
|
1959 if (center) |
|
1960 this.translate(center.negate()); |
|
1961 this._changed(); |
|
1962 return this; |
|
1963 }, |
|
1964 |
|
1965 translate: function(point) { |
|
1966 point = Point.read(arguments); |
|
1967 var x = point.x, |
|
1968 y = point.y; |
|
1969 this._tx += x * this._a + y * this._b; |
|
1970 this._ty += x * this._c + y * this._d; |
|
1971 this._changed(); |
|
1972 return this; |
|
1973 }, |
|
1974 |
|
1975 rotate: function(angle, center) { |
|
1976 center = Point.read(arguments, 1); |
|
1977 angle = angle * Math.PI / 180; |
|
1978 var x = center.x, |
|
1979 y = center.y, |
|
1980 cos = Math.cos(angle), |
|
1981 sin = Math.sin(angle), |
|
1982 tx = x - x * cos + y * sin, |
|
1983 ty = y - x * sin - y * cos, |
|
1984 a = this._a, |
|
1985 b = this._b, |
|
1986 c = this._c, |
|
1987 d = this._d; |
|
1988 this._a = cos * a + sin * b; |
|
1989 this._b = -sin * a + cos * b; |
|
1990 this._c = cos * c + sin * d; |
|
1991 this._d = -sin * c + cos * d; |
|
1992 this._tx += tx * a + ty * b; |
|
1993 this._ty += tx * c + ty * d; |
|
1994 this._changed(); |
|
1995 return this; |
|
1996 }, |
|
1997 |
|
1998 shear: function() { |
|
1999 var point = Point.read(arguments), |
|
2000 center = Point.read(arguments, 0, 0, { readNull: true }); |
|
2001 if (center) |
|
2002 this.translate(center); |
|
2003 var a = this._a, |
|
2004 c = this._c; |
|
2005 this._a += point.y * this._b; |
|
2006 this._c += point.y * this._d; |
|
2007 this._b += point.x * a; |
|
2008 this._d += point.x * c; |
|
2009 if (center) |
|
2010 this.translate(center.negate()); |
|
2011 this._changed(); |
|
2012 return this; |
|
2013 }, |
|
2014 |
|
2015 concatenate: function(mx) { |
|
2016 var a = this._a, |
|
2017 b = this._b, |
|
2018 c = this._c, |
|
2019 d = this._d; |
|
2020 this._a = mx._a * a + mx._c * b; |
|
2021 this._b = mx._b * a + mx._d * b; |
|
2022 this._c = mx._a * c + mx._c * d; |
|
2023 this._d = mx._b * c + mx._d * d; |
|
2024 this._tx += mx._tx * a + mx._ty * b; |
|
2025 this._ty += mx._tx * c + mx._ty * d; |
|
2026 this._changed(); |
|
2027 return this; |
|
2028 }, |
|
2029 |
|
2030 preConcatenate: function(mx) { |
|
2031 var a = this._a, |
|
2032 b = this._b, |
|
2033 c = this._c, |
|
2034 d = this._d, |
|
2035 tx = this._tx, |
|
2036 ty = this._ty; |
|
2037 this._a = mx._a * a + mx._b * c; |
|
2038 this._b = mx._a * b + mx._b * d; |
|
2039 this._c = mx._c * a + mx._d * c; |
|
2040 this._d = mx._c * b + mx._d * d; |
|
2041 this._tx = mx._a * tx + mx._b * ty + mx._tx; |
|
2042 this._ty = mx._c * tx + mx._d * ty + mx._ty; |
|
2043 this._changed(); |
|
2044 return this; |
|
2045 }, |
|
2046 |
|
2047 isIdentity: function() { |
|
2048 return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1 |
|
2049 && this._tx === 0 && this._ty === 0; |
|
2050 }, |
|
2051 |
|
2052 isInvertible: function() { |
|
2053 return !!this._getDeterminant(); |
|
2054 }, |
|
2055 |
|
2056 isSingular: function() { |
|
2057 return !this._getDeterminant(); |
|
2058 }, |
|
2059 |
|
2060 transform: function( src, srcOffset, dst, dstOffset, count) { |
|
2061 return arguments.length < 5 |
|
2062 ? this._transformPoint(Point.read(arguments)) |
|
2063 : this._transformCoordinates(src, srcOffset, dst, dstOffset, count); |
|
2064 }, |
|
2065 |
|
2066 _transformPoint: function(point, dest, _dontNotify) { |
|
2067 var x = point.x, |
|
2068 y = point.y; |
|
2069 if (!dest) |
|
2070 dest = new Point(); |
|
2071 return dest.set( |
|
2072 x * this._a + y * this._b + this._tx, |
|
2073 x * this._c + y * this._d + this._ty, |
|
2074 _dontNotify |
|
2075 ); |
|
2076 }, |
|
2077 |
|
2078 _transformCoordinates: function(src, srcOffset, dst, dstOffset, count) { |
|
2079 var i = srcOffset, |
|
2080 j = dstOffset, |
|
2081 max = i + 2 * count; |
|
2082 while (i < max) { |
|
2083 var x = src[i++], |
|
2084 y = src[i++]; |
|
2085 dst[j++] = x * this._a + y * this._b + this._tx; |
|
2086 dst[j++] = x * this._c + y * this._d + this._ty; |
|
2087 } |
|
2088 return dst; |
|
2089 }, |
|
2090 |
|
2091 _transformCorners: function(rect) { |
|
2092 var x1 = rect.x, |
|
2093 y1 = rect.y, |
|
2094 x2 = x1 + rect.width, |
|
2095 y2 = y1 + rect.height, |
|
2096 coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ]; |
|
2097 return this._transformCoordinates(coords, 0, coords, 0, 4); |
|
2098 }, |
|
2099 |
|
2100 _transformBounds: function(bounds, dest, _dontNotify) { |
|
2101 var coords = this._transformCorners(bounds), |
|
2102 min = coords.slice(0, 2), |
|
2103 max = coords.slice(); |
|
2104 for (var i = 2; i < 8; i++) { |
|
2105 var val = coords[i], |
|
2106 j = i & 1; |
|
2107 if (val < min[j]) |
|
2108 min[j] = val; |
|
2109 else if (val > max[j]) |
|
2110 max[j] = val; |
|
2111 } |
|
2112 if (!dest) |
|
2113 dest = new Rectangle(); |
|
2114 return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1], |
|
2115 _dontNotify); |
|
2116 }, |
|
2117 |
|
2118 inverseTransform: function() { |
|
2119 return this._inverseTransform(Point.read(arguments)); |
|
2120 }, |
|
2121 |
|
2122 _getDeterminant: function() { |
|
2123 var det = this._a * this._d - this._b * this._c; |
|
2124 return isFinite(det) && !Numerical.isZero(det) |
|
2125 && isFinite(this._tx) && isFinite(this._ty) |
|
2126 ? det : null; |
|
2127 }, |
|
2128 |
|
2129 _inverseTransform: function(point, dest, _dontNotify) { |
|
2130 var det = this._getDeterminant(); |
|
2131 if (!det) |
|
2132 return null; |
|
2133 var x = point.x - this._tx, |
|
2134 y = point.y - this._ty; |
|
2135 if (!dest) |
|
2136 dest = new Point(); |
|
2137 return dest.set( |
|
2138 (x * this._d - y * this._b) / det, |
|
2139 (y * this._a - x * this._c) / det, |
|
2140 _dontNotify |
|
2141 ); |
|
2142 }, |
|
2143 |
|
2144 decompose: function() { |
|
2145 var a = this._a, b = this._b, c = this._c, d = this._d; |
|
2146 if (Numerical.isZero(a * d - b * c)) |
|
2147 return null; |
|
2148 |
|
2149 var scaleX = Math.sqrt(a * a + b * b); |
|
2150 a /= scaleX; |
|
2151 b /= scaleX; |
|
2152 |
|
2153 var shear = a * c + b * d; |
|
2154 c -= a * shear; |
|
2155 d -= b * shear; |
|
2156 |
|
2157 var scaleY = Math.sqrt(c * c + d * d); |
|
2158 c /= scaleY; |
|
2159 d /= scaleY; |
|
2160 shear /= scaleY; |
|
2161 |
|
2162 if (a * d < b * c) { |
|
2163 a = -a; |
|
2164 b = -b; |
|
2165 shear = -shear; |
|
2166 scaleX = -scaleX; |
|
2167 } |
|
2168 |
|
2169 return { |
|
2170 translation: this.getTranslation(), |
|
2171 scaling: new Point(scaleX, scaleY), |
|
2172 rotation: -Math.atan2(b, a) * 180 / Math.PI, |
|
2173 shearing: shear |
|
2174 }; |
|
2175 }, |
|
2176 |
|
2177 getValues: function() { |
|
2178 return [ this._a, this._c, this._b, this._d, this._tx, this._ty ]; |
|
2179 }, |
|
2180 |
|
2181 getTranslation: function() { |
|
2182 return new Point(this._tx, this._ty); |
|
2183 }, |
|
2184 |
|
2185 setTranslation: function() { |
|
2186 var point = Point.read(arguments); |
|
2187 this._tx = point.x; |
|
2188 this._ty = point.y; |
|
2189 this._changed(); |
|
2190 }, |
|
2191 |
|
2192 getScaling: function() { |
|
2193 return (this.decompose() || {}).scaling; |
|
2194 }, |
|
2195 |
|
2196 setScaling: function() { |
|
2197 var scaling = this.getScaling(); |
|
2198 if (scaling != null) { |
|
2199 var scale = Point.read(arguments); |
|
2200 (this._owner || this).scale( |
|
2201 scale.x / scaling.x, scale.y / scaling.y); |
|
2202 } |
|
2203 }, |
|
2204 |
|
2205 getRotation: function() { |
|
2206 return (this.decompose() || {}).rotation; |
|
2207 }, |
|
2208 |
|
2209 setRotation: function(angle) { |
|
2210 var rotation = this.getRotation(); |
|
2211 if (rotation != null) |
|
2212 (this._owner || this).rotate(angle - rotation); |
|
2213 }, |
|
2214 |
|
2215 inverted: function() { |
|
2216 var det = this._getDeterminant(); |
|
2217 return det && new Matrix( |
|
2218 this._d / det, |
|
2219 -this._c / det, |
|
2220 -this._b / det, |
|
2221 this._a / det, |
|
2222 (this._b * this._ty - this._d * this._tx) / det, |
|
2223 (this._c * this._tx - this._a * this._ty) / det); |
|
2224 }, |
|
2225 |
|
2226 shiftless: function() { |
|
2227 return new Matrix(this._a, this._c, this._b, this._d, 0, 0); |
|
2228 }, |
|
2229 |
|
2230 applyToContext: function(ctx) { |
|
2231 ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty); |
|
2232 } |
|
2233 }, new function() { |
|
2234 return Base.each({ |
|
2235 scaleX: '_a', |
|
2236 scaleY: '_d', |
|
2237 translateX: '_tx', |
|
2238 translateY: '_ty', |
|
2239 shearX: '_b', |
|
2240 shearY: '_c' |
|
2241 }, function(prop, name) { |
|
2242 name = Base.capitalize(name); |
|
2243 this['get' + name] = function() { |
|
2244 return this[prop]; |
|
2245 }; |
|
2246 this['set' + name] = function(value) { |
|
2247 this[prop] = value; |
|
2248 this._changed(); |
|
2249 }; |
|
2250 }, {}); |
|
2251 }); |
|
2252 |
|
2253 var Line = Base.extend({ |
|
2254 _class: 'Line', |
|
2255 |
|
2256 initialize: function Line(arg0, arg1, arg2, arg3, arg4) { |
|
2257 var asVector = false; |
|
2258 if (arguments.length >= 4) { |
|
2259 this._px = arg0; |
|
2260 this._py = arg1; |
|
2261 this._vx = arg2; |
|
2262 this._vy = arg3; |
|
2263 asVector = arg4; |
|
2264 } else { |
|
2265 this._px = arg0.x; |
|
2266 this._py = arg0.y; |
|
2267 this._vx = arg1.x; |
|
2268 this._vy = arg1.y; |
|
2269 asVector = arg2; |
|
2270 } |
|
2271 if (!asVector) { |
|
2272 this._vx -= this._px; |
|
2273 this._vy -= this._py; |
|
2274 } |
|
2275 }, |
|
2276 |
|
2277 getPoint: function() { |
|
2278 return new Point(this._px, this._py); |
|
2279 }, |
|
2280 |
|
2281 getVector: function() { |
|
2282 return new Point(this._vx, this._vy); |
|
2283 }, |
|
2284 |
|
2285 getLength: function() { |
|
2286 return this.getVector().getLength(); |
|
2287 }, |
|
2288 |
|
2289 intersect: function(line, isInfinite) { |
|
2290 return Line.intersect( |
|
2291 this._px, this._py, this._vx, this._vy, |
|
2292 line._px, line._py, line._vx, line._vy, |
|
2293 true, isInfinite); |
|
2294 }, |
|
2295 |
|
2296 getSide: function(point) { |
|
2297 return Line.getSide( |
|
2298 this._px, this._py, this._vx, this._vy, |
|
2299 point.x, point.y, true); |
|
2300 }, |
|
2301 |
|
2302 getDistance: function(point) { |
|
2303 return Math.abs(Line.getSignedDistance( |
|
2304 this._px, this._py, this._vx, this._vy, |
|
2305 point.x, point.y, true)); |
|
2306 }, |
|
2307 |
|
2308 statics: { |
|
2309 intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector, |
|
2310 isInfinite) { |
|
2311 if (!asVector) { |
|
2312 avx -= apx; |
|
2313 avy -= apy; |
|
2314 bvx -= bpx; |
|
2315 bvy -= bpy; |
|
2316 } |
|
2317 var cross = bvy * avx - bvx * avy; |
|
2318 if (!Numerical.isZero(cross)) { |
|
2319 var dx = apx - bpx, |
|
2320 dy = apy - bpy, |
|
2321 ta = (bvx * dy - bvy * dx) / cross, |
|
2322 tb = (avx * dy - avy * dx) / cross; |
|
2323 if ((isInfinite || 0 <= ta && ta <= 1) |
|
2324 && (isInfinite || 0 <= tb && tb <= 1)) |
|
2325 return new Point( |
|
2326 apx + ta * avx, |
|
2327 apy + ta * avy); |
|
2328 } |
|
2329 }, |
|
2330 |
|
2331 getSide: function(px, py, vx, vy, x, y, asVector) { |
|
2332 if (!asVector) { |
|
2333 vx -= px; |
|
2334 vy -= py; |
|
2335 } |
|
2336 var v2x = x - px, |
|
2337 v2y = y - py, |
|
2338 ccw = v2x * vy - v2y * vx; |
|
2339 if (ccw === 0) { |
|
2340 ccw = v2x * vx + v2y * vy; |
|
2341 if (ccw > 0) { |
|
2342 v2x -= vx; |
|
2343 v2y -= vy; |
|
2344 ccw = v2x * vx + v2y * vy; |
|
2345 if (ccw < 0) |
|
2346 ccw = 0; |
|
2347 } |
|
2348 } |
|
2349 return ccw < 0 ? -1 : ccw > 0 ? 1 : 0; |
|
2350 }, |
|
2351 |
|
2352 getSignedDistance: function(px, py, vx, vy, x, y, asVector) { |
|
2353 if (!asVector) { |
|
2354 vx -= px; |
|
2355 vy -= py; |
|
2356 } |
|
2357 var m = vy / vx, |
|
2358 b = py - m * px; |
|
2359 return (y - (m * x) - b) / Math.sqrt(m * m + 1); |
|
2360 } |
|
2361 } |
|
2362 }); |
|
2363 |
|
2364 var Project = PaperScopeItem.extend({ |
|
2365 _class: 'Project', |
|
2366 _list: 'projects', |
|
2367 _reference: 'project', |
|
2368 |
|
2369 initialize: function Project(view) { |
|
2370 PaperScopeItem.call(this, true); |
|
2371 this.layers = []; |
|
2372 this.symbols = []; |
|
2373 this._currentStyle = new Style(); |
|
2374 this.activeLayer = new Layer(); |
|
2375 if (view) |
|
2376 this.view = view instanceof View ? view : View.create(view); |
|
2377 this._selectedItems = {}; |
|
2378 this._selectedItemCount = 0; |
|
2379 this._drawCount = 0; |
|
2380 this.options = {}; |
|
2381 }, |
|
2382 |
|
2383 _serialize: function(options, dictionary) { |
|
2384 return Base.serialize(this.layers, options, true, dictionary); |
|
2385 }, |
|
2386 |
|
2387 clear: function() { |
|
2388 for (var i = this.layers.length - 1; i >= 0; i--) |
|
2389 this.layers[i].remove(); |
|
2390 this.symbols = []; |
|
2391 }, |
|
2392 |
|
2393 isEmpty: function() { |
|
2394 return this.layers.length <= 1 |
|
2395 && (!this.activeLayer || this.activeLayer.isEmpty()); |
|
2396 }, |
|
2397 |
|
2398 remove: function remove() { |
|
2399 if (!remove.base.call(this)) |
|
2400 return false; |
|
2401 if (this.view) |
|
2402 this.view.remove(); |
|
2403 return true; |
|
2404 }, |
|
2405 |
|
2406 getCurrentStyle: function() { |
|
2407 return this._currentStyle; |
|
2408 }, |
|
2409 |
|
2410 setCurrentStyle: function(style) { |
|
2411 this._currentStyle.initialize(style); |
|
2412 }, |
|
2413 |
|
2414 getIndex: function() { |
|
2415 return this._index; |
|
2416 }, |
|
2417 |
|
2418 addChild: function(child) { |
|
2419 if (child instanceof Layer) { |
|
2420 Base.splice(this.layers, [child]); |
|
2421 if (!this.activeLayer) |
|
2422 this.activeLayer = child; |
|
2423 } else if (child instanceof Item) { |
|
2424 (this.activeLayer |
|
2425 || this.addChild(new Layer({ insert: false }))).addChild(child); |
|
2426 } else { |
|
2427 child = null; |
|
2428 } |
|
2429 return child; |
|
2430 }, |
|
2431 |
|
2432 getSelectedItems: function() { |
|
2433 var items = []; |
|
2434 for (var id in this._selectedItems) { |
|
2435 var item = this._selectedItems[id]; |
|
2436 if (item.isInserted()) |
|
2437 items.push(item); |
|
2438 } |
|
2439 return items; |
|
2440 }, |
|
2441 |
|
2442 _updateSelection: function(item) { |
|
2443 var id = item._id, |
|
2444 selectedItems = this._selectedItems; |
|
2445 if (item._selected) { |
|
2446 if (selectedItems[id] !== item) { |
|
2447 this._selectedItemCount++; |
|
2448 selectedItems[id] = item; |
|
2449 } |
|
2450 } else if (selectedItems[id] === item) { |
|
2451 this._selectedItemCount--; |
|
2452 delete selectedItems[id]; |
|
2453 } |
|
2454 }, |
|
2455 |
|
2456 selectAll: function() { |
|
2457 var layers = this.layers; |
|
2458 for (var i = 0, l = layers.length; i < l; i++) |
|
2459 layers[i].setFullySelected(true); |
|
2460 }, |
|
2461 |
|
2462 deselectAll: function() { |
|
2463 var selectedItems = this._selectedItems; |
|
2464 for (var i in selectedItems) |
|
2465 selectedItems[i].setFullySelected(false); |
|
2466 }, |
|
2467 |
|
2468 hitTest: function(point, options) { |
|
2469 point = Point.read(arguments); |
|
2470 options = HitResult.getOptions(Base.read(arguments)); |
|
2471 for (var i = this.layers.length - 1; i >= 0; i--) { |
|
2472 var res = this.layers[i].hitTest(point, options); |
|
2473 if (res) return res; |
|
2474 } |
|
2475 return null; |
|
2476 } |
|
2477 }, new function() { |
|
2478 function getItems(project, match, list) { |
|
2479 var layers = project.layers, |
|
2480 items = list && []; |
|
2481 for (var i = 0, l = layers.length; i < l; i++) { |
|
2482 var res = layers[i][list ? 'getItems' : 'getItem'](match); |
|
2483 if (list) { |
|
2484 items.push.apply(items, res); |
|
2485 } else if (res) |
|
2486 return res; |
|
2487 } |
|
2488 return list ? items : null; |
|
2489 } |
|
2490 |
|
2491 return { |
|
2492 getItems: function(match) { |
|
2493 return getItems(this, match, true); |
|
2494 }, |
|
2495 |
|
2496 getItem: function(match) { |
|
2497 return getItems(this, match, false); |
|
2498 } |
|
2499 }; |
|
2500 }, { |
|
2501 |
|
2502 importJSON: function(json) { |
|
2503 this.activate(); |
|
2504 var layer = this.activeLayer; |
|
2505 return Base.importJSON(json, layer && layer.isEmpty() && layer); |
|
2506 }, |
|
2507 |
|
2508 draw: function(ctx, matrix, ratio) { |
|
2509 this._drawCount++; |
|
2510 ctx.save(); |
|
2511 matrix.applyToContext(ctx); |
|
2512 var param = new Base({ |
|
2513 offset: new Point(0, 0), |
|
2514 ratio: ratio, |
|
2515 transforms: [matrix], |
|
2516 trackTransforms: true |
|
2517 }); |
|
2518 for (var i = 0, l = this.layers.length; i < l; i++) |
|
2519 this.layers[i].draw(ctx, param); |
|
2520 ctx.restore(); |
|
2521 |
|
2522 if (this._selectedItemCount > 0) { |
|
2523 ctx.save(); |
|
2524 ctx.strokeWidth = 1; |
|
2525 for (var id in this._selectedItems) { |
|
2526 var item = this._selectedItems[id]; |
|
2527 if (item._drawCount === this._drawCount |
|
2528 && (item._drawSelected || item._boundsSelected)) { |
|
2529 var color = item.getSelectedColor() |
|
2530 || item.getLayer().getSelectedColor(); |
|
2531 ctx.strokeStyle = ctx.fillStyle = color |
|
2532 ? color.toCanvasStyle(ctx) : '#009dec'; |
|
2533 var mx = item._globalMatrix; |
|
2534 if (item._drawSelected) |
|
2535 item._drawSelected(ctx, mx); |
|
2536 if (item._boundsSelected) { |
|
2537 var coords = mx._transformCorners( |
|
2538 item._getBounds('getBounds')); |
|
2539 ctx.beginPath(); |
|
2540 for (var i = 0; i < 8; i++) |
|
2541 ctx[i === 0 ? 'moveTo' : 'lineTo']( |
|
2542 coords[i], coords[++i]); |
|
2543 ctx.closePath(); |
|
2544 ctx.stroke(); |
|
2545 for (var i = 0; i < 8; i++) { |
|
2546 ctx.beginPath(); |
|
2547 ctx.rect(coords[i] - 2, coords[++i] - 2, 4, 4); |
|
2548 ctx.fill(); |
|
2549 } |
|
2550 } |
|
2551 } |
|
2552 } |
|
2553 ctx.restore(); |
|
2554 } |
|
2555 } |
|
2556 }); |
|
2557 |
|
2558 var Symbol = Base.extend({ |
|
2559 _class: 'Symbol', |
|
2560 |
|
2561 initialize: function Symbol(item, dontCenter) { |
|
2562 this._id = Symbol._id = (Symbol._id || 0) + 1; |
|
2563 this.project = paper.project; |
|
2564 this.project.symbols.push(this); |
|
2565 if (item) |
|
2566 this.setDefinition(item, dontCenter); |
|
2567 this._instances = {}; |
|
2568 }, |
|
2569 |
|
2570 _serialize: function(options, dictionary) { |
|
2571 return dictionary.add(this, function() { |
|
2572 return Base.serialize([this._class, this._definition], |
|
2573 options, false, dictionary); |
|
2574 }); |
|
2575 }, |
|
2576 |
|
2577 _changed: function(flags) { |
|
2578 Base.each(this._instances, function(item) { |
|
2579 item._changed(flags); |
|
2580 }); |
|
2581 }, |
|
2582 |
|
2583 getDefinition: function() { |
|
2584 return this._definition; |
|
2585 }, |
|
2586 |
|
2587 setDefinition: function(item ) { |
|
2588 if (item._parentSymbol) |
|
2589 item = item.clone(); |
|
2590 if (this._definition) |
|
2591 delete this._definition._parentSymbol; |
|
2592 this._definition = item; |
|
2593 item.remove(); |
|
2594 item.setSelected(false); |
|
2595 if (!arguments[1]) |
|
2596 item.setPosition(new Point()); |
|
2597 item._parentSymbol = this; |
|
2598 this._changed(5); |
|
2599 }, |
|
2600 |
|
2601 place: function(position) { |
|
2602 return new PlacedSymbol(this, position); |
|
2603 }, |
|
2604 |
|
2605 clone: function() { |
|
2606 return new Symbol(this._definition.clone(false)); |
|
2607 } |
|
2608 }); |
|
2609 |
|
2610 var Item = Base.extend(Callback, { |
|
2611 statics: { |
|
2612 extend: function extend(src) { |
|
2613 if (src._serializeFields) |
|
2614 src._serializeFields = new Base( |
|
2615 this.prototype._serializeFields, src._serializeFields); |
|
2616 var res = extend.base.apply(this, arguments), |
|
2617 proto = res.prototype, |
|
2618 name = proto._class; |
|
2619 if (name) |
|
2620 proto._type = Base.hyphenate(name); |
|
2621 return res; |
|
2622 } |
|
2623 }, |
|
2624 |
|
2625 _class: 'Item', |
|
2626 _transformContent: true, |
|
2627 _boundsSelected: false, |
|
2628 _serializeFields: { |
|
2629 name: null, |
|
2630 matrix: new Matrix(), |
|
2631 locked: false, |
|
2632 visible: true, |
|
2633 blendMode: 'normal', |
|
2634 opacity: 1, |
|
2635 guide: false, |
|
2636 selected: false, |
|
2637 clipMask: false, |
|
2638 data: {} |
|
2639 }, |
|
2640 |
|
2641 initialize: function Item() { |
|
2642 }, |
|
2643 |
|
2644 _initialize: function(props, point) { |
|
2645 this._id = Item._id = (Item._id || 0) + 1; |
|
2646 if (!this._project) { |
|
2647 var project = paper.project; |
|
2648 if (props && props.insert === false) { |
|
2649 this._setProject(project); |
|
2650 } else { |
|
2651 (project.activeLayer || new Layer()).addChild(this); |
|
2652 } |
|
2653 } |
|
2654 this._style = new Style(this._project._currentStyle, this); |
|
2655 var matrix = this._matrix = new Matrix(); |
|
2656 if (point) |
|
2657 matrix.translate(point); |
|
2658 matrix._owner = this; |
|
2659 return props ? this._set(props, { insert: true }) : true; |
|
2660 }, |
|
2661 |
|
2662 _events: new function() { |
|
2663 |
|
2664 var mouseFlags = { |
|
2665 mousedown: { |
|
2666 mousedown: 1, |
|
2667 mousedrag: 1, |
|
2668 click: 1, |
|
2669 doubleclick: 1 |
|
2670 }, |
|
2671 mouseup: { |
|
2672 mouseup: 1, |
|
2673 mousedrag: 1, |
|
2674 click: 1, |
|
2675 doubleclick: 1 |
|
2676 }, |
|
2677 mousemove: { |
|
2678 mousedrag: 1, |
|
2679 mousemove: 1, |
|
2680 mouseenter: 1, |
|
2681 mouseleave: 1 |
|
2682 } |
|
2683 }; |
|
2684 |
|
2685 var mouseEvent = { |
|
2686 install: function(type) { |
|
2687 var counters = this._project.view._eventCounters; |
|
2688 if (counters) { |
|
2689 for (var key in mouseFlags) { |
|
2690 counters[key] = (counters[key] || 0) |
|
2691 + (mouseFlags[key][type] || 0); |
|
2692 } |
|
2693 } |
|
2694 }, |
|
2695 uninstall: function(type) { |
|
2696 var counters = this._project.view._eventCounters; |
|
2697 if (counters) { |
|
2698 for (var key in mouseFlags) |
|
2699 counters[key] -= mouseFlags[key][type] || 0; |
|
2700 } |
|
2701 } |
|
2702 }; |
|
2703 |
|
2704 return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick', |
|
2705 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'], |
|
2706 function(name) { |
|
2707 this[name] = mouseEvent; |
|
2708 }, { |
|
2709 onFrame: { |
|
2710 install: function() { |
|
2711 this._animateItem(true); |
|
2712 }, |
|
2713 uninstall: function() { |
|
2714 this._animateItem(false); |
|
2715 } |
|
2716 }, |
|
2717 |
|
2718 onLoad: {} |
|
2719 } |
|
2720 ); |
|
2721 }, |
|
2722 |
|
2723 _animateItem: function(animate) { |
|
2724 this._project.view._animateItem(this, animate); |
|
2725 }, |
|
2726 |
|
2727 _serialize: function(options, dictionary) { |
|
2728 var props = {}, |
|
2729 that = this; |
|
2730 |
|
2731 function serialize(fields) { |
|
2732 for (var key in fields) { |
|
2733 var value = that[key]; |
|
2734 if (!Base.equals(value, key === 'leading' |
|
2735 ? fields.fontSize * 1.2 : fields[key])) { |
|
2736 props[key] = Base.serialize(value, options, |
|
2737 key !== 'data', dictionary); |
|
2738 } |
|
2739 } |
|
2740 } |
|
2741 |
|
2742 serialize(this._serializeFields); |
|
2743 if (!(this instanceof Group)) |
|
2744 serialize(this._style._defaults); |
|
2745 return [ this._class, props ]; |
|
2746 }, |
|
2747 |
|
2748 _changed: function(flags) { |
|
2749 var parent = this._parent, |
|
2750 project = this._project, |
|
2751 symbol = this._parentSymbol; |
|
2752 this._drawCount = null; |
|
2753 if (flags & 4) { |
|
2754 delete this._bounds; |
|
2755 delete this._position; |
|
2756 } |
|
2757 if (parent && (flags |
|
2758 & (4 | 8))) { |
|
2759 parent._clearBoundsCache(); |
|
2760 } |
|
2761 if (flags & 2) { |
|
2762 this._clearBoundsCache(); |
|
2763 } |
|
2764 if (project) { |
|
2765 if (flags & 1) { |
|
2766 project._needsRedraw = true; |
|
2767 } |
|
2768 if (project._changes) { |
|
2769 var entry = project._changesById[this._id]; |
|
2770 if (entry) { |
|
2771 entry.flags |= flags; |
|
2772 } else { |
|
2773 entry = { item: this, flags: flags }; |
|
2774 project._changesById[this._id] = entry; |
|
2775 project._changes.push(entry); |
|
2776 } |
|
2777 } |
|
2778 } |
|
2779 if (symbol) |
|
2780 symbol._changed(flags); |
|
2781 }, |
|
2782 |
|
2783 set: function(props) { |
|
2784 if (props) |
|
2785 this._set(props); |
|
2786 return this; |
|
2787 }, |
|
2788 |
|
2789 getId: function() { |
|
2790 return this._id; |
|
2791 }, |
|
2792 |
|
2793 getType: function() { |
|
2794 return this._type; |
|
2795 }, |
|
2796 |
|
2797 getName: function() { |
|
2798 return this._name; |
|
2799 }, |
|
2800 |
|
2801 setName: function(name, unique) { |
|
2802 |
|
2803 if (this._name) |
|
2804 this._removeNamed(); |
|
2805 if (name === (+name) + '') |
|
2806 throw new Error( |
|
2807 'Names consisting only of numbers are not supported.'); |
|
2808 if (name && this._parent) { |
|
2809 var children = this._parent._children, |
|
2810 namedChildren = this._parent._namedChildren, |
|
2811 orig = name, |
|
2812 i = 1; |
|
2813 while (unique && children[name]) |
|
2814 name = orig + ' ' + (i++); |
|
2815 (namedChildren[name] = namedChildren[name] || []).push(this); |
|
2816 children[name] = this; |
|
2817 } |
|
2818 this._name = name || undefined; |
|
2819 this._changed(32); |
|
2820 }, |
|
2821 |
|
2822 getStyle: function() { |
|
2823 return this._style; |
|
2824 }, |
|
2825 |
|
2826 setStyle: function(style) { |
|
2827 this.getStyle().set(style); |
|
2828 }, |
|
2829 |
|
2830 hasFill: function() { |
|
2831 return this.getStyle().hasFill(); |
|
2832 }, |
|
2833 |
|
2834 hasStroke: function() { |
|
2835 return this.getStyle().hasStroke(); |
|
2836 }, |
|
2837 |
|
2838 hasShadow: function() { |
|
2839 return this.getStyle().hasShadow(); |
|
2840 } |
|
2841 }, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'], |
|
2842 function(name) { |
|
2843 var part = Base.capitalize(name), |
|
2844 name = '_' + name; |
|
2845 this['get' + part] = function() { |
|
2846 return this[name]; |
|
2847 }; |
|
2848 this['set' + part] = function(value) { |
|
2849 if (value != this[name]) { |
|
2850 this[name] = value; |
|
2851 this._changed(name === '_locked' |
|
2852 ? 32 : 33); |
|
2853 } |
|
2854 }; |
|
2855 }, {}), { |
|
2856 |
|
2857 _locked: false, |
|
2858 |
|
2859 _visible: true, |
|
2860 |
|
2861 _blendMode: 'normal', |
|
2862 |
|
2863 _opacity: 1, |
|
2864 |
|
2865 _guide: false, |
|
2866 |
|
2867 isSelected: function() { |
|
2868 if (this._children) { |
|
2869 for (var i = 0, l = this._children.length; i < l; i++) |
|
2870 if (this._children[i].isSelected()) |
|
2871 return true; |
|
2872 } |
|
2873 return this._selected; |
|
2874 }, |
|
2875 |
|
2876 setSelected: function(selected ) { |
|
2877 if (this._children && !arguments[1]) { |
|
2878 for (var i = 0, l = this._children.length; i < l; i++) |
|
2879 this._children[i].setSelected(selected); |
|
2880 } |
|
2881 if ((selected = !!selected) != this._selected) { |
|
2882 this._selected = selected; |
|
2883 this._project._updateSelection(this); |
|
2884 this._changed(33); |
|
2885 } |
|
2886 }, |
|
2887 |
|
2888 _selected: false, |
|
2889 |
|
2890 isFullySelected: function() { |
|
2891 if (this._children && this._selected) { |
|
2892 for (var i = 0, l = this._children.length; i < l; i++) |
|
2893 if (!this._children[i].isFullySelected()) |
|
2894 return false; |
|
2895 return true; |
|
2896 } |
|
2897 return this._selected; |
|
2898 }, |
|
2899 |
|
2900 setFullySelected: function(selected) { |
|
2901 if (this._children) { |
|
2902 for (var i = 0, l = this._children.length; i < l; i++) |
|
2903 this._children[i].setFullySelected(selected); |
|
2904 } |
|
2905 this.setSelected(selected, true); |
|
2906 }, |
|
2907 |
|
2908 isClipMask: function() { |
|
2909 return this._clipMask; |
|
2910 }, |
|
2911 |
|
2912 setClipMask: function(clipMask) { |
|
2913 if (this._clipMask != (clipMask = !!clipMask)) { |
|
2914 this._clipMask = clipMask; |
|
2915 if (clipMask) { |
|
2916 this.setFillColor(null); |
|
2917 this.setStrokeColor(null); |
|
2918 } |
|
2919 this._changed(33); |
|
2920 if (this._parent) |
|
2921 this._parent._changed(256); |
|
2922 } |
|
2923 }, |
|
2924 |
|
2925 _clipMask: false, |
|
2926 |
|
2927 getData: function() { |
|
2928 if (!this._data) |
|
2929 this._data = {}; |
|
2930 return this._data; |
|
2931 }, |
|
2932 |
|
2933 setData: function(data) { |
|
2934 this._data = data; |
|
2935 }, |
|
2936 |
|
2937 getPosition: function() { |
|
2938 var pos = this._position |
|
2939 || (this._position = this.getBounds().getCenter(true)); |
|
2940 return new (arguments[0] ? Point : LinkedPoint) |
|
2941 (pos.x, pos.y, this, 'setPosition'); |
|
2942 }, |
|
2943 |
|
2944 setPosition: function() { |
|
2945 this.translate(Point.read(arguments).subtract(this.getPosition(true))); |
|
2946 } |
|
2947 }, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'], |
|
2948 function(name) { |
|
2949 this[name] = function() { |
|
2950 var getter = this._boundsGetter, |
|
2951 bounds = this._getCachedBounds(typeof getter == 'string' |
|
2952 ? getter : getter && getter[name] || name, arguments[0]); |
|
2953 return name === 'getBounds' |
|
2954 ? new LinkedRectangle(bounds.x, bounds.y, bounds.width, |
|
2955 bounds.height, this, 'setBounds') |
|
2956 : bounds; |
|
2957 }; |
|
2958 }, |
|
2959 { |
|
2960 _getCachedBounds: function(getter, matrix, cacheItem) { |
|
2961 var cache = (!matrix || matrix.equals(this._matrix)) && getter; |
|
2962 if (cacheItem && this._parent) { |
|
2963 var id = cacheItem._id, |
|
2964 ref = this._parent._boundsCache |
|
2965 = this._parent._boundsCache || { |
|
2966 ids: {}, |
|
2967 list: [] |
|
2968 }; |
|
2969 if (!ref.ids[id]) { |
|
2970 ref.list.push(cacheItem); |
|
2971 ref.ids[id] = cacheItem; |
|
2972 } |
|
2973 } |
|
2974 if (cache && this._bounds && this._bounds[cache]) |
|
2975 return this._bounds[cache].clone(); |
|
2976 var identity = this._matrix.isIdentity(); |
|
2977 matrix = !matrix || matrix.isIdentity() |
|
2978 ? identity ? null : this._matrix |
|
2979 : identity ? matrix : matrix.clone().concatenate(this._matrix); |
|
2980 var bounds = this._getBounds(getter, matrix, cache ? this : cacheItem); |
|
2981 if (cache) { |
|
2982 if (!this._bounds) |
|
2983 this._bounds = {}; |
|
2984 this._bounds[cache] = bounds.clone(); |
|
2985 } |
|
2986 return bounds; |
|
2987 }, |
|
2988 |
|
2989 _clearBoundsCache: function() { |
|
2990 if (this._boundsCache) { |
|
2991 for (var i = 0, list = this._boundsCache.list, l = list.length; |
|
2992 i < l; i++) { |
|
2993 var item = list[i]; |
|
2994 delete item._bounds; |
|
2995 if (item != this && item._boundsCache) |
|
2996 item._clearBoundsCache(); |
|
2997 } |
|
2998 delete this._boundsCache; |
|
2999 } |
|
3000 }, |
|
3001 |
|
3002 _getBounds: function(getter, matrix, cacheItem) { |
|
3003 var children = this._children; |
|
3004 if (!children || children.length == 0) |
|
3005 return new Rectangle(); |
|
3006 var x1 = Infinity, |
|
3007 x2 = -x1, |
|
3008 y1 = x1, |
|
3009 y2 = x2; |
|
3010 for (var i = 0, l = children.length; i < l; i++) { |
|
3011 var child = children[i]; |
|
3012 if (child._visible && !child.isEmpty()) { |
|
3013 var rect = child._getCachedBounds(getter, matrix, cacheItem); |
|
3014 x1 = Math.min(rect.x, x1); |
|
3015 y1 = Math.min(rect.y, y1); |
|
3016 x2 = Math.max(rect.x + rect.width, x2); |
|
3017 y2 = Math.max(rect.y + rect.height, y2); |
|
3018 } |
|
3019 } |
|
3020 return isFinite(x1) |
|
3021 ? new Rectangle(x1, y1, x2 - x1, y2 - y1) |
|
3022 : new Rectangle(); |
|
3023 }, |
|
3024 |
|
3025 setBounds: function(rect) { |
|
3026 rect = Rectangle.read(arguments); |
|
3027 var bounds = this.getBounds(), |
|
3028 matrix = new Matrix(), |
|
3029 center = rect.getCenter(); |
|
3030 matrix.translate(center); |
|
3031 if (rect.width != bounds.width || rect.height != bounds.height) { |
|
3032 matrix.scale( |
|
3033 bounds.width != 0 ? rect.width / bounds.width : 1, |
|
3034 bounds.height != 0 ? rect.height / bounds.height : 1); |
|
3035 } |
|
3036 center = bounds.getCenter(); |
|
3037 matrix.translate(-center.x, -center.y); |
|
3038 this.transform(matrix); |
|
3039 } |
|
3040 |
|
3041 }), { |
|
3042 getMatrix: function() { |
|
3043 return this._matrix; |
|
3044 }, |
|
3045 |
|
3046 setMatrix: function(matrix) { |
|
3047 this._matrix.initialize(matrix); |
|
3048 if (this._transformContent) |
|
3049 this.applyMatrix(true); |
|
3050 this._changed(5); |
|
3051 }, |
|
3052 |
|
3053 getGlobalMatrix: function() { |
|
3054 return this._drawCount === this._project._drawCount |
|
3055 && this._globalMatrix || null; |
|
3056 }, |
|
3057 |
|
3058 getTransformContent: function() { |
|
3059 return this._transformContent; |
|
3060 }, |
|
3061 |
|
3062 setTransformContent: function(transform) { |
|
3063 this._transformContent = transform; |
|
3064 if (transform) |
|
3065 this.applyMatrix(); |
|
3066 }, |
|
3067 |
|
3068 getProject: function() { |
|
3069 return this._project; |
|
3070 }, |
|
3071 |
|
3072 _setProject: function(project) { |
|
3073 if (this._project != project) { |
|
3074 var hasOnFrame = this.responds('frame'); |
|
3075 if (hasOnFrame) |
|
3076 this._animateItem(false); |
|
3077 this._project = project; |
|
3078 if (hasOnFrame) |
|
3079 this._animateItem(true); |
|
3080 if (this._children) { |
|
3081 for (var i = 0, l = this._children.length; i < l; i++) { |
|
3082 this._children[i]._setProject(project); |
|
3083 } |
|
3084 } |
|
3085 } |
|
3086 }, |
|
3087 |
|
3088 getLayer: function() { |
|
3089 var parent = this; |
|
3090 while (parent = parent._parent) { |
|
3091 if (parent instanceof Layer) |
|
3092 return parent; |
|
3093 } |
|
3094 return null; |
|
3095 }, |
|
3096 |
|
3097 getParent: function() { |
|
3098 return this._parent; |
|
3099 }, |
|
3100 |
|
3101 setParent: function(item) { |
|
3102 return item.addChild(this); |
|
3103 }, |
|
3104 |
|
3105 getChildren: function() { |
|
3106 return this._children; |
|
3107 }, |
|
3108 |
|
3109 setChildren: function(items) { |
|
3110 this.removeChildren(); |
|
3111 this.addChildren(items); |
|
3112 }, |
|
3113 |
|
3114 getFirstChild: function() { |
|
3115 return this._children && this._children[0] || null; |
|
3116 }, |
|
3117 |
|
3118 getLastChild: function() { |
|
3119 return this._children && this._children[this._children.length - 1] |
|
3120 || null; |
|
3121 }, |
|
3122 |
|
3123 getNextSibling: function() { |
|
3124 return this._parent && this._parent._children[this._index + 1] || null; |
|
3125 }, |
|
3126 |
|
3127 getPreviousSibling: function() { |
|
3128 return this._parent && this._parent._children[this._index - 1] || null; |
|
3129 }, |
|
3130 |
|
3131 getIndex: function() { |
|
3132 return this._index; |
|
3133 }, |
|
3134 |
|
3135 isInserted: function() { |
|
3136 return this._parent ? this._parent.isInserted() : false; |
|
3137 }, |
|
3138 |
|
3139 equals: function(item) { |
|
3140 return item === this || item && this._class === item._class |
|
3141 && this._style.equals(item._style) |
|
3142 && this._matrix.equals(item._matrix) |
|
3143 && this._locked === item._locked |
|
3144 && this._visible === item._visible |
|
3145 && this._blendMode === item._blendMode |
|
3146 && this._opacity === item._opacity |
|
3147 && this._clipMask === item._clipMask |
|
3148 && this._guide === item._guide |
|
3149 && this._equals(item) |
|
3150 || false; |
|
3151 }, |
|
3152 |
|
3153 _equals: function(item) { |
|
3154 return Base.equals(this._children, item._children); |
|
3155 }, |
|
3156 |
|
3157 clone: function(insert) { |
|
3158 return this._clone(new this.constructor({ insert: false }), insert); |
|
3159 }, |
|
3160 |
|
3161 _clone: function(copy, insert) { |
|
3162 copy.setStyle(this._style); |
|
3163 if (this._children) { |
|
3164 for (var i = 0, l = this._children.length; i < l; i++) |
|
3165 copy.addChild(this._children[i].clone(false), true); |
|
3166 } |
|
3167 if (insert || insert === undefined) |
|
3168 copy.insertAbove(this); |
|
3169 var keys = ['_locked', '_visible', '_blendMode', '_opacity', |
|
3170 '_clipMask', '_guide']; |
|
3171 for (var i = 0, l = keys.length; i < l; i++) { |
|
3172 var key = keys[i]; |
|
3173 if (this.hasOwnProperty(key)) |
|
3174 copy[key] = this[key]; |
|
3175 } |
|
3176 copy._matrix.initialize(this._matrix); |
|
3177 copy._data = this._data ? Base.clone(this._data) : null; |
|
3178 copy.setSelected(this._selected); |
|
3179 if (this._name) |
|
3180 copy.setName(this._name, true); |
|
3181 return copy; |
|
3182 }, |
|
3183 |
|
3184 copyTo: function(itemOrProject) { |
|
3185 return itemOrProject.addChild(this.clone(false)); |
|
3186 }, |
|
3187 |
|
3188 rasterize: function(resolution) { |
|
3189 var bounds = this.getStrokeBounds(), |
|
3190 scale = (resolution || 72) / 72, |
|
3191 topLeft = bounds.getTopLeft().floor(), |
|
3192 bottomRight = bounds.getBottomRight().ceil() |
|
3193 size = new Size(bottomRight.subtract(topLeft)), |
|
3194 canvas = CanvasProvider.getCanvas(size), |
|
3195 ctx = canvas.getContext('2d'), |
|
3196 matrix = new Matrix().scale(scale).translate(topLeft.negate()); |
|
3197 ctx.save(); |
|
3198 matrix.applyToContext(ctx); |
|
3199 this.draw(ctx, new Base({ transforms: [matrix] })); |
|
3200 ctx.restore(); |
|
3201 var raster = new Raster({ |
|
3202 canvas: canvas, |
|
3203 insert: false |
|
3204 }); |
|
3205 raster.setPosition(topLeft.add(size.divide(2))); |
|
3206 raster.insertAbove(this); |
|
3207 return raster; |
|
3208 }, |
|
3209 |
|
3210 contains: function() { |
|
3211 return !!this._contains( |
|
3212 this._matrix._inverseTransform(Point.read(arguments))); |
|
3213 }, |
|
3214 |
|
3215 _contains: function(point) { |
|
3216 if (this._children) { |
|
3217 for (var i = this._children.length - 1; i >= 0; i--) { |
|
3218 if (this._children[i].contains(point)) |
|
3219 return true; |
|
3220 } |
|
3221 return false; |
|
3222 } |
|
3223 return point.isInside(this._getBounds('getBounds')); |
|
3224 }, |
|
3225 |
|
3226 hitTest: function(point, options) { |
|
3227 point = Point.read(arguments); |
|
3228 options = HitResult.getOptions(Base.read(arguments)); |
|
3229 |
|
3230 if (this._locked || !this._visible || this._guide && !options.guides) |
|
3231 return null; |
|
3232 |
|
3233 if (!this._children && !this.getRoughBounds() |
|
3234 .expand(2 * options.tolerance)._containsPoint(point)) |
|
3235 return null; |
|
3236 point = this._matrix._inverseTransform(point); |
|
3237 |
|
3238 var that = this, |
|
3239 res; |
|
3240 function checkBounds(type, part) { |
|
3241 var pt = bounds['get' + part](); |
|
3242 if (point.getDistance(pt) < options.tolerance) |
|
3243 return new HitResult(type, that, |
|
3244 { name: Base.hyphenate(part), point: pt }); |
|
3245 } |
|
3246 |
|
3247 if ((options.center || options.bounds) && |
|
3248 !(this instanceof Layer && !this._parent)) { |
|
3249 var bounds = this._getBounds('getBounds'); |
|
3250 if (options.center) |
|
3251 res = checkBounds('center', 'Center'); |
|
3252 if (!res && options.bounds) { |
|
3253 var points = [ |
|
3254 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', |
|
3255 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter' |
|
3256 ]; |
|
3257 for (var i = 0; i < 8 && !res; i++) |
|
3258 res = checkBounds('bounds', points[i]); |
|
3259 } |
|
3260 } |
|
3261 |
|
3262 if ((res || (res = this._children || !(options.guides && !this._guide |
|
3263 || options.selected && !this._selected) |
|
3264 ? this._hitTest(point, options) : null)) |
|
3265 && res.point) { |
|
3266 res.point = that._matrix.transform(res.point); |
|
3267 } |
|
3268 return res; |
|
3269 }, |
|
3270 |
|
3271 _hitTest: function(point, options) { |
|
3272 var children = this._children; |
|
3273 if (children) { |
|
3274 for (var i = children.length - 1, res; i >= 0; i--) |
|
3275 if (res = children[i].hitTest(point, options)) |
|
3276 return res; |
|
3277 } else if (options.fill && this.hasFill() && this._contains(point)) { |
|
3278 return new HitResult('fill', this); |
|
3279 } |
|
3280 }, |
|
3281 |
|
3282 matches: function(match) { |
|
3283 function matchObject(obj1, obj2) { |
|
3284 for (var i in obj1) { |
|
3285 if (obj1.hasOwnProperty(i)) { |
|
3286 var val1 = obj1[i], |
|
3287 val2 = obj2[i]; |
|
3288 if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) { |
|
3289 if (!matchObject(val1, val2)) |
|
3290 return false; |
|
3291 } else if (!Base.equals(val1, val2)) { |
|
3292 return false; |
|
3293 } |
|
3294 } |
|
3295 } |
|
3296 return true; |
|
3297 } |
|
3298 for (var key in match) { |
|
3299 if (match.hasOwnProperty(key)) { |
|
3300 var value = this[key], |
|
3301 compare = match[key]; |
|
3302 if (compare instanceof RegExp) { |
|
3303 if (!compare.test(value)) |
|
3304 return false; |
|
3305 } else if (typeof compare === 'function') { |
|
3306 if (!compare(value)) |
|
3307 return false; |
|
3308 } else if (Base.isPlainObject(compare)) { |
|
3309 if (!matchObject(compare, value)) |
|
3310 return false; |
|
3311 } else if (!Base.equals(value, compare)) { |
|
3312 return false; |
|
3313 } |
|
3314 } |
|
3315 } |
|
3316 return true; |
|
3317 } |
|
3318 }, new function() { |
|
3319 function getItems(item, match, list) { |
|
3320 var children = item._children, |
|
3321 items = list && []; |
|
3322 for (var i = 0, l = children && children.length; i < l; i++) { |
|
3323 var child = children[i]; |
|
3324 if (child.matches(match)) { |
|
3325 if (list) { |
|
3326 items.push(child); |
|
3327 } else { |
|
3328 return child; |
|
3329 } |
|
3330 } |
|
3331 var res = getItems(child, match, list); |
|
3332 if (list) { |
|
3333 items.push.apply(items, res); |
|
3334 } else if (res) { |
|
3335 return res; |
|
3336 } |
|
3337 } |
|
3338 return list ? items : null; |
|
3339 } |
|
3340 |
|
3341 return { |
|
3342 getItems: function(match) { |
|
3343 return getItems(this, match, true); |
|
3344 }, |
|
3345 |
|
3346 getItem: function(match) { |
|
3347 return getItems(this, match, false); |
|
3348 } |
|
3349 }; |
|
3350 }, { |
|
3351 |
|
3352 importJSON: function(json) { |
|
3353 var res = Base.importJSON(json, this); |
|
3354 return res !== this |
|
3355 ? this.addChild(res) |
|
3356 : res; |
|
3357 }, |
|
3358 |
|
3359 addChild: function(item, _preserve) { |
|
3360 return this.insertChild(undefined, item, _preserve); |
|
3361 }, |
|
3362 |
|
3363 insertChild: function(index, item, _preserve) { |
|
3364 var res = this.insertChildren(index, [item], _preserve); |
|
3365 return res && res[0]; |
|
3366 }, |
|
3367 |
|
3368 addChildren: function(items, _preserve) { |
|
3369 return this.insertChildren(this._children.length, items, _preserve); |
|
3370 }, |
|
3371 |
|
3372 insertChildren: function(index, items, _preserve, _type) { |
|
3373 var children = this._children; |
|
3374 if (children && items && items.length > 0) { |
|
3375 items = Array.prototype.slice.apply(items); |
|
3376 for (var i = items.length - 1; i >= 0; i--) { |
|
3377 var item = items[i]; |
|
3378 if (_type && item._type !== _type) |
|
3379 items.splice(i, 1); |
|
3380 else |
|
3381 item._remove(true); |
|
3382 } |
|
3383 Base.splice(children, items, index, 0); |
|
3384 for (var i = 0, l = items.length; i < l; i++) { |
|
3385 var item = items[i]; |
|
3386 item._parent = this; |
|
3387 item._setProject(this._project); |
|
3388 if (item._name) |
|
3389 item.setName(item._name); |
|
3390 } |
|
3391 this._changed(7); |
|
3392 } else { |
|
3393 items = null; |
|
3394 } |
|
3395 return items; |
|
3396 }, |
|
3397 |
|
3398 _insert: function(above, item, _preserve) { |
|
3399 if (!item._parent) |
|
3400 return null; |
|
3401 var index = item._index + (above ? 1 : 0); |
|
3402 if (item._parent === this._parent && index > this._index) |
|
3403 index--; |
|
3404 return item._parent.insertChild(index, this, _preserve); |
|
3405 }, |
|
3406 |
|
3407 insertAbove: function(item, _preserve) { |
|
3408 return this._insert(true, item, _preserve); |
|
3409 }, |
|
3410 |
|
3411 insertBelow: function(item, _preserve) { |
|
3412 return this._insert(false, item, _preserve); |
|
3413 }, |
|
3414 |
|
3415 sendToBack: function() { |
|
3416 return this._parent.insertChild(0, this); |
|
3417 }, |
|
3418 |
|
3419 bringToFront: function() { |
|
3420 return this._parent.addChild(this); |
|
3421 }, |
|
3422 |
|
3423 appendTop: '#addChild', |
|
3424 |
|
3425 appendBottom: function(item) { |
|
3426 return this.insertChild(0, item); |
|
3427 }, |
|
3428 |
|
3429 moveAbove: '#insertAbove', |
|
3430 |
|
3431 moveBelow: '#insertBelow', |
|
3432 |
|
3433 reduce: function() { |
|
3434 if (this._children && this._children.length === 1) { |
|
3435 var child = this._children[0]; |
|
3436 child.insertAbove(this); |
|
3437 child.setStyle(this._style); |
|
3438 this.remove(); |
|
3439 return child; |
|
3440 } |
|
3441 return this; |
|
3442 }, |
|
3443 |
|
3444 _removeNamed: function() { |
|
3445 var children = this._parent._children, |
|
3446 namedChildren = this._parent._namedChildren, |
|
3447 name = this._name, |
|
3448 namedArray = namedChildren[name], |
|
3449 index = namedArray ? namedArray.indexOf(this) : -1; |
|
3450 if (index == -1) |
|
3451 return; |
|
3452 if (children[name] == this) |
|
3453 delete children[name]; |
|
3454 namedArray.splice(index, 1); |
|
3455 if (namedArray.length) { |
|
3456 children[name] = namedArray[namedArray.length - 1]; |
|
3457 } else { |
|
3458 delete namedChildren[name]; |
|
3459 } |
|
3460 }, |
|
3461 |
|
3462 _remove: function(notify) { |
|
3463 if (this._parent) { |
|
3464 if (this._name) |
|
3465 this._removeNamed(); |
|
3466 if (this._index != null) |
|
3467 Base.splice(this._parent._children, null, this._index, 1); |
|
3468 if (this.responds('frame')) |
|
3469 this._animateItem(false); |
|
3470 if (notify) |
|
3471 this._parent._changed(7); |
|
3472 this._parent = null; |
|
3473 return true; |
|
3474 } |
|
3475 return false; |
|
3476 }, |
|
3477 |
|
3478 remove: function() { |
|
3479 return this._remove(true); |
|
3480 }, |
|
3481 |
|
3482 removeChildren: function(from, to) { |
|
3483 if (!this._children) |
|
3484 return null; |
|
3485 from = from || 0; |
|
3486 to = Base.pick(to, this._children.length); |
|
3487 var removed = Base.splice(this._children, null, from, to - from); |
|
3488 for (var i = removed.length - 1; i >= 0; i--) |
|
3489 removed[i]._remove(false); |
|
3490 if (removed.length > 0) |
|
3491 this._changed(7); |
|
3492 return removed; |
|
3493 }, |
|
3494 |
|
3495 clear: '#removeChildren', |
|
3496 |
|
3497 reverseChildren: function() { |
|
3498 if (this._children) { |
|
3499 this._children.reverse(); |
|
3500 for (var i = 0, l = this._children.length; i < l; i++) |
|
3501 this._children[i]._index = i; |
|
3502 this._changed(7); |
|
3503 } |
|
3504 }, |
|
3505 |
|
3506 isEmpty: function() { |
|
3507 return !this._children || this._children.length == 0; |
|
3508 }, |
|
3509 |
|
3510 isEditable: function() { |
|
3511 var item = this; |
|
3512 while (item) { |
|
3513 if (!item._visible || item._locked) |
|
3514 return false; |
|
3515 item = item._parent; |
|
3516 } |
|
3517 return true; |
|
3518 }, |
|
3519 |
|
3520 _getOrder: function(item) { |
|
3521 function getList(item) { |
|
3522 var list = []; |
|
3523 do { |
|
3524 list.unshift(item); |
|
3525 } while (item = item._parent); |
|
3526 return list; |
|
3527 } |
|
3528 var list1 = getList(this), |
|
3529 list2 = getList(item); |
|
3530 for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) { |
|
3531 if (list1[i] != list2[i]) { |
|
3532 return list1[i]._index < list2[i]._index ? 1 : -1; |
|
3533 } |
|
3534 } |
|
3535 return 0; |
|
3536 }, |
|
3537 |
|
3538 hasChildren: function() { |
|
3539 return this._children && this._children.length > 0; |
|
3540 }, |
|
3541 |
|
3542 isAbove: function(item) { |
|
3543 return this._getOrder(item) === -1; |
|
3544 }, |
|
3545 |
|
3546 isBelow: function(item) { |
|
3547 return this._getOrder(item) === 1; |
|
3548 }, |
|
3549 |
|
3550 isParent: function(item) { |
|
3551 return this._parent === item; |
|
3552 }, |
|
3553 |
|
3554 isChild: function(item) { |
|
3555 return item && item._parent === this; |
|
3556 }, |
|
3557 |
|
3558 isDescendant: function(item) { |
|
3559 var parent = this; |
|
3560 while (parent = parent._parent) { |
|
3561 if (parent == item) |
|
3562 return true; |
|
3563 } |
|
3564 return false; |
|
3565 }, |
|
3566 |
|
3567 isAncestor: function(item) { |
|
3568 return item ? item.isDescendant(this) : false; |
|
3569 }, |
|
3570 |
|
3571 isGroupedWith: function(item) { |
|
3572 var parent = this._parent; |
|
3573 while (parent) { |
|
3574 if (parent._parent |
|
3575 && /^(group|layer|compound-path)$/.test(parent._type) |
|
3576 && item.isDescendant(parent)) |
|
3577 return true; |
|
3578 parent = parent._parent; |
|
3579 } |
|
3580 return false; |
|
3581 }, |
|
3582 |
|
3583 scale: function(hor, ver , center) { |
|
3584 if (arguments.length < 2 || typeof ver === 'object') { |
|
3585 center = ver; |
|
3586 ver = hor; |
|
3587 } |
|
3588 return this.transform(new Matrix().scale(hor, ver, |
|
3589 center || this.getPosition(true))); |
|
3590 }, |
|
3591 |
|
3592 translate: function() { |
|
3593 var mx = new Matrix(); |
|
3594 return this.transform(mx.translate.apply(mx, arguments)); |
|
3595 }, |
|
3596 |
|
3597 rotate: function(angle, center) { |
|
3598 return this.transform(new Matrix().rotate(angle, |
|
3599 center || this.getPosition(true))); |
|
3600 }, |
|
3601 |
|
3602 shear: function(hor, ver, center) { |
|
3603 if (arguments.length < 2 || typeof ver === 'object') { |
|
3604 center = ver; |
|
3605 ver = hor; |
|
3606 } |
|
3607 return this.transform(new Matrix().shear(hor, ver, |
|
3608 center || this.getPosition(true))); |
|
3609 }, |
|
3610 |
|
3611 transform: function(matrix ) { |
|
3612 var bounds = this._bounds, |
|
3613 position = this._position; |
|
3614 this._matrix.preConcatenate(matrix); |
|
3615 if (this._transformContent || arguments[1]) |
|
3616 this.applyMatrix(true); |
|
3617 this._changed(5); |
|
3618 if (bounds && matrix.getRotation() % 90 === 0) { |
|
3619 for (var key in bounds) { |
|
3620 var rect = bounds[key]; |
|
3621 matrix._transformBounds(rect, rect); |
|
3622 } |
|
3623 var getter = this._boundsGetter, |
|
3624 rect = bounds[getter && getter.getBounds || getter || 'getBounds']; |
|
3625 if (rect) |
|
3626 this._position = rect.getCenter(true); |
|
3627 this._bounds = bounds; |
|
3628 } else if (position) { |
|
3629 this._position = matrix._transformPoint(position, position); |
|
3630 } |
|
3631 return this; |
|
3632 }, |
|
3633 |
|
3634 _applyMatrix: function(matrix, applyMatrix) { |
|
3635 var children = this._children; |
|
3636 if (children && children.length > 0) { |
|
3637 for (var i = 0, l = children.length; i < l; i++) |
|
3638 children[i].transform(matrix, applyMatrix); |
|
3639 return true; |
|
3640 } |
|
3641 }, |
|
3642 |
|
3643 applyMatrix: function(_dontNotify) { |
|
3644 var matrix = this._matrix; |
|
3645 if (this._applyMatrix(matrix, true)) { |
|
3646 var style = this._style, |
|
3647 fillColor = style.getFillColor(true), |
|
3648 strokeColor = style.getStrokeColor(true); |
|
3649 if (fillColor) |
|
3650 fillColor.transform(matrix); |
|
3651 if (strokeColor) |
|
3652 strokeColor.transform(matrix); |
|
3653 matrix.reset(); |
|
3654 } |
|
3655 if (!_dontNotify) |
|
3656 this._changed(5); |
|
3657 }, |
|
3658 |
|
3659 globalToLocal: function() { |
|
3660 var matrix = this.getGlobalMatrix(); |
|
3661 return matrix && matrix._transformPoint(Point.read(arguments)); |
|
3662 }, |
|
3663 |
|
3664 localToGlobal: function() { |
|
3665 var matrix = this.getGlobalMatrix(); |
|
3666 return matrix && matrix._inverseTransform(Point.read(arguments)); |
|
3667 }, |
|
3668 |
|
3669 fitBounds: function(rectangle, fill) { |
|
3670 rectangle = Rectangle.read(arguments); |
|
3671 var bounds = this.getBounds(), |
|
3672 itemRatio = bounds.height / bounds.width, |
|
3673 rectRatio = rectangle.height / rectangle.width, |
|
3674 scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio) |
|
3675 ? rectangle.width / bounds.width |
|
3676 : rectangle.height / bounds.height, |
|
3677 newBounds = new Rectangle(new Point(), |
|
3678 new Size(bounds.width * scale, bounds.height * scale)); |
|
3679 newBounds.setCenter(rectangle.getCenter()); |
|
3680 this.setBounds(newBounds); |
|
3681 }, |
|
3682 |
|
3683 _setStyles: function(ctx) { |
|
3684 var style = this._style, |
|
3685 fillColor = style.getFillColor(), |
|
3686 strokeColor = style.getStrokeColor(), |
|
3687 shadowColor = style.getShadowColor(); |
|
3688 if (fillColor) |
|
3689 ctx.fillStyle = fillColor.toCanvasStyle(ctx); |
|
3690 if (strokeColor) { |
|
3691 var strokeWidth = style.getStrokeWidth(); |
|
3692 if (strokeWidth > 0) { |
|
3693 ctx.strokeStyle = strokeColor.toCanvasStyle(ctx); |
|
3694 ctx.lineWidth = strokeWidth; |
|
3695 var strokeJoin = style.getStrokeJoin(), |
|
3696 strokeCap = style.getStrokeCap(), |
|
3697 miterLimit = style.getMiterLimit(); |
|
3698 if (strokeJoin) |
|
3699 ctx.lineJoin = strokeJoin; |
|
3700 if (strokeCap) |
|
3701 ctx.lineCap = strokeCap; |
|
3702 if (miterLimit) |
|
3703 ctx.miterLimit = miterLimit; |
|
3704 if (paper.support.nativeDash) { |
|
3705 var dashArray = style.getDashArray(), |
|
3706 dashOffset = style.getDashOffset(); |
|
3707 if (dashArray && dashArray.length) { |
|
3708 if ('setLineDash' in ctx) { |
|
3709 ctx.setLineDash(dashArray); |
|
3710 ctx.lineDashOffset = dashOffset; |
|
3711 } else { |
|
3712 ctx.mozDash = dashArray; |
|
3713 ctx.mozDashOffset = dashOffset; |
|
3714 } |
|
3715 } |
|
3716 } |
|
3717 } |
|
3718 } |
|
3719 if (shadowColor) { |
|
3720 var shadowBlur = style.getShadowBlur(); |
|
3721 if (shadowBlur > 0) { |
|
3722 ctx.shadowColor = shadowColor.toCanvasStyle(ctx); |
|
3723 ctx.shadowBlur = shadowBlur; |
|
3724 var offset = this.getShadowOffset(); |
|
3725 ctx.shadowOffsetX = offset.x; |
|
3726 ctx.shadowOffsetY = offset.y; |
|
3727 } |
|
3728 } |
|
3729 }, |
|
3730 |
|
3731 draw: function(ctx, param) { |
|
3732 if (!this._visible || this._opacity === 0) |
|
3733 return; |
|
3734 this._drawCount = this._project._drawCount; |
|
3735 var trackTransforms = param.trackTransforms, |
|
3736 transforms = param.transforms, |
|
3737 parentMatrix = transforms[transforms.length - 1], |
|
3738 globalMatrix = parentMatrix.clone().concatenate(this._matrix); |
|
3739 if (trackTransforms) |
|
3740 transforms.push(this._globalMatrix = globalMatrix); |
|
3741 var blendMode = this._blendMode, |
|
3742 opacity = this._opacity, |
|
3743 normalBlend = blendMode === 'normal', |
|
3744 nativeBlend = BlendMode.nativeModes[blendMode], |
|
3745 direct = normalBlend && opacity === 1 |
|
3746 || (nativeBlend || normalBlend && opacity < 1) |
|
3747 && this._canComposite(), |
|
3748 mainCtx, itemOffset, prevOffset; |
|
3749 if (!direct) { |
|
3750 var bounds = this.getStrokeBounds(parentMatrix); |
|
3751 if (!bounds.width || !bounds.height) |
|
3752 return; |
|
3753 prevOffset = param.offset; |
|
3754 itemOffset = param.offset = bounds.getTopLeft().floor(); |
|
3755 mainCtx = ctx; |
|
3756 ctx = CanvasProvider.getContext( |
|
3757 bounds.getSize().ceil().add(new Size(1, 1)), param.ratio); |
|
3758 } |
|
3759 ctx.save(); |
|
3760 if (direct) { |
|
3761 ctx.globalAlpha = opacity; |
|
3762 if (nativeBlend) |
|
3763 ctx.globalCompositeOperation = blendMode; |
|
3764 } else { |
|
3765 ctx.translate(-itemOffset.x, -itemOffset.y); |
|
3766 } |
|
3767 (direct ? this._matrix : globalMatrix).applyToContext(ctx); |
|
3768 if (!direct && param.clipItem) |
|
3769 param.clipItem.draw(ctx, param.extend({ clip: true })); |
|
3770 this._draw(ctx, param); |
|
3771 ctx.restore(); |
|
3772 if (trackTransforms) |
|
3773 transforms.pop(); |
|
3774 if (param.clip) |
|
3775 ctx.clip(); |
|
3776 if (!direct) { |
|
3777 BlendMode.process(blendMode, ctx, mainCtx, opacity, |
|
3778 itemOffset.subtract(prevOffset).multiply(param.ratio)); |
|
3779 CanvasProvider.release(ctx); |
|
3780 param.offset = prevOffset; |
|
3781 } |
|
3782 }, |
|
3783 |
|
3784 _canComposite: function() { |
|
3785 return false; |
|
3786 } |
|
3787 }, Base.each(['down', 'drag', 'up', 'move'], function(name) { |
|
3788 this['removeOn' + Base.capitalize(name)] = function() { |
|
3789 var hash = {}; |
|
3790 hash[name] = true; |
|
3791 return this.removeOn(hash); |
|
3792 }; |
|
3793 }, { |
|
3794 |
|
3795 removeOn: function(obj) { |
|
3796 for (var name in obj) { |
|
3797 if (obj[name]) { |
|
3798 var key = 'mouse' + name, |
|
3799 project = this._project, |
|
3800 sets = project._removeSets = project._removeSets || {}; |
|
3801 sets[key] = sets[key] || {}; |
|
3802 sets[key][this._id] = this; |
|
3803 } |
|
3804 } |
|
3805 return this; |
|
3806 } |
|
3807 })); |
|
3808 |
|
3809 var Group = Item.extend({ |
|
3810 _class: 'Group', |
|
3811 _serializeFields: { |
|
3812 children: [] |
|
3813 }, |
|
3814 |
|
3815 initialize: function Group(arg) { |
|
3816 this._children = []; |
|
3817 this._namedChildren = {}; |
|
3818 if (!this._initialize(arg)) |
|
3819 this.addChildren(Array.isArray(arg) ? arg : arguments); |
|
3820 }, |
|
3821 |
|
3822 _changed: function _changed(flags) { |
|
3823 _changed.base.call(this, flags); |
|
3824 if (flags & 2 && this._transformContent |
|
3825 && !this._matrix.isIdentity()) { |
|
3826 this.applyMatrix(); |
|
3827 } |
|
3828 if (flags & (2 | 256)) { |
|
3829 delete this._clipItem; |
|
3830 } |
|
3831 }, |
|
3832 |
|
3833 _getClipItem: function() { |
|
3834 if (this._clipItem !== undefined) |
|
3835 return this._clipItem; |
|
3836 for (var i = 0, l = this._children.length; i < l; i++) { |
|
3837 var child = this._children[i]; |
|
3838 if (child._clipMask) |
|
3839 return this._clipItem = child; |
|
3840 } |
|
3841 return this._clipItem = null; |
|
3842 }, |
|
3843 |
|
3844 isClipped: function() { |
|
3845 return !!this._getClipItem(); |
|
3846 }, |
|
3847 |
|
3848 setClipped: function(clipped) { |
|
3849 var child = this.getFirstChild(); |
|
3850 if (child) |
|
3851 child.setClipMask(clipped); |
|
3852 }, |
|
3853 |
|
3854 _draw: function(ctx, param) { |
|
3855 var clipItem = param.clipItem = this._getClipItem(); |
|
3856 if (clipItem) |
|
3857 clipItem.draw(ctx, param.extend({ clip: true })); |
|
3858 for (var i = 0, l = this._children.length; i < l; i++) { |
|
3859 var item = this._children[i]; |
|
3860 if (item !== clipItem) |
|
3861 item.draw(ctx, param); |
|
3862 } |
|
3863 param.clipItem = null; |
|
3864 } |
|
3865 }); |
|
3866 |
|
3867 var Layer = Group.extend({ |
|
3868 _class: 'Layer', |
|
3869 |
|
3870 initialize: function Layer(arg) { |
|
3871 var props = Base.isPlainObject(arg) |
|
3872 ? new Base(arg) |
|
3873 : { children: Array.isArray(arg) ? arg : arguments }, |
|
3874 insert = props.insert; |
|
3875 props.insert = false; |
|
3876 Group.call(this, props); |
|
3877 if (insert || insert === undefined) { |
|
3878 this._project.addChild(this); |
|
3879 this.activate(); |
|
3880 } |
|
3881 }, |
|
3882 |
|
3883 _remove: function _remove(notify) { |
|
3884 if (this._parent) |
|
3885 return _remove.base.call(this, notify); |
|
3886 if (this._index != null) { |
|
3887 if (this._project.activeLayer === this) |
|
3888 this._project.activeLayer = this.getNextSibling() |
|
3889 || this.getPreviousSibling(); |
|
3890 Base.splice(this._project.layers, null, this._index, 1); |
|
3891 this._project._needsRedraw = true; |
|
3892 return true; |
|
3893 } |
|
3894 return false; |
|
3895 }, |
|
3896 |
|
3897 getNextSibling: function getNextSibling() { |
|
3898 return this._parent ? getNextSibling.base.call(this) |
|
3899 : this._project.layers[this._index + 1] || null; |
|
3900 }, |
|
3901 |
|
3902 getPreviousSibling: function getPreviousSibling() { |
|
3903 return this._parent ? getPreviousSibling.base.call(this) |
|
3904 : this._project.layers[this._index - 1] || null; |
|
3905 }, |
|
3906 |
|
3907 isInserted: function isInserted() { |
|
3908 return this._parent ? isInserted.base.call(this) : this._index != null; |
|
3909 }, |
|
3910 |
|
3911 activate: function() { |
|
3912 this._project.activeLayer = this; |
|
3913 }, |
|
3914 |
|
3915 _insert: function _insert(above, item, _preserve) { |
|
3916 if (item instanceof Layer && !item._parent) { |
|
3917 this._remove(true); |
|
3918 Base.splice(item._project.layers, [this], |
|
3919 item._index + (above ? 1 : 0), 0); |
|
3920 this._setProject(item._project); |
|
3921 return this; |
|
3922 } |
|
3923 return _insert.base.call(this, above, item, _preserve); |
|
3924 } |
|
3925 }); |
|
3926 |
|
3927 var Shape = Item.extend({ |
|
3928 _class: 'Shape', |
|
3929 _transformContent: false, |
|
3930 _boundsSelected: true, |
|
3931 |
|
3932 initialize: function Shape(shape, center, size, radius, props) { |
|
3933 this._shape = shape; |
|
3934 this._size = size; |
|
3935 this._radius = radius; |
|
3936 this._initialize(props, center); |
|
3937 }, |
|
3938 |
|
3939 _equals: function(item) { |
|
3940 return this._shape === item._shape |
|
3941 && this._size.equals(item._size) |
|
3942 && Base.equals(this._radius, item._radius); |
|
3943 }, |
|
3944 |
|
3945 clone: function(insert) { |
|
3946 return this._clone(new Shape(this._shape, this.getPosition(true), |
|
3947 this._size.clone(), |
|
3948 this._radius.clone ? this._radius.clone() : this._radius, |
|
3949 { insert: false }), insert); |
|
3950 }, |
|
3951 |
|
3952 getShape: function() { |
|
3953 return this._shape; |
|
3954 }, |
|
3955 |
|
3956 getSize: function() { |
|
3957 var size = this._size; |
|
3958 return new LinkedSize(size.width, size.height, this, 'setSize'); |
|
3959 }, |
|
3960 |
|
3961 setSize: function() { |
|
3962 var shape = this._shape, |
|
3963 size = Size.read(arguments); |
|
3964 if (!this._size.equals(size)) { |
|
3965 var width = size.width, |
|
3966 height = size.height; |
|
3967 if (shape === 'rectangle') { |
|
3968 var radius = Size.min(this._radius, size.divide(2)); |
|
3969 this._radius.set(radius.width, radius.height); |
|
3970 } else if (shape === 'circle') { |
|
3971 width = height = (width + height) / 2; |
|
3972 this._radius = width / 2; |
|
3973 } else if (shape === 'ellipse') { |
|
3974 this._radius.set(width / 2, height / 2); |
|
3975 } |
|
3976 this._size.set(width, height); |
|
3977 this._changed(5); |
|
3978 } |
|
3979 }, |
|
3980 |
|
3981 getRadius: function() { |
|
3982 var rad = this._radius; |
|
3983 return this._shape === 'circle' |
|
3984 ? rad |
|
3985 : new LinkedSize(rad.width, rad.height, this, 'setRadius'); |
|
3986 }, |
|
3987 |
|
3988 setRadius: function(radius) { |
|
3989 var shape = this._shape; |
|
3990 if (shape === 'circle') { |
|
3991 if (radius === this._radius) |
|
3992 return; |
|
3993 var size = radius * 2; |
|
3994 this._radius = radius; |
|
3995 this._size.set(size, size); |
|
3996 } else { |
|
3997 radius = Size.read(arguments); |
|
3998 if (this._radius.equals(radius)) |
|
3999 return; |
|
4000 this._radius.set(radius.width, radius.height); |
|
4001 if (shape === 'rectangle') { |
|
4002 var size = Size.max(this._size, radius.multiply(2)); |
|
4003 this._size.set(size.width, size.height); |
|
4004 } else if (shape === 'ellipse') { |
|
4005 this._size.set(radius.width * 2, radius.height * 2); |
|
4006 } |
|
4007 } |
|
4008 this._changed(5); |
|
4009 }, |
|
4010 |
|
4011 isEmpty: function() { |
|
4012 return false; |
|
4013 }, |
|
4014 |
|
4015 toPath: function(insert) { |
|
4016 var path = new Path[Base.capitalize(this._shape)]({ |
|
4017 center: new Point(), |
|
4018 size: this._size, |
|
4019 radius: this._radius, |
|
4020 insert: false |
|
4021 }); |
|
4022 path.setStyle(this._style); |
|
4023 path.transform(this._matrix); |
|
4024 if (insert || insert === undefined) |
|
4025 path.insertAbove(this); |
|
4026 return path; |
|
4027 }, |
|
4028 |
|
4029 _draw: function(ctx, param) { |
|
4030 var style = this._style, |
|
4031 hasFill = style.hasFill(), |
|
4032 hasStroke = style.hasStroke(), |
|
4033 clip = param.clip; |
|
4034 if (hasFill || hasStroke || clip) { |
|
4035 var radius = this._radius, |
|
4036 shape = this._shape; |
|
4037 ctx.beginPath(); |
|
4038 if (shape === 'circle') { |
|
4039 ctx.arc(0, 0, radius, 0, Math.PI * 2, true); |
|
4040 } else { |
|
4041 var rx = radius.width, |
|
4042 ry = radius.height, |
|
4043 kappa = Numerical.KAPPA; |
|
4044 if (shape === 'ellipse') { |
|
4045 var cx = rx * kappa, |
|
4046 cy = ry * kappa; |
|
4047 ctx.moveTo(-rx, 0); |
|
4048 ctx.bezierCurveTo(-rx, -cy, -cx, -ry, 0, -ry); |
|
4049 ctx.bezierCurveTo(cx, -ry, rx, -cy, rx, 0); |
|
4050 ctx.bezierCurveTo(rx, cy, cx, ry, 0, ry); |
|
4051 ctx.bezierCurveTo(-cx, ry, -rx, cy, -rx, 0); |
|
4052 } else { |
|
4053 var size = this._size, |
|
4054 width = size.width, |
|
4055 height = size.height; |
|
4056 if (rx === 0 && ry === 0) { |
|
4057 ctx.rect(-width / 2, -height / 2, width, height); |
|
4058 } else { |
|
4059 kappa = 1 - kappa; |
|
4060 var x = width / 2, |
|
4061 y = height / 2, |
|
4062 cx = rx * kappa, |
|
4063 cy = ry * kappa; |
|
4064 ctx.moveTo(-x, -y + ry); |
|
4065 ctx.bezierCurveTo(-x, -y + cy, -x + cx, -y, -x + rx, -y); |
|
4066 ctx.lineTo(x - rx, -y); |
|
4067 ctx.bezierCurveTo(x - cx, -y, x, -y + cy, x, -y + ry); |
|
4068 ctx.lineTo(x, y - ry); |
|
4069 ctx.bezierCurveTo(x, y - cy, x - cx, y, x - rx, y); |
|
4070 ctx.lineTo(-x + rx, y); |
|
4071 ctx.bezierCurveTo(-x + cx, y, -x, y - cy, -x, y - ry); |
|
4072 } |
|
4073 } |
|
4074 } |
|
4075 ctx.closePath(); |
|
4076 } |
|
4077 if (!clip && (hasFill || hasStroke)) { |
|
4078 this._setStyles(ctx); |
|
4079 if (hasFill) { |
|
4080 ctx.fill(style.getWindingRule()); |
|
4081 ctx.shadowColor = 'rgba(0,0,0,0)'; |
|
4082 } |
|
4083 if (hasStroke) |
|
4084 ctx.stroke(); |
|
4085 } |
|
4086 }, |
|
4087 |
|
4088 _canComposite: function() { |
|
4089 return !(this.hasFill() && this.hasStroke()); |
|
4090 }, |
|
4091 |
|
4092 _getBounds: function(getter, matrix) { |
|
4093 var rect = new Rectangle(this._size).setCenter(0, 0); |
|
4094 if (getter !== 'getBounds' && this.hasStroke()) |
|
4095 rect = rect.expand(this.getStrokeWidth()); |
|
4096 return matrix ? matrix._transformBounds(rect) : rect; |
|
4097 } |
|
4098 }, |
|
4099 new function() { |
|
4100 |
|
4101 function getCornerCenter(that, point, expand) { |
|
4102 var radius = that._radius; |
|
4103 if (!radius.isZero()) { |
|
4104 var halfSize = that._size.divide(2); |
|
4105 for (var i = 0; i < 4; i++) { |
|
4106 var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1), |
|
4107 corner = dir.multiply(halfSize), |
|
4108 center = corner.subtract(dir.multiply(radius)), |
|
4109 rect = new Rectangle(corner, center); |
|
4110 if ((expand ? rect.expand(expand) : rect).contains(point)) |
|
4111 return center; |
|
4112 } |
|
4113 } |
|
4114 } |
|
4115 |
|
4116 function getEllipseRadius(point, radius) { |
|
4117 var angle = point.getAngleInRadians(), |
|
4118 width = radius.width * 2, |
|
4119 height = radius.height * 2, |
|
4120 x = width * Math.sin(angle), |
|
4121 y = height * Math.cos(angle); |
|
4122 return width * height / (2 * Math.sqrt(x * x + y * y)); |
|
4123 } |
|
4124 |
|
4125 return { |
|
4126 _contains: function _contains(point) { |
|
4127 if (this._shape === 'rectangle') { |
|
4128 var center = getCornerCenter(this, point); |
|
4129 return center |
|
4130 ? point.subtract(center).divide(this._radius) |
|
4131 .getLength() <= 1 |
|
4132 : _contains.base.call(this, point); |
|
4133 } else { |
|
4134 return point.divide(this.size).getLength() <= 0.5; |
|
4135 } |
|
4136 }, |
|
4137 |
|
4138 _hitTest: function _hitTest(point, options) { |
|
4139 var hit = false; |
|
4140 if (this.hasStroke()) { |
|
4141 var shape = this._shape, |
|
4142 radius = this._radius, |
|
4143 strokeWidth = this.getStrokeWidth() + 2 * options.tolerance; |
|
4144 if (shape === 'rectangle') { |
|
4145 var center = getCornerCenter(this, point, strokeWidth); |
|
4146 if (center) { |
|
4147 var pt = point.subtract(center); |
|
4148 hit = 2 * Math.abs(pt.getLength() |
|
4149 - getEllipseRadius(pt, radius)) <= strokeWidth; |
|
4150 } else { |
|
4151 var rect = new Rectangle(this._size).setCenter(0, 0), |
|
4152 outer = rect.expand(strokeWidth), |
|
4153 inner = rect.expand(-strokeWidth); |
|
4154 hit = outer._containsPoint(point) |
|
4155 && !inner._containsPoint(point); |
|
4156 } |
|
4157 } else { |
|
4158 if (shape === 'ellipse') |
|
4159 radius = getEllipseRadius(point, radius); |
|
4160 hit = 2 * Math.abs(point.getLength() - radius) |
|
4161 <= strokeWidth; |
|
4162 } |
|
4163 } |
|
4164 return hit |
|
4165 ? new HitResult('stroke', this) |
|
4166 : _hitTest.base.apply(this, arguments); |
|
4167 } |
|
4168 }; |
|
4169 }, { |
|
4170 |
|
4171 statics: new function() { |
|
4172 function createShape(shape, point, size, radius, args) { |
|
4173 return new Shape(shape, point, size, radius, Base.getNamed(args)); |
|
4174 } |
|
4175 |
|
4176 return { |
|
4177 Circle: function() { |
|
4178 var center = Point.readNamed(arguments, 'center'), |
|
4179 radius = Base.readNamed(arguments, 'radius'); |
|
4180 return createShape('circle', center, new Size(radius * 2), radius, |
|
4181 arguments); |
|
4182 }, |
|
4183 |
|
4184 Rectangle: function() { |
|
4185 var rect = Rectangle.readNamed(arguments, 'rectangle'), |
|
4186 radius = Size.min(Size.readNamed(arguments, 'radius'), |
|
4187 rect.getSize(true).divide(2)); |
|
4188 return createShape('rectangle', rect.getCenter(true), |
|
4189 rect.getSize(true), radius, arguments); |
|
4190 }, |
|
4191 |
|
4192 Ellipse: function() { |
|
4193 var ellipse = Shape._readEllipse(arguments); |
|
4194 radius = ellipse.radius; |
|
4195 return createShape('ellipse', ellipse.center, radius.multiply(2), |
|
4196 radius, arguments); |
|
4197 }, |
|
4198 |
|
4199 _readEllipse: function(args) { |
|
4200 var center, |
|
4201 radius; |
|
4202 if (Base.hasNamed(args, 'radius')) { |
|
4203 center = Point.readNamed(args, 'center'); |
|
4204 radius = Size.readNamed(args, 'radius'); |
|
4205 } else { |
|
4206 var rect = Rectangle.readNamed(args, 'rectangle'); |
|
4207 center = rect.getCenter(true); |
|
4208 radius = rect.getSize(true).divide(2); |
|
4209 } |
|
4210 return { center: center, radius: radius }; |
|
4211 } |
|
4212 }; |
|
4213 }}); |
|
4214 |
|
4215 var Raster = Item.extend({ |
|
4216 _class: 'Raster', |
|
4217 _transformContent: false, |
|
4218 _boundsGetter: 'getBounds', |
|
4219 _boundsSelected: true, |
|
4220 _serializeFields: { |
|
4221 source: null |
|
4222 }, |
|
4223 |
|
4224 initialize: function Raster(object, position) { |
|
4225 if (!this._initialize(object, |
|
4226 position !== undefined && Point.read(arguments, 1))) { |
|
4227 if (typeof object === 'string') { |
|
4228 this.setSource(object); |
|
4229 } else { |
|
4230 this.setImage(object); |
|
4231 } |
|
4232 } |
|
4233 if (!this._size) |
|
4234 this._size = new Size(); |
|
4235 }, |
|
4236 |
|
4237 _equals: function(item) { |
|
4238 return this.getSource() === item.getSource(); |
|
4239 }, |
|
4240 |
|
4241 clone: function(insert) { |
|
4242 var param = { insert: false }, |
|
4243 image = this._image; |
|
4244 if (image) { |
|
4245 param.image = image; |
|
4246 } else if (this._canvas) { |
|
4247 var canvas = param.canvas = CanvasProvider.getCanvas(this._size); |
|
4248 canvas.getContext('2d').drawImage(this._canvas, 0, 0); |
|
4249 } |
|
4250 return this._clone(new Raster(param), insert); |
|
4251 }, |
|
4252 |
|
4253 getSize: function() { |
|
4254 var size = this._size; |
|
4255 return new LinkedSize(size.width, size.height, this, 'setSize'); |
|
4256 }, |
|
4257 |
|
4258 setSize: function() { |
|
4259 var size = Size.read(arguments); |
|
4260 if (!this._size.equals(size)) { |
|
4261 var element = this.getElement(); |
|
4262 this.setCanvas(CanvasProvider.getCanvas(size)); |
|
4263 if (element) |
|
4264 this.getContext(true).drawImage(element, 0, 0, |
|
4265 size.width, size.height); |
|
4266 } |
|
4267 }, |
|
4268 |
|
4269 getWidth: function() { |
|
4270 return this._size.width; |
|
4271 }, |
|
4272 |
|
4273 getHeight: function() { |
|
4274 return this._size.height; |
|
4275 }, |
|
4276 |
|
4277 isEmpty: function() { |
|
4278 return this._size.width == 0 && this._size.height == 0; |
|
4279 }, |
|
4280 |
|
4281 getPpi: function() { |
|
4282 var matrix = this._matrix, |
|
4283 orig = new Point(0, 0).transform(matrix), |
|
4284 u = new Point(1, 0).transform(matrix).subtract(orig), |
|
4285 v = new Point(0, 1).transform(matrix).subtract(orig); |
|
4286 return new Size( |
|
4287 72 / u.getLength(), |
|
4288 72 / v.getLength() |
|
4289 ); |
|
4290 }, |
|
4291 |
|
4292 getImage: function() { |
|
4293 return this._image; |
|
4294 }, |
|
4295 |
|
4296 setImage: function(image) { |
|
4297 if (this._canvas) |
|
4298 CanvasProvider.release(this._canvas); |
|
4299 if (image.getContext) { |
|
4300 this._image = null; |
|
4301 this._canvas = image; |
|
4302 } else { |
|
4303 this._image = image; |
|
4304 this._canvas = null; |
|
4305 } |
|
4306 this._size = new Size( |
|
4307 image.naturalWidth || image.width, |
|
4308 image.naturalHeight || image.height); |
|
4309 this._context = null; |
|
4310 this._changed(5 | 129); |
|
4311 }, |
|
4312 |
|
4313 getCanvas: function() { |
|
4314 if (!this._canvas) { |
|
4315 var ctx = CanvasProvider.getContext(this._size); |
|
4316 try { |
|
4317 if (this._image) |
|
4318 ctx.drawImage(this._image, 0, 0); |
|
4319 this._canvas = ctx.canvas; |
|
4320 } catch (e) { |
|
4321 CanvasProvider.release(ctx); |
|
4322 } |
|
4323 } |
|
4324 return this._canvas; |
|
4325 }, |
|
4326 |
|
4327 setCanvas: '#setImage', |
|
4328 |
|
4329 getContext: function() { |
|
4330 if (!this._context) |
|
4331 this._context = this.getCanvas().getContext('2d'); |
|
4332 if (arguments[0]) { |
|
4333 this._image = null; |
|
4334 this._changed(129); |
|
4335 } |
|
4336 return this._context; |
|
4337 }, |
|
4338 |
|
4339 setContext: function(context) { |
|
4340 this._context = context; |
|
4341 }, |
|
4342 |
|
4343 getSource: function() { |
|
4344 return this._image && this._image.src || this.toDataURL(); |
|
4345 }, |
|
4346 |
|
4347 setSource: function(src) { |
|
4348 var that = this, |
|
4349 image = document.getElementById(src) || new Image(); |
|
4350 |
|
4351 function loaded() { |
|
4352 var view = that._project.view; |
|
4353 if (view) |
|
4354 paper = view._scope; |
|
4355 that.fire('load'); |
|
4356 if (view) |
|
4357 view.draw(true); |
|
4358 } |
|
4359 |
|
4360 if (image.naturalWidth && image.naturalHeight) { |
|
4361 setTimeout(loaded, 0); |
|
4362 } else { |
|
4363 DomEvent.add(image, { |
|
4364 load: function() { |
|
4365 that.setImage(image); |
|
4366 loaded(); |
|
4367 } |
|
4368 }); |
|
4369 if (!image.src) |
|
4370 image.src = src; |
|
4371 } |
|
4372 this.setImage(image); |
|
4373 }, |
|
4374 |
|
4375 getElement: function() { |
|
4376 return this._canvas || this._image; |
|
4377 }, |
|
4378 |
|
4379 getSubCanvas: function(rect) { |
|
4380 rect = Rectangle.read(arguments); |
|
4381 var ctx = CanvasProvider.getContext(rect.getSize()); |
|
4382 ctx.drawImage(this.getCanvas(), rect.x, rect.y, |
|
4383 rect.width, rect.height, 0, 0, rect.width, rect.height); |
|
4384 return ctx.canvas; |
|
4385 }, |
|
4386 |
|
4387 getSubRaster: function(rect) { |
|
4388 rect = Rectangle.read(arguments); |
|
4389 var raster = new Raster({ |
|
4390 canvas: this.getSubCanvas(rect), |
|
4391 insert: false |
|
4392 }); |
|
4393 raster.translate(rect.getCenter().subtract(this.getSize().divide(2))); |
|
4394 raster._matrix.preConcatenate(this._matrix); |
|
4395 raster.insertAbove(this); |
|
4396 return raster; |
|
4397 }, |
|
4398 |
|
4399 toDataURL: function() { |
|
4400 var src = this._image && this._image.src; |
|
4401 if (/^data:/.test(src)) |
|
4402 return src; |
|
4403 var canvas = this.getCanvas(); |
|
4404 return canvas ? canvas.toDataURL() : null; |
|
4405 }, |
|
4406 |
|
4407 drawImage: function(image, point) { |
|
4408 point = Point.read(arguments, 1); |
|
4409 this.getContext(true).drawImage(image, point.x, point.y); |
|
4410 }, |
|
4411 |
|
4412 getAverageColor: function(object) { |
|
4413 var bounds, path; |
|
4414 if (!object) { |
|
4415 bounds = this.getBounds(); |
|
4416 } else if (object instanceof PathItem) { |
|
4417 path = object; |
|
4418 bounds = object.getBounds(); |
|
4419 } else if (object.width) { |
|
4420 bounds = new Rectangle(object); |
|
4421 } else if (object.x) { |
|
4422 bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1); |
|
4423 } |
|
4424 var sampleSize = 32, |
|
4425 width = Math.min(bounds.width, sampleSize), |
|
4426 height = Math.min(bounds.height, sampleSize); |
|
4427 var ctx = Raster._sampleContext; |
|
4428 if (!ctx) { |
|
4429 ctx = Raster._sampleContext = CanvasProvider.getContext( |
|
4430 new Size(sampleSize)); |
|
4431 } else { |
|
4432 ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1); |
|
4433 } |
|
4434 ctx.save(); |
|
4435 var matrix = new Matrix() |
|
4436 .scale(width / bounds.width, height / bounds.height) |
|
4437 .translate(-bounds.x, -bounds.y); |
|
4438 matrix.applyToContext(ctx); |
|
4439 if (path) |
|
4440 path.draw(ctx, new Base({ clip: true, transforms: [matrix] })); |
|
4441 this._matrix.applyToContext(ctx); |
|
4442 ctx.drawImage(this.getElement(), |
|
4443 -this._size.width / 2, -this._size.height / 2); |
|
4444 ctx.restore(); |
|
4445 var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width), |
|
4446 Math.ceil(height)).data, |
|
4447 channels = [0, 0, 0], |
|
4448 total = 0; |
|
4449 for (var i = 0, l = pixels.length; i < l; i += 4) { |
|
4450 var alpha = pixels[i + 3]; |
|
4451 total += alpha; |
|
4452 alpha /= 255; |
|
4453 channels[0] += pixels[i] * alpha; |
|
4454 channels[1] += pixels[i + 1] * alpha; |
|
4455 channels[2] += pixels[i + 2] * alpha; |
|
4456 } |
|
4457 for (var i = 0; i < 3; i++) |
|
4458 channels[i] /= total; |
|
4459 return total ? Color.read(channels) : null; |
|
4460 }, |
|
4461 |
|
4462 getPixel: function(point) { |
|
4463 point = Point.read(arguments); |
|
4464 var data = this.getContext().getImageData(point.x, point.y, 1, 1).data; |
|
4465 return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255], |
|
4466 data[3] / 255); |
|
4467 }, |
|
4468 |
|
4469 setPixel: function() { |
|
4470 var point = Point.read(arguments), |
|
4471 color = Color.read(arguments), |
|
4472 components = color._convert('rgb'), |
|
4473 alpha = color._alpha, |
|
4474 ctx = this.getContext(true), |
|
4475 imageData = ctx.createImageData(1, 1), |
|
4476 data = imageData.data; |
|
4477 data[0] = components[0] * 255; |
|
4478 data[1] = components[1] * 255; |
|
4479 data[2] = components[2] * 255; |
|
4480 data[3] = alpha != null ? alpha * 255 : 255; |
|
4481 ctx.putImageData(imageData, point.x, point.y); |
|
4482 }, |
|
4483 |
|
4484 createImageData: function(size) { |
|
4485 size = Size.read(arguments); |
|
4486 return this.getContext().createImageData(size.width, size.height); |
|
4487 }, |
|
4488 |
|
4489 getImageData: function(rect) { |
|
4490 rect = Rectangle.read(arguments); |
|
4491 if (rect.isEmpty()) |
|
4492 rect = new Rectangle(this._size); |
|
4493 return this.getContext().getImageData(rect.x, rect.y, |
|
4494 rect.width, rect.height); |
|
4495 }, |
|
4496 |
|
4497 setImageData: function(data, point) { |
|
4498 point = Point.read(arguments, 1); |
|
4499 this.getContext(true).putImageData(data, point.x, point.y); |
|
4500 }, |
|
4501 |
|
4502 _getBounds: function(getter, matrix) { |
|
4503 var rect = new Rectangle(this._size).setCenter(0, 0); |
|
4504 return matrix ? matrix._transformBounds(rect) : rect; |
|
4505 }, |
|
4506 |
|
4507 _hitTest: function(point) { |
|
4508 if (this._contains(point)) { |
|
4509 var that = this; |
|
4510 return new HitResult('pixel', that, { |
|
4511 offset: point.add(that._size.divide(2)).round(), |
|
4512 color: { |
|
4513 get: function() { |
|
4514 return that.getPixel(this.offset); |
|
4515 } |
|
4516 } |
|
4517 }); |
|
4518 } |
|
4519 }, |
|
4520 |
|
4521 _draw: function(ctx) { |
|
4522 var element = this.getElement(); |
|
4523 if (element) { |
|
4524 ctx.globalAlpha = this._opacity; |
|
4525 ctx.drawImage(element, |
|
4526 -this._size.width / 2, -this._size.height / 2); |
|
4527 } |
|
4528 }, |
|
4529 |
|
4530 _canComposite: function() { |
|
4531 return true; |
|
4532 } |
|
4533 }); |
|
4534 |
|
4535 var PlacedSymbol = Item.extend({ |
|
4536 _class: 'PlacedSymbol', |
|
4537 _transformContent: false, |
|
4538 _boundsGetter: { getBounds: 'getStrokeBounds' }, |
|
4539 _boundsSelected: true, |
|
4540 _serializeFields: { |
|
4541 symbol: null |
|
4542 }, |
|
4543 |
|
4544 initialize: function PlacedSymbol(arg0, arg1) { |
|
4545 if (!this._initialize(arg0, |
|
4546 arg1 !== undefined && Point.read(arguments, 1))) |
|
4547 this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0)); |
|
4548 }, |
|
4549 |
|
4550 _equals: function(item) { |
|
4551 return this._symbol === item._symbol; |
|
4552 }, |
|
4553 |
|
4554 getSymbol: function() { |
|
4555 return this._symbol; |
|
4556 }, |
|
4557 |
|
4558 setSymbol: function(symbol) { |
|
4559 if (this._symbol) |
|
4560 delete this._symbol._instances[this._id]; |
|
4561 this._symbol = symbol; |
|
4562 symbol._instances[this._id] = this; |
|
4563 }, |
|
4564 |
|
4565 clone: function(insert) { |
|
4566 return this._clone(new PlacedSymbol({ |
|
4567 symbol: this.symbol, |
|
4568 insert: false |
|
4569 }), insert); |
|
4570 }, |
|
4571 |
|
4572 isEmpty: function() { |
|
4573 return this._symbol._definition.isEmpty(); |
|
4574 }, |
|
4575 |
|
4576 _getBounds: function(getter, matrix) { |
|
4577 return this.symbol._definition._getCachedBounds(getter, matrix); |
|
4578 }, |
|
4579 |
|
4580 _hitTest: function(point, options, matrix) { |
|
4581 var result = this._symbol._definition._hitTest(point, options, matrix); |
|
4582 if (result) |
|
4583 result.item = this; |
|
4584 return result; |
|
4585 }, |
|
4586 |
|
4587 _draw: function(ctx, param) { |
|
4588 this.symbol._definition.draw(ctx, param); |
|
4589 } |
|
4590 |
|
4591 }); |
|
4592 |
|
4593 var HitResult = Base.extend({ |
|
4594 _class: 'HitResult', |
|
4595 |
|
4596 initialize: function HitResult(type, item, values) { |
|
4597 this.type = type; |
|
4598 this.item = item; |
|
4599 if (values) { |
|
4600 values.enumerable = true; |
|
4601 this.inject(values); |
|
4602 } |
|
4603 }, |
|
4604 |
|
4605 statics: { |
|
4606 getOptions: function(options) { |
|
4607 return options && options._merged ? options : new Base({ |
|
4608 type: null, |
|
4609 tolerance: paper.project.options.hitTolerance || 2, |
|
4610 fill: !options, |
|
4611 stroke: !options, |
|
4612 segments: !options, |
|
4613 handles: false, |
|
4614 ends: false, |
|
4615 center: false, |
|
4616 bounds: false, |
|
4617 guides: false, |
|
4618 selected: false, |
|
4619 _merged: true |
|
4620 }, options); |
|
4621 } |
|
4622 } |
|
4623 }); |
|
4624 |
|
4625 var Segment = Base.extend({ |
|
4626 _class: 'Segment', |
|
4627 |
|
4628 initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) { |
|
4629 var count = arguments.length, |
|
4630 point, handleIn, handleOut; |
|
4631 if (count === 0) { |
|
4632 } else if (count === 1) { |
|
4633 if (arg0.point) { |
|
4634 point = arg0.point; |
|
4635 handleIn = arg0.handleIn; |
|
4636 handleOut = arg0.handleOut; |
|
4637 } else { |
|
4638 point = arg0; |
|
4639 } |
|
4640 } else if (count === 2 && typeof arg0 === 'number') { |
|
4641 point = arguments; |
|
4642 } else if (count <= 3) { |
|
4643 point = arg0; |
|
4644 handleIn = arg1; |
|
4645 handleOut = arg2; |
|
4646 } else { |
|
4647 point = arg0 !== undefined ? [ arg0, arg1 ] : null; |
|
4648 handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null; |
|
4649 handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null; |
|
4650 } |
|
4651 new SegmentPoint(point, this, '_point'); |
|
4652 new SegmentPoint(handleIn, this, '_handleIn'); |
|
4653 new SegmentPoint(handleOut, this, '_handleOut'); |
|
4654 }, |
|
4655 |
|
4656 _serialize: function(options) { |
|
4657 return Base.serialize(this.isLinear() ? this._point |
|
4658 : [this._point, this._handleIn, this._handleOut], options, true); |
|
4659 }, |
|
4660 |
|
4661 _changed: function(point) { |
|
4662 if (!this._path) |
|
4663 return; |
|
4664 var curve = this._path._curves && this.getCurve(), |
|
4665 other; |
|
4666 if (curve) { |
|
4667 curve._changed(); |
|
4668 if (other = (curve[point == this._point |
|
4669 || point == this._handleIn && curve._segment1 == this |
|
4670 ? 'getPrevious' : 'getNext']())) { |
|
4671 other._changed(); |
|
4672 } |
|
4673 } |
|
4674 this._path._changed(5); |
|
4675 }, |
|
4676 |
|
4677 getPoint: function() { |
|
4678 return this._point; |
|
4679 }, |
|
4680 |
|
4681 setPoint: function(point) { |
|
4682 point = Point.read(arguments); |
|
4683 this._point.set(point.x, point.y); |
|
4684 }, |
|
4685 |
|
4686 getHandleIn: function() { |
|
4687 return this._handleIn; |
|
4688 }, |
|
4689 |
|
4690 setHandleIn: function(point) { |
|
4691 point = Point.read(arguments); |
|
4692 this._handleIn.set(point.x, point.y); |
|
4693 }, |
|
4694 |
|
4695 getHandleOut: function() { |
|
4696 return this._handleOut; |
|
4697 }, |
|
4698 |
|
4699 setHandleOut: function(point) { |
|
4700 point = Point.read(arguments); |
|
4701 this._handleOut.set(point.x, point.y); |
|
4702 }, |
|
4703 |
|
4704 isLinear: function() { |
|
4705 return this._handleIn.isZero() && this._handleOut.isZero(); |
|
4706 }, |
|
4707 |
|
4708 setLinear: function() { |
|
4709 this._handleIn.set(0, 0); |
|
4710 this._handleOut.set(0, 0); |
|
4711 }, |
|
4712 |
|
4713 isColinear: function(segment) { |
|
4714 var next1 = this.getNext(), |
|
4715 next2 = segment.getNext(); |
|
4716 return this._handleOut.isZero() && next1._handleIn.isZero() |
|
4717 && segment._handleOut.isZero() && next2._handleIn.isZero() |
|
4718 && next1._point.subtract(this._point).isColinear( |
|
4719 next2._point.subtract(segment._point)); |
|
4720 }, |
|
4721 |
|
4722 isOrthogonal: function() { |
|
4723 var prev = this.getPrevious(), |
|
4724 next = this.getNext(); |
|
4725 return prev._handleOut.isZero() && this._handleIn.isZero() |
|
4726 && this._handleOut.isZero() && next._handleIn.isZero() |
|
4727 && this._point.subtract(prev._point).isOrthogonal( |
|
4728 next._point.subtract(this._point)); |
|
4729 }, |
|
4730 |
|
4731 isArc: function() { |
|
4732 var next = this.getNext(), |
|
4733 handle1 = this._handleOut, |
|
4734 handle2 = next._handleIn, |
|
4735 kappa = Numerical.KAPPA; |
|
4736 if (handle1.isOrthogonal(handle2)) { |
|
4737 var from = this._point, |
|
4738 to = next._point, |
|
4739 corner = new Line(from, handle1, true).intersect( |
|
4740 new Line(to, handle2, true), true); |
|
4741 return corner && Numerical.isZero(handle1.getLength() / |
|
4742 corner.subtract(from).getLength() - kappa) |
|
4743 && Numerical.isZero(handle2.getLength() / |
|
4744 corner.subtract(to).getLength() - kappa); |
|
4745 } |
|
4746 return false; |
|
4747 }, |
|
4748 |
|
4749 _isSelected: function(point) { |
|
4750 var state = this._selectionState; |
|
4751 return point == this._point ? !!(state & 4) |
|
4752 : point == this._handleIn ? !!(state & 1) |
|
4753 : point == this._handleOut ? !!(state & 2) |
|
4754 : false; |
|
4755 }, |
|
4756 |
|
4757 _setSelected: function(point, selected) { |
|
4758 var path = this._path, |
|
4759 selected = !!selected, |
|
4760 state = this._selectionState || 0, |
|
4761 selection = [ |
|
4762 !!(state & 4), |
|
4763 !!(state & 1), |
|
4764 !!(state & 2) |
|
4765 ]; |
|
4766 if (point === this._point) { |
|
4767 if (selected) { |
|
4768 selection[1] = selection[2] = false; |
|
4769 } else { |
|
4770 var previous = this.getPrevious(), |
|
4771 next = this.getNext(); |
|
4772 selection[1] = previous && (previous._point.isSelected() |
|
4773 || previous._handleOut.isSelected()); |
|
4774 selection[2] = next && (next._point.isSelected() |
|
4775 || next._handleIn.isSelected()); |
|
4776 } |
|
4777 selection[0] = selected; |
|
4778 } else { |
|
4779 var index = point === this._handleIn ? 1 : 2; |
|
4780 if (selection[index] != selected) { |
|
4781 if (selected) |
|
4782 selection[0] = false; |
|
4783 selection[index] = selected; |
|
4784 } |
|
4785 } |
|
4786 this._selectionState = (selection[0] ? 4 : 0) |
|
4787 | (selection[1] ? 1 : 0) |
|
4788 | (selection[2] ? 2 : 0); |
|
4789 if (path && state != this._selectionState) { |
|
4790 path._updateSelection(this, state, this._selectionState); |
|
4791 path._changed(33); |
|
4792 } |
|
4793 }, |
|
4794 |
|
4795 isSelected: function() { |
|
4796 return this._isSelected(this._point); |
|
4797 }, |
|
4798 |
|
4799 setSelected: function(selected) { |
|
4800 this._setSelected(this._point, selected); |
|
4801 }, |
|
4802 |
|
4803 getIndex: function() { |
|
4804 return this._index !== undefined ? this._index : null; |
|
4805 }, |
|
4806 |
|
4807 getPath: function() { |
|
4808 return this._path || null; |
|
4809 }, |
|
4810 |
|
4811 getCurve: function() { |
|
4812 var path = this._path, |
|
4813 index = this._index; |
|
4814 if (path) { |
|
4815 if (!path._closed && index == path._segments.length - 1) |
|
4816 index--; |
|
4817 return path.getCurves()[index] || null; |
|
4818 } |
|
4819 return null; |
|
4820 }, |
|
4821 |
|
4822 getLocation: function() { |
|
4823 var curve = this.getCurve(); |
|
4824 return curve ? new CurveLocation(curve, curve.getNext() ? 0 : 1) : null; |
|
4825 }, |
|
4826 |
|
4827 getNext: function() { |
|
4828 var segments = this._path && this._path._segments; |
|
4829 return segments && (segments[this._index + 1] |
|
4830 || this._path._closed && segments[0]) || null; |
|
4831 }, |
|
4832 |
|
4833 getPrevious: function() { |
|
4834 var segments = this._path && this._path._segments; |
|
4835 return segments && (segments[this._index - 1] |
|
4836 || this._path._closed && segments[segments.length - 1]) || null; |
|
4837 }, |
|
4838 |
|
4839 reverse: function() { |
|
4840 return new Segment(this._point, this._handleOut, this._handleIn); |
|
4841 }, |
|
4842 |
|
4843 remove: function() { |
|
4844 return this._path ? !!this._path.removeSegment(this._index) : false; |
|
4845 }, |
|
4846 |
|
4847 clone: function() { |
|
4848 return new Segment(this._point, this._handleIn, this._handleOut); |
|
4849 }, |
|
4850 |
|
4851 equals: function(segment) { |
|
4852 return segment === this || segment && this._class === segment._class |
|
4853 && this._point.equals(segment._point) |
|
4854 && this._handleIn.equals(segment._handleIn) |
|
4855 && this._handleOut.equals(segment._handleOut) |
|
4856 || false; |
|
4857 }, |
|
4858 |
|
4859 toString: function() { |
|
4860 var parts = [ 'point: ' + this._point ]; |
|
4861 if (!this._handleIn.isZero()) |
|
4862 parts.push('handleIn: ' + this._handleIn); |
|
4863 if (!this._handleOut.isZero()) |
|
4864 parts.push('handleOut: ' + this._handleOut); |
|
4865 return '{ ' + parts.join(', ') + ' }'; |
|
4866 }, |
|
4867 |
|
4868 _transformCoordinates: function(matrix, coords, change) { |
|
4869 var point = this._point, |
|
4870 handleIn = !change || !this._handleIn.isZero() |
|
4871 ? this._handleIn : null, |
|
4872 handleOut = !change || !this._handleOut.isZero() |
|
4873 ? this._handleOut : null, |
|
4874 x = point._x, |
|
4875 y = point._y, |
|
4876 i = 2; |
|
4877 coords[0] = x; |
|
4878 coords[1] = y; |
|
4879 if (handleIn) { |
|
4880 coords[i++] = handleIn._x + x; |
|
4881 coords[i++] = handleIn._y + y; |
|
4882 } |
|
4883 if (handleOut) { |
|
4884 coords[i++] = handleOut._x + x; |
|
4885 coords[i++] = handleOut._y + y; |
|
4886 } |
|
4887 if (matrix) { |
|
4888 matrix._transformCoordinates(coords, 0, coords, 0, i / 2); |
|
4889 x = coords[0]; |
|
4890 y = coords[1]; |
|
4891 if (change) { |
|
4892 point._x = x; |
|
4893 point._y = y; |
|
4894 i = 2; |
|
4895 if (handleIn) { |
|
4896 handleIn._x = coords[i++] - x; |
|
4897 handleIn._y = coords[i++] - y; |
|
4898 } |
|
4899 if (handleOut) { |
|
4900 handleOut._x = coords[i++] - x; |
|
4901 handleOut._y = coords[i++] - y; |
|
4902 } |
|
4903 } else { |
|
4904 if (!handleIn) { |
|
4905 coords[i++] = x; |
|
4906 coords[i++] = y; |
|
4907 } |
|
4908 if (!handleOut) { |
|
4909 coords[i++] = x; |
|
4910 coords[i++] = y; |
|
4911 } |
|
4912 } |
|
4913 } |
|
4914 return coords; |
|
4915 } |
|
4916 }); |
|
4917 |
|
4918 var SegmentPoint = Point.extend({ |
|
4919 initialize: function SegmentPoint(point, owner, key) { |
|
4920 var x, y, selected; |
|
4921 if (!point) { |
|
4922 x = y = 0; |
|
4923 } else if ((x = point[0]) !== undefined) { |
|
4924 y = point[1]; |
|
4925 } else { |
|
4926 if ((x = point.x) === undefined) { |
|
4927 point = Point.read(arguments); |
|
4928 x = point.x; |
|
4929 } |
|
4930 y = point.y; |
|
4931 selected = point.selected; |
|
4932 } |
|
4933 this._x = x; |
|
4934 this._y = y; |
|
4935 this._owner = owner; |
|
4936 owner[key] = this; |
|
4937 if (selected) |
|
4938 this.setSelected(true); |
|
4939 }, |
|
4940 |
|
4941 set: function(x, y) { |
|
4942 this._x = x; |
|
4943 this._y = y; |
|
4944 this._owner._changed(this); |
|
4945 return this; |
|
4946 }, |
|
4947 |
|
4948 _serialize: function(options) { |
|
4949 var f = options.formatter, |
|
4950 x = f.number(this._x), |
|
4951 y = f.number(this._y); |
|
4952 return this.isSelected() |
|
4953 ? { x: x, y: y, selected: true } |
|
4954 : [x, y]; |
|
4955 }, |
|
4956 |
|
4957 getX: function() { |
|
4958 return this._x; |
|
4959 }, |
|
4960 |
|
4961 setX: function(x) { |
|
4962 this._x = x; |
|
4963 this._owner._changed(this); |
|
4964 }, |
|
4965 |
|
4966 getY: function() { |
|
4967 return this._y; |
|
4968 }, |
|
4969 |
|
4970 setY: function(y) { |
|
4971 this._y = y; |
|
4972 this._owner._changed(this); |
|
4973 }, |
|
4974 |
|
4975 isZero: function() { |
|
4976 return Numerical.isZero(this._x) && Numerical.isZero(this._y); |
|
4977 }, |
|
4978 |
|
4979 setSelected: function(selected) { |
|
4980 this._owner._setSelected(this, selected); |
|
4981 }, |
|
4982 |
|
4983 isSelected: function() { |
|
4984 return this._owner._isSelected(this); |
|
4985 } |
|
4986 }); |
|
4987 |
|
4988 var Curve = Base.extend({ |
|
4989 _class: 'Curve', |
|
4990 initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { |
|
4991 var count = arguments.length; |
|
4992 if (count === 3) { |
|
4993 this._path = arg0; |
|
4994 this._segment1 = arg1; |
|
4995 this._segment2 = arg2; |
|
4996 } else if (count === 0) { |
|
4997 this._segment1 = new Segment(); |
|
4998 this._segment2 = new Segment(); |
|
4999 } else if (count === 1) { |
|
5000 this._segment1 = new Segment(arg0.segment1); |
|
5001 this._segment2 = new Segment(arg0.segment2); |
|
5002 } else if (count === 2) { |
|
5003 this._segment1 = new Segment(arg0); |
|
5004 this._segment2 = new Segment(arg1); |
|
5005 } else { |
|
5006 var point1, handle1, handle2, point2; |
|
5007 if (count === 4) { |
|
5008 point1 = arg0; |
|
5009 handle1 = arg1; |
|
5010 handle2 = arg2; |
|
5011 point2 = arg3; |
|
5012 } else if (count === 8) { |
|
5013 point1 = [arg0, arg1]; |
|
5014 point2 = [arg6, arg7]; |
|
5015 handle1 = [arg2 - arg0, arg3 - arg1]; |
|
5016 handle2 = [arg4 - arg6, arg5 - arg7]; |
|
5017 } |
|
5018 this._segment1 = new Segment(point1, null, handle1); |
|
5019 this._segment2 = new Segment(point2, handle2, null); |
|
5020 } |
|
5021 }, |
|
5022 |
|
5023 _changed: function() { |
|
5024 delete this._length; |
|
5025 delete this._bounds; |
|
5026 }, |
|
5027 |
|
5028 getPoint1: function() { |
|
5029 return this._segment1._point; |
|
5030 }, |
|
5031 |
|
5032 setPoint1: function(point) { |
|
5033 point = Point.read(arguments); |
|
5034 this._segment1._point.set(point.x, point.y); |
|
5035 }, |
|
5036 |
|
5037 getPoint2: function() { |
|
5038 return this._segment2._point; |
|
5039 }, |
|
5040 |
|
5041 setPoint2: function(point) { |
|
5042 point = Point.read(arguments); |
|
5043 this._segment2._point.set(point.x, point.y); |
|
5044 }, |
|
5045 |
|
5046 getHandle1: function() { |
|
5047 return this._segment1._handleOut; |
|
5048 }, |
|
5049 |
|
5050 setHandle1: function(point) { |
|
5051 point = Point.read(arguments); |
|
5052 this._segment1._handleOut.set(point.x, point.y); |
|
5053 }, |
|
5054 |
|
5055 getHandle2: function() { |
|
5056 return this._segment2._handleIn; |
|
5057 }, |
|
5058 |
|
5059 setHandle2: function(point) { |
|
5060 point = Point.read(arguments); |
|
5061 this._segment2._handleIn.set(point.x, point.y); |
|
5062 }, |
|
5063 |
|
5064 getSegment1: function() { |
|
5065 return this._segment1; |
|
5066 }, |
|
5067 |
|
5068 getSegment2: function() { |
|
5069 return this._segment2; |
|
5070 }, |
|
5071 |
|
5072 getPath: function() { |
|
5073 return this._path; |
|
5074 }, |
|
5075 |
|
5076 getIndex: function() { |
|
5077 return this._segment1._index; |
|
5078 }, |
|
5079 |
|
5080 getNext: function() { |
|
5081 var curves = this._path && this._path._curves; |
|
5082 return curves && (curves[this._segment1._index + 1] |
|
5083 || this._path._closed && curves[0]) || null; |
|
5084 }, |
|
5085 |
|
5086 getPrevious: function() { |
|
5087 var curves = this._path && this._path._curves; |
|
5088 return curves && (curves[this._segment1._index - 1] |
|
5089 || this._path._closed && curves[curves.length - 1]) || null; |
|
5090 }, |
|
5091 |
|
5092 isSelected: function() { |
|
5093 return this.getHandle1().isSelected() && this.getHandle2().isSelected(); |
|
5094 }, |
|
5095 |
|
5096 setSelected: function(selected) { |
|
5097 this.getHandle1().setSelected(selected); |
|
5098 this.getHandle2().setSelected(selected); |
|
5099 }, |
|
5100 |
|
5101 getValues: function() { |
|
5102 return Curve.getValues(this._segment1, this._segment2); |
|
5103 }, |
|
5104 |
|
5105 getPoints: function() { |
|
5106 var coords = this.getValues(), |
|
5107 points = []; |
|
5108 for (var i = 0; i < 8; i += 2) |
|
5109 points.push(new Point(coords[i], coords[i + 1])); |
|
5110 return points; |
|
5111 }, |
|
5112 |
|
5113 getLength: function() { |
|
5114 var from = arguments[0], |
|
5115 to = arguments[1], |
|
5116 fullLength = arguments.length === 0 || from === 0 && to === 1; |
|
5117 if (fullLength && this._length != null) |
|
5118 return this._length; |
|
5119 var length = Curve.getLength(this.getValues(), from, to); |
|
5120 if (fullLength) |
|
5121 this._length = length; |
|
5122 return length; |
|
5123 }, |
|
5124 |
|
5125 getArea: function() { |
|
5126 return Curve.getArea(this.getValues()); |
|
5127 }, |
|
5128 |
|
5129 getPart: function(from, to) { |
|
5130 return new Curve(Curve.getPart(this.getValues(), from, to)); |
|
5131 }, |
|
5132 |
|
5133 isLinear: function() { |
|
5134 return this._segment1._handleOut.isZero() |
|
5135 && this._segment2._handleIn.isZero(); |
|
5136 }, |
|
5137 |
|
5138 getIntersections: function(curve) { |
|
5139 return Curve.getIntersections(this.getValues(), curve.getValues(), |
|
5140 this, curve, []); |
|
5141 }, |
|
5142 |
|
5143 reverse: function() { |
|
5144 return new Curve(this._segment2.reverse(), this._segment1.reverse()); |
|
5145 }, |
|
5146 |
|
5147 _getParameter: function(offset, isParameter) { |
|
5148 return isParameter |
|
5149 ? offset |
|
5150 : offset && offset.curve === this |
|
5151 ? offset.parameter |
|
5152 : offset === undefined && isParameter === undefined |
|
5153 ? 0.5 |
|
5154 : this.getParameterAt(offset, 0); |
|
5155 }, |
|
5156 |
|
5157 divide: function(offset, isParameter) { |
|
5158 var parameter = this._getParameter(offset, isParameter), |
|
5159 tolerance = 0.00001, |
|
5160 res = null; |
|
5161 if (parameter > tolerance && parameter < 1 - tolerance) { |
|
5162 var parts = Curve.subdivide(this.getValues(), parameter), |
|
5163 isLinear = this.isLinear(), |
|
5164 left = parts[0], |
|
5165 right = parts[1]; |
|
5166 |
|
5167 if (!isLinear) { |
|
5168 this._segment1._handleOut.set(left[2] - left[0], |
|
5169 left[3] - left[1]); |
|
5170 this._segment2._handleIn.set(right[4] - right[6], |
|
5171 right[5] - right[7]); |
|
5172 } |
|
5173 |
|
5174 var x = left[6], y = left[7], |
|
5175 segment = new Segment(new Point(x, y), |
|
5176 !isLinear && new Point(left[4] - x, left[5] - y), |
|
5177 !isLinear && new Point(right[2] - x, right[3] - y)); |
|
5178 |
|
5179 if (this._path) { |
|
5180 if (this._segment1._index > 0 && this._segment2._index === 0) { |
|
5181 this._path.add(segment); |
|
5182 } else { |
|
5183 this._path.insert(this._segment2._index, segment); |
|
5184 } |
|
5185 res = this; |
|
5186 } else { |
|
5187 var end = this._segment2; |
|
5188 this._segment2 = segment; |
|
5189 res = new Curve(segment, end); |
|
5190 } |
|
5191 } |
|
5192 return res; |
|
5193 }, |
|
5194 |
|
5195 split: function(offset, isParameter) { |
|
5196 return this._path |
|
5197 ? this._path.split(this._segment1._index, |
|
5198 this._getParameter(offset, isParameter)) |
|
5199 : null; |
|
5200 }, |
|
5201 |
|
5202 clone: function() { |
|
5203 return new Curve(this._segment1, this._segment2); |
|
5204 }, |
|
5205 |
|
5206 toString: function() { |
|
5207 var parts = [ 'point1: ' + this._segment1._point ]; |
|
5208 if (!this._segment1._handleOut.isZero()) |
|
5209 parts.push('handle1: ' + this._segment1._handleOut); |
|
5210 if (!this._segment2._handleIn.isZero()) |
|
5211 parts.push('handle2: ' + this._segment2._handleIn); |
|
5212 parts.push('point2: ' + this._segment2._point); |
|
5213 return '{ ' + parts.join(', ') + ' }'; |
|
5214 }, |
|
5215 |
|
5216 statics: { |
|
5217 getValues: function(segment1, segment2) { |
|
5218 var p1 = segment1._point, |
|
5219 h1 = segment1._handleOut, |
|
5220 h2 = segment2._handleIn, |
|
5221 p2 = segment2._point; |
|
5222 return [ |
|
5223 p1._x, p1._y, |
|
5224 p1._x + h1._x, p1._y + h1._y, |
|
5225 p2._x + h2._x, p2._y + h2._y, |
|
5226 p2._x, p2._y |
|
5227 ]; |
|
5228 }, |
|
5229 |
|
5230 evaluate: function(v, t, type) { |
|
5231 var p1x = v[0], p1y = v[1], |
|
5232 c1x = v[2], c1y = v[3], |
|
5233 c2x = v[4], c2y = v[5], |
|
5234 p2x = v[6], p2y = v[7], |
|
5235 x, y; |
|
5236 |
|
5237 if (type === 0 && (t === 0 || t === 1)) { |
|
5238 x = t === 0 ? p1x : p2x; |
|
5239 y = t === 0 ? p1y : p2y; |
|
5240 } else { |
|
5241 var cx = 3 * (c1x - p1x), |
|
5242 bx = 3 * (c2x - c1x) - cx, |
|
5243 ax = p2x - p1x - cx - bx, |
|
5244 |
|
5245 cy = 3 * (c1y - p1y), |
|
5246 by = 3 * (c2y - c1y) - cy, |
|
5247 ay = p2y - p1y - cy - by; |
|
5248 if (type === 0) { |
|
5249 x = ((ax * t + bx) * t + cx) * t + p1x; |
|
5250 y = ((ay * t + by) * t + cy) * t + p1y; |
|
5251 } else { |
|
5252 var tolerance = 0.00001; |
|
5253 if (t < tolerance && c1x === p1x && c1y === p1y |
|
5254 || t > 1 - tolerance && c2x === p2x && c2y === p2y) { |
|
5255 x = p2x - p1x; |
|
5256 y = p2y - p1y; |
|
5257 } else { |
|
5258 x = (3 * ax * t + 2 * bx) * t + cx; |
|
5259 y = (3 * ay * t + 2 * by) * t + cy; |
|
5260 } |
|
5261 if (type === 3) { |
|
5262 var x2 = 6 * ax * t + 2 * bx, |
|
5263 y2 = 6 * ay * t + 2 * by; |
|
5264 return (x * y2 - y * x2) / Math.pow(x * x + y * y, 3 / 2); |
|
5265 } |
|
5266 } |
|
5267 } |
|
5268 return type == 2 ? new Point(y, -x) : new Point(x, y); |
|
5269 }, |
|
5270 |
|
5271 subdivide: function(v, t) { |
|
5272 var p1x = v[0], p1y = v[1], |
|
5273 c1x = v[2], c1y = v[3], |
|
5274 c2x = v[4], c2y = v[5], |
|
5275 p2x = v[6], p2y = v[7]; |
|
5276 if (t === undefined) |
|
5277 t = 0.5; |
|
5278 var u = 1 - t, |
|
5279 p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y, |
|
5280 p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y, |
|
5281 p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y, |
|
5282 p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y, |
|
5283 p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y, |
|
5284 p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y; |
|
5285 return [ |
|
5286 [p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y], |
|
5287 [p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y] |
|
5288 ]; |
|
5289 }, |
|
5290 |
|
5291 solveCubic: function (v, coord, val, roots, min, max) { |
|
5292 var p1 = v[coord], |
|
5293 c1 = v[coord + 2], |
|
5294 c2 = v[coord + 4], |
|
5295 p2 = v[coord + 6], |
|
5296 c = 3 * (c1 - p1), |
|
5297 b = 3 * (c2 - c1) - c, |
|
5298 a = p2 - p1 - c - b; |
|
5299 return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max); |
|
5300 }, |
|
5301 |
|
5302 getParameterOf: function(v, x, y) { |
|
5303 if (Math.abs(v[0] - x) < 0.00001 |
|
5304 && Math.abs(v[1] - y) < 0.00001) |
|
5305 return 0; |
|
5306 if (Math.abs(v[6] - x) < 0.00001 |
|
5307 && Math.abs(v[7] - y) < 0.00001) |
|
5308 return 1; |
|
5309 var txs = [], |
|
5310 tys = [], |
|
5311 sx = Curve.solveCubic(v, 0, x, txs), |
|
5312 sy = Curve.solveCubic(v, 1, y, tys), |
|
5313 tx, ty; |
|
5314 for (var cx = 0; sx == -1 || cx < sx;) { |
|
5315 if (sx == -1 || (tx = txs[cx++]) >= 0 && tx <= 1) { |
|
5316 for (var cy = 0; sy == -1 || cy < sy;) { |
|
5317 if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) { |
|
5318 if (sx == -1) tx = ty; |
|
5319 else if (sy == -1) ty = tx; |
|
5320 if (Math.abs(tx - ty) < 0.00001) |
|
5321 return (tx + ty) * 0.5; |
|
5322 } |
|
5323 } |
|
5324 if (sx == -1) |
|
5325 break; |
|
5326 } |
|
5327 } |
|
5328 return null; |
|
5329 }, |
|
5330 |
|
5331 getPart: function(v, from, to) { |
|
5332 if (from > 0) |
|
5333 v = Curve.subdivide(v, from)[1]; |
|
5334 if (to < 1) |
|
5335 v = Curve.subdivide(v, (to - from) / (1 - from))[0]; |
|
5336 return v; |
|
5337 }, |
|
5338 |
|
5339 isLinear: function(v) { |
|
5340 var isZero = Numerical.isZero; |
|
5341 return isZero(v[0] - v[2]) && isZero(v[1] - v[3]) |
|
5342 && isZero(v[4] - v[6]) && isZero(v[5] - v[7]); |
|
5343 }, |
|
5344 |
|
5345 isFlatEnough: function(v, tolerance) { |
|
5346 var p1x = v[0], p1y = v[1], |
|
5347 c1x = v[2], c1y = v[3], |
|
5348 c2x = v[4], c2y = v[5], |
|
5349 p2x = v[6], p2y = v[7], |
|
5350 ux = 3 * c1x - 2 * p1x - p2x, |
|
5351 uy = 3 * c1y - 2 * p1y - p2y, |
|
5352 vx = 3 * c2x - 2 * p2x - p1x, |
|
5353 vy = 3 * c2y - 2 * p2y - p1y; |
|
5354 return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy) |
|
5355 < 10 * tolerance * tolerance; |
|
5356 }, |
|
5357 |
|
5358 getArea: function(v) { |
|
5359 var p1x = v[0], p1y = v[1], |
|
5360 c1x = v[2], c1y = v[3], |
|
5361 c2x = v[4], c2y = v[5], |
|
5362 p2x = v[6], p2y = v[7]; |
|
5363 return ( 3.0 * c1y * p1x - 1.5 * c1y * c2x |
|
5364 - 1.5 * c1y * p2x - 3.0 * p1y * c1x |
|
5365 - 1.5 * p1y * c2x - 0.5 * p1y * p2x |
|
5366 + 1.5 * c2y * p1x + 1.5 * c2y * c1x |
|
5367 - 3.0 * c2y * p2x + 0.5 * p2y * p1x |
|
5368 + 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10; |
|
5369 }, |
|
5370 |
|
5371 getBounds: function(v) { |
|
5372 var min = v.slice(0, 2), |
|
5373 max = min.slice(), |
|
5374 roots = [0, 0]; |
|
5375 for (var i = 0; i < 2; i++) |
|
5376 Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6], |
|
5377 i, 0, min, max, roots); |
|
5378 return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]); |
|
5379 }, |
|
5380 |
|
5381 _addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) { |
|
5382 function add(value, padding) { |
|
5383 var left = value - padding, |
|
5384 right = value + padding; |
|
5385 if (left < min[coord]) |
|
5386 min[coord] = left; |
|
5387 if (right > max[coord]) |
|
5388 max[coord] = right; |
|
5389 } |
|
5390 var a = 3 * (v1 - v2) - v0 + v3, |
|
5391 b = 2 * (v0 + v2) - 4 * v1, |
|
5392 c = v1 - v0, |
|
5393 count = Numerical.solveQuadratic(a, b, c, roots), |
|
5394 tMin = 0.00001, |
|
5395 tMax = 1 - tMin; |
|
5396 add(v3, 0); |
|
5397 for (var i = 0; i < count; i++) { |
|
5398 var t = roots[i], |
|
5399 u = 1 - t; |
|
5400 if (tMin < t && t < tMax) |
|
5401 add(u * u * u * v0 |
|
5402 + 3 * u * u * t * v1 |
|
5403 + 3 * u * t * t * v2 |
|
5404 + t * t * t * v3, |
|
5405 padding); |
|
5406 } |
|
5407 }, |
|
5408 |
|
5409 _getWinding: function(v, prev, x, y, roots1, roots2) { |
|
5410 var tolerance = 0.00001, |
|
5411 abs = Math.abs; |
|
5412 |
|
5413 function getDirection(v) { |
|
5414 var y0 = v[1], |
|
5415 y1 = v[7], |
|
5416 dir = y0 > y1 ? -1 : 1; |
|
5417 return dir === 1 && (y < y0 || y > y1) |
|
5418 || dir === -1 && (y < y1 || y > y0) |
|
5419 ? 0 |
|
5420 : dir; |
|
5421 } |
|
5422 |
|
5423 if (Curve.isLinear(v)) { |
|
5424 var dir = getDirection(v); |
|
5425 if (!dir) |
|
5426 return 0; |
|
5427 var cross = (v[6] - v[0]) * (y - v[1]) - (v[7] - v[1]) * (x - v[0]); |
|
5428 return (cross < -tolerance ? -1 : 1) == dir ? 0 : dir; |
|
5429 } |
|
5430 |
|
5431 var y0 = v[1], |
|
5432 y1 = v[3], |
|
5433 y2 = v[5], |
|
5434 y3 = v[7]; |
|
5435 var a = 3 * (y1 - y2) - y0 + y3, |
|
5436 b = 2 * (y0 + y2) - 4 * y1, |
|
5437 c = y1 - y0; |
|
5438 var count = Numerical.solveQuadratic(a, b, c, roots1, tolerance, |
|
5439 1 - tolerance), |
|
5440 part, |
|
5441 rest = v, |
|
5442 t1 = roots1[0], |
|
5443 winding = 0; |
|
5444 for (var i = 0; i <= count; i++) { |
|
5445 if (i === count) { |
|
5446 part = rest; |
|
5447 } else { |
|
5448 var curves = Curve.subdivide(rest, t1); |
|
5449 part = curves[0]; |
|
5450 rest = curves[1]; |
|
5451 t1 = roots1[i]; |
|
5452 t1 = (roots1[i + 1] - t1) / (1 - t1); |
|
5453 } |
|
5454 if (i > 0) |
|
5455 part[3] = part[1]; |
|
5456 if (i < count) |
|
5457 part[5] = rest[1]; |
|
5458 var dir = getDirection(part); |
|
5459 if (!dir) |
|
5460 continue; |
|
5461 var t2, |
|
5462 px; |
|
5463 if (Curve.solveCubic(part, 1, y, roots2, -tolerance, 1 + -tolerance) |
|
5464 === 1) { |
|
5465 t2 = roots2[0]; |
|
5466 px = Curve.evaluate(part, t2, 0).x; |
|
5467 } else { |
|
5468 var mid = (part[1] + part[7]) / 2; |
|
5469 t2 = y < mid && dir > 0 ? 0 : 1; |
|
5470 if (t2 === 1 && y == part[7]) |
|
5471 continue; |
|
5472 px = t2 === 0 ? part[0] : part[6]; |
|
5473 } |
|
5474 var slope = Curve.evaluate(part, t2, 1).y, |
|
5475 stationary = abs(slope) < tolerance || t2 < tolerance |
|
5476 && Curve.evaluate(prev, 1, 1).y * slope < 0; |
|
5477 if (x >= px + (stationary ? -tolerance : tolerance * dir) |
|
5478 && !(stationary && (abs(t2) < tolerance |
|
5479 && abs(x - part[0]) > tolerance |
|
5480 || abs(t2 - 1) < tolerance |
|
5481 && abs(x - part[6]) > tolerance))) { |
|
5482 winding += stationary && abs(t2 - (dir > 0 ? 1 : 0)) < tolerance |
|
5483 ? -dir : dir; |
|
5484 } |
|
5485 prev = part; |
|
5486 } |
|
5487 return winding; |
|
5488 } |
|
5489 }}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'], |
|
5490 function(name) { |
|
5491 this[name] = function() { |
|
5492 if (!this._bounds) |
|
5493 this._bounds = {}; |
|
5494 var bounds = this._bounds[name]; |
|
5495 if (!bounds) { |
|
5496 bounds = this._bounds[name] = Path[name]([this._segment1, |
|
5497 this._segment2], false, this._path.getStyle()); |
|
5498 } |
|
5499 return bounds.clone(); |
|
5500 }; |
|
5501 }, |
|
5502 { |
|
5503 |
|
5504 }), Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'], |
|
5505 function(name, index) { |
|
5506 this[name + 'At'] = function(offset, isParameter) { |
|
5507 var values = this.getValues(); |
|
5508 return Curve.evaluate(values, isParameter |
|
5509 ? offset : Curve.getParameterAt(values, offset, 0), index); |
|
5510 }; |
|
5511 this[name] = function(parameter) { |
|
5512 return Curve.evaluate(this.getValues(), parameter, index); |
|
5513 }; |
|
5514 }, |
|
5515 { |
|
5516 getParameterAt: function(offset, start) { |
|
5517 return Curve.getParameterAt(this.getValues(), offset, |
|
5518 start !== undefined ? start : offset < 0 ? 1 : 0); |
|
5519 }, |
|
5520 |
|
5521 getParameterOf: function(point) { |
|
5522 point = Point.read(arguments); |
|
5523 return Curve.getParameterOf(this.getValues(), point.x, point.y); |
|
5524 }, |
|
5525 |
|
5526 getLocationAt: function(offset, isParameter) { |
|
5527 if (!isParameter) |
|
5528 offset = this.getParameterAt(offset); |
|
5529 return new CurveLocation(this, offset); |
|
5530 }, |
|
5531 |
|
5532 getLocationOf: function(point) { |
|
5533 point = Point.read(arguments); |
|
5534 var t = this.getParameterOf(point); |
|
5535 return t != null ? new CurveLocation(this, t) : null; |
|
5536 }, |
|
5537 |
|
5538 getNearestLocation: function(point) { |
|
5539 point = Point.read(arguments); |
|
5540 var values = this.getValues(), |
|
5541 count = 100, |
|
5542 tolerance = Numerical.TOLERANCE, |
|
5543 minDist = Infinity, |
|
5544 minT = 0; |
|
5545 |
|
5546 function refine(t) { |
|
5547 if (t >= 0 && t <= 1) { |
|
5548 var dist = point.getDistance( |
|
5549 Curve.evaluate(values, t, 0), true); |
|
5550 if (dist < minDist) { |
|
5551 minDist = dist; |
|
5552 minT = t; |
|
5553 return true; |
|
5554 } |
|
5555 } |
|
5556 } |
|
5557 |
|
5558 for (var i = 0; i <= count; i++) |
|
5559 refine(i / count); |
|
5560 |
|
5561 var step = 1 / (count * 2); |
|
5562 while (step > tolerance) { |
|
5563 if (!refine(minT - step) && !refine(minT + step)) |
|
5564 step /= 2; |
|
5565 } |
|
5566 var pt = Curve.evaluate(values, minT, 0); |
|
5567 return new CurveLocation(this, minT, pt, null, null, null, |
|
5568 point.getDistance(pt)); |
|
5569 }, |
|
5570 |
|
5571 getNearestPoint: function(point) { |
|
5572 point = Point.read(arguments); |
|
5573 return this.getNearestLocation(point).getPoint(); |
|
5574 } |
|
5575 |
|
5576 }), |
|
5577 new function() { |
|
5578 |
|
5579 function getLengthIntegrand(v) { |
|
5580 var p1x = v[0], p1y = v[1], |
|
5581 c1x = v[2], c1y = v[3], |
|
5582 c2x = v[4], c2y = v[5], |
|
5583 p2x = v[6], p2y = v[7], |
|
5584 |
|
5585 ax = 9 * (c1x - c2x) + 3 * (p2x - p1x), |
|
5586 bx = 6 * (p1x + c2x) - 12 * c1x, |
|
5587 cx = 3 * (c1x - p1x), |
|
5588 |
|
5589 ay = 9 * (c1y - c2y) + 3 * (p2y - p1y), |
|
5590 by = 6 * (p1y + c2y) - 12 * c1y, |
|
5591 cy = 3 * (c1y - p1y); |
|
5592 |
|
5593 return function(t) { |
|
5594 var dx = (ax * t + bx) * t + cx, |
|
5595 dy = (ay * t + by) * t + cy; |
|
5596 return Math.sqrt(dx * dx + dy * dy); |
|
5597 }; |
|
5598 } |
|
5599 |
|
5600 function getIterations(a, b) { |
|
5601 return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32))); |
|
5602 } |
|
5603 |
|
5604 return { |
|
5605 statics: true, |
|
5606 |
|
5607 getLength: function(v, a, b) { |
|
5608 if (a === undefined) |
|
5609 a = 0; |
|
5610 if (b === undefined) |
|
5611 b = 1; |
|
5612 var isZero = Numerical.isZero; |
|
5613 if (isZero(v[0] - v[2]) && isZero(v[1] - v[3]) |
|
5614 && isZero(v[6] - v[4]) && isZero(v[7] - v[5])) { |
|
5615 var dx = v[6] - v[0], |
|
5616 dy = v[7] - v[1]; |
|
5617 return (b - a) * Math.sqrt(dx * dx + dy * dy); |
|
5618 } |
|
5619 var ds = getLengthIntegrand(v); |
|
5620 return Numerical.integrate(ds, a, b, getIterations(a, b)); |
|
5621 }, |
|
5622 |
|
5623 getParameterAt: function(v, offset, start) { |
|
5624 if (offset === 0) |
|
5625 return start; |
|
5626 var forward = offset > 0, |
|
5627 a = forward ? start : 0, |
|
5628 b = forward ? 1 : start, |
|
5629 offset = Math.abs(offset), |
|
5630 ds = getLengthIntegrand(v), |
|
5631 rangeLength = Numerical.integrate(ds, a, b, |
|
5632 getIterations(a, b)); |
|
5633 if (offset >= rangeLength) |
|
5634 return forward ? b : a; |
|
5635 var guess = offset / rangeLength, |
|
5636 length = 0; |
|
5637 function f(t) { |
|
5638 var count = getIterations(start, t); |
|
5639 length += start < t |
|
5640 ? Numerical.integrate(ds, start, t, count) |
|
5641 : -Numerical.integrate(ds, t, start, count); |
|
5642 start = t; |
|
5643 return length - offset; |
|
5644 } |
|
5645 return Numerical.findRoot(f, ds, |
|
5646 forward ? a + guess : b - guess, |
|
5647 a, b, 16, 0.00001); |
|
5648 } |
|
5649 }; |
|
5650 }, new function() { |
|
5651 function addLocation(locations, curve1, t1, point1, curve2, t2, point2) { |
|
5652 var first = locations[0], |
|
5653 last = locations[locations.length - 1]; |
|
5654 if ((!first || !point1.isClose(first._point, Numerical.EPSILON)) |
|
5655 && (!last || !point1.isClose(last._point, Numerical.EPSILON))) |
|
5656 locations.push( |
|
5657 new CurveLocation(curve1, t1, point1, curve2, t2, point2)); |
|
5658 } |
|
5659 |
|
5660 function addCurveIntersections(v1, v2, curve1, curve2, locations, |
|
5661 range1, range2, recursion) { |
|
5662 recursion = (recursion || 0) + 1; |
|
5663 if (recursion > 20) |
|
5664 return; |
|
5665 range1 = range1 || [ 0, 1 ]; |
|
5666 range2 = range2 || [ 0, 1 ]; |
|
5667 var part1 = Curve.getPart(v1, range1[0], range1[1]), |
|
5668 part2 = Curve.getPart(v2, range2[0], range2[1]), |
|
5669 iteration = 0; |
|
5670 while (iteration++ < 20) { |
|
5671 var range, |
|
5672 intersects1 = clipFatLine(part1, part2, range = range2.slice()), |
|
5673 intersects2 = 0; |
|
5674 if (intersects1 === 0) |
|
5675 break; |
|
5676 if (intersects1 > 0) { |
|
5677 range2 = range; |
|
5678 part2 = Curve.getPart(v2, range2[0], range2[1]); |
|
5679 intersects2 = clipFatLine(part2, part1, range = range1.slice()); |
|
5680 if (intersects2 === 0) |
|
5681 break; |
|
5682 if (intersects1 > 0) { |
|
5683 range1 = range; |
|
5684 part1 = Curve.getPart(v1, range1[0], range1[1]); |
|
5685 } |
|
5686 } |
|
5687 if (intersects1 < 0 || intersects2 < 0) { |
|
5688 if (range1[1] - range1[0] > range2[1] - range2[0]) { |
|
5689 var t = (range1[0] + range1[1]) / 2; |
|
5690 addCurveIntersections(v1, v2, curve1, curve2, locations, |
|
5691 [ range1[0], t ], range2, recursion); |
|
5692 addCurveIntersections(v1, v2, curve1, curve2, locations, |
|
5693 [ t, range1[1] ], range2, recursion); |
|
5694 break; |
|
5695 } else { |
|
5696 var t = (range2[0] + range2[1]) / 2; |
|
5697 addCurveIntersections(v1, v2, curve1, curve2, locations, |
|
5698 range1, [ range2[0], t ], recursion); |
|
5699 addCurveIntersections(v1, v2, curve1, curve2, locations, |
|
5700 range1, [ t, range2[1] ], recursion); |
|
5701 break; |
|
5702 } |
|
5703 } |
|
5704 if (Math.abs(range1[1] - range1[0]) <= 0.00001 && |
|
5705 Math.abs(range2[1] - range2[0]) <= 0.00001) { |
|
5706 var t1 = (range1[0] + range1[1]) / 2, |
|
5707 t2 = (range2[0] + range2[1]) / 2; |
|
5708 addLocation(locations, |
|
5709 curve1, t1, Curve.evaluate(v1, t1, 0), |
|
5710 curve2, t2, Curve.evaluate(v2, t2, 0)); |
|
5711 break; |
|
5712 } |
|
5713 } |
|
5714 } |
|
5715 |
|
5716 function clipFatLine(v1, v2, range2) { |
|
5717 var p0x = v1[0], p0y = v1[1], p1x = v1[2], p1y = v1[3], |
|
5718 p2x = v1[4], p2y = v1[5], p3x = v1[6], p3y = v1[7], |
|
5719 q0x = v2[0], q0y = v2[1], q1x = v2[2], q1y = v2[3], |
|
5720 q2x = v2[4], q2y = v2[5], q3x = v2[6], q3y = v2[7], |
|
5721 getSignedDistance = Line.getSignedDistance, |
|
5722 d1 = getSignedDistance(p0x, p0y, p3x, p3y, p1x, p1y) || 0, |
|
5723 d2 = getSignedDistance(p0x, p0y, p3x, p3y, p2x, p2y) || 0, |
|
5724 factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9, |
|
5725 dmin = factor * Math.min(0, d1, d2), |
|
5726 dmax = factor * Math.max(0, d1, d2), |
|
5727 dq0 = getSignedDistance(p0x, p0y, p3x, p3y, q0x, q0y), |
|
5728 dq1 = getSignedDistance(p0x, p0y, p3x, p3y, q1x, q1y), |
|
5729 dq2 = getSignedDistance(p0x, p0y, p3x, p3y, q2x, q2y), |
|
5730 dq3 = getSignedDistance(p0x, p0y, p3x, p3y, q3x, q3y); |
|
5731 if (dmin > Math.max(dq0, dq1, dq2, dq3) |
|
5732 || dmax < Math.min(dq0, dq1, dq2, dq3)) |
|
5733 return 0; |
|
5734 var hull = getConvexHull(dq0, dq1, dq2, dq3), |
|
5735 swap; |
|
5736 if (dq3 < dq0) { |
|
5737 swap = dmin; |
|
5738 dmin = dmax; |
|
5739 dmax = swap; |
|
5740 } |
|
5741 var tmaxdmin = -Infinity, |
|
5742 tmin = Infinity, |
|
5743 tmax = -Infinity; |
|
5744 for (var i = 0, l = hull.length; i < l; i++) { |
|
5745 var p1 = hull[i], |
|
5746 p2 = hull[(i + 1) % l]; |
|
5747 if (p2[1] < p1[1]) { |
|
5748 swap = p2; |
|
5749 p2 = p1; |
|
5750 p1 = swap; |
|
5751 } |
|
5752 var x1 = p1[0], |
|
5753 y1 = p1[1], |
|
5754 x2 = p2[0], |
|
5755 y2 = p2[1]; |
|
5756 var inv = (y2 - y1) / (x2 - x1); |
|
5757 if (dmin >= y1 && dmin <= y2) { |
|
5758 var ixdx = x1 + (dmin - y1) / inv; |
|
5759 if (ixdx < tmin) |
|
5760 tmin = ixdx; |
|
5761 if (ixdx > tmaxdmin) |
|
5762 tmaxdmin = ixdx; |
|
5763 } |
|
5764 if (dmax >= y1 && dmax <= y2) { |
|
5765 var ixdx = x1 + (dmax - y1) / inv; |
|
5766 if (ixdx > tmax) |
|
5767 tmax = ixdx; |
|
5768 if (ixdx < tmin) |
|
5769 tmin = 0; |
|
5770 } |
|
5771 } |
|
5772 if (tmin !== Infinity && tmax !== -Infinity) { |
|
5773 var min = Math.min(dmin, dmax), |
|
5774 max = Math.max(dmin, dmax); |
|
5775 if (dq3 > min && dq3 < max) |
|
5776 tmax = 1; |
|
5777 if (dq0 > min && dq0 < max) |
|
5778 tmin = 0; |
|
5779 if (tmaxdmin > tmax) |
|
5780 tmax = 1; |
|
5781 var v2tmin = range2[0], |
|
5782 tdiff = range2[1] - v2tmin; |
|
5783 range2[0] = v2tmin + tmin * tdiff; |
|
5784 range2[1] = v2tmin + tmax * tdiff; |
|
5785 if ((tdiff - (range2[1] - range2[0])) / tdiff >= 0.2) |
|
5786 return 1; |
|
5787 } |
|
5788 if (Curve.getBounds(v1).touches(Curve.getBounds(v2))) |
|
5789 return -1; |
|
5790 return 0; |
|
5791 } |
|
5792 |
|
5793 function getConvexHull(dq0, dq1, dq2, dq3) { |
|
5794 var p0 = [ 0, dq0 ], |
|
5795 p1 = [ 1 / 3, dq1 ], |
|
5796 p2 = [ 2 / 3, dq2 ], |
|
5797 p3 = [ 1, dq3 ], |
|
5798 getSignedDistance = Line.getSignedDistance, |
|
5799 dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1), |
|
5800 dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2); |
|
5801 if (dist1 * dist2 < 0) { |
|
5802 return [ p0, p1, p3, p2 ]; |
|
5803 } |
|
5804 var pmax, cross; |
|
5805 if (Math.abs(dist1) > Math.abs(dist2)) { |
|
5806 pmax = p1; |
|
5807 cross = (dq3 - dq2 - (dq3 - dq0) / 3) |
|
5808 * (2 * (dq3 - dq2) - dq3 + dq1) / 3; |
|
5809 } else { |
|
5810 pmax = p2; |
|
5811 cross = (dq1 - dq0 + (dq0 - dq3) / 3) |
|
5812 * (-2 * (dq0 - dq1) + dq0 - dq2) / 3; |
|
5813 } |
|
5814 return cross < 0 |
|
5815 ? [ p0, pmax, p3 ] |
|
5816 : [ p0, p1, p2, p3 ]; |
|
5817 } |
|
5818 |
|
5819 function addCurveLineIntersections(v1, v2, curve1, curve2, locations) { |
|
5820 var flip = Curve.isLinear(v1), |
|
5821 vc = flip ? v2 : v1, |
|
5822 vl = flip ? v1 : v2, |
|
5823 lx1 = vl[0], ly1 = vl[1], |
|
5824 lx2 = vl[6], ly2 = vl[7], |
|
5825 ldx = lx2 - lx1, |
|
5826 ldy = ly2 - ly1, |
|
5827 angle = Math.atan2(-ldy, ldx), |
|
5828 sin = Math.sin(angle), |
|
5829 cos = Math.cos(angle), |
|
5830 rlx2 = ldx * cos - ldy * sin, |
|
5831 rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0], |
|
5832 rvc = []; |
|
5833 for(var i = 0; i < 8; i += 2) { |
|
5834 var x = vc[i] - lx1, |
|
5835 y = vc[i + 1] - ly1; |
|
5836 rvc.push( |
|
5837 x * cos - y * sin, |
|
5838 y * cos + x * sin); |
|
5839 } |
|
5840 var roots = [], |
|
5841 count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1); |
|
5842 for (var i = 0; i < count; i++) { |
|
5843 var tc = roots[i], |
|
5844 x = Curve.evaluate(rvc, tc, 0).x; |
|
5845 if (x >= 0 && x <= rlx2) { |
|
5846 var tl = Curve.getParameterOf(rvl, x, 0), |
|
5847 t1 = flip ? tl : tc, |
|
5848 t2 = flip ? tc : tl; |
|
5849 addLocation(locations, |
|
5850 curve1, t1, Curve.evaluate(v1, t1, 0), |
|
5851 curve2, t2, Curve.evaluate(v2, t2, 0)); |
|
5852 } |
|
5853 } |
|
5854 } |
|
5855 |
|
5856 function addLineIntersection(v1, v2, curve1, curve2, locations) { |
|
5857 var point = Line.intersect( |
|
5858 v1[0], v1[1], v1[6], v1[7], |
|
5859 v2[0], v2[1], v2[6], v2[7]); |
|
5860 if (point) |
|
5861 addLocation(locations, curve1, null, point, curve2); |
|
5862 } |
|
5863 |
|
5864 return { statics: { |
|
5865 getIntersections: function(v1, v2, curve1, curve2, locations) { |
|
5866 var linear1 = Curve.isLinear(v1), |
|
5867 linear2 = Curve.isLinear(v2), |
|
5868 c1p1 = curve1.getPoint1(), |
|
5869 c1p2 = curve1.getPoint2(), |
|
5870 c2p1 = curve2.getPoint1(), |
|
5871 c2p2 = curve2.getPoint2(), |
|
5872 tolerance = 0.00001; |
|
5873 if (c1p1.isClose(c2p1, tolerance)) |
|
5874 addLocation(locations, curve1, 0, c1p1, curve2, 0, c1p1); |
|
5875 if (c1p1.isClose(c2p2, tolerance)) |
|
5876 addLocation(locations, curve1, 0, c1p1, curve2, 1, c1p1); |
|
5877 (linear1 && linear2 |
|
5878 ? addLineIntersection |
|
5879 : linear1 || linear2 |
|
5880 ? addCurveLineIntersections |
|
5881 : addCurveIntersections)(v1, v2, curve1, curve2, locations); |
|
5882 if (c1p2.isClose(c2p1, tolerance)) |
|
5883 addLocation(locations, curve1, 1, c1p2, curve2, 0, c1p2); |
|
5884 if (c1p2.isClose(c2p2, tolerance)) |
|
5885 addLocation(locations, curve1, 1, c1p2, curve2, 1, c1p2); |
|
5886 return locations; |
|
5887 } |
|
5888 }}; |
|
5889 }); |
|
5890 |
|
5891 var CurveLocation = Base.extend({ |
|
5892 _class: 'CurveLocation', |
|
5893 initialize: function CurveLocation(curve, parameter, point, _curve2, |
|
5894 _parameter2, _point2, _distance) { |
|
5895 this._id = CurveLocation._id = (CurveLocation._id || 0) + 1; |
|
5896 this._curve = curve; |
|
5897 this._segment1 = curve._segment1; |
|
5898 this._segment2 = curve._segment2; |
|
5899 this._parameter = parameter; |
|
5900 this._point = point; |
|
5901 this._curve2 = _curve2; |
|
5902 this._parameter2 = _parameter2; |
|
5903 this._point2 = _point2; |
|
5904 this._distance = _distance; |
|
5905 }, |
|
5906 |
|
5907 getSegment: function() { |
|
5908 if (!this._segment) { |
|
5909 var curve = this.getCurve(), |
|
5910 parameter = this.getParameter(); |
|
5911 if (parameter === 1) { |
|
5912 this._segment = curve._segment2; |
|
5913 } else if (parameter === 0 || arguments[0]) { |
|
5914 this._segment = curve._segment1; |
|
5915 } else if (parameter == null) { |
|
5916 return null; |
|
5917 } else { |
|
5918 this._segment = curve.getLength(0, parameter) |
|
5919 < curve.getLength(parameter, 1) |
|
5920 ? curve._segment1 |
|
5921 : curve._segment2; |
|
5922 } |
|
5923 } |
|
5924 return this._segment; |
|
5925 }, |
|
5926 |
|
5927 getCurve: function() { |
|
5928 if (!this._curve || arguments[0]) { |
|
5929 this._curve = this._segment1.getCurve(); |
|
5930 if (this._curve.getParameterOf(this._point) == null) |
|
5931 this._curve = this._segment2.getPrevious().getCurve(); |
|
5932 } |
|
5933 return this._curve; |
|
5934 }, |
|
5935 |
|
5936 getIntersection: function() { |
|
5937 var intersection = this._intersection; |
|
5938 if (!intersection && this._curve2) { |
|
5939 var param = this._parameter2; |
|
5940 this._intersection = intersection = new CurveLocation( |
|
5941 this._curve2, param, this._point2 || this._point, this); |
|
5942 intersection._intersection = this; |
|
5943 } |
|
5944 return intersection; |
|
5945 }, |
|
5946 |
|
5947 getPath: function() { |
|
5948 var curve = this.getCurve(); |
|
5949 return curve && curve._path; |
|
5950 }, |
|
5951 |
|
5952 getIndex: function() { |
|
5953 var curve = this.getCurve(); |
|
5954 return curve && curve.getIndex(); |
|
5955 }, |
|
5956 |
|
5957 getOffset: function() { |
|
5958 var path = this.getPath(); |
|
5959 return path && path._getOffset(this); |
|
5960 }, |
|
5961 |
|
5962 getCurveOffset: function() { |
|
5963 var curve = this.getCurve(), |
|
5964 parameter = this.getParameter(); |
|
5965 return parameter != null && curve && curve.getLength(0, parameter); |
|
5966 }, |
|
5967 |
|
5968 getParameter: function() { |
|
5969 if ((this._parameter == null || arguments[0]) && this._point) { |
|
5970 var curve = this.getCurve(arguments[0] && this._point); |
|
5971 this._parameter = curve && curve.getParameterOf(this._point); |
|
5972 } |
|
5973 return this._parameter; |
|
5974 }, |
|
5975 |
|
5976 getPoint: function() { |
|
5977 if ((!this._point || arguments[0]) && this._parameter != null) { |
|
5978 var curve = this.getCurve(); |
|
5979 this._point = curve && curve.getPointAt(this._parameter, true); |
|
5980 } |
|
5981 return this._point; |
|
5982 }, |
|
5983 |
|
5984 getTangent: function() { |
|
5985 var parameter = this.getParameter(), |
|
5986 curve = this.getCurve(); |
|
5987 return parameter != null && curve && curve.getTangentAt(parameter, true); |
|
5988 }, |
|
5989 |
|
5990 getNormal: function() { |
|
5991 var parameter = this.getParameter(), |
|
5992 curve = this.getCurve(); |
|
5993 return parameter != null && curve && curve.getNormalAt(parameter, true); |
|
5994 }, |
|
5995 |
|
5996 getDistance: function() { |
|
5997 return this._distance; |
|
5998 }, |
|
5999 |
|
6000 divide: function() { |
|
6001 var curve = this.getCurve(true); |
|
6002 return curve && curve.divide(this.getParameter(true), true); |
|
6003 }, |
|
6004 |
|
6005 split: function() { |
|
6006 var curve = this.getCurve(true); |
|
6007 return curve && curve.split(this.getParameter(true), true); |
|
6008 }, |
|
6009 |
|
6010 toString: function() { |
|
6011 var parts = [], |
|
6012 point = this.getPoint(), |
|
6013 f = Formatter.instance; |
|
6014 if (point) |
|
6015 parts.push('point: ' + point); |
|
6016 var index = this.getIndex(); |
|
6017 if (index != null) |
|
6018 parts.push('index: ' + index); |
|
6019 var parameter = this.getParameter(); |
|
6020 if (parameter != null) |
|
6021 parts.push('parameter: ' + f.number(parameter)); |
|
6022 if (this._distance != null) |
|
6023 parts.push('distance: ' + f.number(this._distance)); |
|
6024 return '{ ' + parts.join(', ') + ' }'; |
|
6025 } |
|
6026 }); |
|
6027 |
|
6028 var PathItem = Item.extend({ |
|
6029 _class: 'PathItem', |
|
6030 |
|
6031 initialize: function PathItem() { |
|
6032 }, |
|
6033 |
|
6034 getIntersections: function(path) { |
|
6035 if (!this.getBounds().touches(path.getBounds())) |
|
6036 return []; |
|
6037 var locations = [], |
|
6038 curves1 = this.getCurves(), |
|
6039 curves2 = path.getCurves(), |
|
6040 length2 = curves2.length, |
|
6041 values2 = []; |
|
6042 for (var i = 0; i < length2; i++) |
|
6043 values2[i] = curves2[i].getValues(); |
|
6044 for (var i = 0, l = curves1.length; i < l; i++) { |
|
6045 var curve1 = curves1[i], |
|
6046 values1 = curve1.getValues(); |
|
6047 for (var j = 0; j < length2; j++) |
|
6048 Curve.getIntersections(values1, values2[j], curve1, curves2[j], |
|
6049 locations); |
|
6050 } |
|
6051 return locations; |
|
6052 }, |
|
6053 |
|
6054 setPathData: function(data) { |
|
6055 |
|
6056 var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig), |
|
6057 coords, |
|
6058 relative = false, |
|
6059 control, |
|
6060 current = new Point(); |
|
6061 |
|
6062 function getCoord(index, coord, isCurrent) { |
|
6063 var val = parseFloat(coords[index]); |
|
6064 if (relative) |
|
6065 val += current[coord]; |
|
6066 if (isCurrent) |
|
6067 current[coord] = val; |
|
6068 return val; |
|
6069 } |
|
6070 |
|
6071 function getPoint(index, isCurrent) { |
|
6072 return new Point( |
|
6073 getCoord(index, 'x', isCurrent), |
|
6074 getCoord(index + 1, 'y', isCurrent) |
|
6075 ); |
|
6076 } |
|
6077 |
|
6078 this.clear(); |
|
6079 |
|
6080 for (var i = 0, l = parts.length; i < l; i++) { |
|
6081 var part = parts[i], |
|
6082 cmd = part[0], |
|
6083 lower = cmd.toLowerCase(); |
|
6084 coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g); |
|
6085 var length = coords && coords.length; |
|
6086 relative = cmd === lower; |
|
6087 switch (lower) { |
|
6088 case 'm': |
|
6089 case 'l': |
|
6090 for (var j = 0; j < length; j += 2) |
|
6091 this[j === 0 && lower === 'm' ? 'moveTo' : 'lineTo']( |
|
6092 getPoint(j, true)); |
|
6093 control = current; |
|
6094 break; |
|
6095 case 'h': |
|
6096 case 'v': |
|
6097 var coord = lower == 'h' ? 'x' : 'y'; |
|
6098 for (var j = 0; j < length; j++) { |
|
6099 getCoord(j, coord, true); |
|
6100 this.lineTo(current); |
|
6101 } |
|
6102 control = current; |
|
6103 break; |
|
6104 case 'c': |
|
6105 for (var j = 0; j < length; j += 6) { |
|
6106 this.cubicCurveTo( |
|
6107 getPoint(j), |
|
6108 control = getPoint(j + 2), |
|
6109 getPoint(j + 4, true)); |
|
6110 } |
|
6111 break; |
|
6112 case 's': |
|
6113 for (var j = 0; j < length; j += 4) { |
|
6114 this.cubicCurveTo( |
|
6115 current.multiply(2).subtract(control), |
|
6116 control = getPoint(j), |
|
6117 getPoint(j + 2, true)); |
|
6118 } |
|
6119 break; |
|
6120 case 'q': |
|
6121 for (var j = 0; j < length; j += 4) { |
|
6122 this.quadraticCurveTo( |
|
6123 control = getPoint(j), |
|
6124 getPoint(j + 2, true)); |
|
6125 } |
|
6126 break; |
|
6127 case 't': |
|
6128 for (var j = 0; j < length; j += 2) { |
|
6129 this.quadraticCurveTo( |
|
6130 control = current.multiply(2).subtract(control), |
|
6131 getPoint(j, true)); |
|
6132 } |
|
6133 break; |
|
6134 case 'a': |
|
6135 break; |
|
6136 case 'z': |
|
6137 this.closePath(); |
|
6138 break; |
|
6139 } |
|
6140 } |
|
6141 }, |
|
6142 |
|
6143 _canComposite: function() { |
|
6144 return !(this.hasFill() && this.hasStroke()); |
|
6145 }, |
|
6146 |
|
6147 _contains: function(point) { |
|
6148 var winding = this._getWinding(point); |
|
6149 return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding); |
|
6150 } |
|
6151 |
|
6152 }); |
|
6153 |
|
6154 var Path = PathItem.extend({ |
|
6155 _class: 'Path', |
|
6156 _serializeFields: { |
|
6157 segments: [], |
|
6158 closed: false |
|
6159 }, |
|
6160 |
|
6161 initialize: function Path(arg) { |
|
6162 this._closed = false; |
|
6163 this._segments = []; |
|
6164 var segments = Array.isArray(arg) |
|
6165 ? typeof arg[0] === 'object' |
|
6166 ? arg |
|
6167 : arguments |
|
6168 : arg && (arg.point !== undefined && arg.size === undefined |
|
6169 || arg.x !== undefined) |
|
6170 ? arguments |
|
6171 : null; |
|
6172 this.setSegments(segments || []); |
|
6173 this._initialize(!segments && arg); |
|
6174 }, |
|
6175 |
|
6176 _equals: function(item) { |
|
6177 return Base.equals(this._segments, item._segments); |
|
6178 }, |
|
6179 |
|
6180 clone: function(insert) { |
|
6181 var copy = this._clone(new Path({ |
|
6182 segments: this._segments, |
|
6183 insert: false |
|
6184 }), insert); |
|
6185 copy._closed = this._closed; |
|
6186 if (this._clockwise !== undefined) |
|
6187 copy._clockwise = this._clockwise; |
|
6188 return copy; |
|
6189 }, |
|
6190 |
|
6191 _changed: function _changed(flags) { |
|
6192 _changed.base.call(this, flags); |
|
6193 if (flags & 4) { |
|
6194 delete this._length; |
|
6195 delete this._clockwise; |
|
6196 if (this._curves) { |
|
6197 for (var i = 0, l = this._curves.length; i < l; i++) |
|
6198 this._curves[i]._changed(5); |
|
6199 } |
|
6200 } else if (flags & 8) { |
|
6201 delete this._bounds; |
|
6202 } |
|
6203 }, |
|
6204 |
|
6205 getSegments: function() { |
|
6206 return this._segments; |
|
6207 }, |
|
6208 |
|
6209 setSegments: function(segments) { |
|
6210 var fullySelected = this.isFullySelected(); |
|
6211 this._segments.length = 0; |
|
6212 this._selectedSegmentState = 0; |
|
6213 delete this._curves; |
|
6214 this._add(Segment.readAll(segments)); |
|
6215 if (fullySelected) |
|
6216 this.setFullySelected(true); |
|
6217 }, |
|
6218 |
|
6219 getFirstSegment: function() { |
|
6220 return this._segments[0]; |
|
6221 }, |
|
6222 |
|
6223 getLastSegment: function() { |
|
6224 return this._segments[this._segments.length - 1]; |
|
6225 }, |
|
6226 |
|
6227 getCurves: function() { |
|
6228 var curves = this._curves, |
|
6229 segments = this._segments; |
|
6230 if (!curves) { |
|
6231 var length = this._countCurves(); |
|
6232 curves = this._curves = new Array(length); |
|
6233 for (var i = 0; i < length; i++) |
|
6234 curves[i] = new Curve(this, segments[i], |
|
6235 segments[i + 1] || segments[0]); |
|
6236 } |
|
6237 return curves; |
|
6238 }, |
|
6239 |
|
6240 getFirstCurve: function() { |
|
6241 return this.getCurves()[0]; |
|
6242 }, |
|
6243 |
|
6244 getLastCurve: function() { |
|
6245 var curves = this.getCurves(); |
|
6246 return curves[curves.length - 1]; |
|
6247 }, |
|
6248 |
|
6249 getPathData: function() { |
|
6250 var segments = this._segments, |
|
6251 precision = arguments[0], |
|
6252 f = Formatter.instance, |
|
6253 parts = []; |
|
6254 |
|
6255 function addCurve(seg1, seg2, skipLine) { |
|
6256 var point1 = seg1._point, |
|
6257 point2 = seg2._point, |
|
6258 handle1 = seg1._handleOut, |
|
6259 handle2 = seg2._handleIn; |
|
6260 if (handle1.isZero() && handle2.isZero()) { |
|
6261 if (!skipLine) { |
|
6262 parts.push('L' + f.point(point2, precision)); |
|
6263 } |
|
6264 } else { |
|
6265 var end = point2.subtract(point1); |
|
6266 parts.push('c' + f.point(handle1, precision) |
|
6267 + ' ' + f.point(end.add(handle2), precision) |
|
6268 + ' ' + f.point(end, precision)); |
|
6269 } |
|
6270 } |
|
6271 |
|
6272 if (segments.length === 0) |
|
6273 return ''; |
|
6274 parts.push('M' + f.point(segments[0]._point)); |
|
6275 for (var i = 0, l = segments.length - 1; i < l; i++) |
|
6276 addCurve(segments[i], segments[i + 1], false); |
|
6277 if (this._closed) { |
|
6278 addCurve(segments[segments.length - 1], segments[0], true); |
|
6279 parts.push('z'); |
|
6280 } |
|
6281 return parts.join(''); |
|
6282 }, |
|
6283 |
|
6284 isClosed: function() { |
|
6285 return this._closed; |
|
6286 }, |
|
6287 |
|
6288 setClosed: function(closed) { |
|
6289 if (this._closed != (closed = !!closed)) { |
|
6290 this._closed = closed; |
|
6291 if (this._curves) { |
|
6292 var length = this._curves.length = this._countCurves(); |
|
6293 if (closed) |
|
6294 this._curves[length - 1] = new Curve(this, |
|
6295 this._segments[length - 1], this._segments[0]); |
|
6296 } |
|
6297 this._changed(5); |
|
6298 } |
|
6299 }, |
|
6300 |
|
6301 isEmpty: function() { |
|
6302 return this._segments.length === 0; |
|
6303 }, |
|
6304 |
|
6305 isPolygon: function() { |
|
6306 for (var i = 0, l = this._segments.length; i < l; i++) { |
|
6307 if (!this._segments[i].isLinear()) |
|
6308 return false; |
|
6309 } |
|
6310 return true; |
|
6311 }, |
|
6312 |
|
6313 _applyMatrix: function(matrix) { |
|
6314 var coords = new Array(6); |
|
6315 for (var i = 0, l = this._segments.length; i < l; i++) |
|
6316 this._segments[i]._transformCoordinates(matrix, coords, true); |
|
6317 return true; |
|
6318 }, |
|
6319 |
|
6320 _add: function(segs, index) { |
|
6321 var segments = this._segments, |
|
6322 curves = this._curves, |
|
6323 amount = segs.length, |
|
6324 append = index == null, |
|
6325 index = append ? segments.length : index; |
|
6326 for (var i = 0; i < amount; i++) { |
|
6327 var segment = segs[i]; |
|
6328 if (segment._path) |
|
6329 segment = segs[i] = segment.clone(); |
|
6330 segment._path = this; |
|
6331 segment._index = index + i; |
|
6332 if (segment._selectionState) |
|
6333 this._updateSelection(segment, 0, segment._selectionState); |
|
6334 } |
|
6335 if (append) { |
|
6336 segments.push.apply(segments, segs); |
|
6337 } else { |
|
6338 segments.splice.apply(segments, [index, 0].concat(segs)); |
|
6339 for (var i = index + amount, l = segments.length; i < l; i++) |
|
6340 segments[i]._index = i; |
|
6341 } |
|
6342 if (curves || segs._curves) { |
|
6343 if (!curves) |
|
6344 curves = this._curves = []; |
|
6345 var from = index > 0 ? index - 1 : index, |
|
6346 start = from, |
|
6347 to = Math.min(from + amount, this._countCurves()); |
|
6348 if (segs._curves) { |
|
6349 curves.splice.apply(curves, [from, 0].concat(segs._curves)); |
|
6350 start += segs._curves.length; |
|
6351 } |
|
6352 for (var i = start; i < to; i++) |
|
6353 curves.splice(i, 0, new Curve(this, null, null)); |
|
6354 this._adjustCurves(from, to); |
|
6355 } |
|
6356 this._changed(5); |
|
6357 return segs; |
|
6358 }, |
|
6359 |
|
6360 _adjustCurves: function(from, to) { |
|
6361 var segments = this._segments, |
|
6362 curves = this._curves, |
|
6363 curve; |
|
6364 for (var i = from; i < to; i++) { |
|
6365 curve = curves[i]; |
|
6366 curve._path = this; |
|
6367 curve._segment1 = segments[i]; |
|
6368 curve._segment2 = segments[i + 1] || segments[0]; |
|
6369 } |
|
6370 if (curve = curves[this._closed && from === 0 ? segments.length - 1 |
|
6371 : from - 1]) |
|
6372 curve._segment2 = segments[from] || segments[0]; |
|
6373 if (curve = curves[to]) |
|
6374 curve._segment1 = segments[to]; |
|
6375 }, |
|
6376 |
|
6377 _countCurves: function() { |
|
6378 var length = this._segments.length; |
|
6379 return !this._closed && length > 0 ? length - 1 : length; |
|
6380 }, |
|
6381 |
|
6382 add: function(segment1 ) { |
|
6383 return arguments.length > 1 && typeof segment1 !== 'number' |
|
6384 ? this._add(Segment.readAll(arguments)) |
|
6385 : this._add([ Segment.read(arguments) ])[0]; |
|
6386 }, |
|
6387 |
|
6388 insert: function(index, segment1 ) { |
|
6389 return arguments.length > 2 && typeof segment1 !== 'number' |
|
6390 ? this._add(Segment.readAll(arguments, 1), index) |
|
6391 : this._add([ Segment.read(arguments, 1) ], index)[0]; |
|
6392 }, |
|
6393 |
|
6394 addSegment: function() { |
|
6395 return this._add([ Segment.read(arguments) ])[0]; |
|
6396 }, |
|
6397 |
|
6398 insertSegment: function(index ) { |
|
6399 return this._add([ Segment.read(arguments, 1) ], index)[0]; |
|
6400 }, |
|
6401 |
|
6402 addSegments: function(segments) { |
|
6403 return this._add(Segment.readAll(segments)); |
|
6404 }, |
|
6405 |
|
6406 insertSegments: function(index, segments) { |
|
6407 return this._add(Segment.readAll(segments), index); |
|
6408 }, |
|
6409 |
|
6410 removeSegment: function(index) { |
|
6411 return this.removeSegments(index, index + 1)[0] || null; |
|
6412 }, |
|
6413 |
|
6414 removeSegments: function(from, to) { |
|
6415 from = from || 0; |
|
6416 to = Base.pick(to, this._segments.length); |
|
6417 var segments = this._segments, |
|
6418 curves = this._curves, |
|
6419 count = segments.length, |
|
6420 removed = segments.splice(from, to - from), |
|
6421 amount = removed.length; |
|
6422 if (!amount) |
|
6423 return removed; |
|
6424 for (var i = 0; i < amount; i++) { |
|
6425 var segment = removed[i]; |
|
6426 if (segment._selectionState) |
|
6427 this._updateSelection(segment, segment._selectionState, 0); |
|
6428 delete segment._index; |
|
6429 delete segment._path; |
|
6430 } |
|
6431 for (var i = from, l = segments.length; i < l; i++) |
|
6432 segments[i]._index = i; |
|
6433 if (curves) { |
|
6434 var index = from > 0 && to === count + (this._closed ? 1 : 0) |
|
6435 ? from - 1 |
|
6436 : from, |
|
6437 curves = curves.splice(index, amount); |
|
6438 if (arguments[2]) |
|
6439 removed._curves = curves.slice(1); |
|
6440 this._adjustCurves(index, index); |
|
6441 } |
|
6442 this._changed(5); |
|
6443 return removed; |
|
6444 }, |
|
6445 |
|
6446 clear: '#removeSegments', |
|
6447 |
|
6448 isFullySelected: function() { |
|
6449 var length = this._segments.length; |
|
6450 return this._selected && length > 0 && this._selectedSegmentState |
|
6451 === length * 4; |
|
6452 }, |
|
6453 |
|
6454 setFullySelected: function(selected) { |
|
6455 if (selected) |
|
6456 this._selectSegments(true); |
|
6457 this.setSelected(selected); |
|
6458 }, |
|
6459 |
|
6460 setSelected: function setSelected(selected) { |
|
6461 if (!selected) |
|
6462 this._selectSegments(false); |
|
6463 setSelected.base.call(this, selected); |
|
6464 }, |
|
6465 |
|
6466 _selectSegments: function(selected) { |
|
6467 var length = this._segments.length; |
|
6468 this._selectedSegmentState = selected |
|
6469 ? length * 4 : 0; |
|
6470 for (var i = 0; i < length; i++) |
|
6471 this._segments[i]._selectionState = selected |
|
6472 ? 4 : 0; |
|
6473 }, |
|
6474 |
|
6475 _updateSelection: function(segment, oldState, newState) { |
|
6476 segment._selectionState = newState; |
|
6477 var total = this._selectedSegmentState += newState - oldState; |
|
6478 if (total > 0) |
|
6479 this.setSelected(true); |
|
6480 }, |
|
6481 |
|
6482 flatten: function(maxDistance) { |
|
6483 var flattener = new PathFlattener(this), |
|
6484 pos = 0, |
|
6485 step = flattener.length / Math.ceil(flattener.length / maxDistance), |
|
6486 end = flattener.length + (this._closed ? -step : step) / 2; |
|
6487 var segments = []; |
|
6488 while (pos <= end) { |
|
6489 segments.push(new Segment(flattener.evaluate(pos, 0))); |
|
6490 pos += step; |
|
6491 } |
|
6492 this.setSegments(segments); |
|
6493 }, |
|
6494 |
|
6495 simplify: function(tolerance) { |
|
6496 if (this._segments.length > 2) { |
|
6497 var fitter = new PathFitter(this, tolerance || 2.5); |
|
6498 this.setSegments(fitter.fit()); |
|
6499 } |
|
6500 }, |
|
6501 |
|
6502 split: function(index, parameter) { |
|
6503 if (parameter === null) |
|
6504 return; |
|
6505 if (arguments.length === 1) { |
|
6506 var arg = index; |
|
6507 if (typeof arg === 'number') |
|
6508 arg = this.getLocationAt(arg); |
|
6509 index = arg.index; |
|
6510 parameter = arg.parameter; |
|
6511 } |
|
6512 if (parameter >= 1) { |
|
6513 index++; |
|
6514 parameter--; |
|
6515 } |
|
6516 var curves = this.getCurves(); |
|
6517 if (index >= 0 && index < curves.length) { |
|
6518 if (parameter > 0) { |
|
6519 curves[index++].divide(parameter, true); |
|
6520 } |
|
6521 var segs = this.removeSegments(index, this._segments.length, true), |
|
6522 path; |
|
6523 if (this._closed) { |
|
6524 this.setClosed(false); |
|
6525 path = this; |
|
6526 } else if (index > 0) { |
|
6527 path = this._clone(new Path().insertAbove(this, true)); |
|
6528 } |
|
6529 path._add(segs, 0); |
|
6530 this.addSegment(segs[0]); |
|
6531 return path; |
|
6532 } |
|
6533 return null; |
|
6534 }, |
|
6535 |
|
6536 isClockwise: function() { |
|
6537 if (this._clockwise !== undefined) |
|
6538 return this._clockwise; |
|
6539 return Path.isClockwise(this._segments); |
|
6540 }, |
|
6541 |
|
6542 setClockwise: function(clockwise) { |
|
6543 if (this.isClockwise() != (clockwise = !!clockwise)) |
|
6544 this.reverse(); |
|
6545 this._clockwise = clockwise; |
|
6546 }, |
|
6547 |
|
6548 reverse: function() { |
|
6549 this._segments.reverse(); |
|
6550 for (var i = 0, l = this._segments.length; i < l; i++) { |
|
6551 var segment = this._segments[i]; |
|
6552 var handleIn = segment._handleIn; |
|
6553 segment._handleIn = segment._handleOut; |
|
6554 segment._handleOut = handleIn; |
|
6555 segment._index = i; |
|
6556 } |
|
6557 delete this._curves; |
|
6558 if (this._clockwise !== undefined) |
|
6559 this._clockwise = !this._clockwise; |
|
6560 }, |
|
6561 |
|
6562 join: function(path) { |
|
6563 if (path) { |
|
6564 var segments = path._segments, |
|
6565 last1 = this.getLastSegment(), |
|
6566 last2 = path.getLastSegment(); |
|
6567 if (last1._point.equals(last2._point)) |
|
6568 path.reverse(); |
|
6569 var first1, |
|
6570 first2 = path.getFirstSegment(); |
|
6571 if (last1._point.equals(first2._point)) { |
|
6572 last1.setHandleOut(first2._handleOut); |
|
6573 this._add(segments.slice(1)); |
|
6574 } else { |
|
6575 first1 = this.getFirstSegment(); |
|
6576 if (first1._point.equals(first2._point)) |
|
6577 path.reverse(); |
|
6578 last2 = path.getLastSegment(); |
|
6579 if (first1._point.equals(last2._point)) { |
|
6580 first1.setHandleIn(last2._handleIn); |
|
6581 this._add(segments.slice(0, segments.length - 1), 0); |
|
6582 } else { |
|
6583 this._add(segments.slice()); |
|
6584 } |
|
6585 } |
|
6586 if (path.closed) |
|
6587 this._add([segments[0]]); |
|
6588 path.remove(); |
|
6589 first1 = this.getFirstSegment(); |
|
6590 last1 = this.getLastSegment(); |
|
6591 if (last1._point.equals(first1._point)) { |
|
6592 first1.setHandleIn(last1._handleIn); |
|
6593 last1.remove(); |
|
6594 this.setClosed(true); |
|
6595 } |
|
6596 this._changed(5); |
|
6597 return true; |
|
6598 } |
|
6599 return false; |
|
6600 }, |
|
6601 |
|
6602 getLength: function() { |
|
6603 if (this._length == null) { |
|
6604 var curves = this.getCurves(); |
|
6605 this._length = 0; |
|
6606 for (var i = 0, l = curves.length; i < l; i++) |
|
6607 this._length += curves[i].getLength(); |
|
6608 } |
|
6609 return this._length; |
|
6610 }, |
|
6611 |
|
6612 getArea: function() { |
|
6613 var curves = this.getCurves(); |
|
6614 var area = 0; |
|
6615 for (var i = 0, l = curves.length; i < l; i++) |
|
6616 area += curves[i].getArea(); |
|
6617 return area; |
|
6618 }, |
|
6619 |
|
6620 _getOffset: function(location) { |
|
6621 var index = location && location.getIndex(); |
|
6622 if (index != null) { |
|
6623 var curves = this.getCurves(), |
|
6624 offset = 0; |
|
6625 for (var i = 0; i < index; i++) |
|
6626 offset += curves[i].getLength(); |
|
6627 var curve = curves[index]; |
|
6628 return offset + curve.getLength(0, location.getParameter()); |
|
6629 } |
|
6630 return null; |
|
6631 }, |
|
6632 |
|
6633 getLocationOf: function(point) { |
|
6634 point = Point.read(arguments); |
|
6635 var curves = this.getCurves(); |
|
6636 for (var i = 0, l = curves.length; i < l; i++) { |
|
6637 var loc = curves[i].getLocationOf(point); |
|
6638 if (loc) |
|
6639 return loc; |
|
6640 } |
|
6641 return null; |
|
6642 }, |
|
6643 |
|
6644 getLocationAt: function(offset, isParameter) { |
|
6645 var curves = this.getCurves(), |
|
6646 length = 0; |
|
6647 if (isParameter) { |
|
6648 var index = ~~offset; |
|
6649 return curves[index].getLocationAt(offset - index, true); |
|
6650 } |
|
6651 for (var i = 0, l = curves.length; i < l; i++) { |
|
6652 var start = length, |
|
6653 curve = curves[i]; |
|
6654 length += curve.getLength(); |
|
6655 if (length >= offset) { |
|
6656 return curve.getLocationAt(offset - start); |
|
6657 } |
|
6658 } |
|
6659 if (offset <= this.getLength()) |
|
6660 return new CurveLocation(curves[curves.length - 1], 1); |
|
6661 return null; |
|
6662 }, |
|
6663 |
|
6664 getPointAt: function(offset, isParameter) { |
|
6665 var loc = this.getLocationAt(offset, isParameter); |
|
6666 return loc && loc.getPoint(); |
|
6667 }, |
|
6668 |
|
6669 getTangentAt: function(offset, isParameter) { |
|
6670 var loc = this.getLocationAt(offset, isParameter); |
|
6671 return loc && loc.getTangent(); |
|
6672 }, |
|
6673 |
|
6674 getNormalAt: function(offset, isParameter) { |
|
6675 var loc = this.getLocationAt(offset, isParameter); |
|
6676 return loc && loc.getNormal(); |
|
6677 }, |
|
6678 |
|
6679 getNearestLocation: function(point) { |
|
6680 point = Point.read(arguments); |
|
6681 var curves = this.getCurves(), |
|
6682 minDist = Infinity, |
|
6683 minLoc = null; |
|
6684 for (var i = 0, l = curves.length; i < l; i++) { |
|
6685 var loc = curves[i].getNearestLocation(point); |
|
6686 if (loc._distance < minDist) { |
|
6687 minDist = loc._distance; |
|
6688 minLoc = loc; |
|
6689 } |
|
6690 } |
|
6691 return minLoc; |
|
6692 }, |
|
6693 |
|
6694 getNearestPoint: function(point) { |
|
6695 point = Point.read(arguments); |
|
6696 return this.getNearestLocation(point).getPoint(); |
|
6697 }, |
|
6698 |
|
6699 getStyle: function() { |
|
6700 var parent = this._parent; |
|
6701 return (parent && parent._type === 'compound-path' |
|
6702 ? parent : this)._style; |
|
6703 }, |
|
6704 |
|
6705 toShape: function(insert) { |
|
6706 if (!this._closed) |
|
6707 return null; |
|
6708 |
|
6709 var segments = this._segments, |
|
6710 type, |
|
6711 size, |
|
6712 radius, |
|
6713 topCenter; |
|
6714 |
|
6715 function isColinear(i, j) { |
|
6716 return segments[i].isColinear(segments[j]); |
|
6717 } |
|
6718 |
|
6719 function isOrthogonal(i) { |
|
6720 return segments[i].isOrthogonal(); |
|
6721 } |
|
6722 |
|
6723 function isArc(i) { |
|
6724 return segments[i].isArc(); |
|
6725 } |
|
6726 |
|
6727 function getDistance(i, j) { |
|
6728 return segments[i]._point.getDistance(segments[j]._point); |
|
6729 } |
|
6730 |
|
6731 if (this.isPolygon() && segments.length === 4 |
|
6732 && isColinear(0, 2) && isColinear(1, 3) && isOrthogonal(1)) { |
|
6733 type = Shape.Rectangle; |
|
6734 size = new Size(getDistance(0, 3), getDistance(0, 1)); |
|
6735 topCenter = segments[1]._point.add(segments[2]._point).divide(2); |
|
6736 } else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4) |
|
6737 && isArc(6) && isColinear(1, 5) && isColinear(3, 7)) { |
|
6738 type = Shape.Rectangle; |
|
6739 size = new Size(getDistance(1, 6), getDistance(0, 3)); |
|
6740 radius = size.subtract(new Size(getDistance(0, 7), |
|
6741 getDistance(1, 2))).divide(2); |
|
6742 topCenter = segments[3]._point.add(segments[4]._point).divide(2); |
|
6743 } else if (segments.length === 4 |
|
6744 && isArc(0) && isArc(1) && isArc(2) && isArc(3)) { |
|
6745 if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) { |
|
6746 type = Shape.Circle; |
|
6747 radius = getDistance(0, 2) / 2; |
|
6748 } else { |
|
6749 type = Shape.Ellipse; |
|
6750 radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2); |
|
6751 } |
|
6752 topCenter = segments[1]._point; |
|
6753 } |
|
6754 |
|
6755 if (type) { |
|
6756 var center = this.getPosition(true), |
|
6757 shape = new type({ |
|
6758 center: center, |
|
6759 size: size, |
|
6760 radius: radius, |
|
6761 insert: false |
|
6762 }); |
|
6763 shape.rotate(topCenter.subtract(center).getAngle() + 90); |
|
6764 shape.setStyle(this._style); |
|
6765 if (insert || insert === undefined) |
|
6766 shape.insertAbove(this); |
|
6767 return shape; |
|
6768 } |
|
6769 return null; |
|
6770 }, |
|
6771 |
|
6772 _getWinding: function(point) { |
|
6773 var closed = this._closed; |
|
6774 if (!closed && !this.hasFill() |
|
6775 || !this._getBounds('getRoughBounds')._containsPoint(point)) |
|
6776 return 0; |
|
6777 var curves = this.getCurves(), |
|
6778 segments = this._segments, |
|
6779 winding = 0, |
|
6780 roots1 = [], |
|
6781 roots2 = [], |
|
6782 last = (closed |
|
6783 ? curves[curves.length - 1] |
|
6784 : new Curve(segments[segments.length - 1]._point, |
|
6785 segments[0]._point)).getValues(), |
|
6786 previous = last; |
|
6787 for (var i = 0, l = curves.length; i < l; i++) { |
|
6788 var curve = curves[i].getValues(), |
|
6789 x = curve[0], |
|
6790 y = curve[1]; |
|
6791 if (!(x === curve[2] && y === curve[3] && x === curve[4] |
|
6792 && y === curve[5] && x === curve[6] && y === curve[7])) { |
|
6793 winding += Curve._getWinding(curve, previous, point.x, point.y, |
|
6794 roots1, roots2); |
|
6795 previous = curve; |
|
6796 } |
|
6797 } |
|
6798 if (!closed) { |
|
6799 winding += Curve._getWinding(last, previous, point.x, point.y, |
|
6800 roots1, roots2); |
|
6801 } |
|
6802 return winding; |
|
6803 }, |
|
6804 |
|
6805 _hitTest: function(point, options) { |
|
6806 var style = this.getStyle(), |
|
6807 segments = this._segments, |
|
6808 closed = this._closed, |
|
6809 tolerance = options.tolerance, |
|
6810 radius = 0, join, cap, miterLimit, |
|
6811 that = this, |
|
6812 area, loc, res; |
|
6813 |
|
6814 if (options.stroke) { |
|
6815 radius = style.getStrokeWidth() / 2; |
|
6816 if (radius > 0) { |
|
6817 join = style.getStrokeJoin(); |
|
6818 cap = style.getStrokeCap(); |
|
6819 miterLimit = radius * style.getMiterLimit(); |
|
6820 } else { |
|
6821 join = cap = 'round'; |
|
6822 } |
|
6823 radius += tolerance; |
|
6824 } |
|
6825 |
|
6826 function checkPoint(seg, pt, name) { |
|
6827 if (point.getDistance(pt) < tolerance) |
|
6828 return new HitResult(name, that, { segment: seg, point: pt }); |
|
6829 } |
|
6830 |
|
6831 function checkSegmentPoints(seg, ends) { |
|
6832 var pt = seg._point; |
|
6833 return (ends || options.segments) && checkPoint(seg, pt, 'segment') |
|
6834 || (!ends && options.handles) && ( |
|
6835 checkPoint(seg, pt.add(seg._handleIn), 'handle-in') || |
|
6836 checkPoint(seg, pt.add(seg._handleOut), 'handle-out')); |
|
6837 } |
|
6838 |
|
6839 function addAreaPoint(point) { |
|
6840 area.push(point); |
|
6841 } |
|
6842 |
|
6843 function getAreaCurve(index) { |
|
6844 var p1 = area[index], |
|
6845 p2 = area[(index + 1) % area.length]; |
|
6846 return [p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p2.x ,p2.y]; |
|
6847 } |
|
6848 |
|
6849 function isInArea(point) { |
|
6850 var length = area.length, |
|
6851 previous = getAreaCurve(length - 1), |
|
6852 roots1 = [], |
|
6853 roots2 = [], |
|
6854 winding = 0; |
|
6855 for (var i = 0; i < length; i++) { |
|
6856 var curve = getAreaCurve(i); |
|
6857 winding += Curve._getWinding(curve, previous, point.x, point.y, |
|
6858 roots1, roots2); |
|
6859 previous = curve; |
|
6860 } |
|
6861 return !!winding; |
|
6862 } |
|
6863 |
|
6864 function checkSegmentStroke(segment) { |
|
6865 if (join !== 'round' || cap !== 'round') { |
|
6866 area = []; |
|
6867 if (closed || segment._index > 0 |
|
6868 && segment._index < segments.length - 1) { |
|
6869 if (join !== 'round' && (segment._handleIn.isZero() |
|
6870 || segment._handleOut.isZero())) |
|
6871 Path._addSquareJoin(segment, join, radius, miterLimit, |
|
6872 addAreaPoint, true); |
|
6873 } else if (cap !== 'round') { |
|
6874 Path._addSquareCap(segment, cap, radius, addAreaPoint, true); |
|
6875 } |
|
6876 if (area.length > 0) |
|
6877 return isInArea(point); |
|
6878 } |
|
6879 return point.getDistance(segment._point) <= radius; |
|
6880 } |
|
6881 |
|
6882 if (options.ends && !options.segments && !closed) { |
|
6883 if (res = checkSegmentPoints(segments[0], true) |
|
6884 || checkSegmentPoints(segments[segments.length - 1], true)) |
|
6885 return res; |
|
6886 } else if (options.segments || options.handles) { |
|
6887 for (var i = 0, l = segments.length; i < l; i++) { |
|
6888 if (res = checkSegmentPoints(segments[i])) |
|
6889 return res; |
|
6890 } |
|
6891 } |
|
6892 if (radius > 0) { |
|
6893 loc = this.getNearestLocation(point); |
|
6894 if (loc) { |
|
6895 var parameter = loc.getParameter(); |
|
6896 if (parameter === 0 || parameter === 1) { |
|
6897 if (!checkSegmentStroke(loc.getSegment())) |
|
6898 loc = null; |
|
6899 } else if (loc._distance > radius) { |
|
6900 loc = null; |
|
6901 } |
|
6902 } |
|
6903 if (!loc && join === 'miter') { |
|
6904 for (var i = 0, l = segments.length; i < l; i++) { |
|
6905 var segment = segments[i]; |
|
6906 if (point.getDistance(segment._point) <= miterLimit |
|
6907 && checkSegmentStroke(segment)) { |
|
6908 loc = segment.getLocation(); |
|
6909 break; |
|
6910 } |
|
6911 } |
|
6912 } |
|
6913 } |
|
6914 return !loc && options.fill && this.hasFill() && this._contains(point) |
|
6915 ? new HitResult('fill', this) |
|
6916 : loc |
|
6917 ? new HitResult('stroke', this, { location: loc }) |
|
6918 : null; |
|
6919 } |
|
6920 |
|
6921 }, new function() { |
|
6922 |
|
6923 function drawHandles(ctx, segments, matrix, size) { |
|
6924 var half = size / 2; |
|
6925 |
|
6926 function drawHandle(index) { |
|
6927 var hX = coords[index], |
|
6928 hY = coords[index + 1]; |
|
6929 if (pX != hX || pY != hY) { |
|
6930 ctx.beginPath(); |
|
6931 ctx.moveTo(pX, pY); |
|
6932 ctx.lineTo(hX, hY); |
|
6933 ctx.stroke(); |
|
6934 ctx.beginPath(); |
|
6935 ctx.arc(hX, hY, half, 0, Math.PI * 2, true); |
|
6936 ctx.fill(); |
|
6937 } |
|
6938 } |
|
6939 |
|
6940 var coords = new Array(6); |
|
6941 for (var i = 0, l = segments.length; i < l; i++) { |
|
6942 var segment = segments[i]; |
|
6943 segment._transformCoordinates(matrix, coords, false); |
|
6944 var state = segment._selectionState, |
|
6945 selected = state & 4, |
|
6946 pX = coords[0], |
|
6947 pY = coords[1]; |
|
6948 if (selected || (state & 1)) |
|
6949 drawHandle(2); |
|
6950 if (selected || (state & 2)) |
|
6951 drawHandle(4); |
|
6952 ctx.save(); |
|
6953 ctx.beginPath(); |
|
6954 ctx.rect(pX - half, pY - half, size, size); |
|
6955 ctx.fill(); |
|
6956 if (!selected) { |
|
6957 ctx.beginPath(); |
|
6958 ctx.rect(pX - half + 1, pY - half + 1, size - 2, size - 2); |
|
6959 ctx.fillStyle = '#ffffff'; |
|
6960 ctx.fill(); |
|
6961 } |
|
6962 ctx.restore(); |
|
6963 } |
|
6964 } |
|
6965 |
|
6966 function drawSegments(ctx, path, matrix) { |
|
6967 var segments = path._segments, |
|
6968 length = segments.length, |
|
6969 coords = new Array(6), |
|
6970 first = true, |
|
6971 curX, curY, |
|
6972 prevX, prevY, |
|
6973 inX, inY, |
|
6974 outX, outY; |
|
6975 |
|
6976 function drawSegment(i) { |
|
6977 var segment = segments[i]; |
|
6978 if (matrix) { |
|
6979 segment._transformCoordinates(matrix, coords, false); |
|
6980 curX = coords[0]; |
|
6981 curY = coords[1]; |
|
6982 } else { |
|
6983 var point = segment._point; |
|
6984 curX = point._x; |
|
6985 curY = point._y; |
|
6986 } |
|
6987 if (first) { |
|
6988 ctx.moveTo(curX, curY); |
|
6989 first = false; |
|
6990 } else { |
|
6991 if (matrix) { |
|
6992 inX = coords[2]; |
|
6993 inY = coords[3]; |
|
6994 } else { |
|
6995 var handle = segment._handleIn; |
|
6996 inX = curX + handle._x; |
|
6997 inY = curY + handle._y; |
|
6998 } |
|
6999 if (inX == curX && inY == curY && outX == prevX && outY == prevY) { |
|
7000 ctx.lineTo(curX, curY); |
|
7001 } else { |
|
7002 ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY); |
|
7003 } |
|
7004 } |
|
7005 prevX = curX; |
|
7006 prevY = curY; |
|
7007 if (matrix) { |
|
7008 outX = coords[4]; |
|
7009 outY = coords[5]; |
|
7010 } else { |
|
7011 var handle = segment._handleOut; |
|
7012 outX = prevX + handle._x; |
|
7013 outY = prevY + handle._y; |
|
7014 } |
|
7015 } |
|
7016 |
|
7017 for (var i = 0; i < length; i++) |
|
7018 drawSegment(i); |
|
7019 if (path._closed && length > 1) |
|
7020 drawSegment(0); |
|
7021 } |
|
7022 |
|
7023 return { |
|
7024 _draw: function(ctx, param) { |
|
7025 var clip = param.clip, |
|
7026 compound = param.compound; |
|
7027 if (!compound) |
|
7028 ctx.beginPath(); |
|
7029 |
|
7030 var style = this.getStyle(), |
|
7031 hasFill = style.hasFill(), |
|
7032 hasStroke = style.hasStroke(), |
|
7033 dashArray = style.getDashArray(), |
|
7034 dashLength = !paper.support.nativeDash && hasStroke |
|
7035 && dashArray && dashArray.length; |
|
7036 |
|
7037 function getOffset(i) { |
|
7038 return dashArray[((i % dashLength) + dashLength) % dashLength]; |
|
7039 } |
|
7040 |
|
7041 if (hasFill || hasStroke && !dashLength || compound || clip) |
|
7042 drawSegments(ctx, this); |
|
7043 if (this._closed) |
|
7044 ctx.closePath(); |
|
7045 |
|
7046 if (!clip && !compound && (hasFill || hasStroke)) { |
|
7047 this._setStyles(ctx); |
|
7048 if (hasFill) { |
|
7049 ctx.fill(style.getWindingRule()); |
|
7050 ctx.shadowColor = 'rgba(0,0,0,0)'; |
|
7051 } |
|
7052 if (hasStroke) { |
|
7053 if (dashLength) { |
|
7054 ctx.beginPath(); |
|
7055 var flattener = new PathFlattener(this), |
|
7056 length = flattener.length, |
|
7057 from = -style.getDashOffset(), to, |
|
7058 i = 0; |
|
7059 from = from % length; |
|
7060 while (from > 0) { |
|
7061 from -= getOffset(i--) + getOffset(i--); |
|
7062 } |
|
7063 while (from < length) { |
|
7064 to = from + getOffset(i++); |
|
7065 if (from > 0 || to > 0) |
|
7066 flattener.drawPart(ctx, |
|
7067 Math.max(from, 0), Math.max(to, 0)); |
|
7068 from = to + getOffset(i++); |
|
7069 } |
|
7070 } |
|
7071 ctx.stroke(); |
|
7072 } |
|
7073 } |
|
7074 }, |
|
7075 |
|
7076 _drawSelected: function(ctx, matrix) { |
|
7077 ctx.beginPath(); |
|
7078 drawSegments(ctx, this, matrix); |
|
7079 ctx.stroke(); |
|
7080 drawHandles(ctx, this._segments, matrix, |
|
7081 this._project.options.handleSize || 4); |
|
7082 } |
|
7083 }; |
|
7084 }, new function() { |
|
7085 |
|
7086 function getFirstControlPoints(rhs) { |
|
7087 var n = rhs.length, |
|
7088 x = [], |
|
7089 tmp = [], |
|
7090 b = 2; |
|
7091 x[0] = rhs[0] / b; |
|
7092 for (var i = 1; i < n; i++) { |
|
7093 tmp[i] = 1 / b; |
|
7094 b = (i < n - 1 ? 4 : 2) - tmp[i]; |
|
7095 x[i] = (rhs[i] - x[i - 1]) / b; |
|
7096 } |
|
7097 for (var i = 1; i < n; i++) { |
|
7098 x[n - i - 1] -= tmp[n - i] * x[n - i]; |
|
7099 } |
|
7100 return x; |
|
7101 } |
|
7102 |
|
7103 return { |
|
7104 smooth: function() { |
|
7105 var segments = this._segments, |
|
7106 size = segments.length, |
|
7107 closed = this._closed, |
|
7108 n = size, |
|
7109 overlap = 0; |
|
7110 if (size <= 2) |
|
7111 return; |
|
7112 if (closed) { |
|
7113 overlap = Math.min(size, 4); |
|
7114 n += Math.min(size, overlap) * 2; |
|
7115 } |
|
7116 var knots = []; |
|
7117 for (var i = 0; i < size; i++) |
|
7118 knots[i + overlap] = segments[i]._point; |
|
7119 if (closed) { |
|
7120 for (var i = 0; i < overlap; i++) { |
|
7121 knots[i] = segments[i + size - overlap]._point; |
|
7122 knots[i + size + overlap] = segments[i]._point; |
|
7123 } |
|
7124 } else { |
|
7125 n--; |
|
7126 } |
|
7127 var rhs = []; |
|
7128 |
|
7129 for (var i = 1; i < n - 1; i++) |
|
7130 rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x; |
|
7131 rhs[0] = knots[0]._x + 2 * knots[1]._x; |
|
7132 rhs[n - 1] = 3 * knots[n - 1]._x; |
|
7133 var x = getFirstControlPoints(rhs); |
|
7134 |
|
7135 for (var i = 1; i < n - 1; i++) |
|
7136 rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y; |
|
7137 rhs[0] = knots[0]._y + 2 * knots[1]._y; |
|
7138 rhs[n - 1] = 3 * knots[n - 1]._y; |
|
7139 var y = getFirstControlPoints(rhs); |
|
7140 |
|
7141 if (closed) { |
|
7142 for (var i = 0, j = size; i < overlap; i++, j++) { |
|
7143 var f1 = i / overlap, |
|
7144 f2 = 1 - f1, |
|
7145 ie = i + overlap, |
|
7146 je = j + overlap; |
|
7147 x[j] = x[i] * f1 + x[j] * f2; |
|
7148 y[j] = y[i] * f1 + y[j] * f2; |
|
7149 x[je] = x[ie] * f2 + x[je] * f1; |
|
7150 y[je] = y[ie] * f2 + y[je] * f1; |
|
7151 } |
|
7152 n--; |
|
7153 } |
|
7154 var handleIn = null; |
|
7155 for (var i = overlap; i <= n - overlap; i++) { |
|
7156 var segment = segments[i - overlap]; |
|
7157 if (handleIn) |
|
7158 segment.setHandleIn(handleIn.subtract(segment._point)); |
|
7159 if (i < n) { |
|
7160 segment.setHandleOut( |
|
7161 new Point(x[i], y[i]).subtract(segment._point)); |
|
7162 handleIn = i < n - 1 |
|
7163 ? new Point( |
|
7164 2 * knots[i + 1]._x - x[i + 1], |
|
7165 2 * knots[i + 1]._y - y[i + 1]) |
|
7166 : new Point( |
|
7167 (knots[n]._x + x[n - 1]) / 2, |
|
7168 (knots[n]._y + y[n - 1]) / 2); |
|
7169 } |
|
7170 } |
|
7171 if (closed && handleIn) { |
|
7172 var segment = this._segments[0]; |
|
7173 segment.setHandleIn(handleIn.subtract(segment._point)); |
|
7174 } |
|
7175 } |
|
7176 }; |
|
7177 }, new function() { |
|
7178 function getCurrentSegment(that) { |
|
7179 var segments = that._segments; |
|
7180 if (segments.length == 0) |
|
7181 throw new Error('Use a moveTo() command first'); |
|
7182 return segments[segments.length - 1]; |
|
7183 } |
|
7184 |
|
7185 return { |
|
7186 moveTo: function() { |
|
7187 if (this._segments.length === 1) |
|
7188 this.removeSegment(0); |
|
7189 if (!this._segments.length) |
|
7190 this._add([ new Segment(Point.read(arguments)) ]); |
|
7191 }, |
|
7192 |
|
7193 moveBy: function() { |
|
7194 throw new Error('moveBy() is unsupported on Path items.'); |
|
7195 }, |
|
7196 |
|
7197 lineTo: function() { |
|
7198 this._add([ new Segment(Point.read(arguments)) ]); |
|
7199 }, |
|
7200 |
|
7201 cubicCurveTo: function() { |
|
7202 var handle1 = Point.read(arguments), |
|
7203 handle2 = Point.read(arguments), |
|
7204 to = Point.read(arguments), |
|
7205 current = getCurrentSegment(this); |
|
7206 current.setHandleOut(handle1.subtract(current._point)); |
|
7207 this._add([ new Segment(to, handle2.subtract(to)) ]); |
|
7208 }, |
|
7209 |
|
7210 quadraticCurveTo: function() { |
|
7211 var handle = Point.read(arguments), |
|
7212 to = Point.read(arguments), |
|
7213 current = getCurrentSegment(this)._point; |
|
7214 this.cubicCurveTo( |
|
7215 handle.add(current.subtract(handle).multiply(1 / 3)), |
|
7216 handle.add(to.subtract(handle).multiply(1 / 3)), |
|
7217 to |
|
7218 ); |
|
7219 }, |
|
7220 |
|
7221 curveTo: function() { |
|
7222 var through = Point.read(arguments), |
|
7223 to = Point.read(arguments), |
|
7224 t = Base.pick(Base.read(arguments), 0.5), |
|
7225 t1 = 1 - t, |
|
7226 current = getCurrentSegment(this)._point, |
|
7227 handle = through.subtract(current.multiply(t1 * t1)) |
|
7228 .subtract(to.multiply(t * t)).divide(2 * t * t1); |
|
7229 if (handle.isNaN()) |
|
7230 throw new Error( |
|
7231 'Cannot put a curve through points with parameter = ' + t); |
|
7232 this.quadraticCurveTo(handle, to); |
|
7233 }, |
|
7234 |
|
7235 arcTo: function() { |
|
7236 var current = getCurrentSegment(this), |
|
7237 from = current._point, |
|
7238 through, |
|
7239 to = Point.read(arguments), |
|
7240 clockwise = Base.pick(Base.peek(arguments), true); |
|
7241 if (typeof clockwise === 'boolean') { |
|
7242 var middle = from.add(to).divide(2), |
|
7243 through = middle.add(middle.subtract(from).rotate( |
|
7244 clockwise ? -90 : 90)); |
|
7245 } else { |
|
7246 through = to; |
|
7247 to = Point.read(arguments); |
|
7248 } |
|
7249 var l1 = new Line(from.add(through).divide(2), |
|
7250 through.subtract(from).rotate(90), true), |
|
7251 l2 = new Line(through.add(to).divide(2), |
|
7252 to.subtract(through).rotate(90), true), |
|
7253 center = l1.intersect(l2, true), |
|
7254 line = new Line(from, to), |
|
7255 throughSide = line.getSide(through); |
|
7256 if (!center) { |
|
7257 if (!throughSide) |
|
7258 return this.lineTo(to); |
|
7259 throw new Error('Cannot put an arc through the given points: ' |
|
7260 + [from, through, to]); |
|
7261 } |
|
7262 var vector = from.subtract(center), |
|
7263 extent = vector.getDirectedAngle(to.subtract(center)), |
|
7264 centerSide = line.getSide(center); |
|
7265 if (centerSide == 0) { |
|
7266 extent = throughSide * Math.abs(extent); |
|
7267 } else if (throughSide == centerSide) { |
|
7268 extent -= 360 * (extent < 0 ? -1 : 1); |
|
7269 } |
|
7270 var ext = Math.abs(extent), |
|
7271 count = ext >= 360 ? 4 : Math.ceil(ext / 90), |
|
7272 inc = extent / count, |
|
7273 half = inc * Math.PI / 360, |
|
7274 z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)), |
|
7275 segments = []; |
|
7276 for (var i = 0; i <= count; i++) { |
|
7277 var pt = i < count ? center.add(vector) : to; |
|
7278 var out = i < count ? vector.rotate(90).multiply(z) : null; |
|
7279 if (i == 0) { |
|
7280 current.setHandleOut(out); |
|
7281 } else { |
|
7282 segments.push( |
|
7283 new Segment(pt, vector.rotate(-90).multiply(z), out)); |
|
7284 } |
|
7285 vector = vector.rotate(inc); |
|
7286 } |
|
7287 this._add(segments); |
|
7288 }, |
|
7289 |
|
7290 lineBy: function() { |
|
7291 var to = Point.read(arguments), |
|
7292 current = getCurrentSegment(this)._point; |
|
7293 this.lineTo(current.add(to)); |
|
7294 }, |
|
7295 |
|
7296 curveBy: function() { |
|
7297 var through = Point.read(arguments), |
|
7298 to = Point.read(arguments), |
|
7299 parameter = Base.read(arguments), |
|
7300 current = getCurrentSegment(this)._point; |
|
7301 this.curveTo(current.add(through), current.add(to), parameter); |
|
7302 }, |
|
7303 |
|
7304 cubicCurveBy: function() { |
|
7305 var handle1 = Point.read(arguments), |
|
7306 handle2 = Point.read(arguments), |
|
7307 to = Point.read(arguments), |
|
7308 current = getCurrentSegment(this)._point; |
|
7309 this.cubicCurveTo(current.add(handle1), current.add(handle2), |
|
7310 current.add(to)); |
|
7311 }, |
|
7312 |
|
7313 quadraticCurveBy: function() { |
|
7314 var handle = Point.read(arguments), |
|
7315 to = Point.read(arguments), |
|
7316 current = getCurrentSegment(this)._point; |
|
7317 this.quadraticCurveTo(current.add(handle), current.add(to)); |
|
7318 }, |
|
7319 |
|
7320 arcBy: function() { |
|
7321 var current = getCurrentSegment(this)._point, |
|
7322 point = current.add(Point.read(arguments)), |
|
7323 clockwise = Base.pick(Base.peek(arguments), true); |
|
7324 if (typeof clockwise === 'boolean') { |
|
7325 this.arcTo(point, clockwise); |
|
7326 } else { |
|
7327 this.arcTo(point, current.add(Point.read(arguments))); |
|
7328 } |
|
7329 }, |
|
7330 |
|
7331 closePath: function() { |
|
7332 var first = this.getFirstSegment(), |
|
7333 last = this.getLastSegment(); |
|
7334 if (first._point.equals(last._point)) { |
|
7335 first.setHandleIn(last._handleIn); |
|
7336 last.remove(); |
|
7337 } |
|
7338 this.setClosed(true); |
|
7339 } |
|
7340 }; |
|
7341 }, { |
|
7342 |
|
7343 _getBounds: function(getter, matrix) { |
|
7344 return Path[getter](this._segments, this._closed, this.getStyle(), |
|
7345 matrix); |
|
7346 }, |
|
7347 |
|
7348 statics: { |
|
7349 isClockwise: function(segments) { |
|
7350 var sum = 0; |
|
7351 for (var i = 0, l = segments.length; i < l; i++) { |
|
7352 var v = Curve.getValues( |
|
7353 segments[i], segments[i + 1 < l ? i + 1 : 0]); |
|
7354 for (var j = 2; j < 8; j += 2) |
|
7355 sum += (v[j - 2] - v[j]) * (v[j + 1] + v[j - 1]); |
|
7356 } |
|
7357 return sum > 0; |
|
7358 }, |
|
7359 |
|
7360 getBounds: function(segments, closed, style, matrix, strokePadding) { |
|
7361 var first = segments[0]; |
|
7362 if (!first) |
|
7363 return new Rectangle(); |
|
7364 var coords = new Array(6), |
|
7365 prevCoords = first._transformCoordinates(matrix, new Array(6), false), |
|
7366 min = prevCoords.slice(0, 2), |
|
7367 max = min.slice(), |
|
7368 roots = new Array(2); |
|
7369 |
|
7370 function processSegment(segment) { |
|
7371 segment._transformCoordinates(matrix, coords, false); |
|
7372 for (var i = 0; i < 2; i++) { |
|
7373 Curve._addBounds( |
|
7374 prevCoords[i], |
|
7375 prevCoords[i + 4], |
|
7376 coords[i + 2], |
|
7377 coords[i], |
|
7378 i, strokePadding ? strokePadding[i] : 0, min, max, roots); |
|
7379 } |
|
7380 var tmp = prevCoords; |
|
7381 prevCoords = coords; |
|
7382 coords = tmp; |
|
7383 } |
|
7384 |
|
7385 for (var i = 1, l = segments.length; i < l; i++) |
|
7386 processSegment(segments[i]); |
|
7387 if (closed) |
|
7388 processSegment(first); |
|
7389 return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]); |
|
7390 }, |
|
7391 |
|
7392 getStrokeBounds: function(segments, closed, style, matrix) { |
|
7393 function getPenPadding(radius, matrix) { |
|
7394 if (!matrix) |
|
7395 return [radius, radius]; |
|
7396 var mx = matrix.shiftless(), |
|
7397 hor = mx.transform(new Point(radius, 0)), |
|
7398 ver = mx.transform(new Point(0, radius)), |
|
7399 phi = hor.getAngleInRadians(), |
|
7400 a = hor.getLength(), |
|
7401 b = ver.getLength(); |
|
7402 var sin = Math.sin(phi), |
|
7403 cos = Math.cos(phi), |
|
7404 tan = Math.tan(phi), |
|
7405 tx = -Math.atan(b * tan / a), |
|
7406 ty = Math.atan(b / (tan * a)); |
|
7407 return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin), |
|
7408 Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)]; |
|
7409 } |
|
7410 |
|
7411 if (!style.hasStroke()) |
|
7412 return Path.getBounds(segments, closed, style, matrix); |
|
7413 var length = segments.length - (closed ? 0 : 1), |
|
7414 radius = style.getStrokeWidth() / 2, |
|
7415 padding = getPenPadding(radius, matrix), |
|
7416 bounds = Path.getBounds(segments, closed, style, matrix, padding), |
|
7417 join = style.getStrokeJoin(), |
|
7418 cap = style.getStrokeCap(), |
|
7419 miterLimit = radius * style.getMiterLimit(); |
|
7420 var joinBounds = new Rectangle(new Size(padding).multiply(2)); |
|
7421 |
|
7422 function add(point) { |
|
7423 bounds = bounds.include(matrix |
|
7424 ? matrix._transformPoint(point, point) : point); |
|
7425 } |
|
7426 |
|
7427 function addJoin(segment, join) { |
|
7428 if (join === 'round' || !segment._handleIn.isZero() |
|
7429 && !segment._handleOut.isZero()) { |
|
7430 bounds = bounds.unite(joinBounds.setCenter(matrix |
|
7431 ? matrix._transformPoint(segment._point) : segment._point)); |
|
7432 } else { |
|
7433 Path._addSquareJoin(segment, join, radius, miterLimit, add); |
|
7434 } |
|
7435 } |
|
7436 |
|
7437 function addCap(segment, cap) { |
|
7438 switch (cap) { |
|
7439 case 'round': |
|
7440 addJoin(segment, cap); |
|
7441 break; |
|
7442 case 'butt': |
|
7443 case 'square': |
|
7444 Path._addSquareCap(segment, cap, radius, add); |
|
7445 break; |
|
7446 } |
|
7447 } |
|
7448 |
|
7449 for (var i = 1; i < length; i++) |
|
7450 addJoin(segments[i], join); |
|
7451 if (closed) { |
|
7452 addJoin(segments[0], join); |
|
7453 } else { |
|
7454 addCap(segments[0], cap); |
|
7455 addCap(segments[segments.length - 1], cap); |
|
7456 } |
|
7457 return bounds; |
|
7458 }, |
|
7459 |
|
7460 _addSquareJoin: function(segment, join, radius, miterLimit, addPoint, area) { |
|
7461 var curve2 = segment.getCurve(), |
|
7462 curve1 = curve2.getPrevious(), |
|
7463 point = curve2.getPointAt(0, true), |
|
7464 normal1 = curve1.getNormalAt(1, true), |
|
7465 normal2 = curve2.getNormalAt(0, true), |
|
7466 step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius; |
|
7467 normal1.setLength(step); |
|
7468 normal2.setLength(step); |
|
7469 if (area) { |
|
7470 addPoint(point); |
|
7471 addPoint(point.add(normal1)); |
|
7472 } |
|
7473 if (join === 'miter') { |
|
7474 var corner = new Line( |
|
7475 point.add(normal1), |
|
7476 new Point(-normal1.y, normal1.x), true |
|
7477 ).intersect(new Line( |
|
7478 point.add(normal2), |
|
7479 new Point(-normal2.y, normal2.x), true |
|
7480 ), true); |
|
7481 if (corner && point.getDistance(corner) <= miterLimit) { |
|
7482 addPoint(corner); |
|
7483 if (!area) |
|
7484 return; |
|
7485 } |
|
7486 } |
|
7487 if (!area) |
|
7488 addPoint(point.add(normal1)); |
|
7489 addPoint(point.add(normal2)); |
|
7490 }, |
|
7491 |
|
7492 _addSquareCap: function(segment, cap, radius, addPoint, area) { |
|
7493 var point = segment._point, |
|
7494 loc = segment.getLocation(), |
|
7495 normal = loc.getNormal().normalize(radius); |
|
7496 if (area) { |
|
7497 addPoint(point.subtract(normal)); |
|
7498 addPoint(point.add(normal)); |
|
7499 } |
|
7500 if (cap === 'square') |
|
7501 point = point.add(normal.rotate(loc.getParameter() == 0 ? -90 : 90)); |
|
7502 addPoint(point.add(normal)); |
|
7503 addPoint(point.subtract(normal)); |
|
7504 }, |
|
7505 |
|
7506 getHandleBounds: function(segments, closed, style, matrix, strokePadding, |
|
7507 joinPadding) { |
|
7508 var coords = new Array(6), |
|
7509 x1 = Infinity, |
|
7510 x2 = -x1, |
|
7511 y1 = x1, |
|
7512 y2 = x2; |
|
7513 strokePadding = strokePadding / 2 || 0; |
|
7514 joinPadding = joinPadding / 2 || 0; |
|
7515 for (var i = 0, l = segments.length; i < l; i++) { |
|
7516 var segment = segments[i]; |
|
7517 segment._transformCoordinates(matrix, coords, false); |
|
7518 for (var j = 0; j < 6; j += 2) { |
|
7519 var padding = j == 0 ? joinPadding : strokePadding, |
|
7520 x = coords[j], |
|
7521 y = coords[j + 1], |
|
7522 xn = x - padding, |
|
7523 xx = x + padding, |
|
7524 yn = y - padding, |
|
7525 yx = y + padding; |
|
7526 if (xn < x1) x1 = xn; |
|
7527 if (xx > x2) x2 = xx; |
|
7528 if (yn < y1) y1 = yn; |
|
7529 if (yx > y2) y2 = yx; |
|
7530 } |
|
7531 } |
|
7532 return new Rectangle(x1, y1, x2 - x1, y2 - y1); |
|
7533 }, |
|
7534 |
|
7535 getRoughBounds: function(segments, closed, style, matrix) { |
|
7536 var strokeWidth = style.getStrokeColor() ? style.getStrokeWidth() : 0, |
|
7537 joinWidth = strokeWidth; |
|
7538 if (strokeWidth === 0) { |
|
7539 strokeWidth = 0.00001; |
|
7540 } else { |
|
7541 if (style.getStrokeJoin() === 'miter') |
|
7542 joinWidth = strokeWidth * style.getMiterLimit(); |
|
7543 if (style.getStrokeCap() === 'square') |
|
7544 joinWidth = Math.max(joinWidth, strokeWidth * Math.sqrt(2)); |
|
7545 } |
|
7546 return Path.getHandleBounds(segments, closed, style, matrix, |
|
7547 strokeWidth, joinWidth); |
|
7548 } |
|
7549 }}); |
|
7550 |
|
7551 Path.inject({ statics: new function() { |
|
7552 |
|
7553 var kappa = Numerical.KAPPA, |
|
7554 ellipseSegments = [ |
|
7555 new Segment([-1, 0], [0, kappa ], [0, -kappa]), |
|
7556 new Segment([0, -1], [-kappa, 0], [kappa, 0 ]), |
|
7557 new Segment([1, 0], [0, -kappa], [0, kappa ]), |
|
7558 new Segment([0, 1], [kappa, 0 ], [-kappa, 0]) |
|
7559 ]; |
|
7560 |
|
7561 function createEllipse(center, radius, args) { |
|
7562 var path = new Path(), |
|
7563 segments = new Array(4); |
|
7564 for (var i = 0; i < 4; i++) { |
|
7565 var segment = ellipseSegments[i]; |
|
7566 segments[i] = new Segment( |
|
7567 segment._point.multiply(radius).add(center), |
|
7568 segment._handleIn.multiply(radius), |
|
7569 segment._handleOut.multiply(radius) |
|
7570 ); |
|
7571 } |
|
7572 path._add(segments); |
|
7573 path._closed = true; |
|
7574 return path.set(Base.getNamed(args)); |
|
7575 } |
|
7576 |
|
7577 return { |
|
7578 Line: function() { |
|
7579 return new Path( |
|
7580 Point.readNamed(arguments, 'from'), |
|
7581 Point.readNamed(arguments, 'to') |
|
7582 ).set(Base.getNamed(arguments)); |
|
7583 }, |
|
7584 |
|
7585 Circle: function() { |
|
7586 var center = Point.readNamed(arguments, 'center'), |
|
7587 radius = Base.readNamed(arguments, 'radius'); |
|
7588 return createEllipse(center, new Size(radius), arguments); |
|
7589 }, |
|
7590 |
|
7591 Rectangle: function() { |
|
7592 var rect = Rectangle.readNamed(arguments, 'rectangle'), |
|
7593 radius = Size.readNamed(arguments, 'radius', 0, 0, |
|
7594 { readNull: true }), |
|
7595 bl = rect.getBottomLeft(true), |
|
7596 tl = rect.getTopLeft(true), |
|
7597 tr = rect.getTopRight(true), |
|
7598 br = rect.getBottomRight(true); |
|
7599 path = new Path(); |
|
7600 if (!radius || radius.isZero()) { |
|
7601 path._add([ |
|
7602 new Segment(bl), |
|
7603 new Segment(tl), |
|
7604 new Segment(tr), |
|
7605 new Segment(br) |
|
7606 ]); |
|
7607 } else { |
|
7608 radius = Size.min(radius, rect.getSize(true).divide(2)); |
|
7609 var rx = radius.width, |
|
7610 ry = radius.height, |
|
7611 hx = rx * kappa, |
|
7612 hy = ry * kappa; |
|
7613 path._add([ |
|
7614 new Segment(bl.add(rx, 0), null, [-hx, 0]), |
|
7615 new Segment(bl.subtract(0, ry), [0, hy]), |
|
7616 new Segment(tl.add(0, ry), null, [0, -hy]), |
|
7617 new Segment(tl.add(rx, 0), [-hx, 0], null), |
|
7618 new Segment(tr.subtract(rx, 0), null, [hx, 0]), |
|
7619 new Segment(tr.add(0, ry), [0, -hy], null), |
|
7620 new Segment(br.subtract(0, ry), null, [0, hy]), |
|
7621 new Segment(br.subtract(rx, 0), [hx, 0]) |
|
7622 ]); |
|
7623 } |
|
7624 path._closed = true; |
|
7625 return path.set(Base.getNamed(arguments)); |
|
7626 }, |
|
7627 |
|
7628 RoundRectangle: '#Rectangle', |
|
7629 |
|
7630 Ellipse: function() { |
|
7631 var ellipse = Shape._readEllipse(arguments); |
|
7632 return createEllipse(ellipse.center, ellipse.radius, arguments); |
|
7633 }, |
|
7634 |
|
7635 Oval: '#Ellipse', |
|
7636 |
|
7637 Arc: function() { |
|
7638 var from = Point.readNamed(arguments, 'from'), |
|
7639 through = Point.readNamed(arguments, 'through'), |
|
7640 to = Point.readNamed(arguments, 'to'), |
|
7641 path = new Path(); |
|
7642 path.moveTo(from); |
|
7643 path.arcTo(through, to); |
|
7644 return path.set(Base.getNamed(arguments)); |
|
7645 }, |
|
7646 |
|
7647 RegularPolygon: function() { |
|
7648 var center = Point.readNamed(arguments, 'center'), |
|
7649 sides = Base.readNamed(arguments, 'sides'), |
|
7650 radius = Base.readNamed(arguments, 'radius'), |
|
7651 path = new Path(), |
|
7652 step = 360 / sides, |
|
7653 three = !(sides % 3), |
|
7654 vector = new Point(0, three ? -radius : radius), |
|
7655 offset = three ? -1 : 0.5, |
|
7656 segments = new Array(sides); |
|
7657 for (var i = 0; i < sides; i++) { |
|
7658 segments[i] = new Segment(center.add( |
|
7659 vector.rotate((i + offset) * step))); |
|
7660 } |
|
7661 path._add(segments); |
|
7662 path._closed = true; |
|
7663 return path.set(Base.getNamed(arguments)); |
|
7664 }, |
|
7665 |
|
7666 Star: function() { |
|
7667 var center = Point.readNamed(arguments, 'center'), |
|
7668 points = Base.readNamed(arguments, 'points') * 2, |
|
7669 radius1 = Base.readNamed(arguments, 'radius1'), |
|
7670 radius2 = Base.readNamed(arguments, 'radius2'), |
|
7671 path = new Path(), |
|
7672 step = 360 / points, |
|
7673 vector = new Point(0, -1), |
|
7674 segments = new Array(points); |
|
7675 for (var i = 0; i < points; i++) { |
|
7676 segments[i] = new Segment(center.add( |
|
7677 vector.rotate(step * i).multiply(i % 2 ? radius2 : radius1))); |
|
7678 } |
|
7679 path._add(segments); |
|
7680 path._closed = true; |
|
7681 return path.set(Base.getNamed(arguments)); |
|
7682 } |
|
7683 }; |
|
7684 }}); |
|
7685 |
|
7686 var CompoundPath = PathItem.extend({ |
|
7687 _class: 'CompoundPath', |
|
7688 _serializeFields: { |
|
7689 children: [] |
|
7690 }, |
|
7691 |
|
7692 initialize: function CompoundPath(arg) { |
|
7693 this._children = []; |
|
7694 this._namedChildren = {}; |
|
7695 if (!this._initialize(arg)) |
|
7696 this.addChildren(Array.isArray(arg) ? arg : arguments); |
|
7697 }, |
|
7698 |
|
7699 insertChildren: function insertChildren(index, items, _preserve) { |
|
7700 items = insertChildren.base.call(this, index, items, _preserve, 'path'); |
|
7701 for (var i = 0, l = !_preserve && items && items.length; i < l; i++) { |
|
7702 var item = items[i]; |
|
7703 if (item._clockwise === undefined) |
|
7704 item.setClockwise(item._index === 0); |
|
7705 } |
|
7706 return items; |
|
7707 }, |
|
7708 |
|
7709 reverse: function() { |
|
7710 var children = this._children; |
|
7711 for (var i = 0, l = children.length; i < l; i++) |
|
7712 children[i].reverse(); |
|
7713 }, |
|
7714 |
|
7715 smooth: function() { |
|
7716 for (var i = 0, l = this._children.length; i < l; i++) |
|
7717 this._children[i].smooth(); |
|
7718 }, |
|
7719 |
|
7720 isClockwise: function() { |
|
7721 var child = this.getFirstChild(); |
|
7722 return child && child.isClockwise(); |
|
7723 }, |
|
7724 |
|
7725 setClockwise: function(clockwise) { |
|
7726 if (this.isClockwise() != !!clockwise) |
|
7727 this.reverse(); |
|
7728 }, |
|
7729 |
|
7730 getFirstSegment: function() { |
|
7731 var first = this.getFirstChild(); |
|
7732 return first && first.getFirstSegment(); |
|
7733 }, |
|
7734 |
|
7735 getLastSegment: function() { |
|
7736 var last = this.getLastChild(); |
|
7737 return last && last.getLastSegment(); |
|
7738 }, |
|
7739 |
|
7740 getCurves: function() { |
|
7741 var children = this._children, |
|
7742 curves = []; |
|
7743 for (var i = 0, l = children.length; i < l; i++) |
|
7744 curves = curves.concat(children[i].getCurves()); |
|
7745 return curves; |
|
7746 }, |
|
7747 |
|
7748 getFirstCurve: function() { |
|
7749 var first = this.getFirstChild(); |
|
7750 return first && first.getFirstCurve(); |
|
7751 }, |
|
7752 |
|
7753 getLastCurve: function() { |
|
7754 var last = this.getLastChild(); |
|
7755 return last && last.getFirstCurve(); |
|
7756 }, |
|
7757 |
|
7758 getArea: function() { |
|
7759 var children = this._children, |
|
7760 area = 0; |
|
7761 for (var i = 0, l = children.length; i < l; i++) |
|
7762 area += children[i].getArea(); |
|
7763 return area; |
|
7764 }, |
|
7765 |
|
7766 getPathData: function() { |
|
7767 var children = this._children, |
|
7768 paths = []; |
|
7769 for (var i = 0, l = children.length; i < l; i++) |
|
7770 paths.push(children[i].getPathData(arguments[0])); |
|
7771 return paths.join(' '); |
|
7772 }, |
|
7773 |
|
7774 _getWinding: function(point) { |
|
7775 var children = this._children, |
|
7776 winding = 0; |
|
7777 for (var i = 0, l = children.length; i < l; i++) |
|
7778 winding += children[i]._getWinding(point); |
|
7779 return winding; |
|
7780 }, |
|
7781 |
|
7782 _hitTest : function _hitTest(point, options) { |
|
7783 var res = _hitTest.base.call(this, point, |
|
7784 new Base(options, { fill: false })); |
|
7785 if (!res) { |
|
7786 if (options.compoundChildren) { |
|
7787 var children = this._children; |
|
7788 for (var i = children.length - 1; i >= 0 && !res; i--) |
|
7789 res = children[i]._hitTest(point, options); |
|
7790 } else if (options.fill && this.hasFill() |
|
7791 && this._contains(point)) { |
|
7792 res = new HitResult('fill', this); |
|
7793 } |
|
7794 } |
|
7795 return res; |
|
7796 }, |
|
7797 |
|
7798 _draw: function(ctx, param) { |
|
7799 var children = this._children; |
|
7800 if (children.length === 0) |
|
7801 return; |
|
7802 |
|
7803 ctx.beginPath(); |
|
7804 param = param.extend({ compound: true }); |
|
7805 for (var i = 0, l = children.length; i < l; i++) |
|
7806 children[i].draw(ctx, param); |
|
7807 |
|
7808 if (!param.clip) { |
|
7809 this._setStyles(ctx); |
|
7810 var style = this._style; |
|
7811 if (style.hasFill()) { |
|
7812 ctx.fill(style.getWindingRule()); |
|
7813 ctx.shadowColor = 'rgba(0,0,0,0)'; |
|
7814 } |
|
7815 if (style.hasStroke()) |
|
7816 ctx.stroke(); |
|
7817 } |
|
7818 } |
|
7819 }, new function() { |
|
7820 function getCurrentPath(that) { |
|
7821 if (!that._children.length) |
|
7822 throw new Error('Use a moveTo() command first'); |
|
7823 return that._children[that._children.length - 1]; |
|
7824 } |
|
7825 |
|
7826 var fields = { |
|
7827 moveTo: function() { |
|
7828 var path = new Path(); |
|
7829 this.addChild(path); |
|
7830 path.moveTo.apply(path, arguments); |
|
7831 }, |
|
7832 |
|
7833 moveBy: function() { |
|
7834 this.moveTo(getCurrentPath(this).getLastSegment()._point.add( |
|
7835 Point.read(arguments))); |
|
7836 }, |
|
7837 |
|
7838 closePath: function() { |
|
7839 getCurrentPath(this).closePath(); |
|
7840 } |
|
7841 }; |
|
7842 |
|
7843 Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo', |
|
7844 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'], |
|
7845 function(key) { |
|
7846 fields[key] = function() { |
|
7847 var path = getCurrentPath(this); |
|
7848 path[key].apply(path, arguments); |
|
7849 }; |
|
7850 } |
|
7851 ); |
|
7852 |
|
7853 return fields; |
|
7854 }); |
|
7855 |
|
7856 var PathFlattener = Base.extend({ |
|
7857 initialize: function(path) { |
|
7858 this.curves = []; |
|
7859 this.parts = []; |
|
7860 this.length = 0; |
|
7861 this.index = 0; |
|
7862 |
|
7863 var segments = path._segments, |
|
7864 segment1 = segments[0], |
|
7865 segment2, |
|
7866 that = this; |
|
7867 |
|
7868 function addCurve(segment1, segment2) { |
|
7869 var curve = Curve.getValues(segment1, segment2); |
|
7870 that.curves.push(curve); |
|
7871 that._computeParts(curve, segment1._index, 0, 1); |
|
7872 } |
|
7873 |
|
7874 for (var i = 1, l = segments.length; i < l; i++) { |
|
7875 segment2 = segments[i]; |
|
7876 addCurve(segment1, segment2); |
|
7877 segment1 = segment2; |
|
7878 } |
|
7879 if (path._closed) |
|
7880 addCurve(segment2, segments[0]); |
|
7881 }, |
|
7882 |
|
7883 _computeParts: function(curve, index, minT, maxT) { |
|
7884 if ((maxT - minT) > 1 / 32 && !Curve.isFlatEnough(curve, 0.25)) { |
|
7885 var curves = Curve.subdivide(curve); |
|
7886 var halfT = (minT + maxT) / 2; |
|
7887 this._computeParts(curves[0], index, minT, halfT); |
|
7888 this._computeParts(curves[1], index, halfT, maxT); |
|
7889 } else { |
|
7890 var x = curve[6] - curve[0], |
|
7891 y = curve[7] - curve[1], |
|
7892 dist = Math.sqrt(x * x + y * y); |
|
7893 if (dist > 0.00001) { |
|
7894 this.length += dist; |
|
7895 this.parts.push({ |
|
7896 offset: this.length, |
|
7897 value: maxT, |
|
7898 index: index |
|
7899 }); |
|
7900 } |
|
7901 } |
|
7902 }, |
|
7903 |
|
7904 getParameterAt: function(offset) { |
|
7905 var i, j = this.index; |
|
7906 for (;;) { |
|
7907 i = j; |
|
7908 if (j == 0 || this.parts[--j].offset < offset) |
|
7909 break; |
|
7910 } |
|
7911 for (var l = this.parts.length; i < l; i++) { |
|
7912 var part = this.parts[i]; |
|
7913 if (part.offset >= offset) { |
|
7914 this.index = i; |
|
7915 var prev = this.parts[i - 1]; |
|
7916 var prevVal = prev && prev.index == part.index ? prev.value : 0, |
|
7917 prevLen = prev ? prev.offset : 0; |
|
7918 return { |
|
7919 value: prevVal + (part.value - prevVal) |
|
7920 * (offset - prevLen) / (part.offset - prevLen), |
|
7921 index: part.index |
|
7922 }; |
|
7923 } |
|
7924 } |
|
7925 var part = this.parts[this.parts.length - 1]; |
|
7926 return { |
|
7927 value: 1, |
|
7928 index: part.index |
|
7929 }; |
|
7930 }, |
|
7931 |
|
7932 evaluate: function(offset, type) { |
|
7933 var param = this.getParameterAt(offset); |
|
7934 return Curve.evaluate(this.curves[param.index], param.value, type); |
|
7935 }, |
|
7936 |
|
7937 drawPart: function(ctx, from, to) { |
|
7938 from = this.getParameterAt(from); |
|
7939 to = this.getParameterAt(to); |
|
7940 for (var i = from.index; i <= to.index; i++) { |
|
7941 var curve = Curve.getPart(this.curves[i], |
|
7942 i == from.index ? from.value : 0, |
|
7943 i == to.index ? to.value : 1); |
|
7944 if (i == from.index) |
|
7945 ctx.moveTo(curve[0], curve[1]); |
|
7946 ctx.bezierCurveTo.apply(ctx, curve.slice(2)); |
|
7947 } |
|
7948 } |
|
7949 }); |
|
7950 |
|
7951 var PathFitter = Base.extend({ |
|
7952 initialize: function(path, error) { |
|
7953 this.points = []; |
|
7954 var segments = path._segments, |
|
7955 prev; |
|
7956 for (var i = 0, l = segments.length; i < l; i++) { |
|
7957 var point = segments[i].point.clone(); |
|
7958 if (!prev || !prev.equals(point)) { |
|
7959 this.points.push(point); |
|
7960 prev = point; |
|
7961 } |
|
7962 } |
|
7963 this.error = error; |
|
7964 }, |
|
7965 |
|
7966 fit: function() { |
|
7967 var points = this.points, |
|
7968 length = points.length; |
|
7969 this.segments = length > 0 ? [new Segment(points[0])] : []; |
|
7970 if (length > 1) |
|
7971 this.fitCubic(0, length - 1, |
|
7972 points[1].subtract(points[0]).normalize(), |
|
7973 points[length - 2].subtract(points[length - 1]).normalize()); |
|
7974 return this.segments; |
|
7975 }, |
|
7976 |
|
7977 fitCubic: function(first, last, tan1, tan2) { |
|
7978 if (last - first == 1) { |
|
7979 var pt1 = this.points[first], |
|
7980 pt2 = this.points[last], |
|
7981 dist = pt1.getDistance(pt2) / 3; |
|
7982 this.addCurve([pt1, pt1.add(tan1.normalize(dist)), |
|
7983 pt2.add(tan2.normalize(dist)), pt2]); |
|
7984 return; |
|
7985 } |
|
7986 var uPrime = this.chordLengthParameterize(first, last), |
|
7987 maxError = Math.max(this.error, this.error * this.error), |
|
7988 split; |
|
7989 for (var i = 0; i <= 4; i++) { |
|
7990 var curve = this.generateBezier(first, last, uPrime, tan1, tan2); |
|
7991 var max = this.findMaxError(first, last, curve, uPrime); |
|
7992 if (max.error < this.error) { |
|
7993 this.addCurve(curve); |
|
7994 return; |
|
7995 } |
|
7996 split = max.index; |
|
7997 if (max.error >= maxError) |
|
7998 break; |
|
7999 this.reparameterize(first, last, uPrime, curve); |
|
8000 maxError = max.error; |
|
8001 } |
|
8002 var V1 = this.points[split - 1].subtract(this.points[split]), |
|
8003 V2 = this.points[split].subtract(this.points[split + 1]), |
|
8004 tanCenter = V1.add(V2).divide(2).normalize(); |
|
8005 this.fitCubic(first, split, tan1, tanCenter); |
|
8006 this.fitCubic(split, last, tanCenter.negate(), tan2); |
|
8007 }, |
|
8008 |
|
8009 addCurve: function(curve) { |
|
8010 var prev = this.segments[this.segments.length - 1]; |
|
8011 prev.setHandleOut(curve[1].subtract(curve[0])); |
|
8012 this.segments.push( |
|
8013 new Segment(curve[3], curve[2].subtract(curve[3]))); |
|
8014 }, |
|
8015 |
|
8016 generateBezier: function(first, last, uPrime, tan1, tan2) { |
|
8017 var epsilon = 1e-11, |
|
8018 pt1 = this.points[first], |
|
8019 pt2 = this.points[last], |
|
8020 C = [[0, 0], [0, 0]], |
|
8021 X = [0, 0]; |
|
8022 |
|
8023 for (var i = 0, l = last - first + 1; i < l; i++) { |
|
8024 var u = uPrime[i], |
|
8025 t = 1 - u, |
|
8026 b = 3 * u * t, |
|
8027 b0 = t * t * t, |
|
8028 b1 = b * t, |
|
8029 b2 = b * u, |
|
8030 b3 = u * u * u, |
|
8031 a1 = tan1.normalize(b1), |
|
8032 a2 = tan2.normalize(b2), |
|
8033 tmp = this.points[first + i] |
|
8034 .subtract(pt1.multiply(b0 + b1)) |
|
8035 .subtract(pt2.multiply(b2 + b3)); |
|
8036 C[0][0] += a1.dot(a1); |
|
8037 C[0][1] += a1.dot(a2); |
|
8038 C[1][0] = C[0][1]; |
|
8039 C[1][1] += a2.dot(a2); |
|
8040 X[0] += a1.dot(tmp); |
|
8041 X[1] += a2.dot(tmp); |
|
8042 } |
|
8043 |
|
8044 var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1], |
|
8045 alpha1, alpha2; |
|
8046 if (Math.abs(detC0C1) > epsilon) { |
|
8047 var detC0X = C[0][0] * X[1] - C[1][0] * X[0], |
|
8048 detXC1 = X[0] * C[1][1] - X[1] * C[0][1]; |
|
8049 alpha1 = detXC1 / detC0C1; |
|
8050 alpha2 = detC0X / detC0C1; |
|
8051 } else { |
|
8052 var c0 = C[0][0] + C[0][1], |
|
8053 c1 = C[1][0] + C[1][1]; |
|
8054 if (Math.abs(c0) > epsilon) { |
|
8055 alpha1 = alpha2 = X[0] / c0; |
|
8056 } else if (Math.abs(c1) > epsilon) { |
|
8057 alpha1 = alpha2 = X[1] / c1; |
|
8058 } else { |
|
8059 alpha1 = alpha2 = 0; |
|
8060 } |
|
8061 } |
|
8062 |
|
8063 var segLength = pt2.getDistance(pt1); |
|
8064 epsilon *= segLength; |
|
8065 if (alpha1 < epsilon || alpha2 < epsilon) { |
|
8066 alpha1 = alpha2 = segLength / 3; |
|
8067 } |
|
8068 |
|
8069 return [pt1, pt1.add(tan1.normalize(alpha1)), |
|
8070 pt2.add(tan2.normalize(alpha2)), pt2]; |
|
8071 }, |
|
8072 |
|
8073 reparameterize: function(first, last, u, curve) { |
|
8074 for (var i = first; i <= last; i++) { |
|
8075 u[i - first] = this.findRoot(curve, this.points[i], u[i - first]); |
|
8076 } |
|
8077 }, |
|
8078 |
|
8079 findRoot: function(curve, point, u) { |
|
8080 var curve1 = [], |
|
8081 curve2 = []; |
|
8082 for (var i = 0; i <= 2; i++) { |
|
8083 curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3); |
|
8084 } |
|
8085 for (var i = 0; i <= 1; i++) { |
|
8086 curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2); |
|
8087 } |
|
8088 var pt = this.evaluate(3, curve, u), |
|
8089 pt1 = this.evaluate(2, curve1, u), |
|
8090 pt2 = this.evaluate(1, curve2, u), |
|
8091 diff = pt.subtract(point), |
|
8092 df = pt1.dot(pt1) + diff.dot(pt2); |
|
8093 if (Math.abs(df) < 0.00001) |
|
8094 return u; |
|
8095 return u - diff.dot(pt1) / df; |
|
8096 }, |
|
8097 |
|
8098 evaluate: function(degree, curve, t) { |
|
8099 var tmp = curve.slice(); |
|
8100 for (var i = 1; i <= degree; i++) { |
|
8101 for (var j = 0; j <= degree - i; j++) { |
|
8102 tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t)); |
|
8103 } |
|
8104 } |
|
8105 return tmp[0]; |
|
8106 }, |
|
8107 |
|
8108 chordLengthParameterize: function(first, last) { |
|
8109 var u = [0]; |
|
8110 for (var i = first + 1; i <= last; i++) { |
|
8111 u[i - first] = u[i - first - 1] |
|
8112 + this.points[i].getDistance(this.points[i - 1]); |
|
8113 } |
|
8114 for (var i = 1, m = last - first; i <= m; i++) { |
|
8115 u[i] /= u[m]; |
|
8116 } |
|
8117 return u; |
|
8118 }, |
|
8119 |
|
8120 findMaxError: function(first, last, curve, u) { |
|
8121 var index = Math.floor((last - first + 1) / 2), |
|
8122 maxDist = 0; |
|
8123 for (var i = first + 1; i < last; i++) { |
|
8124 var P = this.evaluate(3, curve, u[i - first]); |
|
8125 var v = P.subtract(this.points[i]); |
|
8126 var dist = v.x * v.x + v.y * v.y; |
|
8127 if (dist >= maxDist) { |
|
8128 maxDist = dist; |
|
8129 index = i; |
|
8130 } |
|
8131 } |
|
8132 return { |
|
8133 error: maxDist, |
|
8134 index: index |
|
8135 }; |
|
8136 } |
|
8137 }); |
|
8138 |
|
8139 PathItem.inject(new function() { |
|
8140 |
|
8141 function splitPath(intersections, collectOthers) { |
|
8142 intersections.sort(function(loc1, loc2) { |
|
8143 var path1 = loc1.getPath(), |
|
8144 path2 = loc2.getPath(); |
|
8145 return path1 === path2 |
|
8146 ? (loc1.getIndex() + loc1.getParameter()) |
|
8147 - (loc2.getIndex() + loc2.getParameter()) |
|
8148 : path1._id - path2._id; |
|
8149 }); |
|
8150 var others = collectOthers && []; |
|
8151 for (var i = intersections.length - 1; i >= 0; i--) { |
|
8152 var loc = intersections[i], |
|
8153 other = loc.getIntersection(), |
|
8154 curve = loc.divide(), |
|
8155 segment = curve && curve.getSegment1() || loc.getSegment(); |
|
8156 if (others) |
|
8157 others.push(other); |
|
8158 segment._intersection = other; |
|
8159 loc._segment = segment; |
|
8160 } |
|
8161 return others; |
|
8162 } |
|
8163 |
|
8164 function reorientPath(path) { |
|
8165 if (path instanceof CompoundPath) { |
|
8166 var children = path.removeChildren(), |
|
8167 length = children.length, |
|
8168 bounds = new Array(length), |
|
8169 counters = new Array(length), |
|
8170 clockwise; |
|
8171 children.sort(function(a, b){ |
|
8172 var b1 = a.getBounds(), b2 = b.getBounds(); |
|
8173 return b1._width * b1._height < b2._width * b2._height; |
|
8174 }); |
|
8175 path.addChildren(children); |
|
8176 clockwise = children[0].isClockwise(); |
|
8177 for (var i = 0; i < length; i++) { |
|
8178 bounds[i] = children[i].getBounds(); |
|
8179 counters[i] = 0; |
|
8180 } |
|
8181 for (var i = 0; i < length; i++) { |
|
8182 for (var j = 1; j < length; j++) { |
|
8183 if (i !== j && bounds[i].contains(bounds[j])) |
|
8184 counters[j]++; |
|
8185 } |
|
8186 if (i > 0 && counters[i] % 2 === 0) |
|
8187 children[i].setClockwise(clockwise); |
|
8188 } |
|
8189 } |
|
8190 return path; |
|
8191 } |
|
8192 |
|
8193 function computeBoolean(path1, path2, operator, subtract) { |
|
8194 path1 = reorientPath(path1.clone(false)); |
|
8195 path2 = reorientPath(path2.clone(false)); |
|
8196 var path1Clockwise = path1.isClockwise(), |
|
8197 path2Clockwise = path2.isClockwise(), |
|
8198 intersections = path1.getIntersections(path2); |
|
8199 splitPath(splitPath(intersections, true)); |
|
8200 if (!path1Clockwise) |
|
8201 path1.reverse(); |
|
8202 if (!(subtract ^ path2Clockwise)) |
|
8203 path2.reverse(); |
|
8204 path1Clockwise = true; |
|
8205 path2Clockwise = !subtract; |
|
8206 var paths = [] |
|
8207 .concat(path1._children || [path1]) |
|
8208 .concat(path2._children || [path2]), |
|
8209 segments = [], |
|
8210 result = new CompoundPath(); |
|
8211 for (var i = 0, l = paths.length; i < l; i++) { |
|
8212 var path = paths[i], |
|
8213 parent = path._parent, |
|
8214 clockwise = path.isClockwise(), |
|
8215 segs = path._segments; |
|
8216 path = parent instanceof CompoundPath ? parent : path; |
|
8217 for (var j = segs.length - 1; j >= 0; j--) { |
|
8218 var segment = segs[j], |
|
8219 midPoint = segment.getCurve().getPoint(0.5), |
|
8220 insidePath1 = path !== path1 && path1.contains(midPoint) |
|
8221 && (clockwise === path1Clockwise || subtract |
|
8222 || !testOnCurve(path1, midPoint)), |
|
8223 insidePath2 = path !== path2 && path2.contains(midPoint) |
|
8224 && (clockwise === path2Clockwise |
|
8225 || !testOnCurve(path2, midPoint)); |
|
8226 if (operator(path === path1, insidePath1, insidePath2)) { |
|
8227 segment._invalid = true; |
|
8228 } else { |
|
8229 segments.push(segment); |
|
8230 } |
|
8231 } |
|
8232 } |
|
8233 for (var i = 0, l = segments.length; i < l; i++) { |
|
8234 var segment = segments[i]; |
|
8235 if (segment._visited) |
|
8236 continue; |
|
8237 var path = new Path(), |
|
8238 loc = segment._intersection, |
|
8239 intersection = loc && loc.getSegment(true); |
|
8240 if (segment.getPrevious()._invalid) |
|
8241 segment.setHandleIn(intersection |
|
8242 ? intersection._handleIn |
|
8243 : new Point(0, 0)); |
|
8244 do { |
|
8245 segment._visited = true; |
|
8246 if (segment._invalid && segment._intersection) { |
|
8247 var inter = segment._intersection.getSegment(true); |
|
8248 path.add(new Segment(segment._point, segment._handleIn, |
|
8249 inter._handleOut)); |
|
8250 inter._visited = true; |
|
8251 segment = inter; |
|
8252 } else { |
|
8253 path.add(segment.clone()); |
|
8254 } |
|
8255 segment = segment.getNext(); |
|
8256 } while (segment && !segment._visited && segment !== intersection); |
|
8257 var amount = path._segments.length; |
|
8258 if (amount > 1 && (amount > 2 || !path.isPolygon())) { |
|
8259 path.setClosed(true); |
|
8260 result.addChild(path, true); |
|
8261 } else { |
|
8262 path.remove(); |
|
8263 } |
|
8264 } |
|
8265 path1.remove(); |
|
8266 path2.remove(); |
|
8267 return result.reduce(); |
|
8268 } |
|
8269 |
|
8270 function testOnCurve(path, point) { |
|
8271 var curves = path.getCurves(), |
|
8272 bounds = path.getBounds(); |
|
8273 if (bounds.contains(point)) { |
|
8274 for (var i = 0, l = curves.length; i < l; i++) { |
|
8275 var curve = curves[i]; |
|
8276 if (curve.getBounds().contains(point) |
|
8277 && curve.getParameterOf(point)) |
|
8278 return true; |
|
8279 } |
|
8280 } |
|
8281 return false; |
|
8282 } |
|
8283 |
|
8284 return { |
|
8285 unite: function(path) { |
|
8286 return computeBoolean(this, path, |
|
8287 function(isPath1, isInPath1, isInPath2) { |
|
8288 return isInPath1 || isInPath2; |
|
8289 }); |
|
8290 }, |
|
8291 |
|
8292 intersect: function(path) { |
|
8293 return computeBoolean(this, path, |
|
8294 function(isPath1, isInPath1, isInPath2) { |
|
8295 return !(isInPath1 || isInPath2); |
|
8296 }); |
|
8297 }, |
|
8298 |
|
8299 subtract: function(path) { |
|
8300 return computeBoolean(this, path, |
|
8301 function(isPath1, isInPath1, isInPath2) { |
|
8302 return isPath1 && isInPath2 || !isPath1 && !isInPath1; |
|
8303 }, true); |
|
8304 }, |
|
8305 |
|
8306 exclude: function(path) { |
|
8307 return new Group([this.subtract(path), path.subtract(this)]); |
|
8308 }, |
|
8309 |
|
8310 divide: function(path) { |
|
8311 return new Group([this.subtract(path), this.intersect(path)]); |
|
8312 } |
|
8313 }; |
|
8314 }); |
|
8315 |
|
8316 var TextItem = Item.extend({ |
|
8317 _class: 'TextItem', |
|
8318 _boundsSelected: true, |
|
8319 _serializeFields: { |
|
8320 content: null |
|
8321 }, |
|
8322 _boundsGetter: 'getBounds', |
|
8323 |
|
8324 initialize: function TextItem(arg) { |
|
8325 this._content = ''; |
|
8326 this._lines = []; |
|
8327 var hasProps = arg && Base.isPlainObject(arg) |
|
8328 && arg.x === undefined && arg.y === undefined; |
|
8329 this._initialize(hasProps && arg, !hasProps && Point.read(arguments)); |
|
8330 }, |
|
8331 |
|
8332 _equals: function(item) { |
|
8333 return this._content === item._content; |
|
8334 }, |
|
8335 |
|
8336 _clone: function _clone(copy) { |
|
8337 copy.setContent(this._content); |
|
8338 return _clone.base.call(this, copy); |
|
8339 }, |
|
8340 |
|
8341 getContent: function() { |
|
8342 return this._content; |
|
8343 }, |
|
8344 |
|
8345 setContent: function(content) { |
|
8346 this._content = '' + content; |
|
8347 this._lines = this._content.split(/\r\n|\n|\r/mg); |
|
8348 this._changed(69); |
|
8349 }, |
|
8350 |
|
8351 isEmpty: function() { |
|
8352 return !this._content; |
|
8353 }, |
|
8354 |
|
8355 getCharacterStyle: '#getStyle', |
|
8356 setCharacterStyle: '#setStyle', |
|
8357 |
|
8358 getParagraphStyle: '#getStyle', |
|
8359 setParagraphStyle: '#setStyle' |
|
8360 }); |
|
8361 |
|
8362 var PointText = TextItem.extend({ |
|
8363 _class: 'PointText', |
|
8364 |
|
8365 initialize: function PointText() { |
|
8366 TextItem.apply(this, arguments); |
|
8367 }, |
|
8368 |
|
8369 clone: function(insert) { |
|
8370 return this._clone(new PointText({ insert: false }), insert); |
|
8371 }, |
|
8372 |
|
8373 getPoint: function() { |
|
8374 var point = this._matrix.getTranslation(); |
|
8375 return new LinkedPoint(point.x, point.y, this, 'setPoint'); |
|
8376 }, |
|
8377 |
|
8378 setPoint: function(point) { |
|
8379 point = Point.read(arguments); |
|
8380 this.translate(point.subtract(this._matrix.getTranslation())); |
|
8381 }, |
|
8382 |
|
8383 _draw: function(ctx) { |
|
8384 if (!this._content) |
|
8385 return; |
|
8386 this._setStyles(ctx); |
|
8387 var style = this._style, |
|
8388 lines = this._lines, |
|
8389 leading = style.getLeading(), |
|
8390 shadowColor = ctx.shadowColor; |
|
8391 |
|
8392 ctx.font = style.getFontStyle(); |
|
8393 ctx.textAlign = style.getJustification(); |
|
8394 for (var i = 0, l = lines.length; i < l; i++) { |
|
8395 ctx.shadowColor = shadowColor; |
|
8396 var line = lines[i]; |
|
8397 if (style.hasFill()) { |
|
8398 ctx.fillText(line, 0, 0); |
|
8399 ctx.shadowColor = 'rgba(0,0,0,0)'; |
|
8400 } |
|
8401 if (style.hasStroke()) |
|
8402 ctx.strokeText(line, 0, 0); |
|
8403 ctx.translate(0, leading); |
|
8404 } |
|
8405 } |
|
8406 }, new function() { |
|
8407 var measureCtx = null; |
|
8408 |
|
8409 return { |
|
8410 _getBounds: function(getter, matrix) { |
|
8411 if (!measureCtx) |
|
8412 measureCtx = CanvasProvider.getContext(1, 1); |
|
8413 var style = this._style, |
|
8414 lines = this._lines, |
|
8415 count = lines.length, |
|
8416 justification = style.getJustification(), |
|
8417 leading = style.getLeading(), |
|
8418 x = 0; |
|
8419 measureCtx.font = style.getFontStyle(); |
|
8420 var width = 0; |
|
8421 for (var i = 0; i < count; i++) |
|
8422 width = Math.max(width, measureCtx.measureText(lines[i]).width); |
|
8423 if (justification !== 'left') |
|
8424 x -= width / (justification === 'center' ? 2: 1); |
|
8425 var bounds = new Rectangle(x, |
|
8426 count ? - 0.75 * leading : 0, |
|
8427 width, count * leading); |
|
8428 return matrix ? matrix._transformBounds(bounds, bounds) : bounds; |
|
8429 } |
|
8430 }; |
|
8431 }); |
|
8432 |
|
8433 var Color = Base.extend(new function() { |
|
8434 |
|
8435 var types = { |
|
8436 gray: ['gray'], |
|
8437 rgb: ['red', 'green', 'blue'], |
|
8438 hsb: ['hue', 'saturation', 'brightness'], |
|
8439 hsl: ['hue', 'saturation', 'lightness'], |
|
8440 gradient: ['gradient', 'origin', 'destination', 'highlight'] |
|
8441 }; |
|
8442 |
|
8443 var componentParsers = {}, |
|
8444 colorCache = {}, |
|
8445 colorCtx; |
|
8446 |
|
8447 function fromCSS(string) { |
|
8448 var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/), |
|
8449 components; |
|
8450 if (match) { |
|
8451 components = [0, 0, 0]; |
|
8452 for (var i = 0; i < 3; i++) { |
|
8453 var value = match[i + 1]; |
|
8454 components[i] = parseInt(value.length == 1 |
|
8455 ? value + value : value, 16) / 255; |
|
8456 } |
|
8457 } else if (match = string.match(/^rgba?\((.*)\)$/)) { |
|
8458 components = match[1].split(','); |
|
8459 for (var i = 0, l = components.length; i < l; i++) { |
|
8460 var value = parseFloat(components[i]); |
|
8461 components[i] = i < 3 ? value / 255 : value; |
|
8462 } |
|
8463 } else { |
|
8464 var cached = colorCache[string]; |
|
8465 if (!cached) { |
|
8466 if (!colorCtx) { |
|
8467 colorCtx = CanvasProvider.getContext(1, 1); |
|
8468 colorCtx.globalCompositeOperation = 'copy'; |
|
8469 } |
|
8470 colorCtx.fillStyle = 'rgba(0,0,0,0)'; |
|
8471 colorCtx.fillStyle = string; |
|
8472 colorCtx.fillRect(0, 0, 1, 1); |
|
8473 var data = colorCtx.getImageData(0, 0, 1, 1).data; |
|
8474 cached = colorCache[string] = [ |
|
8475 data[0] / 255, |
|
8476 data[1] / 255, |
|
8477 data[2] / 255 |
|
8478 ]; |
|
8479 } |
|
8480 components = cached.slice(); |
|
8481 } |
|
8482 return components; |
|
8483 } |
|
8484 |
|
8485 var hsbIndices = [ |
|
8486 [0, 3, 1], |
|
8487 [2, 0, 1], |
|
8488 [1, 0, 3], |
|
8489 [1, 2, 0], |
|
8490 [3, 1, 0], |
|
8491 [0, 1, 2] |
|
8492 ]; |
|
8493 |
|
8494 var converters = { |
|
8495 'rgb-hsb': function(r, g, b) { |
|
8496 var max = Math.max(r, g, b), |
|
8497 min = Math.min(r, g, b), |
|
8498 delta = max - min, |
|
8499 h = delta === 0 ? 0 |
|
8500 : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) |
|
8501 : max == g ? (b - r) / delta + 2 |
|
8502 : (r - g) / delta + 4) * 60; |
|
8503 return [h, max === 0 ? 0 : delta / max, max]; |
|
8504 }, |
|
8505 |
|
8506 'hsb-rgb': function(h, s, b) { |
|
8507 var h = (h / 60) % 6, |
|
8508 i = Math.floor(h), |
|
8509 f = h - i, |
|
8510 i = hsbIndices[i], |
|
8511 v = [ |
|
8512 b, |
|
8513 b * (1 - s), |
|
8514 b * (1 - s * f), |
|
8515 b * (1 - s * (1 - f)) |
|
8516 ]; |
|
8517 return [v[i[0]], v[i[1]], v[i[2]]]; |
|
8518 }, |
|
8519 |
|
8520 'rgb-hsl': function(r, g, b) { |
|
8521 var max = Math.max(r, g, b), |
|
8522 min = Math.min(r, g, b), |
|
8523 delta = max - min, |
|
8524 achromatic = delta === 0, |
|
8525 h = achromatic ? 0 |
|
8526 : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) |
|
8527 : max == g ? (b - r) / delta + 2 |
|
8528 : (r - g) / delta + 4) * 60, |
|
8529 l = (max + min) / 2, |
|
8530 s = achromatic ? 0 : l < 0.5 |
|
8531 ? delta / (max + min) |
|
8532 : delta / (2 - max - min); |
|
8533 return [h, s, l]; |
|
8534 }, |
|
8535 |
|
8536 'hsl-rgb': function(h, s, l) { |
|
8537 h /= 360; |
|
8538 if (s === 0) |
|
8539 return [l, l, l]; |
|
8540 var t3s = [ h + 1 / 3, h, h - 1 / 3 ], |
|
8541 t2 = l < 0.5 ? l * (1 + s) : l + s - l * s, |
|
8542 t1 = 2 * l - t2, |
|
8543 c = []; |
|
8544 for (var i = 0; i < 3; i++) { |
|
8545 var t3 = t3s[i]; |
|
8546 if (t3 < 0) t3 += 1; |
|
8547 if (t3 > 1) t3 -= 1; |
|
8548 c[i] = 6 * t3 < 1 |
|
8549 ? t1 + (t2 - t1) * 6 * t3 |
|
8550 : 2 * t3 < 1 |
|
8551 ? t2 |
|
8552 : 3 * t3 < 2 |
|
8553 ? t1 + (t2 - t1) * ((2 / 3) - t3) * 6 |
|
8554 : t1; |
|
8555 } |
|
8556 return c; |
|
8557 }, |
|
8558 |
|
8559 'rgb-gray': function(r, g, b) { |
|
8560 return [r * 0.2989 + g * 0.587 + b * 0.114]; |
|
8561 }, |
|
8562 |
|
8563 'gray-rgb': function(g) { |
|
8564 return [g, g, g]; |
|
8565 }, |
|
8566 |
|
8567 'gray-hsb': function(g) { |
|
8568 return [0, 0, g]; |
|
8569 }, |
|
8570 |
|
8571 'gray-hsl': function(g) { |
|
8572 return [0, 0, g]; |
|
8573 }, |
|
8574 |
|
8575 'gradient-rgb': function() { |
|
8576 return []; |
|
8577 }, |
|
8578 |
|
8579 'rgb-gradient': function() { |
|
8580 return []; |
|
8581 } |
|
8582 |
|
8583 }; |
|
8584 |
|
8585 return Base.each(types, function(properties, type) { |
|
8586 componentParsers[type] = []; |
|
8587 Base.each(properties, function(name, index) { |
|
8588 var part = Base.capitalize(name), |
|
8589 hasOverlap = /^(hue|saturation)$/.test(name), |
|
8590 parser = componentParsers[type][index] = name === 'gradient' |
|
8591 ? function(value) { |
|
8592 var current = this._components[0]; |
|
8593 value = Gradient.read( |
|
8594 Array.isArray(value) ? value : arguments, |
|
8595 0, 0, { readNull: true }); |
|
8596 if (current !== value) { |
|
8597 if (current) |
|
8598 current._removeOwner(this); |
|
8599 if (value) |
|
8600 value._addOwner(this); |
|
8601 } |
|
8602 return value; |
|
8603 } |
|
8604 : name === 'hue' |
|
8605 ? function(value) { |
|
8606 return isNaN(value) ? 0 |
|
8607 : ((value % 360) + 360) % 360; |
|
8608 } |
|
8609 : type === 'gradient' |
|
8610 ? function() { |
|
8611 return Point.read(arguments, 0, 0, { |
|
8612 readNull: name === 'highlight', |
|
8613 clone: true |
|
8614 }); |
|
8615 } |
|
8616 : function(value) { |
|
8617 return isNaN(value) ? 0 |
|
8618 : Math.min(Math.max(value, 0), 1); |
|
8619 }; |
|
8620 |
|
8621 this['get' + part] = function() { |
|
8622 return this._type === type |
|
8623 || hasOverlap && /^hs[bl]$/.test(this._type) |
|
8624 ? this._components[index] |
|
8625 : this._convert(type)[index]; |
|
8626 }; |
|
8627 |
|
8628 this['set' + part] = function(value) { |
|
8629 if (this._type !== type |
|
8630 && !(hasOverlap && /^hs[bl]$/.test(this._type))) { |
|
8631 this._components = this._convert(type); |
|
8632 this._properties = types[type]; |
|
8633 this._type = type; |
|
8634 } |
|
8635 value = parser.call(this, value); |
|
8636 if (value != null) { |
|
8637 this._components[index] = value; |
|
8638 this._changed(); |
|
8639 } |
|
8640 }; |
|
8641 }, this); |
|
8642 }, { |
|
8643 _class: 'Color', |
|
8644 _readIndex: true, |
|
8645 |
|
8646 initialize: function Color(arg) { |
|
8647 var slice = Array.prototype.slice, |
|
8648 args = arguments, |
|
8649 read = 0, |
|
8650 parse = true, |
|
8651 type, |
|
8652 components, |
|
8653 alpha, |
|
8654 values; |
|
8655 if (Array.isArray(arg)) { |
|
8656 args = arg; |
|
8657 arg = args[0]; |
|
8658 } |
|
8659 var argType = arg != null && typeof arg; |
|
8660 if (argType === 'string' && arg in types) { |
|
8661 type = arg; |
|
8662 arg = args[1]; |
|
8663 if (Array.isArray(arg)) { |
|
8664 components = arg; |
|
8665 alpha = args[2]; |
|
8666 } else { |
|
8667 if (this.__read) |
|
8668 read = 1; |
|
8669 args = slice.call(args, 1); |
|
8670 argType = typeof arg; |
|
8671 } |
|
8672 } |
|
8673 if (!components) { |
|
8674 parse = !(this.__options && this.__options.dontParse); |
|
8675 values = argType === 'number' |
|
8676 ? args |
|
8677 : argType === 'object' && arg.length != null |
|
8678 ? arg |
|
8679 : null; |
|
8680 if (values) { |
|
8681 if (!type) |
|
8682 type = values.length >= 3 |
|
8683 ? 'rgb' |
|
8684 : 'gray'; |
|
8685 var length = types[type].length; |
|
8686 alpha = values[length]; |
|
8687 if (this.__read) |
|
8688 read += values === arguments |
|
8689 ? length + (alpha != null ? 1 : 0) |
|
8690 : 1; |
|
8691 if (values.length > length) |
|
8692 values = slice.call(values, 0, length); |
|
8693 } else if (argType === 'string') { |
|
8694 type = 'rgb'; |
|
8695 components = fromCSS(arg); |
|
8696 if (components.length === 4) { |
|
8697 alpha = components[3]; |
|
8698 components.length--; |
|
8699 } |
|
8700 } else if (argType === 'object') { |
|
8701 if (arg.constructor === Color) { |
|
8702 type = arg._type; |
|
8703 components = arg._components.slice(); |
|
8704 alpha = arg._alpha; |
|
8705 if (type === 'gradient') { |
|
8706 for (var i = 1, l = components.length; i < l; i++) { |
|
8707 var point = components[i]; |
|
8708 if (point) |
|
8709 components[i] = point.clone(); |
|
8710 } |
|
8711 } |
|
8712 } else if (arg.constructor === Gradient) { |
|
8713 type = 'gradient'; |
|
8714 values = args; |
|
8715 } else { |
|
8716 type = 'hue' in arg |
|
8717 ? 'lightness' in arg |
|
8718 ? 'hsl' |
|
8719 : 'hsb' |
|
8720 : 'gradient' in arg || 'stops' in arg |
|
8721 || 'radial' in arg |
|
8722 ? 'gradient' |
|
8723 : 'gray' in arg |
|
8724 ? 'gray' |
|
8725 : 'rgb'; |
|
8726 var properties = types[type]; |
|
8727 parsers = parse && componentParsers[type]; |
|
8728 this._components = components = []; |
|
8729 for (var i = 0, l = properties.length; i < l; i++) { |
|
8730 var value = arg[properties[i]]; |
|
8731 if (value == null && i === 0 && type === 'gradient' |
|
8732 && 'stops' in arg) { |
|
8733 value = { |
|
8734 stops: arg.stops, |
|
8735 radial: arg.radial |
|
8736 }; |
|
8737 } |
|
8738 if (parse) |
|
8739 value = parsers[i].call(this, value); |
|
8740 if (value != null) |
|
8741 components[i] = value; |
|
8742 } |
|
8743 alpha = arg.alpha; |
|
8744 } |
|
8745 } |
|
8746 if (this.__read && type) |
|
8747 read = 1; |
|
8748 } |
|
8749 this._type = type || 'rgb'; |
|
8750 if (type === 'gradient') |
|
8751 this._id = Color._id = (Color._id || 0) + 1; |
|
8752 if (!components) { |
|
8753 this._components = components = []; |
|
8754 var parsers = componentParsers[this._type]; |
|
8755 for (var i = 0, l = parsers.length; i < l; i++) { |
|
8756 var value = values && values[i]; |
|
8757 if (parse) |
|
8758 value = parsers[i].call(this, value); |
|
8759 if (value != null) |
|
8760 components[i] = value; |
|
8761 } |
|
8762 } |
|
8763 this._components = components; |
|
8764 this._properties = types[this._type]; |
|
8765 this._alpha = alpha; |
|
8766 if (this.__read) |
|
8767 this.__read = read; |
|
8768 }, |
|
8769 |
|
8770 _serialize: function(options, dictionary) { |
|
8771 var components = this.getComponents(); |
|
8772 return Base.serialize( |
|
8773 /^(gray|rgb)$/.test(this._type) |
|
8774 ? components |
|
8775 : [this._type].concat(components), |
|
8776 options, true, dictionary); |
|
8777 }, |
|
8778 |
|
8779 _changed: function() { |
|
8780 this._canvasStyle = null; |
|
8781 if (this._owner) |
|
8782 this._owner._changed(17); |
|
8783 }, |
|
8784 |
|
8785 _convert: function(type) { |
|
8786 var converter; |
|
8787 return this._type === type |
|
8788 ? this._components.slice() |
|
8789 : (converter = converters[this._type + '-' + type]) |
|
8790 ? converter.apply(this, this._components) |
|
8791 : converters['rgb-' + type].apply(this, |
|
8792 converters[this._type + '-rgb'].apply(this, |
|
8793 this._components)); |
|
8794 }, |
|
8795 |
|
8796 convert: function(type) { |
|
8797 return new Color(type, this._convert(type), this._alpha); |
|
8798 }, |
|
8799 |
|
8800 getType: function() { |
|
8801 return this._type; |
|
8802 }, |
|
8803 |
|
8804 setType: function(type) { |
|
8805 this._components = this._convert(type); |
|
8806 this._properties = types[type]; |
|
8807 this._type = type; |
|
8808 }, |
|
8809 |
|
8810 getComponents: function() { |
|
8811 var components = this._components.slice(); |
|
8812 if (this._alpha != null) |
|
8813 components.push(this._alpha); |
|
8814 return components; |
|
8815 }, |
|
8816 |
|
8817 getAlpha: function() { |
|
8818 return this._alpha != null ? this._alpha : 1; |
|
8819 }, |
|
8820 |
|
8821 setAlpha: function(alpha) { |
|
8822 this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1); |
|
8823 this._changed(); |
|
8824 }, |
|
8825 |
|
8826 hasAlpha: function() { |
|
8827 return this._alpha != null; |
|
8828 }, |
|
8829 |
|
8830 equals: function(color) { |
|
8831 if (Base.isPlainValue(color)) |
|
8832 color = Color.read(arguments); |
|
8833 return color === this || color && this._class === color._class |
|
8834 && this._type === color._type |
|
8835 && this._alpha === color._alpha |
|
8836 && Base.equals(this._components, color._components) |
|
8837 || false; |
|
8838 }, |
|
8839 |
|
8840 toString: function() { |
|
8841 var properties = this._properties, |
|
8842 parts = [], |
|
8843 isGradient = this._type === 'gradient', |
|
8844 f = Formatter.instance; |
|
8845 for (var i = 0, l = properties.length; i < l; i++) { |
|
8846 var value = this._components[i]; |
|
8847 if (value != null) |
|
8848 parts.push(properties[i] + ': ' |
|
8849 + (isGradient ? value : f.number(value))); |
|
8850 } |
|
8851 if (this._alpha != null) |
|
8852 parts.push('alpha: ' + f.number(this._alpha)); |
|
8853 return '{ ' + parts.join(', ') + ' }'; |
|
8854 }, |
|
8855 |
|
8856 toCSS: function(hex) { |
|
8857 var components = this._convert('rgb'), |
|
8858 alpha = hex || this._alpha == null ? 1 : this._alpha; |
|
8859 components = [ |
|
8860 Math.round(components[0] * 255), |
|
8861 Math.round(components[1] * 255), |
|
8862 Math.round(components[2] * 255) |
|
8863 ]; |
|
8864 if (alpha < 1) |
|
8865 components.push(alpha); |
|
8866 return hex |
|
8867 ? '#' + ((1 << 24) + (components[0] << 16) |
|
8868 + (components[1] << 8) |
|
8869 + components[2]).toString(16).slice(1) |
|
8870 : (components.length == 4 ? 'rgba(' : 'rgb(') |
|
8871 + components.join(',') + ')'; |
|
8872 }, |
|
8873 |
|
8874 toCanvasStyle: function(ctx) { |
|
8875 if (this._canvasStyle) |
|
8876 return this._canvasStyle; |
|
8877 if (this._type !== 'gradient') |
|
8878 return this._canvasStyle = this.toCSS(); |
|
8879 var components = this._components, |
|
8880 gradient = components[0], |
|
8881 stops = gradient._stops, |
|
8882 origin = components[1], |
|
8883 destination = components[2], |
|
8884 canvasGradient; |
|
8885 if (gradient._radial) { |
|
8886 var radius = destination.getDistance(origin), |
|
8887 highlight = components[3]; |
|
8888 if (highlight) { |
|
8889 var vector = highlight.subtract(origin); |
|
8890 if (vector.getLength() > radius) |
|
8891 highlight = origin.add(vector.normalize(radius - 0.1)); |
|
8892 } |
|
8893 var start = highlight || origin; |
|
8894 canvasGradient = ctx.createRadialGradient(start.x, start.y, |
|
8895 0, origin.x, origin.y, radius); |
|
8896 } else { |
|
8897 canvasGradient = ctx.createLinearGradient(origin.x, origin.y, |
|
8898 destination.x, destination.y); |
|
8899 } |
|
8900 for (var i = 0, l = stops.length; i < l; i++) { |
|
8901 var stop = stops[i]; |
|
8902 canvasGradient.addColorStop(stop._rampPoint, |
|
8903 stop._color.toCanvasStyle()); |
|
8904 } |
|
8905 return this._canvasStyle = canvasGradient; |
|
8906 }, |
|
8907 |
|
8908 transform: function(matrix) { |
|
8909 if (this._type === 'gradient') { |
|
8910 var components = this._components; |
|
8911 for (var i = 1, l = components.length; i < l; i++) { |
|
8912 var point = components[i]; |
|
8913 matrix._transformPoint(point, point, true); |
|
8914 } |
|
8915 this._changed(); |
|
8916 } |
|
8917 }, |
|
8918 |
|
8919 statics: { |
|
8920 _types: types, |
|
8921 |
|
8922 random: function() { |
|
8923 var random = Math.random; |
|
8924 return new Color(random(), random(), random()); |
|
8925 } |
|
8926 } |
|
8927 }); |
|
8928 }, new function() { |
|
8929 function clamp(value, hue) { |
|
8930 return value < 0 |
|
8931 ? 0 |
|
8932 : hue && value > 360 |
|
8933 ? 360 |
|
8934 : !hue && value > 1 |
|
8935 ? 1 |
|
8936 : value; |
|
8937 } |
|
8938 |
|
8939 var operators = { |
|
8940 add: function(a, b, hue) { |
|
8941 return clamp(a + b, hue); |
|
8942 }, |
|
8943 |
|
8944 subtract: function(a, b, hue) { |
|
8945 return clamp(a - b, hue); |
|
8946 }, |
|
8947 |
|
8948 multiply: function(a, b, hue) { |
|
8949 return clamp(a * b, hue); |
|
8950 }, |
|
8951 |
|
8952 divide: function(a, b, hue) { |
|
8953 return clamp(a / b, hue); |
|
8954 } |
|
8955 }; |
|
8956 |
|
8957 return Base.each(operators, function(operator, name) { |
|
8958 var options = { dontParse: /^(multiply|divide)$/.test(name) }; |
|
8959 |
|
8960 this[name] = function(color) { |
|
8961 color = Color.read(arguments, 0, 0, options); |
|
8962 var type = this._type, |
|
8963 properties = this._properties, |
|
8964 components1 = this._components, |
|
8965 components2 = color._convert(type); |
|
8966 for (var i = 0, l = components1.length; i < l; i++) |
|
8967 components2[i] = operator(components1[i], components2[i], |
|
8968 properties[i] === 'hue'); |
|
8969 return new Color(type, components2, |
|
8970 this._alpha != null |
|
8971 ? operator(this._alpha, color.getAlpha()) |
|
8972 : null); |
|
8973 }; |
|
8974 }, { |
|
8975 }); |
|
8976 }); |
|
8977 |
|
8978 Base.each(Color._types, function(properties, type) { |
|
8979 var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) { |
|
8980 var argType = arg != null && typeof arg, |
|
8981 components = argType === 'object' && arg.length != null |
|
8982 ? arg |
|
8983 : argType === 'string' |
|
8984 ? null |
|
8985 : arguments; |
|
8986 return components |
|
8987 ? new Color(type, components) |
|
8988 : new Color(arg); |
|
8989 }; |
|
8990 if (type.length == 3) { |
|
8991 var acronym = type.toUpperCase(); |
|
8992 Color[acronym] = this[acronym + 'Color'] = ctor; |
|
8993 } |
|
8994 }, Base.exports); |
|
8995 |
|
8996 var Gradient = Base.extend({ |
|
8997 _class: 'Gradient', |
|
8998 |
|
8999 initialize: function Gradient(stops, radial) { |
|
9000 this._id = Gradient._id = (Gradient._id || 0) + 1; |
|
9001 if (stops && this._set(stops)) |
|
9002 stops = radial = null; |
|
9003 if (!this._stops) |
|
9004 this.setStops(stops || ['white', 'black']); |
|
9005 if (this._radial == null) |
|
9006 this.setRadial(typeof radial === 'string' && radial === 'radial' |
|
9007 || radial || false); |
|
9008 }, |
|
9009 |
|
9010 _serialize: function(options, dictionary) { |
|
9011 return dictionary.add(this, function() { |
|
9012 return Base.serialize([this._stops, this._radial], |
|
9013 options, true, dictionary); |
|
9014 }); |
|
9015 }, |
|
9016 |
|
9017 _changed: function() { |
|
9018 for (var i = 0, l = this._owners && this._owners.length; i < l; i++) |
|
9019 this._owners[i]._changed(); |
|
9020 }, |
|
9021 |
|
9022 _addOwner: function(color) { |
|
9023 if (!this._owners) |
|
9024 this._owners = []; |
|
9025 this._owners.push(color); |
|
9026 }, |
|
9027 |
|
9028 _removeOwner: function(color) { |
|
9029 var index = this._owners ? this._owners.indexOf(color) : -1; |
|
9030 if (index != -1) { |
|
9031 this._owners.splice(index, 1); |
|
9032 if (this._owners.length === 0) |
|
9033 delete this._owners; |
|
9034 } |
|
9035 }, |
|
9036 |
|
9037 clone: function() { |
|
9038 var stops = []; |
|
9039 for (var i = 0, l = this._stops.length; i < l; i++) |
|
9040 stops[i] = this._stops[i].clone(); |
|
9041 return new Gradient(stops); |
|
9042 }, |
|
9043 |
|
9044 getStops: function() { |
|
9045 return this._stops; |
|
9046 }, |
|
9047 |
|
9048 setStops: function(stops) { |
|
9049 if (this.stops) { |
|
9050 for (var i = 0, l = this._stops.length; i < l; i++) |
|
9051 delete this._stops[i]._owner; |
|
9052 } |
|
9053 if (stops.length < 2) |
|
9054 throw new Error( |
|
9055 'Gradient stop list needs to contain at least two stops.'); |
|
9056 this._stops = GradientStop.readAll(stops, 0, false, true); |
|
9057 for (var i = 0, l = this._stops.length; i < l; i++) { |
|
9058 var stop = this._stops[i]; |
|
9059 stop._owner = this; |
|
9060 if (stop._defaultRamp) |
|
9061 stop.setRampPoint(i / (l - 1)); |
|
9062 } |
|
9063 this._changed(); |
|
9064 }, |
|
9065 |
|
9066 getRadial: function() { |
|
9067 return this._radial; |
|
9068 }, |
|
9069 |
|
9070 setRadial: function(radial) { |
|
9071 this._radial = radial; |
|
9072 this._changed(); |
|
9073 }, |
|
9074 |
|
9075 equals: function(gradient) { |
|
9076 if (gradient === this) |
|
9077 return true; |
|
9078 if (gradient && this._class === gradient._class |
|
9079 && this._stops.length === gradient._stops.length) { |
|
9080 for (var i = 0, l = this._stops.length; i < l; i++) { |
|
9081 if (!this._stops[i].equals(gradient._stops[i])) |
|
9082 return false; |
|
9083 } |
|
9084 return true; |
|
9085 } |
|
9086 return false; |
|
9087 } |
|
9088 }); |
|
9089 |
|
9090 var GradientStop = Base.extend({ |
|
9091 _class: 'GradientStop', |
|
9092 |
|
9093 initialize: function GradientStop(arg0, arg1) { |
|
9094 if (arg0) { |
|
9095 var color, rampPoint; |
|
9096 if (arg1 === undefined && Array.isArray(arg0)) { |
|
9097 color = arg0[0]; |
|
9098 rampPoint = arg0[1]; |
|
9099 } else if (arg0.color) { |
|
9100 color = arg0.color; |
|
9101 rampPoint = arg0.rampPoint; |
|
9102 } else { |
|
9103 color = arg0; |
|
9104 rampPoint = arg1; |
|
9105 } |
|
9106 this.setColor(color); |
|
9107 this.setRampPoint(rampPoint); |
|
9108 } |
|
9109 }, |
|
9110 |
|
9111 clone: function() { |
|
9112 return new GradientStop(this._color.clone(), this._rampPoint); |
|
9113 }, |
|
9114 |
|
9115 _serialize: function(options, dictionary) { |
|
9116 return Base.serialize([this._color, this._rampPoint], options, true, |
|
9117 dictionary); |
|
9118 }, |
|
9119 |
|
9120 _changed: function() { |
|
9121 if (this._owner) |
|
9122 this._owner._changed(17); |
|
9123 }, |
|
9124 |
|
9125 getRampPoint: function() { |
|
9126 return this._rampPoint; |
|
9127 }, |
|
9128 |
|
9129 setRampPoint: function(rampPoint) { |
|
9130 this._defaultRamp = rampPoint == null; |
|
9131 this._rampPoint = rampPoint || 0; |
|
9132 this._changed(); |
|
9133 }, |
|
9134 |
|
9135 getColor: function() { |
|
9136 return this._color; |
|
9137 }, |
|
9138 |
|
9139 setColor: function(color) { |
|
9140 this._color = Color.read(arguments); |
|
9141 if (this._color === color) |
|
9142 this._color = color.clone(); |
|
9143 this._color._owner = this; |
|
9144 this._changed(); |
|
9145 }, |
|
9146 |
|
9147 equals: function(stop) { |
|
9148 return stop === this || stop && this._class === stop._class |
|
9149 && this._color.equals(stop._color) |
|
9150 && this._rampPoint == stop._rampPoint |
|
9151 || false; |
|
9152 } |
|
9153 }); |
|
9154 |
|
9155 var Style = Base.extend(new function() { |
|
9156 var defaults = { |
|
9157 fillColor: undefined, |
|
9158 strokeColor: undefined, |
|
9159 strokeWidth: 1, |
|
9160 strokeCap: 'butt', |
|
9161 strokeJoin: 'miter', |
|
9162 miterLimit: 10, |
|
9163 dashOffset: 0, |
|
9164 dashArray: [], |
|
9165 windingRule: 'nonzero', |
|
9166 shadowColor: undefined, |
|
9167 shadowBlur: 0, |
|
9168 shadowOffset: new Point(), |
|
9169 selectedColor: undefined, |
|
9170 font: 'sans-serif', |
|
9171 fontSize: 12, |
|
9172 leading: null, |
|
9173 justification: 'left' |
|
9174 }; |
|
9175 |
|
9176 var flags = { |
|
9177 strokeWidth: 25, |
|
9178 strokeCap: 25, |
|
9179 strokeJoin: 25, |
|
9180 miterLimit: 25, |
|
9181 font: 5, |
|
9182 fontSize: 5, |
|
9183 leading: 5, |
|
9184 justification: 5 |
|
9185 }; |
|
9186 |
|
9187 var item = {}, |
|
9188 fields = { |
|
9189 _defaults: defaults, |
|
9190 _textDefaults: new Base(defaults, { |
|
9191 fillColor: new Color() |
|
9192 }) |
|
9193 }; |
|
9194 |
|
9195 Base.each(defaults, function(value, key) { |
|
9196 var isColor = /Color$/.test(key), |
|
9197 part = Base.capitalize(key), |
|
9198 flag = flags[key], |
|
9199 set = 'set' + part, |
|
9200 get = 'get' + part; |
|
9201 |
|
9202 fields[set] = function(value) { |
|
9203 var children = this._item && this._item._children; |
|
9204 if (children && children.length > 0 |
|
9205 && this._item._type !== 'compound-path') { |
|
9206 for (var i = 0, l = children.length; i < l; i++) |
|
9207 children[i]._style[set](value); |
|
9208 } else { |
|
9209 var old = this._values[key]; |
|
9210 if (old != value) { |
|
9211 if (isColor) { |
|
9212 if (old) |
|
9213 delete old._owner; |
|
9214 if (value && value.constructor === Color) { |
|
9215 if (value._owner) |
|
9216 value = value.clone(); |
|
9217 value._owner = this._item; |
|
9218 } |
|
9219 } |
|
9220 this._values[key] = value; |
|
9221 if (this._item) |
|
9222 this._item._changed(flag || 17); |
|
9223 } |
|
9224 } |
|
9225 }; |
|
9226 |
|
9227 fields[get] = function() { |
|
9228 var value, |
|
9229 children = this._item && this._item._children; |
|
9230 if (!children || children.length === 0 || arguments[0] |
|
9231 || this._item._type === 'compound-path') { |
|
9232 var value = this._values[key]; |
|
9233 if (value === undefined) { |
|
9234 value = this._defaults[key]; |
|
9235 if (value && value.clone) |
|
9236 value = value.clone(); |
|
9237 this._values[key] = value; |
|
9238 } else if (isColor && !(value && value.constructor === Color)) { |
|
9239 this._values[key] = value = Color.read( |
|
9240 [value], 0, 0, { readNull: true, clone: true }); |
|
9241 if (value) |
|
9242 value._owner = this._item; |
|
9243 } |
|
9244 return value; |
|
9245 } |
|
9246 for (var i = 0, l = children.length; i < l; i++) { |
|
9247 var childValue = children[i]._style[get](); |
|
9248 if (i === 0) { |
|
9249 value = childValue; |
|
9250 } else if (!Base.equals(value, childValue)) { |
|
9251 return undefined; |
|
9252 } |
|
9253 } |
|
9254 return value; |
|
9255 }; |
|
9256 |
|
9257 item[get] = function() { |
|
9258 return this._style[get](); |
|
9259 }; |
|
9260 |
|
9261 item[set] = function(value) { |
|
9262 this._style[set](value); |
|
9263 }; |
|
9264 }); |
|
9265 |
|
9266 Item.inject(item); |
|
9267 return fields; |
|
9268 }, { |
|
9269 _class: 'Style', |
|
9270 |
|
9271 initialize: function Style(style, _item) { |
|
9272 this._values = {}; |
|
9273 this._item = _item; |
|
9274 if (_item instanceof TextItem) |
|
9275 this._defaults = this._textDefaults; |
|
9276 if (style) |
|
9277 this.set(style); |
|
9278 }, |
|
9279 |
|
9280 set: function(style) { |
|
9281 var isStyle = style instanceof Style, |
|
9282 values = isStyle ? style._values : style; |
|
9283 if (values) { |
|
9284 for (var key in values) { |
|
9285 if (key in this._defaults) { |
|
9286 var value = values[key]; |
|
9287 this[key] = value && isStyle && value.clone |
|
9288 ? value.clone() : value; |
|
9289 } |
|
9290 } |
|
9291 } |
|
9292 }, |
|
9293 |
|
9294 equals: function(style) { |
|
9295 return style === this || style && this._class === style._class |
|
9296 && Base.equals(this._values, style._values) |
|
9297 || false; |
|
9298 }, |
|
9299 |
|
9300 hasFill: function() { |
|
9301 return !!this.getFillColor(); |
|
9302 }, |
|
9303 |
|
9304 hasStroke: function() { |
|
9305 return !!this.getStrokeColor() && this.getStrokeWidth() > 0; |
|
9306 }, |
|
9307 |
|
9308 hasShadow: function() { |
|
9309 return !!this.getShadowColor() && this.getShadowBlur() > 0; |
|
9310 }, |
|
9311 |
|
9312 getLeading: function getLeading() { |
|
9313 var leading = getLeading.base.call(this); |
|
9314 return leading != null ? leading : this.getFontSize() * 1.2; |
|
9315 }, |
|
9316 |
|
9317 getFontStyle: function() { |
|
9318 var size = this.getFontSize(); |
|
9319 return size + (/[a-z]/i.test(size + '') ? ' ' : 'px ') + this.getFont(); |
|
9320 } |
|
9321 |
|
9322 }); |
|
9323 |
|
9324 var DomElement = new function() { |
|
9325 |
|
9326 var special = /^(checked|value|selected|disabled)$/i, |
|
9327 translated = { text: 'textContent', html: 'innerHTML' }, |
|
9328 unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1 }; |
|
9329 |
|
9330 function create(nodes, parent) { |
|
9331 var res = []; |
|
9332 for (var i = 0, l = nodes && nodes.length; i < l;) { |
|
9333 var el = nodes[i++]; |
|
9334 if (typeof el === 'string') { |
|
9335 el = document.createElement(el); |
|
9336 } else if (!el || !el.nodeType) { |
|
9337 continue; |
|
9338 } |
|
9339 if (Base.isPlainObject(nodes[i])) |
|
9340 DomElement.set(el, nodes[i++]); |
|
9341 if (Array.isArray(nodes[i])) |
|
9342 create(nodes[i++], el); |
|
9343 if (parent) |
|
9344 parent.appendChild(el); |
|
9345 res.push(el); |
|
9346 } |
|
9347 return res; |
|
9348 } |
|
9349 |
|
9350 return { |
|
9351 create: function(nodes, parent) { |
|
9352 var isArray = Array.isArray(nodes), |
|
9353 res = create(isArray ? nodes : arguments, isArray ? parent : null); |
|
9354 return res.length == 1 ? res[0] : res; |
|
9355 }, |
|
9356 |
|
9357 find: function(selector, root) { |
|
9358 return (root || document).querySelector(selector); |
|
9359 }, |
|
9360 |
|
9361 findAll: function(selector, root) { |
|
9362 return (root || document).querySelectorAll(selector); |
|
9363 }, |
|
9364 |
|
9365 get: function(el, key) { |
|
9366 return el |
|
9367 ? special.test(key) |
|
9368 ? key === 'value' || typeof el[key] !== 'string' |
|
9369 ? el[key] |
|
9370 : true |
|
9371 : key in translated |
|
9372 ? el[translated[key]] |
|
9373 : el.getAttribute(key) |
|
9374 : null; |
|
9375 }, |
|
9376 |
|
9377 set: function(el, key, value) { |
|
9378 if (typeof key !== 'string') { |
|
9379 for (var name in key) |
|
9380 if (key.hasOwnProperty(name)) |
|
9381 this.set(el, name, key[name]); |
|
9382 } else if (!el || value === undefined) { |
|
9383 return el; |
|
9384 } else if (special.test(key)) { |
|
9385 el[key] = value; |
|
9386 } else if (key in translated) { |
|
9387 el[translated[key]] = value; |
|
9388 } else if (key === 'style') { |
|
9389 this.setStyle(el, value); |
|
9390 } else if (key === 'events') { |
|
9391 DomEvent.add(el, value); |
|
9392 } else { |
|
9393 el.setAttribute(key, value); |
|
9394 } |
|
9395 return el; |
|
9396 }, |
|
9397 |
|
9398 getStyles: function(el) { |
|
9399 var doc = el && el.nodeType !== 9 ? el.ownerDocument : el, |
|
9400 view = doc && doc.defaultView; |
|
9401 return view && view.getComputedStyle(el, ''); |
|
9402 }, |
|
9403 |
|
9404 getStyle: function(el, key) { |
|
9405 return el && el.style[key] || this.getStyles(el)[key] || null; |
|
9406 }, |
|
9407 |
|
9408 setStyle: function(el, key, value) { |
|
9409 if (typeof key !== 'string') { |
|
9410 for (var name in key) |
|
9411 if (key.hasOwnProperty(name)) |
|
9412 this.setStyle(el, name, key[name]); |
|
9413 } else { |
|
9414 if (/^-?[\d\.]+$/.test(value) && !(key in unitless)) |
|
9415 value += 'px'; |
|
9416 el.style[key] = value; |
|
9417 } |
|
9418 return el; |
|
9419 }, |
|
9420 |
|
9421 hasClass: function(el, cls) { |
|
9422 return new RegExp('\\s*' + cls + '\\s*').test(el.className); |
|
9423 }, |
|
9424 |
|
9425 addClass: function(el, cls) { |
|
9426 el.className = (el.className + ' ' + cls).trim(); |
|
9427 }, |
|
9428 |
|
9429 removeClass: function(el, cls) { |
|
9430 el.className = el.className.replace( |
|
9431 new RegExp('\\s*' + cls + '\\s*'), ' ').trim(); |
|
9432 }, |
|
9433 |
|
9434 remove: function(el) { |
|
9435 if (el.parentNode) |
|
9436 el.parentNode.removeChild(el); |
|
9437 }, |
|
9438 |
|
9439 removeChildren: function(el) { |
|
9440 while (el.firstChild) |
|
9441 el.removeChild(el.firstChild); |
|
9442 }, |
|
9443 |
|
9444 getBounds: function(el, viewport) { |
|
9445 var doc = el.ownerDocument, |
|
9446 body = doc.body, |
|
9447 html = doc.documentElement, |
|
9448 rect; |
|
9449 try { |
|
9450 rect = el.getBoundingClientRect(); |
|
9451 } catch (e) { |
|
9452 rect = { left: 0, top: 0, width: 0, height: 0 }; |
|
9453 } |
|
9454 var x = rect.left - (html.clientLeft || body.clientLeft || 0), |
|
9455 y = rect.top - (html.clientTop || body.clientTop || 0); |
|
9456 if (!viewport) { |
|
9457 var view = doc.defaultView; |
|
9458 x += view.pageXOffset || html.scrollLeft || body.scrollLeft; |
|
9459 y += view.pageYOffset || html.scrollTop || body.scrollTop; |
|
9460 } |
|
9461 return new Rectangle(x, y, rect.width, rect.height); |
|
9462 }, |
|
9463 |
|
9464 getViewportBounds: function(el) { |
|
9465 var doc = el.ownerDocument, |
|
9466 view = doc.defaultView, |
|
9467 html = doc.documentElement; |
|
9468 return new Rectangle(0, 0, |
|
9469 view.innerWidth || html.clientWidth, |
|
9470 view.innerHeight || html.clientHeight |
|
9471 ); |
|
9472 }, |
|
9473 |
|
9474 getOffset: function(el, viewport) { |
|
9475 return this.getBounds(el, viewport).getPoint(); |
|
9476 }, |
|
9477 |
|
9478 getSize: function(el) { |
|
9479 return this.getBounds(el, true).getSize(); |
|
9480 }, |
|
9481 |
|
9482 isInvisible: function(el) { |
|
9483 return this.getSize(el).equals(new Size(0, 0)); |
|
9484 }, |
|
9485 |
|
9486 isInView: function(el) { |
|
9487 return !this.isInvisible(el) && this.getViewportBounds(el).intersects( |
|
9488 this.getBounds(el, true)); |
|
9489 }, |
|
9490 |
|
9491 getPrefixValue: function(el, name) { |
|
9492 var value = el[name], |
|
9493 prefixes = ['webkit', 'moz', 'ms', 'o'], |
|
9494 suffix = name[0].toUpperCase() + name.substring(1); |
|
9495 for (var i = 0; i < 4 && value == null; i++) |
|
9496 value = el[prefixes[i] + suffix]; |
|
9497 return value; |
|
9498 } |
|
9499 }; |
|
9500 }; |
|
9501 |
|
9502 var DomEvent = { |
|
9503 add: function(el, events) { |
|
9504 for (var type in events) { |
|
9505 var func = events[type]; |
|
9506 if (el.addEventListener) { |
|
9507 el.addEventListener(type, func, false); |
|
9508 } else if (el.attachEvent) { |
|
9509 el.attachEvent('on' + type, func.bound = function() { |
|
9510 func.call(el, window.event); |
|
9511 }); |
|
9512 } |
|
9513 } |
|
9514 }, |
|
9515 |
|
9516 remove: function(el, events) { |
|
9517 for (var type in events) { |
|
9518 var func = events[type]; |
|
9519 if (el.removeEventListener) { |
|
9520 el.removeEventListener(type, func, false); |
|
9521 } else if (el.detachEvent) { |
|
9522 el.detachEvent('on' + type, func.bound); |
|
9523 } |
|
9524 } |
|
9525 }, |
|
9526 |
|
9527 getPoint: function(event) { |
|
9528 var pos = event.targetTouches |
|
9529 ? event.targetTouches.length |
|
9530 ? event.targetTouches[0] |
|
9531 : event.changedTouches[0] |
|
9532 : event; |
|
9533 return new Point( |
|
9534 pos.pageX || pos.clientX + document.documentElement.scrollLeft, |
|
9535 pos.pageY || pos.clientY + document.documentElement.scrollTop |
|
9536 ); |
|
9537 }, |
|
9538 |
|
9539 getTarget: function(event) { |
|
9540 return event.target || event.srcElement; |
|
9541 }, |
|
9542 |
|
9543 getOffset: function(event, target) { |
|
9544 return DomEvent.getPoint(event).subtract(DomElement.getOffset( |
|
9545 target || DomEvent.getTarget(event))); |
|
9546 }, |
|
9547 |
|
9548 preventDefault: function(event) { |
|
9549 if (event.preventDefault) { |
|
9550 event.preventDefault(); |
|
9551 } else { |
|
9552 event.returnValue = false; |
|
9553 } |
|
9554 }, |
|
9555 |
|
9556 stopPropagation: function(event) { |
|
9557 if (event.stopPropagation) { |
|
9558 event.stopPropagation(); |
|
9559 } else { |
|
9560 event.cancelBubble = true; |
|
9561 } |
|
9562 }, |
|
9563 |
|
9564 stop: function(event) { |
|
9565 DomEvent.stopPropagation(event); |
|
9566 DomEvent.preventDefault(event); |
|
9567 } |
|
9568 }; |
|
9569 |
|
9570 DomEvent.requestAnimationFrame = new function() { |
|
9571 var nativeRequest = DomElement.getPrefixValue(window, |
|
9572 'requestAnimationFrame'), |
|
9573 requested = false, |
|
9574 callbacks = [], |
|
9575 focused = true, |
|
9576 timer; |
|
9577 |
|
9578 DomEvent.add(window, { |
|
9579 focus: function() { |
|
9580 focused = true; |
|
9581 }, |
|
9582 blur: function() { |
|
9583 focused = false; |
|
9584 } |
|
9585 }); |
|
9586 |
|
9587 function handleCallbacks() { |
|
9588 for (var i = callbacks.length - 1; i >= 0; i--) { |
|
9589 var entry = callbacks[i], |
|
9590 func = entry[0], |
|
9591 el = entry[1]; |
|
9592 if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true' |
|
9593 || focused) && DomElement.isInView(el)) { |
|
9594 callbacks.splice(i, 1); |
|
9595 func(); |
|
9596 } |
|
9597 } |
|
9598 if (nativeRequest) { |
|
9599 if (callbacks.length) { |
|
9600 nativeRequest(handleCallbacks); |
|
9601 } else { |
|
9602 requested = false; |
|
9603 } |
|
9604 } |
|
9605 } |
|
9606 |
|
9607 return function(callback, element) { |
|
9608 callbacks.push([callback, element]); |
|
9609 if (nativeRequest) { |
|
9610 if (!requested) { |
|
9611 nativeRequest(handleCallbacks); |
|
9612 requested = true; |
|
9613 } |
|
9614 } else if (!timer) { |
|
9615 timer = setInterval(handleCallbacks, 1000 / 60); |
|
9616 } |
|
9617 }; |
|
9618 }; |
|
9619 |
|
9620 var View = Base.extend(Callback, { |
|
9621 _class: 'View', |
|
9622 |
|
9623 initialize: function View(element) { |
|
9624 this._scope = paper; |
|
9625 this._project = paper.project; |
|
9626 this._element = element; |
|
9627 var size; |
|
9628 this._id = element.getAttribute('id'); |
|
9629 if (this._id == null) |
|
9630 element.setAttribute('id', this._id = 'view-' + View._id++); |
|
9631 DomEvent.add(element, this._viewHandlers); |
|
9632 if (PaperScope.hasAttribute(element, 'resize')) { |
|
9633 var offset = DomElement.getOffset(element, true), |
|
9634 that = this; |
|
9635 size = DomElement.getViewportBounds(element) |
|
9636 .getSize().subtract(offset); |
|
9637 this._windowHandlers = { |
|
9638 resize: function() { |
|
9639 if (!DomElement.isInvisible(element)) |
|
9640 offset = DomElement.getOffset(element, true); |
|
9641 that.setViewSize(DomElement.getViewportBounds(element) |
|
9642 .getSize().subtract(offset)); |
|
9643 } |
|
9644 }; |
|
9645 DomEvent.add(window, this._windowHandlers); |
|
9646 } else { |
|
9647 size = DomElement.getSize(element); |
|
9648 if (size.isNaN() || size.isZero()) |
|
9649 size = new Size(parseInt(element.getAttribute('width'), 10), |
|
9650 parseInt(element.getAttribute('height'), 10)); |
|
9651 } |
|
9652 this._setViewSize(size); |
|
9653 if (PaperScope.hasAttribute(element, 'stats') |
|
9654 && typeof Stats !== 'undefined') { |
|
9655 this._stats = new Stats(); |
|
9656 var stats = this._stats.domElement, |
|
9657 style = stats.style, |
|
9658 offset = DomElement.getOffset(element); |
|
9659 style.position = 'absolute'; |
|
9660 style.left = offset.x + 'px'; |
|
9661 style.top = offset.y + 'px'; |
|
9662 document.body.appendChild(stats); |
|
9663 } |
|
9664 View._views.push(this); |
|
9665 View._viewsById[this._id] = this; |
|
9666 this._viewSize = size; |
|
9667 (this._matrix = new Matrix())._owner = this; |
|
9668 this._zoom = 1; |
|
9669 if (!View._focused) |
|
9670 View._focused = this; |
|
9671 this._frameItems = {}; |
|
9672 this._frameItemCount = 0; |
|
9673 }, |
|
9674 |
|
9675 remove: function() { |
|
9676 if (!this._project) |
|
9677 return false; |
|
9678 if (View._focused == this) |
|
9679 View._focused = null; |
|
9680 View._views.splice(View._views.indexOf(this), 1); |
|
9681 delete View._viewsById[this._id]; |
|
9682 if (this._project.view == this) |
|
9683 this._project.view = null; |
|
9684 DomEvent.remove(this._element, this._viewHandlers); |
|
9685 DomEvent.remove(window, this._windowHandlers); |
|
9686 this._element = this._project = null; |
|
9687 this.detach('frame'); |
|
9688 this._frameItems = {}; |
|
9689 return true; |
|
9690 }, |
|
9691 |
|
9692 _events: { |
|
9693 onFrame: { |
|
9694 install: function() { |
|
9695 this._animate = true; |
|
9696 if (!this._requested) |
|
9697 this._requestFrame(); |
|
9698 }, |
|
9699 |
|
9700 uninstall: function() { |
|
9701 this._animate = false; |
|
9702 } |
|
9703 }, |
|
9704 |
|
9705 onResize: {} |
|
9706 }, |
|
9707 |
|
9708 _animate: false, |
|
9709 _time: 0, |
|
9710 _count: 0, |
|
9711 |
|
9712 _requestFrame: function() { |
|
9713 var that = this; |
|
9714 DomEvent.requestAnimationFrame(function() { |
|
9715 that._requested = false; |
|
9716 if (!that._animate) |
|
9717 return; |
|
9718 that._requestFrame(); |
|
9719 that._handleFrame(); |
|
9720 }, this._element); |
|
9721 this._requested = true; |
|
9722 }, |
|
9723 |
|
9724 _handleFrame: function() { |
|
9725 paper = this._scope; |
|
9726 var now = Date.now() / 1000, |
|
9727 delta = this._before ? now - this._before : 0; |
|
9728 this._before = now; |
|
9729 this._handlingFrame = true; |
|
9730 this.fire('frame', new Base({ |
|
9731 delta: delta, |
|
9732 time: this._time += delta, |
|
9733 count: this._count++ |
|
9734 })); |
|
9735 if (this._stats) |
|
9736 this._stats.update(); |
|
9737 this._handlingFrame = false; |
|
9738 this.draw(true); |
|
9739 }, |
|
9740 |
|
9741 _animateItem: function(item, animate) { |
|
9742 var items = this._frameItems; |
|
9743 if (animate) { |
|
9744 items[item._id] = { |
|
9745 item: item, |
|
9746 time: 0, |
|
9747 count: 0 |
|
9748 }; |
|
9749 if (++this._frameItemCount === 1) |
|
9750 this.attach('frame', this._handleFrameItems); |
|
9751 } else { |
|
9752 delete items[item._id]; |
|
9753 if (--this._frameItemCount === 0) { |
|
9754 this.detach('frame', this._handleFrameItems); |
|
9755 } |
|
9756 } |
|
9757 }, |
|
9758 |
|
9759 _handleFrameItems: function(event) { |
|
9760 for (var i in this._frameItems) { |
|
9761 var entry = this._frameItems[i]; |
|
9762 entry.item.fire('frame', new Base(event, { |
|
9763 time: entry.time += event.delta, |
|
9764 count: entry.count++ |
|
9765 })); |
|
9766 } |
|
9767 }, |
|
9768 |
|
9769 _redraw: function() { |
|
9770 this._project._needsRedraw = true; |
|
9771 if (this._handlingFrame) |
|
9772 return; |
|
9773 if (this._animate) { |
|
9774 this._handleFrame(); |
|
9775 } else { |
|
9776 this.draw(); |
|
9777 } |
|
9778 }, |
|
9779 |
|
9780 _changed: function(flags) { |
|
9781 if (flags & 1) |
|
9782 this._project._needsRedraw = true; |
|
9783 }, |
|
9784 |
|
9785 _transform: function(matrix) { |
|
9786 this._matrix.concatenate(matrix); |
|
9787 this._bounds = null; |
|
9788 this._redraw(); |
|
9789 }, |
|
9790 |
|
9791 getElement: function() { |
|
9792 return this._element; |
|
9793 }, |
|
9794 |
|
9795 getViewSize: function() { |
|
9796 var size = this._viewSize; |
|
9797 return new LinkedSize(size.width, size.height, this, 'setViewSize'); |
|
9798 }, |
|
9799 |
|
9800 setViewSize: function(size) { |
|
9801 size = Size.read(arguments); |
|
9802 var delta = size.subtract(this._viewSize); |
|
9803 if (delta.isZero()) |
|
9804 return; |
|
9805 this._viewSize.set(size.width, size.height); |
|
9806 this._setViewSize(size); |
|
9807 this._bounds = null; |
|
9808 this.fire('resize', { |
|
9809 size: size, |
|
9810 delta: delta |
|
9811 }); |
|
9812 this._redraw(); |
|
9813 }, |
|
9814 |
|
9815 _setViewSize: function(size) { |
|
9816 var element = this._element; |
|
9817 element.width = size.width; |
|
9818 element.height = size.height; |
|
9819 }, |
|
9820 |
|
9821 getBounds: function() { |
|
9822 if (!this._bounds) |
|
9823 this._bounds = this._matrix.inverted()._transformBounds( |
|
9824 new Rectangle(new Point(), this._viewSize)); |
|
9825 return this._bounds; |
|
9826 }, |
|
9827 |
|
9828 getSize: function() { |
|
9829 return this.getBounds().getSize(arguments[0]); |
|
9830 }, |
|
9831 |
|
9832 getCenter: function() { |
|
9833 return this.getBounds().getCenter(arguments[0]); |
|
9834 }, |
|
9835 |
|
9836 setCenter: function(center) { |
|
9837 center = Point.read(arguments); |
|
9838 this.scrollBy(center.subtract(this.getCenter())); |
|
9839 }, |
|
9840 |
|
9841 getZoom: function() { |
|
9842 return this._zoom; |
|
9843 }, |
|
9844 |
|
9845 setZoom: function(zoom) { |
|
9846 this._transform(new Matrix().scale(zoom / this._zoom, |
|
9847 this.getCenter())); |
|
9848 this._zoom = zoom; |
|
9849 }, |
|
9850 |
|
9851 isVisible: function() { |
|
9852 return DomElement.isInView(this._element); |
|
9853 }, |
|
9854 |
|
9855 scrollBy: function() { |
|
9856 this._transform(new Matrix().translate(Point.read(arguments).negate())); |
|
9857 }, |
|
9858 |
|
9859 projectToView: function() { |
|
9860 return this._matrix._transformPoint(Point.read(arguments)); |
|
9861 }, |
|
9862 |
|
9863 viewToProject: function() { |
|
9864 return this._matrix._inverseTransform(Point.read(arguments)); |
|
9865 }, |
|
9866 |
|
9867 }, { |
|
9868 statics: { |
|
9869 _views: [], |
|
9870 _viewsById: {}, |
|
9871 _id: 0, |
|
9872 |
|
9873 create: function(element) { |
|
9874 if (typeof element === 'string') |
|
9875 element = document.getElementById(element); |
|
9876 return new CanvasView(element); |
|
9877 } |
|
9878 } |
|
9879 }, new function() { |
|
9880 var tool, |
|
9881 prevFocus, |
|
9882 tempFocus, |
|
9883 dragging = false; |
|
9884 |
|
9885 function getView(event) { |
|
9886 var target = DomEvent.getTarget(event); |
|
9887 return target.getAttribute && View._viewsById[target.getAttribute('id')]; |
|
9888 } |
|
9889 |
|
9890 function viewToProject(view, event) { |
|
9891 return view.viewToProject(DomEvent.getOffset(event, view._element)); |
|
9892 } |
|
9893 |
|
9894 function updateFocus() { |
|
9895 if (!View._focused || !View._focused.isVisible()) { |
|
9896 for (var i = 0, l = View._views.length; i < l; i++) { |
|
9897 var view = View._views[i]; |
|
9898 if (view && view.isVisible()) { |
|
9899 View._focused = tempFocus = view; |
|
9900 break; |
|
9901 } |
|
9902 } |
|
9903 } |
|
9904 } |
|
9905 |
|
9906 function mousedown(event) { |
|
9907 var view = View._focused = getView(event), |
|
9908 point = viewToProject(view, event); |
|
9909 dragging = true; |
|
9910 if (view._onMouseDown) |
|
9911 view._onMouseDown(event, point); |
|
9912 if (tool = view._scope._tool) |
|
9913 tool._onHandleEvent('mousedown', point, event); |
|
9914 view.draw(true); |
|
9915 } |
|
9916 |
|
9917 function mousemove(event) { |
|
9918 var view; |
|
9919 if (!dragging) { |
|
9920 view = getView(event); |
|
9921 if (view) { |
|
9922 prevFocus = View._focused; |
|
9923 View._focused = tempFocus = view; |
|
9924 } else if (tempFocus && tempFocus == View._focused) { |
|
9925 View._focused = prevFocus; |
|
9926 updateFocus(); |
|
9927 } |
|
9928 } |
|
9929 if (!(view = view || View._focused)) |
|
9930 return; |
|
9931 var point = event && viewToProject(view, event); |
|
9932 if (view._onMouseMove) |
|
9933 view._onMouseMove(event, point); |
|
9934 if (tool = view._scope._tool) { |
|
9935 if (tool._onHandleEvent(dragging && tool.responds('mousedrag') |
|
9936 ? 'mousedrag' : 'mousemove', point, event)) |
|
9937 DomEvent.stop(event); |
|
9938 } |
|
9939 view.draw(true); |
|
9940 } |
|
9941 |
|
9942 function mouseup(event) { |
|
9943 var view = View._focused; |
|
9944 if (!view || !dragging) |
|
9945 return; |
|
9946 var point = viewToProject(view, event); |
|
9947 curPoint = null; |
|
9948 dragging = false; |
|
9949 if (view._onMouseUp) |
|
9950 view._onMouseUp(event, point); |
|
9951 if (tool && tool._onHandleEvent('mouseup', point, event)) |
|
9952 DomEvent.stop(event); |
|
9953 view.draw(true); |
|
9954 } |
|
9955 |
|
9956 function selectstart(event) { |
|
9957 if (dragging) |
|
9958 DomEvent.stop(event); |
|
9959 } |
|
9960 |
|
9961 DomEvent.add(document, { |
|
9962 mousemove: mousemove, |
|
9963 mouseup: mouseup, |
|
9964 touchmove: mousemove, |
|
9965 touchend: mouseup, |
|
9966 selectstart: selectstart, |
|
9967 scroll: updateFocus |
|
9968 }); |
|
9969 |
|
9970 DomEvent.add(window, { |
|
9971 load: updateFocus |
|
9972 }); |
|
9973 |
|
9974 return { |
|
9975 _viewHandlers: { |
|
9976 mousedown: mousedown, |
|
9977 touchstart: mousedown, |
|
9978 selectstart: selectstart |
|
9979 }, |
|
9980 |
|
9981 statics: { |
|
9982 updateFocus: updateFocus |
|
9983 } |
|
9984 }; |
|
9985 }); |
|
9986 |
|
9987 var CanvasView = View.extend({ |
|
9988 _class: 'CanvasView', |
|
9989 |
|
9990 initialize: function CanvasView(canvas) { |
|
9991 if (!(canvas instanceof HTMLCanvasElement)) { |
|
9992 var size = Size.read(arguments); |
|
9993 if (size.isZero()) |
|
9994 throw new Error( |
|
9995 'Cannot create CanvasView with the provided argument: ' |
|
9996 + canvas); |
|
9997 canvas = CanvasProvider.getCanvas(size); |
|
9998 } |
|
9999 this._context = canvas.getContext('2d'); |
|
10000 this._eventCounters = {}; |
|
10001 this._ratio = 1; |
|
10002 if (PaperScope.getAttribute(canvas, 'hidpi') !== 'off') { |
|
10003 var deviceRatio = window.devicePixelRatio || 1, |
|
10004 backingStoreRatio = DomElement.getPrefixValue(this._context, |
|
10005 'backingStorePixelRatio') || 1; |
|
10006 this._ratio = deviceRatio / backingStoreRatio; |
|
10007 } |
|
10008 View.call(this, canvas); |
|
10009 }, |
|
10010 |
|
10011 _setViewSize: function(size) { |
|
10012 var width = size.width, |
|
10013 height = size.height, |
|
10014 ratio = this._ratio, |
|
10015 element = this._element, |
|
10016 style = element.style; |
|
10017 element.width = width * ratio; |
|
10018 element.height = height * ratio; |
|
10019 if (ratio !== 1) { |
|
10020 style.width = width + 'px'; |
|
10021 style.height = height + 'px'; |
|
10022 this._context.scale(ratio, ratio); |
|
10023 } |
|
10024 }, |
|
10025 |
|
10026 draw: function(checkRedraw) { |
|
10027 if (checkRedraw && !this._project._needsRedraw) |
|
10028 return false; |
|
10029 var ctx = this._context, |
|
10030 size = this._viewSize; |
|
10031 ctx.clearRect(0, 0, size.width + 1, size.height + 1); |
|
10032 this._project.draw(ctx, this._matrix, this._ratio); |
|
10033 this._project._needsRedraw = false; |
|
10034 return true; |
|
10035 } |
|
10036 }, new function() { |
|
10037 |
|
10038 var downPoint, |
|
10039 lastPoint, |
|
10040 overPoint, |
|
10041 downItem, |
|
10042 lastItem, |
|
10043 overItem, |
|
10044 hasDrag, |
|
10045 doubleClick, |
|
10046 clickTime; |
|
10047 |
|
10048 function callEvent(type, event, point, target, lastPoint, bubble) { |
|
10049 var item = target, |
|
10050 mouseEvent; |
|
10051 while (item) { |
|
10052 if (item.responds(type)) { |
|
10053 if (!mouseEvent) |
|
10054 mouseEvent = new MouseEvent(type, event, point, target, |
|
10055 lastPoint ? point.subtract(lastPoint) : null); |
|
10056 if (item.fire(type, mouseEvent) |
|
10057 && (!bubble || mouseEvent._stopped)) |
|
10058 return false; |
|
10059 } |
|
10060 item = item.getParent(); |
|
10061 } |
|
10062 return true; |
|
10063 } |
|
10064 |
|
10065 function handleEvent(view, type, event, point, lastPoint) { |
|
10066 if (view._eventCounters[type]) { |
|
10067 var project = view._project, |
|
10068 hit = project.hitTest(point, { |
|
10069 tolerance: project.options.hitTolerance || 0, |
|
10070 fill: true, |
|
10071 stroke: true |
|
10072 }), |
|
10073 item = hit && hit.item; |
|
10074 if (item) { |
|
10075 if (type === 'mousemove' && item != overItem) |
|
10076 lastPoint = point; |
|
10077 if (type !== 'mousemove' || !hasDrag) |
|
10078 callEvent(type, event, point, item, lastPoint); |
|
10079 return item; |
|
10080 } |
|
10081 } |
|
10082 } |
|
10083 |
|
10084 return { |
|
10085 _onMouseDown: function(event, point) { |
|
10086 var item = handleEvent(this, 'mousedown', event, point); |
|
10087 doubleClick = lastItem == item && (Date.now() - clickTime < 300); |
|
10088 downItem = lastItem = item; |
|
10089 downPoint = lastPoint = overPoint = point; |
|
10090 hasDrag = downItem && downItem.responds('mousedrag'); |
|
10091 }, |
|
10092 |
|
10093 _onMouseUp: function(event, point) { |
|
10094 var item = handleEvent(this, 'mouseup', event, point); |
|
10095 if (hasDrag) { |
|
10096 if (lastPoint && !lastPoint.equals(point)) |
|
10097 callEvent('mousedrag', event, point, downItem, lastPoint); |
|
10098 if (item != downItem) { |
|
10099 overPoint = point; |
|
10100 callEvent('mousemove', event, point, item, overPoint); |
|
10101 } |
|
10102 } |
|
10103 if (item === downItem) { |
|
10104 clickTime = Date.now(); |
|
10105 if (!doubleClick |
|
10106 || callEvent('doubleclick', event, downPoint, item)) |
|
10107 callEvent('click', event, downPoint, item); |
|
10108 doubleClick = false; |
|
10109 } |
|
10110 downItem = null; |
|
10111 hasDrag = false; |
|
10112 }, |
|
10113 |
|
10114 _onMouseMove: function(event, point) { |
|
10115 if (downItem) |
|
10116 callEvent('mousedrag', event, point, downItem, lastPoint); |
|
10117 var item = handleEvent(this, 'mousemove', event, point, overPoint); |
|
10118 lastPoint = overPoint = point; |
|
10119 if (item !== overItem) { |
|
10120 callEvent('mouseleave', event, point, overItem); |
|
10121 overItem = item; |
|
10122 callEvent('mouseenter', event, point, item); |
|
10123 } |
|
10124 } |
|
10125 }; |
|
10126 }); |
|
10127 |
|
10128 var Event = Base.extend({ |
|
10129 _class: 'Event', |
|
10130 |
|
10131 initialize: function Event(event) { |
|
10132 this.event = event; |
|
10133 }, |
|
10134 |
|
10135 preventDefault: function() { |
|
10136 this._prevented = true; |
|
10137 DomEvent.preventDefault(this.event); |
|
10138 }, |
|
10139 |
|
10140 stopPropagation: function() { |
|
10141 this._stopped = true; |
|
10142 DomEvent.stopPropagation(this.event); |
|
10143 }, |
|
10144 |
|
10145 stop: function() { |
|
10146 this.stopPropagation(); |
|
10147 this.preventDefault(); |
|
10148 }, |
|
10149 |
|
10150 getModifiers: function() { |
|
10151 return Key.modifiers; |
|
10152 } |
|
10153 }); |
|
10154 |
|
10155 var KeyEvent = Event.extend({ |
|
10156 _class: 'KeyEvent', |
|
10157 |
|
10158 initialize: function KeyEvent(down, key, character, event) { |
|
10159 Event.call(this, event); |
|
10160 this.type = down ? 'keydown' : 'keyup'; |
|
10161 this.key = key; |
|
10162 this.character = character; |
|
10163 }, |
|
10164 |
|
10165 toString: function() { |
|
10166 return "{ type: '" + this.type |
|
10167 + "', key: '" + this.key |
|
10168 + "', character: '" + this.character |
|
10169 + "', modifiers: " + this.getModifiers() |
|
10170 + " }"; |
|
10171 } |
|
10172 }); |
|
10173 |
|
10174 var Key = new function() { |
|
10175 |
|
10176 var specialKeys = { |
|
10177 8: 'backspace', |
|
10178 9: 'tab', |
|
10179 13: 'enter', |
|
10180 16: 'shift', |
|
10181 17: 'control', |
|
10182 18: 'option', |
|
10183 19: 'pause', |
|
10184 20: 'caps-lock', |
|
10185 27: 'escape', |
|
10186 32: 'space', |
|
10187 35: 'end', |
|
10188 36: 'home', |
|
10189 37: 'left', |
|
10190 38: 'up', |
|
10191 39: 'right', |
|
10192 40: 'down', |
|
10193 46: 'delete', |
|
10194 91: 'command', |
|
10195 93: 'command', |
|
10196 224: 'command' |
|
10197 }, |
|
10198 |
|
10199 specialChars = { |
|
10200 9: true, |
|
10201 13: true, |
|
10202 32: true |
|
10203 }, |
|
10204 |
|
10205 modifiers = new Base({ |
|
10206 shift: false, |
|
10207 control: false, |
|
10208 option: false, |
|
10209 command: false, |
|
10210 capsLock: false, |
|
10211 space: false |
|
10212 }), |
|
10213 |
|
10214 charCodeMap = {}, |
|
10215 keyMap = {}, |
|
10216 downCode; |
|
10217 |
|
10218 function handleKey(down, keyCode, charCode, event) { |
|
10219 var character = charCode ? String.fromCharCode(charCode) : '', |
|
10220 specialKey = specialKeys[keyCode], |
|
10221 key = specialKey || character.toLowerCase(), |
|
10222 type = down ? 'keydown' : 'keyup', |
|
10223 view = View._focused, |
|
10224 scope = view && view.isVisible() && view._scope, |
|
10225 tool = scope && scope._tool, |
|
10226 name; |
|
10227 keyMap[key] = down; |
|
10228 if (specialKey && (name = Base.camelize(specialKey)) in modifiers) |
|
10229 modifiers[name] = down; |
|
10230 if (down) { |
|
10231 charCodeMap[keyCode] = charCode; |
|
10232 } else { |
|
10233 delete charCodeMap[keyCode]; |
|
10234 } |
|
10235 if (tool && tool.responds(type)) { |
|
10236 paper = scope; |
|
10237 tool.fire(type, new KeyEvent(down, key, character, event)); |
|
10238 if (view) |
|
10239 view.draw(true); |
|
10240 } |
|
10241 } |
|
10242 |
|
10243 DomEvent.add(document, { |
|
10244 keydown: function(event) { |
|
10245 var code = event.which || event.keyCode; |
|
10246 if (code in specialKeys) { |
|
10247 handleKey(true, code, code in specialChars ? code : 0, event); |
|
10248 } else { |
|
10249 downCode = code; |
|
10250 } |
|
10251 }, |
|
10252 |
|
10253 keypress: function(event) { |
|
10254 if (downCode != null) { |
|
10255 handleKey(true, downCode, event.which || event.keyCode, event); |
|
10256 downCode = null; |
|
10257 } |
|
10258 }, |
|
10259 |
|
10260 keyup: function(event) { |
|
10261 var code = event.which || event.keyCode; |
|
10262 if (code in charCodeMap) |
|
10263 handleKey(false, code, charCodeMap[code], event); |
|
10264 } |
|
10265 }); |
|
10266 |
|
10267 DomEvent.add(window, { |
|
10268 blur: function(event) { |
|
10269 for (var code in charCodeMap) |
|
10270 handleKey(false, code, charCodeMap[code], event); |
|
10271 } |
|
10272 }); |
|
10273 |
|
10274 return { |
|
10275 modifiers: modifiers, |
|
10276 |
|
10277 isDown: function(key) { |
|
10278 return !!keyMap[key]; |
|
10279 } |
|
10280 }; |
|
10281 }; |
|
10282 |
|
10283 var MouseEvent = Event.extend({ |
|
10284 _class: 'MouseEvent', |
|
10285 |
|
10286 initialize: function MouseEvent(type, event, point, target, delta) { |
|
10287 Event.call(this, event); |
|
10288 this.type = type; |
|
10289 this.point = point; |
|
10290 this.target = target; |
|
10291 this.delta = delta; |
|
10292 }, |
|
10293 |
|
10294 toString: function() { |
|
10295 return "{ type: '" + this.type |
|
10296 + "', point: " + this.point |
|
10297 + ', target: ' + this.target |
|
10298 + (this.delta ? ', delta: ' + this.delta : '') |
|
10299 + ', modifiers: ' + this.getModifiers() |
|
10300 + ' }'; |
|
10301 } |
|
10302 }); |
|
10303 |
|
10304 Base.extend(Callback, { |
|
10305 _class: 'Palette', |
|
10306 _events: [ 'onChange' ], |
|
10307 |
|
10308 initialize: function Palette(title, components, values) { |
|
10309 var parent = DomElement.find('.palettejs-panel') |
|
10310 || DomElement.find('body').appendChild( |
|
10311 DomElement.create('div', { 'class': 'palettejs-panel' })); |
|
10312 this._element = parent.appendChild( |
|
10313 DomElement.create('table', { 'class': 'palettejs-pane' })); |
|
10314 this._title = title; |
|
10315 if (!values) |
|
10316 values = {}; |
|
10317 for (var name in (this.components = components)) { |
|
10318 var component = components[name]; |
|
10319 if (!(component instanceof Component)) { |
|
10320 if (component.value == null) |
|
10321 component.value = values[name]; |
|
10322 component.name = name; |
|
10323 component = components[name] = new Component(component); |
|
10324 } |
|
10325 this._element.appendChild(component._element); |
|
10326 component._palette = this; |
|
10327 if (values[name] === undefined) |
|
10328 values[name] = component.value; |
|
10329 } |
|
10330 this.values = Base.each(values, function(value, name) { |
|
10331 var component = components[name]; |
|
10332 if (component) { |
|
10333 Base.define(values, name, { |
|
10334 enumerable: true, |
|
10335 configurable: true, |
|
10336 get: function() { |
|
10337 return component._value; |
|
10338 }, |
|
10339 set: function(val) { |
|
10340 component.setValue(val); |
|
10341 } |
|
10342 }); |
|
10343 } |
|
10344 }); |
|
10345 if (window.paper) |
|
10346 paper.palettes.push(this); |
|
10347 }, |
|
10348 |
|
10349 reset: function() { |
|
10350 for (var i in this.components) |
|
10351 this.components[i].reset(); |
|
10352 }, |
|
10353 |
|
10354 remove: function() { |
|
10355 DomElement.remove(this._element); |
|
10356 } |
|
10357 }); |
|
10358 |
|
10359 var Component = Base.extend(Callback, { |
|
10360 _class: 'Component', |
|
10361 _events: [ 'onChange', 'onClick' ], |
|
10362 |
|
10363 _types: { |
|
10364 'boolean': { |
|
10365 type: 'checkbox', |
|
10366 value: 'checked' |
|
10367 }, |
|
10368 |
|
10369 string: { |
|
10370 type: 'text' |
|
10371 }, |
|
10372 |
|
10373 number: { |
|
10374 type: 'number', |
|
10375 number: true |
|
10376 }, |
|
10377 |
|
10378 button: { |
|
10379 type: 'button' |
|
10380 }, |
|
10381 |
|
10382 text: { |
|
10383 tag: 'div', |
|
10384 value: 'text' |
|
10385 }, |
|
10386 |
|
10387 slider: { |
|
10388 type: 'range', |
|
10389 number: true |
|
10390 }, |
|
10391 |
|
10392 list: { |
|
10393 tag: 'select', |
|
10394 |
|
10395 setOptions: function() { |
|
10396 DomElement.removeChildren(this._input); |
|
10397 DomElement.create(Base.each(this._options, function(option) { |
|
10398 this.push('option', { value: option, text: option }); |
|
10399 }, []), this._input); |
|
10400 } |
|
10401 }, |
|
10402 |
|
10403 color: { |
|
10404 type: 'color', |
|
10405 |
|
10406 getValue: function(value) { |
|
10407 return new Color(value); |
|
10408 }, |
|
10409 |
|
10410 setValue: function(value) { |
|
10411 return new Color(value).toCSS( |
|
10412 DomElement.get(this._input, 'type') === 'color'); |
|
10413 } |
|
10414 } |
|
10415 }, |
|
10416 |
|
10417 initialize: function Component(obj) { |
|
10418 this._id = Component._id = (Component._id || 0) + 1; |
|
10419 this._type = obj.type in this._types |
|
10420 ? obj.type |
|
10421 : 'options' in obj |
|
10422 ? 'list' |
|
10423 : 'onClick' in obj |
|
10424 ? 'button' |
|
10425 : typeof obj.value; |
|
10426 this._meta = this._types[this._type] || { type: this._type }; |
|
10427 var that = this, |
|
10428 id = 'component-' + this._id; |
|
10429 this._dontFire = true; |
|
10430 this._input = DomElement.create(this._meta.tag || 'input', { |
|
10431 id: id, |
|
10432 type: this._meta.type, |
|
10433 events: { |
|
10434 change: function() { |
|
10435 that.setValue( |
|
10436 DomElement.get(this, that._meta.value || 'value')); |
|
10437 }, |
|
10438 click: function() { |
|
10439 that.fire('click'); |
|
10440 } |
|
10441 } |
|
10442 }); |
|
10443 this.attach('change', function(value) { |
|
10444 if (!this._dontFire) |
|
10445 this._palette.fire('change', this, this.name, value); |
|
10446 }); |
|
10447 this._element = DomElement.create('tr', [ |
|
10448 'td', [this._label = DomElement.create('label', { 'for': id })], |
|
10449 'td', [this._input] |
|
10450 ]); |
|
10451 Base.each(obj, function(value, key) { |
|
10452 this[key] = value; |
|
10453 }, this); |
|
10454 this._defaultValue = this._value; |
|
10455 this._dontFire = false; |
|
10456 }, |
|
10457 |
|
10458 getType: function() { |
|
10459 return this._type; |
|
10460 }, |
|
10461 |
|
10462 getLabel: function() { |
|
10463 return this.__label; |
|
10464 }, |
|
10465 |
|
10466 setLabel: function(label) { |
|
10467 this.__label = label; |
|
10468 DomElement.set(this._label, 'text', label + ':'); |
|
10469 }, |
|
10470 |
|
10471 getOptions: function() { |
|
10472 return this._options; |
|
10473 }, |
|
10474 |
|
10475 setOptions: function(options) { |
|
10476 this._options = options; |
|
10477 var setOptions = this._meta.setOptions; |
|
10478 if (setOptions) |
|
10479 setOptions.call(this); |
|
10480 }, |
|
10481 |
|
10482 getValue: function() { |
|
10483 var value = this._value, |
|
10484 getValue = this._meta.getValue; |
|
10485 return getValue ? getValue.call(this, value) : value; |
|
10486 }, |
|
10487 |
|
10488 setValue: function(value) { |
|
10489 var key = this._meta.value || 'value', |
|
10490 setValue = this._meta.setValue; |
|
10491 if (setValue) |
|
10492 value = setValue.call(this, value); |
|
10493 DomElement.set(this._input, key, value); |
|
10494 value = DomElement.get(this._input, key); |
|
10495 if (this._meta.number) |
|
10496 value = parseFloat(value, 10); |
|
10497 if (this._value !== value) { |
|
10498 this._value = value; |
|
10499 if (!this._dontFire) |
|
10500 this.fire('change', this.getValue()); |
|
10501 } |
|
10502 }, |
|
10503 |
|
10504 getRange: function() { |
|
10505 return [parseFloat(DomElement.get(this._input, 'min')), |
|
10506 parseFloat(DomElement.get(this._input, 'max'))]; |
|
10507 }, |
|
10508 |
|
10509 setRange: function(min, max) { |
|
10510 var range = Array.isArray(min) ? min : [min, max]; |
|
10511 DomElement.set(this._input, { min: range[0], max: range[1] }); |
|
10512 }, |
|
10513 |
|
10514 getMin: function() { |
|
10515 return this.getRange()[0]; |
|
10516 }, |
|
10517 |
|
10518 setMin: function(min) { |
|
10519 this.setRange(min, this.getMax()); |
|
10520 }, |
|
10521 |
|
10522 getMax: function() { |
|
10523 return this.getRange()[1]; |
|
10524 }, |
|
10525 |
|
10526 setMax: function(max) { |
|
10527 this.setRange(this.getMin(), max); |
|
10528 }, |
|
10529 |
|
10530 getStep: function() { |
|
10531 return parseFloat(DomElement.get(this._input, 'step')); |
|
10532 }, |
|
10533 |
|
10534 setStep: function(step) { |
|
10535 DomElement.set(this._input, 'step', step); |
|
10536 }, |
|
10537 |
|
10538 reset: function() { |
|
10539 this.setValue(this._defaultValue); |
|
10540 } |
|
10541 }); |
|
10542 |
|
10543 var ToolEvent = Event.extend({ |
|
10544 _class: 'ToolEvent', |
|
10545 _item: null, |
|
10546 |
|
10547 initialize: function ToolEvent(tool, type, event) { |
|
10548 this.tool = tool; |
|
10549 this.type = type; |
|
10550 this.event = event; |
|
10551 }, |
|
10552 |
|
10553 _choosePoint: function(point, toolPoint) { |
|
10554 return point ? point : toolPoint ? toolPoint.clone() : null; |
|
10555 }, |
|
10556 |
|
10557 getPoint: function() { |
|
10558 return this._choosePoint(this._point, this.tool._point); |
|
10559 }, |
|
10560 |
|
10561 setPoint: function(point) { |
|
10562 this._point = point; |
|
10563 }, |
|
10564 |
|
10565 getLastPoint: function() { |
|
10566 return this._choosePoint(this._lastPoint, this.tool._lastPoint); |
|
10567 }, |
|
10568 |
|
10569 setLastPoint: function(lastPoint) { |
|
10570 this._lastPoint = lastPoint; |
|
10571 }, |
|
10572 |
|
10573 getDownPoint: function() { |
|
10574 return this._choosePoint(this._downPoint, this.tool._downPoint); |
|
10575 }, |
|
10576 |
|
10577 setDownPoint: function(downPoint) { |
|
10578 this._downPoint = downPoint; |
|
10579 }, |
|
10580 |
|
10581 getMiddlePoint: function() { |
|
10582 if (!this._middlePoint && this.tool._lastPoint) { |
|
10583 return this.tool._point.add(this.tool._lastPoint).divide(2); |
|
10584 } |
|
10585 return this._middlePoint; |
|
10586 }, |
|
10587 |
|
10588 setMiddlePoint: function(middlePoint) { |
|
10589 this._middlePoint = middlePoint; |
|
10590 }, |
|
10591 |
|
10592 getDelta: function() { |
|
10593 return !this._delta && this.tool._lastPoint |
|
10594 ? this.tool._point.subtract(this.tool._lastPoint) |
|
10595 : this._delta; |
|
10596 }, |
|
10597 |
|
10598 setDelta: function(delta) { |
|
10599 this._delta = delta; |
|
10600 }, |
|
10601 |
|
10602 getCount: function() { |
|
10603 return /^mouse(down|up)$/.test(this.type) |
|
10604 ? this.tool._downCount |
|
10605 : this.tool._count; |
|
10606 }, |
|
10607 |
|
10608 setCount: function(count) { |
|
10609 this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count'] |
|
10610 = count; |
|
10611 }, |
|
10612 |
|
10613 getItem: function() { |
|
10614 if (!this._item) { |
|
10615 var result = this.tool._scope.project.hitTest(this.getPoint()); |
|
10616 if (result) { |
|
10617 var item = result.item, |
|
10618 parent = item._parent; |
|
10619 while (/^(group|compound-path)$/.test(parent._type)) { |
|
10620 item = parent; |
|
10621 parent = parent._parent; |
|
10622 } |
|
10623 this._item = item; |
|
10624 } |
|
10625 } |
|
10626 return this._item; |
|
10627 }, |
|
10628 setItem: function(item) { |
|
10629 this._item = item; |
|
10630 }, |
|
10631 |
|
10632 toString: function() { |
|
10633 return '{ type: ' + this.type |
|
10634 + ', point: ' + this.getPoint() |
|
10635 + ', count: ' + this.getCount() |
|
10636 + ', modifiers: ' + this.getModifiers() |
|
10637 + ' }'; |
|
10638 } |
|
10639 }); |
|
10640 |
|
10641 var Tool = PaperScopeItem.extend({ |
|
10642 _class: 'Tool', |
|
10643 _list: 'tools', |
|
10644 _reference: '_tool', |
|
10645 _events: [ 'onActivate', 'onDeactivate', 'onEditOptions', |
|
10646 'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove', |
|
10647 'onKeyDown', 'onKeyUp' ], |
|
10648 |
|
10649 initialize: function Tool(props) { |
|
10650 PaperScopeItem.call(this); |
|
10651 this._firstMove = true; |
|
10652 this._count = 0; |
|
10653 this._downCount = 0; |
|
10654 this._set(props); |
|
10655 }, |
|
10656 |
|
10657 getMinDistance: function() { |
|
10658 return this._minDistance; |
|
10659 }, |
|
10660 |
|
10661 setMinDistance: function(minDistance) { |
|
10662 this._minDistance = minDistance; |
|
10663 if (this._minDistance != null && this._maxDistance != null |
|
10664 && this._minDistance > this._maxDistance) { |
|
10665 this._maxDistance = this._minDistance; |
|
10666 } |
|
10667 }, |
|
10668 |
|
10669 getMaxDistance: function() { |
|
10670 return this._maxDistance; |
|
10671 }, |
|
10672 |
|
10673 setMaxDistance: function(maxDistance) { |
|
10674 this._maxDistance = maxDistance; |
|
10675 if (this._minDistance != null && this._maxDistance != null |
|
10676 && this._maxDistance < this._minDistance) { |
|
10677 this._minDistance = maxDistance; |
|
10678 } |
|
10679 }, |
|
10680 |
|
10681 getFixedDistance: function() { |
|
10682 return this._minDistance == this._maxDistance |
|
10683 ? this._minDistance : null; |
|
10684 }, |
|
10685 |
|
10686 setFixedDistance: function(distance) { |
|
10687 this._minDistance = distance; |
|
10688 this._maxDistance = distance; |
|
10689 }, |
|
10690 |
|
10691 _updateEvent: function(type, point, minDistance, maxDistance, start, |
|
10692 needsChange, matchMaxDistance) { |
|
10693 if (!start) { |
|
10694 if (minDistance != null || maxDistance != null) { |
|
10695 var minDist = minDistance != null ? minDistance : 0, |
|
10696 vector = point.subtract(this._point), |
|
10697 distance = vector.getLength(); |
|
10698 if (distance < minDist) |
|
10699 return false; |
|
10700 var maxDist = maxDistance != null ? maxDistance : 0; |
|
10701 if (maxDist != 0) { |
|
10702 if (distance > maxDist) { |
|
10703 point = this._point.add(vector.normalize(maxDist)); |
|
10704 } else if (matchMaxDistance) { |
|
10705 return false; |
|
10706 } |
|
10707 } |
|
10708 } |
|
10709 if (needsChange && point.equals(this._point)) |
|
10710 return false; |
|
10711 } |
|
10712 this._lastPoint = start && type == 'mousemove' ? point : this._point; |
|
10713 this._point = point; |
|
10714 switch (type) { |
|
10715 case 'mousedown': |
|
10716 this._lastPoint = this._downPoint; |
|
10717 this._downPoint = this._point; |
|
10718 this._downCount++; |
|
10719 break; |
|
10720 case 'mouseup': |
|
10721 this._lastPoint = this._downPoint; |
|
10722 break; |
|
10723 } |
|
10724 this._count = start ? 0 : this._count + 1; |
|
10725 return true; |
|
10726 }, |
|
10727 |
|
10728 _fireEvent: function(type, event) { |
|
10729 var sets = paper.project._removeSets; |
|
10730 if (sets) { |
|
10731 if (type === 'mouseup') |
|
10732 sets.mousedrag = null; |
|
10733 var set = sets[type]; |
|
10734 if (set) { |
|
10735 for (var id in set) { |
|
10736 var item = set[id]; |
|
10737 for (var key in sets) { |
|
10738 var other = sets[key]; |
|
10739 if (other && other != set) |
|
10740 delete other[item._id]; |
|
10741 } |
|
10742 item.remove(); |
|
10743 } |
|
10744 sets[type] = null; |
|
10745 } |
|
10746 } |
|
10747 return this.responds(type) |
|
10748 && this.fire(type, new ToolEvent(this, type, event)); |
|
10749 }, |
|
10750 |
|
10751 _onHandleEvent: function(type, point, event) { |
|
10752 paper = this._scope; |
|
10753 var called = false; |
|
10754 switch (type) { |
|
10755 case 'mousedown': |
|
10756 this._updateEvent(type, point, null, null, true, false, false); |
|
10757 called = this._fireEvent(type, event); |
|
10758 break; |
|
10759 case 'mousedrag': |
|
10760 var needsChange = false, |
|
10761 matchMaxDistance = false; |
|
10762 while (this._updateEvent(type, point, this.minDistance, |
|
10763 this.maxDistance, false, needsChange, matchMaxDistance)) { |
|
10764 called = this._fireEvent(type, event) || called; |
|
10765 needsChange = true; |
|
10766 matchMaxDistance = true; |
|
10767 } |
|
10768 break; |
|
10769 case 'mouseup': |
|
10770 if (!point.equals(this._point) |
|
10771 && this._updateEvent('mousedrag', point, this.minDistance, |
|
10772 this.maxDistance, false, false, false)) { |
|
10773 called = this._fireEvent('mousedrag', event); |
|
10774 } |
|
10775 this._updateEvent(type, point, null, this.maxDistance, false, |
|
10776 false, false); |
|
10777 called = this._fireEvent(type, event) || called; |
|
10778 this._updateEvent(type, point, null, null, true, false, false); |
|
10779 this._firstMove = true; |
|
10780 break; |
|
10781 case 'mousemove': |
|
10782 while (this._updateEvent(type, point, this.minDistance, |
|
10783 this.maxDistance, this._firstMove, true, false)) { |
|
10784 called = this._fireEvent(type, event) || called; |
|
10785 this._firstMove = false; |
|
10786 } |
|
10787 break; |
|
10788 } |
|
10789 return called; |
|
10790 } |
|
10791 |
|
10792 }); |
|
10793 |
|
10794 var Http = { |
|
10795 request: function(method, url, callback) { |
|
10796 var xhr = new (window.ActiveXObject || XMLHttpRequest)( |
|
10797 'Microsoft.XMLHTTP'); |
|
10798 xhr.open(method.toUpperCase(), url, true); |
|
10799 if ('overrideMimeType' in xhr) |
|
10800 xhr.overrideMimeType('text/plain'); |
|
10801 xhr.onreadystatechange = function() { |
|
10802 if (xhr.readyState === 4) { |
|
10803 var status = xhr.status; |
|
10804 if (status === 0 || status === 200) { |
|
10805 callback.call(xhr, xhr.responseText); |
|
10806 } else { |
|
10807 throw new Error('Could not load ' + url + ' (Error ' |
|
10808 + status + ')'); |
|
10809 } |
|
10810 } |
|
10811 }; |
|
10812 return xhr.send(null); |
|
10813 } |
|
10814 }; |
|
10815 |
|
10816 var CanvasProvider = { |
|
10817 canvases: [], |
|
10818 |
|
10819 getCanvas: function(width, height, ratio) { |
|
10820 var canvas, |
|
10821 init = true; |
|
10822 if (typeof width === 'object') { |
|
10823 ratio = height; |
|
10824 height = width.height; |
|
10825 width = width.width; |
|
10826 } |
|
10827 if (!ratio) { |
|
10828 ratio = 1; |
|
10829 } else if (ratio !== 1) { |
|
10830 width *= ratio; |
|
10831 height *= ratio; |
|
10832 } |
|
10833 if (this.canvases.length) { |
|
10834 canvas = this.canvases.pop(); |
|
10835 } else { |
|
10836 canvas = document.createElement('canvas'); |
|
10837 } |
|
10838 var ctx = canvas.getContext('2d'); |
|
10839 if (canvas.width === width && canvas.height === height) { |
|
10840 if (init) |
|
10841 ctx.clearRect(0, 0, width + 1, height + 1); |
|
10842 } else { |
|
10843 canvas.width = width; |
|
10844 canvas.height = height; |
|
10845 } |
|
10846 ctx.save(); |
|
10847 if (ratio !== 1) |
|
10848 ctx.scale(ratio, ratio); |
|
10849 return canvas; |
|
10850 }, |
|
10851 |
|
10852 getContext: function(width, height) { |
|
10853 return this.getCanvas(width, height).getContext('2d'); |
|
10854 }, |
|
10855 |
|
10856 release: function(obj) { |
|
10857 var canvas = obj.canvas ? obj.canvas : obj; |
|
10858 canvas.getContext('2d').restore(); |
|
10859 this.canvases.push(canvas); |
|
10860 } |
|
10861 }; |
|
10862 |
|
10863 var BlendMode = new function() { |
|
10864 var min = Math.min, |
|
10865 max = Math.max, |
|
10866 abs = Math.abs, |
|
10867 sr, sg, sb, sa, |
|
10868 br, bg, bb, ba, |
|
10869 dr, dg, db; |
|
10870 |
|
10871 function getLum(r, g, b) { |
|
10872 return 0.2989 * r + 0.587 * g + 0.114 * b; |
|
10873 } |
|
10874 |
|
10875 function setLum(r, g, b, l) { |
|
10876 var d = l - getLum(r, g, b); |
|
10877 dr = r + d; |
|
10878 dg = g + d; |
|
10879 db = b + d; |
|
10880 var l = getLum(dr, dg, db), |
|
10881 mn = min(dr, dg, db), |
|
10882 mx = max(dr, dg, db); |
|
10883 if (mn < 0) { |
|
10884 var lmn = l - mn; |
|
10885 dr = l + (dr - l) * l / lmn; |
|
10886 dg = l + (dg - l) * l / lmn; |
|
10887 db = l + (db - l) * l / lmn; |
|
10888 } |
|
10889 if (mx > 255) { |
|
10890 var ln = 255 - l, |
|
10891 mxl = mx - l; |
|
10892 dr = l + (dr - l) * ln / mxl; |
|
10893 dg = l + (dg - l) * ln / mxl; |
|
10894 db = l + (db - l) * ln / mxl; |
|
10895 } |
|
10896 } |
|
10897 |
|
10898 function getSat(r, g, b) { |
|
10899 return max(r, g, b) - min(r, g, b); |
|
10900 } |
|
10901 |
|
10902 function setSat(r, g, b, s) { |
|
10903 var col = [r, g, b], |
|
10904 mx = max(r, g, b), |
|
10905 mn = min(r, g, b), |
|
10906 md; |
|
10907 mn = mn === r ? 0 : mn === g ? 1 : 2; |
|
10908 mx = mx === r ? 0 : mx === g ? 1 : 2; |
|
10909 md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0; |
|
10910 if (col[mx] > col[mn]) { |
|
10911 col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]); |
|
10912 col[mx] = s; |
|
10913 } else { |
|
10914 col[md] = col[mx] = 0; |
|
10915 } |
|
10916 col[mn] = 0; |
|
10917 dr = col[0]; |
|
10918 dg = col[1]; |
|
10919 db = col[2]; |
|
10920 } |
|
10921 |
|
10922 var modes = { |
|
10923 multiply: function() { |
|
10924 dr = br * sr / 255; |
|
10925 dg = bg * sg / 255; |
|
10926 db = bb * sb / 255; |
|
10927 }, |
|
10928 |
|
10929 screen: function() { |
|
10930 dr = br + sr - (br * sr / 255); |
|
10931 dg = bg + sg - (bg * sg / 255); |
|
10932 db = bb + sb - (bb * sb / 255); |
|
10933 }, |
|
10934 |
|
10935 overlay: function() { |
|
10936 dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255; |
|
10937 dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255; |
|
10938 db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255; |
|
10939 }, |
|
10940 |
|
10941 'soft-light': function() { |
|
10942 var t = sr * br / 255; |
|
10943 dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255; |
|
10944 t = sg * bg / 255; |
|
10945 dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255; |
|
10946 t = sb * bb / 255; |
|
10947 db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255; |
|
10948 }, |
|
10949 |
|
10950 'hard-light': function() { |
|
10951 dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255; |
|
10952 dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255; |
|
10953 db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255; |
|
10954 }, |
|
10955 |
|
10956 'color-dodge': function() { |
|
10957 dr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr)); |
|
10958 dg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg)); |
|
10959 db = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb)); |
|
10960 }, |
|
10961 |
|
10962 'color-burn': function() { |
|
10963 dr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr); |
|
10964 dg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg); |
|
10965 db = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb); |
|
10966 }, |
|
10967 |
|
10968 darken: function() { |
|
10969 dr = br < sr ? br : sr; |
|
10970 dg = bg < sg ? bg : sg; |
|
10971 db = bb < sb ? bb : sb; |
|
10972 }, |
|
10973 |
|
10974 lighten: function() { |
|
10975 dr = br > sr ? br : sr; |
|
10976 dg = bg > sg ? bg : sg; |
|
10977 db = bb > sb ? bb : sb; |
|
10978 }, |
|
10979 |
|
10980 difference: function() { |
|
10981 dr = br - sr; |
|
10982 if (dr < 0) |
|
10983 dr = -dr; |
|
10984 dg = bg - sg; |
|
10985 if (dg < 0) |
|
10986 dg = -dg; |
|
10987 db = bb - sb; |
|
10988 if (db < 0) |
|
10989 db = -db; |
|
10990 }, |
|
10991 |
|
10992 exclusion: function() { |
|
10993 dr = br + sr * (255 - br - br) / 255; |
|
10994 dg = bg + sg * (255 - bg - bg) / 255; |
|
10995 db = bb + sb * (255 - bb - bb) / 255; |
|
10996 }, |
|
10997 |
|
10998 hue: function() { |
|
10999 setSat(sr, sg, sb, getSat(br, bg, bb)); |
|
11000 setLum(dr, dg, db, getLum(br, bg, bb)); |
|
11001 }, |
|
11002 |
|
11003 saturation: function() { |
|
11004 setSat(br, bg, bb, getSat(sr, sg, sb)); |
|
11005 setLum(dr, dg, db, getLum(br, bg, bb)); |
|
11006 }, |
|
11007 |
|
11008 luminosity: function() { |
|
11009 setLum(br, bg, bb, getLum(sr, sg, sb)); |
|
11010 }, |
|
11011 |
|
11012 color: function() { |
|
11013 setLum(sr, sg, sb, getLum(br, bg, bb)); |
|
11014 }, |
|
11015 |
|
11016 add: function() { |
|
11017 dr = min(br + sr, 255); |
|
11018 dg = min(bg + sg, 255); |
|
11019 db = min(bb + sb, 255); |
|
11020 }, |
|
11021 |
|
11022 subtract: function() { |
|
11023 dr = max(br - sr, 0); |
|
11024 dg = max(bg - sg, 0); |
|
11025 db = max(bb - sb, 0); |
|
11026 }, |
|
11027 |
|
11028 average: function() { |
|
11029 dr = (br + sr) / 2; |
|
11030 dg = (bg + sg) / 2; |
|
11031 db = (bb + sb) / 2; |
|
11032 }, |
|
11033 |
|
11034 negation: function() { |
|
11035 dr = 255 - abs(255 - sr - br); |
|
11036 dg = 255 - abs(255 - sg - bg); |
|
11037 db = 255 - abs(255 - sb - bb); |
|
11038 } |
|
11039 }; |
|
11040 |
|
11041 var nativeModes = this.nativeModes = Base.each([ |
|
11042 'source-over', 'source-in', 'source-out', 'source-atop', |
|
11043 'destination-over', 'destination-in', 'destination-out', |
|
11044 'destination-atop', 'lighter', 'darker', 'copy', 'xor' |
|
11045 ], function(mode) { |
|
11046 this[mode] = true; |
|
11047 }, {}); |
|
11048 |
|
11049 var ctx = CanvasProvider.getContext(1, 1); |
|
11050 Base.each(modes, function(func, mode) { |
|
11051 ctx.save(); |
|
11052 var darken = mode === 'darken', |
|
11053 ok = false; |
|
11054 ctx.fillStyle = darken ? '#300' : '#a00'; |
|
11055 ctx.fillRect(0, 0, 1, 1); |
|
11056 ctx.globalCompositeOperation = mode; |
|
11057 if (ctx.globalCompositeOperation === mode) { |
|
11058 ctx.fillStyle = darken ? '#a00' : '#300'; |
|
11059 ctx.fillRect(0, 0, 1, 1); |
|
11060 ok = ctx.getImageData(0, 0, 1, 1).data[0] !== (darken ? 170 : 51); |
|
11061 } |
|
11062 nativeModes[mode] = ok; |
|
11063 ctx.restore(); |
|
11064 }); |
|
11065 CanvasProvider.release(ctx); |
|
11066 |
|
11067 this.process = function(mode, srcContext, dstContext, alpha, offset) { |
|
11068 var srcCanvas = srcContext.canvas, |
|
11069 normal = mode === 'normal'; |
|
11070 if (normal || nativeModes[mode]) { |
|
11071 dstContext.save(); |
|
11072 dstContext.setTransform(1, 0, 0, 1, 0, 0); |
|
11073 dstContext.globalAlpha = alpha; |
|
11074 if (!normal) |
|
11075 dstContext.globalCompositeOperation = mode; |
|
11076 dstContext.drawImage(srcCanvas, offset.x, offset.y); |
|
11077 dstContext.restore(); |
|
11078 } else { |
|
11079 var process = modes[mode]; |
|
11080 if (!process) |
|
11081 return; |
|
11082 var dstData = dstContext.getImageData(offset.x, offset.y, |
|
11083 srcCanvas.width, srcCanvas.height), |
|
11084 dst = dstData.data, |
|
11085 src = srcContext.getImageData(0, 0, |
|
11086 srcCanvas.width, srcCanvas.height).data; |
|
11087 for (var i = 0, l = dst.length; i < l; i += 4) { |
|
11088 sr = src[i]; |
|
11089 br = dst[i]; |
|
11090 sg = src[i + 1]; |
|
11091 bg = dst[i + 1]; |
|
11092 sb = src[i + 2]; |
|
11093 bb = dst[i + 2]; |
|
11094 sa = src[i + 3]; |
|
11095 ba = dst[i + 3]; |
|
11096 process(); |
|
11097 var a1 = sa * alpha / 255, |
|
11098 a2 = 1 - a1; |
|
11099 dst[i] = a1 * dr + a2 * br; |
|
11100 dst[i + 1] = a1 * dg + a2 * bg; |
|
11101 dst[i + 2] = a1 * db + a2 * bb; |
|
11102 dst[i + 3] = sa * alpha + a2 * ba; |
|
11103 } |
|
11104 dstContext.putImageData(dstData, offset.x, offset.y); |
|
11105 } |
|
11106 }; |
|
11107 }; |
|
11108 |
|
11109 var SVGStyles = Base.each({ |
|
11110 fillColor: ['fill', 'color'], |
|
11111 strokeColor: ['stroke', 'color'], |
|
11112 strokeWidth: ['stroke-width', 'number'], |
|
11113 strokeCap: ['stroke-linecap', 'string'], |
|
11114 strokeJoin: ['stroke-linejoin', 'string'], |
|
11115 miterLimit: ['stroke-miterlimit', 'number'], |
|
11116 dashArray: ['stroke-dasharray', 'array'], |
|
11117 dashOffset: ['stroke-dashoffset', 'number'], |
|
11118 font: ['font-family', 'string'], |
|
11119 fontSize: ['font-size', 'number'], |
|
11120 justification: ['text-anchor', 'lookup', { |
|
11121 left: 'start', |
|
11122 center: 'middle', |
|
11123 right: 'end' |
|
11124 }], |
|
11125 opacity: ['opacity', 'number'], |
|
11126 blendMode: ['mix-blend-mode', 'string'] |
|
11127 }, function(entry, key) { |
|
11128 var part = Base.capitalize(key), |
|
11129 lookup = entry[2]; |
|
11130 this[key] = { |
|
11131 type: entry[1], |
|
11132 property: key, |
|
11133 attribute: entry[0], |
|
11134 toSVG: lookup, |
|
11135 fromSVG: lookup && Base.each(lookup, function(value, name) { |
|
11136 this[value] = name; |
|
11137 }, {}), |
|
11138 get: 'get' + part, |
|
11139 set: 'set' + part |
|
11140 }; |
|
11141 }, {}); |
|
11142 |
|
11143 var SVGNamespaces = { |
|
11144 href: 'http://www.w3.org/1999/xlink', |
|
11145 xlink: 'http://www.w3.org/2000/xmlns' |
|
11146 }; |
|
11147 |
|
11148 new function() { |
|
11149 var formatter; |
|
11150 |
|
11151 function setAttributes(node, attrs) { |
|
11152 for (var key in attrs) { |
|
11153 var val = attrs[key], |
|
11154 namespace = SVGNamespaces[key]; |
|
11155 if (typeof val === 'number') |
|
11156 val = formatter.number(val); |
|
11157 if (namespace) { |
|
11158 node.setAttributeNS(namespace, key, val); |
|
11159 } else { |
|
11160 node.setAttribute(key, val); |
|
11161 } |
|
11162 } |
|
11163 return node; |
|
11164 } |
|
11165 |
|
11166 function createElement(tag, attrs) { |
|
11167 return setAttributes( |
|
11168 document.createElementNS('http://www.w3.org/2000/svg', tag), attrs); |
|
11169 } |
|
11170 |
|
11171 function getTransform(item, coordinates, center) { |
|
11172 var matrix = item._matrix, |
|
11173 trans = matrix.getTranslation(), |
|
11174 attrs = {}; |
|
11175 if (coordinates) { |
|
11176 matrix = matrix.shiftless(); |
|
11177 var point = matrix._inverseTransform(trans); |
|
11178 attrs[center ? 'cx' : 'x'] = point.x; |
|
11179 attrs[center ? 'cy' : 'y'] = point.y; |
|
11180 trans = null; |
|
11181 } |
|
11182 if (matrix.isIdentity()) |
|
11183 return attrs; |
|
11184 var decomposed = matrix.decompose(); |
|
11185 if (decomposed && !decomposed.shearing) { |
|
11186 var parts = [], |
|
11187 angle = decomposed.rotation, |
|
11188 scale = decomposed.scaling; |
|
11189 if (trans && !trans.isZero()) |
|
11190 parts.push('translate(' + formatter.point(trans) + ')'); |
|
11191 if (!Numerical.isZero(scale.x - 1) || !Numerical.isZero(scale.y - 1)) |
|
11192 parts.push('scale(' + formatter.point(scale) +')'); |
|
11193 if (angle) |
|
11194 parts.push('rotate(' + formatter.number(angle) + ')'); |
|
11195 attrs.transform = parts.join(' '); |
|
11196 } else { |
|
11197 attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')'; |
|
11198 } |
|
11199 return attrs; |
|
11200 } |
|
11201 |
|
11202 function exportGroup(item, options) { |
|
11203 var attrs = getTransform(item), |
|
11204 children = item._children; |
|
11205 var node = createElement('g', attrs); |
|
11206 for (var i = 0, l = children.length; i < l; i++) { |
|
11207 var child = children[i]; |
|
11208 var childNode = exportSVG(child, options); |
|
11209 if (childNode) { |
|
11210 if (child.isClipMask()) { |
|
11211 var clip = createElement('clipPath'); |
|
11212 clip.appendChild(childNode); |
|
11213 setDefinition(child, clip, 'clip'); |
|
11214 setAttributes(node, { |
|
11215 'clip-path': 'url(#' + clip.id + ')' |
|
11216 }); |
|
11217 } else { |
|
11218 node.appendChild(childNode); |
|
11219 } |
|
11220 } |
|
11221 } |
|
11222 return node; |
|
11223 } |
|
11224 |
|
11225 function exportRaster(item) { |
|
11226 var attrs = getTransform(item, true), |
|
11227 size = item.getSize(); |
|
11228 attrs.x -= size.width / 2; |
|
11229 attrs.y -= size.height / 2; |
|
11230 attrs.width = size.width; |
|
11231 attrs.height = size.height; |
|
11232 attrs.href = item.toDataURL(); |
|
11233 return createElement('image', attrs); |
|
11234 } |
|
11235 |
|
11236 function exportPath(item, options) { |
|
11237 if (options.matchShapes) { |
|
11238 var shape = item.toShape(false); |
|
11239 if (shape) |
|
11240 return exportShape(shape, options); |
|
11241 } |
|
11242 var segments = item._segments, |
|
11243 type, |
|
11244 attrs; |
|
11245 if (segments.length === 0) |
|
11246 return null; |
|
11247 if (item.isPolygon()) { |
|
11248 if (segments.length >= 3) { |
|
11249 type = item._closed ? 'polygon' : 'polyline'; |
|
11250 var parts = []; |
|
11251 for(i = 0, l = segments.length; i < l; i++) |
|
11252 parts.push(formatter.point(segments[i]._point)); |
|
11253 attrs = { |
|
11254 points: parts.join(' ') |
|
11255 }; |
|
11256 } else { |
|
11257 type = 'line'; |
|
11258 var first = segments[0]._point, |
|
11259 last = segments[segments.length - 1]._point; |
|
11260 attrs = { |
|
11261 x1: first.x, |
|
11262 y1: first.y, |
|
11263 x2: last.x, |
|
11264 y2: last.y |
|
11265 }; |
|
11266 } |
|
11267 } else { |
|
11268 type = 'path'; |
|
11269 var data = item.getPathData(); |
|
11270 attrs = data && { d: data }; |
|
11271 } |
|
11272 return createElement(type, attrs); |
|
11273 } |
|
11274 |
|
11275 function exportShape(item) { |
|
11276 var shape = item._shape, |
|
11277 radius = item._radius, |
|
11278 attrs = getTransform(item, true, shape !== 'rectangle'); |
|
11279 if (shape === 'rectangle') { |
|
11280 shape = 'rect'; |
|
11281 var size = item._size, |
|
11282 width = size.width, |
|
11283 height = size.height; |
|
11284 attrs.x -= width / 2; |
|
11285 attrs.y -= height / 2; |
|
11286 attrs.width = width; |
|
11287 attrs.height = height; |
|
11288 if (radius.isZero()) |
|
11289 radius = null; |
|
11290 } |
|
11291 if (radius) { |
|
11292 if (shape === 'circle') { |
|
11293 attrs.r = radius; |
|
11294 } else { |
|
11295 attrs.rx = radius.width; |
|
11296 attrs.ry = radius.height; |
|
11297 } |
|
11298 } |
|
11299 return createElement(shape, attrs); |
|
11300 } |
|
11301 |
|
11302 function exportCompoundPath(item) { |
|
11303 var attrs = getTransform(item, true); |
|
11304 var data = item.getPathData(); |
|
11305 if (data) |
|
11306 attrs.d = data; |
|
11307 return createElement('path', attrs); |
|
11308 } |
|
11309 |
|
11310 function exportPlacedSymbol(item, options) { |
|
11311 var attrs = getTransform(item, true), |
|
11312 symbol = item.getSymbol(), |
|
11313 symbolNode = getDefinition(symbol, 'symbol'), |
|
11314 definition = symbol.getDefinition(), |
|
11315 bounds = definition.getBounds(); |
|
11316 if (!symbolNode) { |
|
11317 symbolNode = createElement('symbol', { |
|
11318 viewBox: formatter.rectangle(bounds) |
|
11319 }); |
|
11320 symbolNode.appendChild(exportSVG(definition, options)); |
|
11321 setDefinition(symbol, symbolNode, 'symbol'); |
|
11322 } |
|
11323 attrs.href = '#' + symbolNode.id; |
|
11324 attrs.x += bounds.x; |
|
11325 attrs.y += bounds.y; |
|
11326 attrs.width = formatter.number(bounds.width); |
|
11327 attrs.height = formatter.number(bounds.height); |
|
11328 return createElement('use', attrs); |
|
11329 } |
|
11330 |
|
11331 function exportGradient(color) { |
|
11332 var gradientNode = getDefinition(color, 'color'); |
|
11333 if (!gradientNode) { |
|
11334 var gradient = color.getGradient(), |
|
11335 radial = gradient._radial, |
|
11336 origin = color.getOrigin().transform(), |
|
11337 destination = color.getDestination().transform(), |
|
11338 attrs; |
|
11339 if (radial) { |
|
11340 attrs = { |
|
11341 cx: origin.x, |
|
11342 cy: origin.y, |
|
11343 r: origin.getDistance(destination) |
|
11344 }; |
|
11345 var highlight = color.getHighlight(); |
|
11346 if (highlight) { |
|
11347 highlight = highlight.transform(); |
|
11348 attrs.fx = highlight.x; |
|
11349 attrs.fy = highlight.y; |
|
11350 } |
|
11351 } else { |
|
11352 attrs = { |
|
11353 x1: origin.x, |
|
11354 y1: origin.y, |
|
11355 x2: destination.x, |
|
11356 y2: destination.y |
|
11357 }; |
|
11358 } |
|
11359 attrs.gradientUnits = 'userSpaceOnUse'; |
|
11360 gradientNode = createElement( |
|
11361 (radial ? 'radial' : 'linear') + 'Gradient', attrs); |
|
11362 var stops = gradient._stops; |
|
11363 for (var i = 0, l = stops.length; i < l; i++) { |
|
11364 var stop = stops[i], |
|
11365 stopColor = stop._color, |
|
11366 alpha = stopColor.getAlpha(); |
|
11367 attrs = { |
|
11368 offset: stop._rampPoint, |
|
11369 'stop-color': stopColor.toCSS(true) |
|
11370 }; |
|
11371 if (alpha < 1) |
|
11372 attrs['stop-opacity'] = alpha; |
|
11373 gradientNode.appendChild(createElement('stop', attrs)); |
|
11374 } |
|
11375 setDefinition(color, gradientNode, 'color'); |
|
11376 } |
|
11377 return 'url(#' + gradientNode.id + ')'; |
|
11378 } |
|
11379 |
|
11380 function exportText(item) { |
|
11381 var node = createElement('text', getTransform(item, true)); |
|
11382 node.textContent = item._content; |
|
11383 return node; |
|
11384 } |
|
11385 |
|
11386 var exporters = { |
|
11387 group: exportGroup, |
|
11388 layer: exportGroup, |
|
11389 raster: exportRaster, |
|
11390 path: exportPath, |
|
11391 shape: exportShape, |
|
11392 'compound-path': exportCompoundPath, |
|
11393 'placed-symbol': exportPlacedSymbol, |
|
11394 'point-text': exportText |
|
11395 }; |
|
11396 |
|
11397 function applyStyle(item, node) { |
|
11398 var attrs = {}, |
|
11399 parent = item.getParent(); |
|
11400 |
|
11401 if (item._name != null) |
|
11402 attrs.id = item._name; |
|
11403 |
|
11404 Base.each(SVGStyles, function(entry) { |
|
11405 var get = entry.get, |
|
11406 type = entry.type, |
|
11407 value = item[get](); |
|
11408 if (!parent || !Base.equals(parent[get](), value)) { |
|
11409 if (type === 'color' && value != null) { |
|
11410 var alpha = value.getAlpha(); |
|
11411 if (alpha < 1) |
|
11412 attrs[entry.attribute + '-opacity'] = alpha; |
|
11413 } |
|
11414 attrs[entry.attribute] = value == null |
|
11415 ? 'none' |
|
11416 : type === 'number' |
|
11417 ? formatter.number(value) |
|
11418 : type === 'color' |
|
11419 ? value.gradient |
|
11420 ? exportGradient(value, item) |
|
11421 : value.toCSS(true) |
|
11422 : type === 'array' |
|
11423 ? value.join(',') |
|
11424 : type === 'lookup' |
|
11425 ? entry.toSVG[value] |
|
11426 : value; |
|
11427 } |
|
11428 }); |
|
11429 |
|
11430 if (attrs.opacity === 1) |
|
11431 delete attrs.opacity; |
|
11432 |
|
11433 if (item._visibility != null && !item._visibility) |
|
11434 attrs.visibility = 'hidden'; |
|
11435 |
|
11436 return setAttributes(node, attrs); |
|
11437 } |
|
11438 |
|
11439 var definitions; |
|
11440 function getDefinition(item, type) { |
|
11441 if (!definitions) |
|
11442 definitions = { ids: {}, svgs: {} }; |
|
11443 return item && definitions.svgs[type + '-' + item._id]; |
|
11444 } |
|
11445 |
|
11446 function setDefinition(item, node, type) { |
|
11447 if (!definitions) |
|
11448 getDefinition(); |
|
11449 var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1; |
|
11450 node.id = type + '-' + id; |
|
11451 definitions.svgs[type + '-' + item._id] = node; |
|
11452 } |
|
11453 |
|
11454 function exportDefinitions(node, options) { |
|
11455 var svg = node, |
|
11456 defs = null; |
|
11457 if (definitions) { |
|
11458 svg = node.nodeName.toLowerCase() === 'svg' && node; |
|
11459 for (var i in definitions.svgs) { |
|
11460 if (!defs) { |
|
11461 if (!svg) { |
|
11462 svg = createElement('svg'); |
|
11463 svg.appendChild(node); |
|
11464 } |
|
11465 defs = svg.insertBefore(createElement('defs'), |
|
11466 svg.firstChild); |
|
11467 } |
|
11468 defs.appendChild(definitions.svgs[i]); |
|
11469 } |
|
11470 definitions = null; |
|
11471 } |
|
11472 return options.asString |
|
11473 ? new XMLSerializer().serializeToString(svg) |
|
11474 : svg; |
|
11475 } |
|
11476 |
|
11477 function exportSVG(item, options) { |
|
11478 var exporter = exporters[item._type], |
|
11479 node = exporter && exporter(item, options); |
|
11480 if (node && item._data) |
|
11481 node.setAttribute('data-paper-data', JSON.stringify(item._data)); |
|
11482 return node && applyStyle(item, node); |
|
11483 } |
|
11484 |
|
11485 function setOptions(options) { |
|
11486 if (!options) |
|
11487 options = {}; |
|
11488 formatter = new Formatter(options.precision); |
|
11489 return options; |
|
11490 } |
|
11491 |
|
11492 Item.inject({ |
|
11493 exportSVG: function(options) { |
|
11494 options = setOptions(options); |
|
11495 return exportDefinitions(exportSVG(this, options), options); |
|
11496 } |
|
11497 }); |
|
11498 |
|
11499 Project.inject({ |
|
11500 exportSVG: function(options) { |
|
11501 options = setOptions(options); |
|
11502 var layers = this.layers, |
|
11503 size = this.view.getSize(), |
|
11504 node = createElement('svg', { |
|
11505 x: 0, |
|
11506 y: 0, |
|
11507 width: size.width, |
|
11508 height: size.height, |
|
11509 version: '1.1', |
|
11510 xmlns: 'http://www.w3.org/2000/svg', |
|
11511 'xmlns:xlink': 'http://www.w3.org/1999/xlink' |
|
11512 }); |
|
11513 for (var i = 0, l = layers.length; i < l; i++) |
|
11514 node.appendChild(exportSVG(layers[i], options)); |
|
11515 return exportDefinitions(node, options); |
|
11516 } |
|
11517 }); |
|
11518 }; |
|
11519 |
|
11520 new function() { |
|
11521 |
|
11522 function getValue(node, name, isString, allowNull) { |
|
11523 var namespace = SVGNamespaces[name], |
|
11524 value = namespace |
|
11525 ? node.getAttributeNS(namespace, name) |
|
11526 : node.getAttribute(name); |
|
11527 if (value === 'null') |
|
11528 value = null; |
|
11529 return value == null |
|
11530 ? allowNull |
|
11531 ? null |
|
11532 : isString |
|
11533 ? '' |
|
11534 : 0 |
|
11535 : isString |
|
11536 ? value |
|
11537 : parseFloat(value); |
|
11538 } |
|
11539 |
|
11540 function getPoint(node, x, y, allowNull) { |
|
11541 x = getValue(node, x, false, allowNull); |
|
11542 y = getValue(node, y, false, allowNull); |
|
11543 return allowNull && (x == null || y == null) ? null |
|
11544 : new Point(x, y); |
|
11545 } |
|
11546 |
|
11547 function getSize(node, w, h, allowNull) { |
|
11548 w = getValue(node, w, false, allowNull); |
|
11549 h = getValue(node, h, false, allowNull); |
|
11550 return allowNull && (w == null || h == null) ? null |
|
11551 : new Size(w, h); |
|
11552 } |
|
11553 |
|
11554 function convertValue(value, type, lookup) { |
|
11555 return value === 'none' |
|
11556 ? null |
|
11557 : type === 'number' |
|
11558 ? parseFloat(value) |
|
11559 : type === 'array' |
|
11560 ? value ? value.split(/[\s,]+/g).map(parseFloat) : [] |
|
11561 : type === 'color' |
|
11562 ? getDefinition(value) || value |
|
11563 : type === 'lookup' |
|
11564 ? lookup[value] |
|
11565 : value; |
|
11566 } |
|
11567 |
|
11568 function importGroup(node, type, isRoot, options) { |
|
11569 var nodes = node.childNodes, |
|
11570 isClip = type === 'clippath', |
|
11571 item = new Group(), |
|
11572 project = item._project, |
|
11573 currentStyle = project._currentStyle, |
|
11574 children = []; |
|
11575 if (!isClip) { |
|
11576 item._transformContent = false; |
|
11577 item = applyAttributes(item, node, isRoot); |
|
11578 project._currentStyle = item._style.clone(); |
|
11579 } |
|
11580 for (var i = 0, l = nodes.length; i < l; i++) { |
|
11581 var childNode = nodes[i], |
|
11582 child; |
|
11583 if (childNode.nodeType === 1 |
|
11584 && (child = importSVG(childNode, false, options)) |
|
11585 && !(child instanceof Symbol)) |
|
11586 children.push(child); |
|
11587 } |
|
11588 item.addChildren(children); |
|
11589 if (isClip) |
|
11590 item = applyAttributes(item.reduce(), node, isRoot); |
|
11591 project._currentStyle = currentStyle; |
|
11592 if (isClip || type === 'defs') { |
|
11593 item.remove(); |
|
11594 item = null; |
|
11595 } |
|
11596 return item; |
|
11597 } |
|
11598 |
|
11599 function importPoly(node, type) { |
|
11600 var path = new Path(), |
|
11601 points = node.points; |
|
11602 path.moveTo(points.getItem(0)); |
|
11603 for (var i = 1, l = points.numberOfItems; i < l; i++) |
|
11604 path.lineTo(points.getItem(i)); |
|
11605 if (type === 'polygon') |
|
11606 path.closePath(); |
|
11607 return path; |
|
11608 } |
|
11609 |
|
11610 function importPath(node) { |
|
11611 var data = node.getAttribute('d'), |
|
11612 path = data.match(/m/gi).length > 1 |
|
11613 ? new CompoundPath() |
|
11614 : new Path(); |
|
11615 path.setPathData(data); |
|
11616 return path; |
|
11617 } |
|
11618 |
|
11619 function importGradient(node, type) { |
|
11620 var nodes = node.childNodes, |
|
11621 stops = []; |
|
11622 for (var i = 0, l = nodes.length; i < l; i++) { |
|
11623 var child = nodes[i]; |
|
11624 if (child.nodeType === 1) |
|
11625 stops.push(applyAttributes(new GradientStop(), child)); |
|
11626 } |
|
11627 var isRadial = type === 'radialgradient', |
|
11628 gradient = new Gradient(stops, isRadial), |
|
11629 origin, destination, highlight; |
|
11630 if (isRadial) { |
|
11631 origin = getPoint(node, 'cx', 'cy'); |
|
11632 destination = origin.add(getValue(node, 'r'), 0); |
|
11633 highlight = getPoint(node, 'fx', 'fy', true); |
|
11634 } else { |
|
11635 origin = getPoint(node, 'x1', 'y1'); |
|
11636 destination = getPoint(node, 'x2', 'y2'); |
|
11637 } |
|
11638 applyAttributes( |
|
11639 new Color(gradient, origin, destination, highlight), node); |
|
11640 return null; |
|
11641 } |
|
11642 |
|
11643 var importers = { |
|
11644 '#document': function (node, type, isRoot, options) { |
|
11645 var nodes = node.childNodes; |
|
11646 for (var i = 0, l = nodes.length; i < l; i++) { |
|
11647 var child = nodes[i]; |
|
11648 if (child.nodeType === 1) { |
|
11649 var next = child.nextSibling; |
|
11650 document.body.appendChild(child); |
|
11651 var item = importSVG(child, isRoot, options); |
|
11652 if (next) { |
|
11653 node.insertBefore(child, next); |
|
11654 } else { |
|
11655 node.appendChild(child); |
|
11656 } |
|
11657 return item; |
|
11658 } |
|
11659 } |
|
11660 }, |
|
11661 g: importGroup, |
|
11662 svg: importGroup, |
|
11663 clippath: importGroup, |
|
11664 polygon: importPoly, |
|
11665 polyline: importPoly, |
|
11666 path: importPath, |
|
11667 lineargradient: importGradient, |
|
11668 radialgradient: importGradient, |
|
11669 |
|
11670 image: function (node) { |
|
11671 var raster = new Raster(getValue(node, 'href', true)); |
|
11672 raster.attach('load', function() { |
|
11673 var size = getSize(node, 'width', 'height'); |
|
11674 this.setSize(size); |
|
11675 var center = this._matrix._transformPoint( |
|
11676 getPoint(node, 'x', 'y').add(size.divide(2))); |
|
11677 this.translate(center); |
|
11678 }); |
|
11679 return raster; |
|
11680 }, |
|
11681 |
|
11682 symbol: function(node, type, isRoot, options) { |
|
11683 return new Symbol(importGroup(node, type, isRoot, options), true); |
|
11684 }, |
|
11685 |
|
11686 defs: importGroup, |
|
11687 |
|
11688 use: function(node) { |
|
11689 var id = (getValue(node, 'href', true) || '').substring(1), |
|
11690 definition = definitions[id], |
|
11691 point = getPoint(node, 'x', 'y'); |
|
11692 return definition |
|
11693 ? definition instanceof Symbol |
|
11694 ? definition.place(point) |
|
11695 : definition.clone().translate(point) |
|
11696 : null; |
|
11697 }, |
|
11698 |
|
11699 circle: function(node) { |
|
11700 return new Shape.Circle(getPoint(node, 'cx', 'cy'), |
|
11701 getValue(node, 'r')); |
|
11702 }, |
|
11703 |
|
11704 ellipse: function(node) { |
|
11705 return new Shape.Ellipse({ |
|
11706 center: getPoint(node, 'cx', 'cy'), |
|
11707 radius: getSize(node, 'rx', 'ry') |
|
11708 }); |
|
11709 }, |
|
11710 |
|
11711 rect: function(node) { |
|
11712 var point = getPoint(node, 'x', 'y'), |
|
11713 size = getSize(node, 'width', 'height'), |
|
11714 radius = getSize(node, 'rx', 'ry'); |
|
11715 return new Shape.Rectangle(new Rectangle(point, size), radius); |
|
11716 }, |
|
11717 |
|
11718 line: function(node) { |
|
11719 return new Path.Line(getPoint(node, 'x1', 'y1'), |
|
11720 getPoint(node, 'x2', 'y2')); |
|
11721 }, |
|
11722 |
|
11723 text: function(node) { |
|
11724 var text = new PointText(getPoint(node, 'x', 'y') |
|
11725 .add(getPoint(node, 'dx', 'dy'))); |
|
11726 text.setContent(node.textContent.trim() || ''); |
|
11727 return text; |
|
11728 } |
|
11729 }; |
|
11730 |
|
11731 function applyTransform(item, value, name, node) { |
|
11732 var transforms = (node.getAttribute(name) || '').split(/\)\s*/g), |
|
11733 matrix = new Matrix(); |
|
11734 for (var i = 0, l = transforms.length; i < l; i++) { |
|
11735 var transform = transforms[i]; |
|
11736 if (!transform) |
|
11737 break; |
|
11738 var parts = transform.split('('), |
|
11739 command = parts[0], |
|
11740 v = parts[1].split(/[\s,]+/g); |
|
11741 for (var j = 0, m = v.length; j < m; j++) |
|
11742 v[j] = parseFloat(v[j]); |
|
11743 switch (command) { |
|
11744 case 'matrix': |
|
11745 matrix.concatenate( |
|
11746 new Matrix(v[0], v[1], v[2], v[3], v[4], v[5])); |
|
11747 break; |
|
11748 case 'rotate': |
|
11749 matrix.rotate(v[0], v[1], v[2]); |
|
11750 break; |
|
11751 case 'translate': |
|
11752 matrix.translate(v[0], v[1]); |
|
11753 break; |
|
11754 case 'scale': |
|
11755 matrix.scale(v); |
|
11756 break; |
|
11757 case 'skewX': |
|
11758 case 'skewY': |
|
11759 var value = Math.tan(v[0] * Math.PI / 180), |
|
11760 isX = command == 'skewX'; |
|
11761 matrix.shear(isX ? value : 0, isX ? 0 : value); |
|
11762 break; |
|
11763 } |
|
11764 } |
|
11765 item.transform(matrix); |
|
11766 } |
|
11767 |
|
11768 function applyOpacity(item, value, name) { |
|
11769 var color = item[name === 'fill-opacity' ? 'getFillColor' |
|
11770 : 'getStrokeColor'](); |
|
11771 if (color) |
|
11772 color.setAlpha(parseFloat(value)); |
|
11773 } |
|
11774 |
|
11775 var attributes = Base.each(SVGStyles, function(entry) { |
|
11776 this[entry.attribute] = function(item, value) { |
|
11777 item[entry.set](convertValue(value, entry.type, entry.fromSVG)); |
|
11778 if (entry.type === 'color' && item instanceof Shape) { |
|
11779 var color = item[entry.get](); |
|
11780 if (color) |
|
11781 color.transform(new Matrix().translate( |
|
11782 item.getPosition(true).negate())); |
|
11783 } |
|
11784 }; |
|
11785 }, { |
|
11786 id: function(item, value) { |
|
11787 definitions[value] = item; |
|
11788 if (item.setName) |
|
11789 item.setName(value); |
|
11790 }, |
|
11791 |
|
11792 'clip-path': function(item, value) { |
|
11793 var clip = getDefinition(value); |
|
11794 if (clip) { |
|
11795 clip = clip.clone(); |
|
11796 clip.setClipMask(true); |
|
11797 if (item instanceof Group) { |
|
11798 item.insertChild(0, clip); |
|
11799 } else { |
|
11800 return new Group(clip, item); |
|
11801 } |
|
11802 } |
|
11803 }, |
|
11804 |
|
11805 gradientTransform: applyTransform, |
|
11806 transform: applyTransform, |
|
11807 |
|
11808 'fill-opacity': applyOpacity, |
|
11809 'stroke-opacity': applyOpacity, |
|
11810 |
|
11811 visibility: function(item, value) { |
|
11812 item.setVisible(value === 'visible'); |
|
11813 }, |
|
11814 |
|
11815 'stop-color': function(item, value) { |
|
11816 if (item.setColor) |
|
11817 item.setColor(value); |
|
11818 }, |
|
11819 |
|
11820 'stop-opacity': function(item, value) { |
|
11821 if (item._color) |
|
11822 item._color.setAlpha(parseFloat(value)); |
|
11823 }, |
|
11824 |
|
11825 offset: function(item, value) { |
|
11826 var percentage = value.match(/(.*)%$/); |
|
11827 item.setRampPoint(percentage |
|
11828 ? percentage[1] / 100 |
|
11829 : parseFloat(value)); |
|
11830 }, |
|
11831 |
|
11832 viewBox: function(item, value, name, node, styles) { |
|
11833 var rect = new Rectangle(convertValue(value, 'array')), |
|
11834 size = getSize(node, 'width', 'height', true); |
|
11835 if (item instanceof Group) { |
|
11836 var scale = size ? rect.getSize().divide(size) : 1, |
|
11837 matrix = new Matrix().translate(rect.getPoint()).scale(scale); |
|
11838 item.transform(matrix.inverted()); |
|
11839 } else if (item instanceof Symbol) { |
|
11840 if (size) |
|
11841 rect.setSize(size); |
|
11842 var clip = getAttribute(node, 'overflow', styles) != 'visible', |
|
11843 group = item._definition; |
|
11844 if (clip && !rect.contains(group.getBounds())) { |
|
11845 clip = new Shape.Rectangle(rect).transform(group._matrix); |
|
11846 clip.setClipMask(true); |
|
11847 group.addChild(clip); |
|
11848 } |
|
11849 } |
|
11850 } |
|
11851 }); |
|
11852 |
|
11853 function getAttribute(node, name, styles) { |
|
11854 var attr = node.attributes[name], |
|
11855 value = attr && attr.value; |
|
11856 if (!value) { |
|
11857 var style = Base.camelize(name); |
|
11858 value = node.style[style]; |
|
11859 if (!value && styles.node[style] !== styles.parent[style]) |
|
11860 value = styles.node[style]; |
|
11861 } |
|
11862 return !value |
|
11863 ? undefined |
|
11864 : value === 'none' |
|
11865 ? null |
|
11866 : value; |
|
11867 } |
|
11868 |
|
11869 function applyAttributes(item, node, isRoot) { |
|
11870 var styles = { |
|
11871 node: DomElement.getStyles(node) || {}, |
|
11872 parent: !isRoot && DomElement.getStyles(node.parentNode) || {} |
|
11873 }; |
|
11874 Base.each(attributes, function(apply, name) { |
|
11875 var value = getAttribute(node, name, styles); |
|
11876 if (value !== undefined) |
|
11877 item = Base.pick(apply(item, value, name, node, styles), item); |
|
11878 }); |
|
11879 return item; |
|
11880 } |
|
11881 |
|
11882 var definitions = {}; |
|
11883 function getDefinition(value) { |
|
11884 var match = value && value.match(/\((?:#|)([^)']+)/); |
|
11885 return match && definitions[match[1]]; |
|
11886 } |
|
11887 |
|
11888 function importSVG(source, isRoot, options) { |
|
11889 if (!source) |
|
11890 return null; |
|
11891 if (!options) { |
|
11892 options = {}; |
|
11893 } else if (typeof options === 'function') { |
|
11894 options = { onLoad: options }; |
|
11895 } |
|
11896 |
|
11897 var node = source, |
|
11898 scope = paper; |
|
11899 |
|
11900 function onLoadCallback(svg) { |
|
11901 paper = scope; |
|
11902 var item = importSVG(svg, isRoot, options), |
|
11903 onLoad = options.onLoad, |
|
11904 view = scope.project && scope.project.view; |
|
11905 if (onLoad) |
|
11906 onLoad.call(this, item); |
|
11907 view.draw(true); |
|
11908 } |
|
11909 |
|
11910 if (isRoot) { |
|
11911 if (typeof source === 'string' && !/^.*</.test(source)) { |
|
11912 node = document.getElementById(source); |
|
11913 if (node) { |
|
11914 source = null; |
|
11915 } else { |
|
11916 return Http.request('get', source, onLoadCallback); |
|
11917 } |
|
11918 } else if (typeof File !== 'undefined' && source instanceof File) { |
|
11919 var reader = new FileReader(); |
|
11920 reader.onload = function() { |
|
11921 onLoadCallback(reader.result); |
|
11922 }; |
|
11923 return reader.readAsText(source); |
|
11924 } |
|
11925 } |
|
11926 |
|
11927 if (typeof source === 'string') |
|
11928 node = new DOMParser().parseFromString(source, 'image/svg+xml'); |
|
11929 if (!node.nodeName) |
|
11930 throw new Error('Unsupported SVG source: ' + source); |
|
11931 var type = node.nodeName.toLowerCase(), |
|
11932 importer = importers[type], |
|
11933 item = importer && importer(node, type, isRoot, options) || null, |
|
11934 data = node.getAttribute && node.getAttribute('data-paper-data'); |
|
11935 if (item) { |
|
11936 if (!(item instanceof Group)) |
|
11937 item = applyAttributes(item, node, isRoot); |
|
11938 if (options.expandShapes && item instanceof Shape) { |
|
11939 item.remove(); |
|
11940 item = item.toPath(); |
|
11941 } |
|
11942 if (data) |
|
11943 item._data = JSON.parse(data); |
|
11944 } |
|
11945 if (isRoot) |
|
11946 definitions = {}; |
|
11947 return item; |
|
11948 } |
|
11949 |
|
11950 Item.inject({ |
|
11951 importSVG: function(node, options) { |
|
11952 return this.addChild(importSVG(node, true, options)); |
|
11953 } |
|
11954 }); |
|
11955 |
|
11956 Project.inject({ |
|
11957 importSVG: function(node, options) { |
|
11958 this.activate(); |
|
11959 return importSVG(node, true, options); |
|
11960 } |
|
11961 }); |
|
11962 }; |
|
11963 |
|
11964 paper = new (PaperScope.inject(new Base(Base.exports, { |
|
11965 enumerable: true, |
|
11966 Base: Base, |
|
11967 Numerical: Numerical, |
|
11968 DomElement: DomElement, |
|
11969 DomEvent: DomEvent, |
|
11970 Http: Http, |
|
11971 Key: Key |
|
11972 })))(); |
|
11973 |
|
11974 if (typeof define === 'function' && define.amd) |
|
11975 define('paper', paper); |
|
11976 |
|
11977 return paper; |
|
11978 }; |
|
11979 |
|
11980 paper.PaperScope.prototype.PaperScript = (function(root) { |
|
11981 var Base = paper.Base, |
|
11982 PaperScope = paper.PaperScope, |
|
11983 PaperScript, |
|
11984 exports, define, |
|
11985 scope = this; |
|
11986 !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"}}); |
|
11987 |
|
11988 var binaryOperators = { |
|
11989 '+': '_add', |
|
11990 '-': '_subtract', |
|
11991 '*': '_multiply', |
|
11992 '/': '_divide', |
|
11993 '%': '_modulo', |
|
11994 '==': 'equals', |
|
11995 '!=': 'equals' |
|
11996 }; |
|
11997 |
|
11998 var unaryOperators = { |
|
11999 '-': '_negate', |
|
12000 '+': null |
|
12001 }; |
|
12002 |
|
12003 var fields = Base.each( |
|
12004 ['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'], |
|
12005 function(name) { |
|
12006 this['_' + name] = '#' + name; |
|
12007 }, |
|
12008 {} |
|
12009 ); |
|
12010 paper.Point.inject(fields); |
|
12011 paper.Size.inject(fields); |
|
12012 paper.Color.inject(fields); |
|
12013 |
|
12014 function _$_(left, operator, right) { |
|
12015 var handler = binaryOperators[operator]; |
|
12016 if (left && left[handler]) { |
|
12017 var res = left[handler](right); |
|
12018 return operator === '!=' ? !res : res; |
|
12019 } |
|
12020 switch (operator) { |
|
12021 case '+': return left + right; |
|
12022 case '-': return left - right; |
|
12023 case '*': return left * right; |
|
12024 case '/': return left / right; |
|
12025 case '%': return left % right; |
|
12026 case '==': return left == right; |
|
12027 case '!=': return left != right; |
|
12028 } |
|
12029 } |
|
12030 |
|
12031 function $_(operator, value) { |
|
12032 var handler = unaryOperators[operator]; |
|
12033 if (handler && value && value[handler]) |
|
12034 return value[handler](); |
|
12035 switch (operator) { |
|
12036 case '+': return +value; |
|
12037 case '-': return -value; |
|
12038 } |
|
12039 } |
|
12040 |
|
12041 function compile(code) { |
|
12042 |
|
12043 var insertions = []; |
|
12044 |
|
12045 function getOffset(offset) { |
|
12046 for (var i = 0, l = insertions.length; i < l; i++) { |
|
12047 var insertion = insertions[i]; |
|
12048 if (insertion[0] >= offset) |
|
12049 break; |
|
12050 offset += insertion[1]; |
|
12051 } |
|
12052 return offset; |
|
12053 } |
|
12054 |
|
12055 function getCode(node) { |
|
12056 return code.substring(getOffset(node.range[0]), |
|
12057 getOffset(node.range[1])); |
|
12058 } |
|
12059 |
|
12060 function replaceCode(node, str) { |
|
12061 var start = getOffset(node.range[0]), |
|
12062 end = getOffset(node.range[1]); |
|
12063 var insert = 0; |
|
12064 for (var i = insertions.length - 1; i >= 0; i--) { |
|
12065 if (start > insertions[i][0]) { |
|
12066 insert = i + 1; |
|
12067 break; |
|
12068 } |
|
12069 } |
|
12070 insertions.splice(insert, 0, [start, str.length - end + start]); |
|
12071 code = code.substring(0, start) + str + code.substring(end); |
|
12072 } |
|
12073 |
|
12074 function walkAST(node, parent) { |
|
12075 if (!node) |
|
12076 return; |
|
12077 for (var key in node) { |
|
12078 if (key === 'range') |
|
12079 continue; |
|
12080 var value = node[key]; |
|
12081 if (Array.isArray(value)) { |
|
12082 for (var i = 0, l = value.length; i < l; i++) |
|
12083 walkAST(value[i], node); |
|
12084 } else if (value && typeof value === 'object') { |
|
12085 walkAST(value, node); |
|
12086 } |
|
12087 } |
|
12088 switch (node && node.type) { |
|
12089 case 'BinaryExpression': |
|
12090 if (node.operator in binaryOperators |
|
12091 && node.left.type !== 'Literal') { |
|
12092 var left = getCode(node.left), |
|
12093 right = getCode(node.right); |
|
12094 replaceCode(node, '_$_(' + left + ', "' + node.operator |
|
12095 + '", ' + right + ')'); |
|
12096 } |
|
12097 break; |
|
12098 case 'AssignmentExpression': |
|
12099 if (/^.=$/.test(node.operator) |
|
12100 && node.left.type !== 'Literal') { |
|
12101 var left = getCode(node.left), |
|
12102 right = getCode(node.right); |
|
12103 replaceCode(node, left + ' = _$_(' + left + ', "' |
|
12104 + node.operator[0] + '", ' + right + ')'); |
|
12105 } |
|
12106 break; |
|
12107 case 'UpdateExpression': |
|
12108 if (!node.prefix && !(parent && ( |
|
12109 parent.type === 'BinaryExpression' |
|
12110 && /^[=!<>]/.test(parent.operator) |
|
12111 || parent.type === 'MemberExpression' |
|
12112 && parent.computed))) { |
|
12113 var arg = getCode(node.argument); |
|
12114 replaceCode(node, arg + ' = _$_(' + arg + ', "' |
|
12115 + node.operator[0] + '", 1)'); |
|
12116 } |
|
12117 break; |
|
12118 case 'UnaryExpression': |
|
12119 if (node.operator in unaryOperators |
|
12120 && node.argument.type !== 'Literal') { |
|
12121 var arg = getCode(node.argument); |
|
12122 replaceCode(node, '$_("' + node.operator + '", ' |
|
12123 + arg + ')'); |
|
12124 } |
|
12125 break; |
|
12126 } |
|
12127 } |
|
12128 walkAST(scope.acorn.parse(code, { ranges: true })); |
|
12129 return code; |
|
12130 } |
|
12131 |
|
12132 function evaluate(code, scope) { |
|
12133 paper = scope; |
|
12134 var view = scope.project && scope.project.view, |
|
12135 res; |
|
12136 with (scope) { |
|
12137 (function() { |
|
12138 var onActivate, onDeactivate, onEditOptions, |
|
12139 onMouseDown, onMouseUp, onMouseDrag, onMouseMove, |
|
12140 onKeyDown, onKeyUp, onFrame, onResize; |
|
12141 code = compile(code); |
|
12142 if (root.InstallTrigger) { |
|
12143 var handle = PaperScript.handleException; |
|
12144 if (!handle) { |
|
12145 handle = PaperScript.handleException = function(e) { |
|
12146 throw e.lineNumber >= lineNumber |
|
12147 ? new Error(e.message, e.fileName, |
|
12148 e.lineNumber - lineNumber) |
|
12149 : e; |
|
12150 } |
|
12151 var lineNumber = new Error().lineNumber; |
|
12152 lineNumber += (new Error().lineNumber - lineNumber) * 3; |
|
12153 } |
|
12154 try { |
|
12155 res = eval(';' + code); |
|
12156 } catch (e) { |
|
12157 handle(e); |
|
12158 } |
|
12159 } else { |
|
12160 res = eval(code); |
|
12161 } |
|
12162 if (/on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)) { |
|
12163 Base.each(paper.Tool.prototype._events, function(key) { |
|
12164 var value = eval(key); |
|
12165 if (value) { |
|
12166 scope.getTool()[key] = value; |
|
12167 } |
|
12168 }); |
|
12169 } |
|
12170 if (view) { |
|
12171 view.setOnResize(onResize); |
|
12172 view.fire('resize', { |
|
12173 size: view.size, |
|
12174 delta: new Point() |
|
12175 }); |
|
12176 if (onFrame) |
|
12177 view.setOnFrame(onFrame); |
|
12178 view.draw(); |
|
12179 } |
|
12180 }).call(scope); |
|
12181 } |
|
12182 return res; |
|
12183 } |
|
12184 |
|
12185 function load() { |
|
12186 Base.each(document.getElementsByTagName('script'), function(script) { |
|
12187 if (/^text\/(?:x-|)paperscript$/.test(script.type) |
|
12188 && !script.getAttribute('data-paper-ignore')) { |
|
12189 var canvas = PaperScope.getAttribute(script, 'canvas'), |
|
12190 scope = PaperScope.get(canvas) |
|
12191 || new PaperScope(script).setup(canvas), |
|
12192 src = script.src; |
|
12193 if (src) { |
|
12194 paper.Http.request('get', src, function(code) { |
|
12195 evaluate(code, scope); |
|
12196 }); |
|
12197 } else { |
|
12198 evaluate(script.innerHTML, scope); |
|
12199 } |
|
12200 script.setAttribute('data-paper-ignore', true); |
|
12201 } |
|
12202 }, this); |
|
12203 } |
|
12204 |
|
12205 if (document.readyState === 'complete') { |
|
12206 setTimeout(load); |
|
12207 } else { |
|
12208 paper.DomEvent.add(window, { load: load }); |
|
12209 } |
|
12210 |
|
12211 return PaperScript = { |
|
12212 compile: compile, |
|
12213 evaluate: evaluate, |
|
12214 load: load, |
|
12215 lineNumberBase: 0 |
|
12216 }; |
|
12217 |
|
12218 })(this); |