View difference between Paste ID: 2Qq8vg3C and pwHQVUAd
SHOW: | | - or go back to the newest paste.
1
/**
2
* two.js
3
* a two-dimensional drawing api meant for modern browsers. It is renderer
4
* agnostic enabling the same api for rendering in multiple contexts: webgl,
5
* canvas2d, and svg.
6
*
7
* Copyright (c) 2012 - 2017 jonobr1 / http://jonobr1.com
8
*
9
* Permission is hereby granted, free of charge, to any person obtaining a copy
10
* of this software and associated documentation files (the "Software"), to deal
11
* in the Software without restriction, including without limitation the rights
12
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
* copies of the Software, and to permit persons to whom the Software is
14
* furnished to do so, subject to the following conditions:
15
*
16
* The above copyright notice and this permission notice shall be included in
17
* all copies or substantial portions of the Software.
18
*
19
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
* THE SOFTWARE.
26
*
27
*/
28
29
this.Two = (function (previousTwo) {
30
31
    var root = typeof window != 'undefined' ? window : typeof global != 'undefined' ? global : null;
32
    var toString = Object.prototype.toString;
33
    var _ = {
34
        // http://underscorejs.org/ • 1.8.3
35
        _indexAmount: 0,
36
        natural: {
37
            slice: Array.prototype.slice,
38
            indexOf: Array.prototype.indexOf,
39
            keys: Object.keys,
40
            bind: Function.prototype.bind,
41
            create: Object.create
42
        },
43
        identity: function (value) {
44
            return value;
45
        },
46
        isArguments: function (obj) {
47
            return toString.call(obj) === '[object Arguments]';
48
        },
49
        isFunction: function (obj) {
50
            return toString.call(obj) === '[object Function]';
51
        },
52
        isString: function (obj) {
53
            return toString.call(obj) === '[object String]';
54
        },
55
        isNumber: function (obj) {
56
            return toString.call(obj) === '[object Number]';
57
        },
58
        isDate: function (obj) {
59
            return toString.call(obj) === '[object Date]';
60
        },
61
        isRegExp: function (obj) {
62
            return toString.call(obj) === '[object RegExp]';
63
        },
64
        isError: function (obj) {
65
            return toString.call(obj) === '[object Error]';
66
        },
67
        isFinite: function (obj) {
68
            return isFinite(obj) && !isNaN(parseFloat(obj));
69
        },
70
        isNaN: function (obj) {
71
            return _.isNumber(obj) && obj !== +obj;
72
        },
73
        isBoolean: function (obj) {
74
            return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
75
        },
76
        isNull: function (obj) {
77
            return obj === null;
78
        },
79
        isUndefined: function (obj) {
80
            return obj === void 0;
81
        },
82
        isEmpty: function (obj) {
83
            if (obj == null) return true;
84
            if (isArrayLike && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
85
            return _.keys(obj).length === 0;
86
        },
87
        isElement: function (obj) {
88
            return !!(obj && obj.nodeType === 1);
89
        },
90
        isArray: Array.isArray || function (obj) {
91
            return toString.call(obj) === '[object Array]';
92
        },
93
        isObject: function (obj) {
94
            var type = typeof obj;
95
            return type === 'function' || type === 'object' && !!obj;
96
        },
97
        toArray: function (obj) {
98
            if (!obj) {
99
                return [];
100
            }
101
            if (_.isArray(obj)) {
102
                return slice.call(obj);
103
            }
104
            if (isArrayLike(obj)) {
105
                return _.map(obj, _.identity);
106
            }
107
            return _.values(obj);
108
        },
109
        range: function (start, stop, step) {
110
            if (stop == null) {
111
                stop = start || 0;
112
                start = 0;
113
            }
114
            step = step || 1;
115
116
            var length = Math.max(Math.ceil((stop - start) / step), 0);
117
            var range = Array(length);
118
119
            for (var idx = 0; idx < length; idx++ , start += step) {
120
                range[idx] = start;
121
            }
122
123
            return range;
124
        },
125
        indexOf: function (list, item) {
126
            if (!!_.natural.indexOf) {
127
                return _.natural.indexOf.call(list, item);
128
            }
129
            for (var i = 0; i < list.length; i++) {
130
                if (list[i] === item) {
131
                    return i;
132
                }
133
            }
134
            return -1;
135
        },
136
        has: function (obj, key) {
137
            return obj != null && hasOwnProperty.call(obj, key);
138
        },
139
        bind: function (func, ctx) {
140
            var natural = _.natural.bind;
141
            if (natural && func.bind === natural) {
142
                return natural.apply(func, slice.call(arguments, 1));
143
            }
144
            var args = slice.call(arguments, 2);
145
            return function () {
146
                func.apply(ctx, args);
147
            };
148
        },
149
        extend: function (base) {
150
            var sources = slice.call(arguments, 1);
151
            for (var i = 0; i < sources.length; i++) {
152
                var obj = sources[i];
153
                for (var k in obj) {
154
                    base[k] = obj[k];
155
                }
156
            }
157
            return base;
158
        },
159
        defaults: function (base) {
160
            var sources = slice.call(arguments, 1);
161
            for (var i = 0; i < sources.length; i++) {
162
                var obj = sources[i];
163
                for (var k in obj) {
164
                    if (base[k] === void 0) {
165
                        base[k] = obj[k];
166
                    }
167
                }
168
            }
169
            return base;
170
        },
171
        keys: function (obj) {
172
            if (!_.isObject(obj)) {
173
                return [];
174
            }
175
            if (_.natural.keys) {
176
                return _.natural.keys(obj);
177
            }
178
            var keys = [];
179
            for (var k in obj) {
180
                if (_.has(obj, k)) {
181
                    keys.push(k);
182
                }
183
            }
184
            return keys;
185
        },
186
        values: function (obj) {
187
            var keys = _.keys(obj);
188
            var values = [];
189
            for (var i = 0; i < keys.length; i++) {
190
                var k = keys[i];
191
                values.push(obj[k]);
192
            }
193
            return values;
194
        },
195
        each: function (obj, iteratee, context) {
196
            var ctx = context || this;
197
            var keys = !isArrayLike(obj) && _.keys(obj);
198
            var length = (keys || obj).length;
199
            for (var i = 0; i < length; i++) {
200
                var k = keys ? keys[i] : i;
201
                iteratee.call(ctx, obj[k], k, obj);
202
            }
203
            return obj;
204
        },
205
        map: function (obj, iteratee, context) {
206
            var ctx = context || this;
207
            var keys = !isArrayLike(obj) && _.keys(obj);
208
            var length = (keys || obj).length;
209
            var result = [];
210
            for (var i = 0; i < length; i++) {
211
                var k = keys ? keys[i] : i;
212
                result[i] = iteratee.call(ctx, obj[k], k, obj);
213
            }
214
            return result;
215
        },
216
        once: function (func) {
217
            var init = false;
218
            return function () {
219
                if (!!init) {
220
                    return func;
221
                }
222
                init = true;
223
                return func.apply(this, arguments);
224
            }
225
        },
226
        after: function (times, func) {
227
            return function () {
228
                while (--times < 1) {
229
                    return func.apply(this, arguments);
230
                }
231
            }
232
        },
233
        uniqueId: function (prefix) {
234
            var id = ++_._indexAmount + '';
235
            return prefix ? prefix + id : id;
236
        }
237
    };
238
239
    /**
240
        * Constants
241
        */
242
243
    var sin = Math.sin,
244
        cos = Math.cos,
245
        atan2 = Math.atan2,
246
        sqrt = Math.sqrt,
247
        round = Math.round,
248
        abs = Math.abs,
249
        PI = Math.PI,
250
        TWO_PI = PI * 2,
251
        HALF_PI = PI / 2,
252
        pow = Math.pow,
253
        min = Math.min,
254
        max = Math.max;
255
256
    /**
257
        * Localized variables
258
        */
259
260
    var count = 0;
261
    var slice = _.natural.slice;
262
    var perf = ((root.performance && root.performance.now) ? root.performance : Date);
263
    var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
264
    var getLength = function (obj) {
265
        return obj == null ? void 0 : obj['length'];
266
    };
267
    var isArrayLike = function (collection) {
268
        var length = getLength(collection);
269
        return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
270
    };
271
272
    /**
273
        * Cross browser dom events.
274
        */
275
    var dom = {
276
277
        temp: (root.document ? root.document.createElement('div') : {}),
278
279
        hasEventListeners: _.isFunction(root.addEventListener),
280
281
        bind: function (elem, event, func, bool) {
282
            if (this.hasEventListeners) {
283
                elem.addEventListener(event, func, !!bool);
284
            } else {
285
                elem.attachEvent('on' + event, func);
286
            }
287
            return dom;
288
        },
289
290
        unbind: function (elem, event, func, bool) {
291
            if (dom.hasEventListeners) {
292
                elem.removeEventListeners(event, func, !!bool);
293
            } else {
294
                elem.detachEvent('on' + event, func);
295
            }
296
            return dom;
297
        },
298
299
        getRequestAnimationFrame: function () {
300
301
            var lastTime = 0;
302
            var vendors = ['ms', 'moz', 'webkit', 'o'];
303
            var request = root.requestAnimationFrame, cancel;
304
305
            if (!request) {
306
                for (var i = 0; i < vendors.length; i++) {
307
                    request = root[vendors[i] + 'RequestAnimationFrame'] || request;
308
                    cancel = root[vendors[i] + 'CancelAnimationFrame']
309
                        || root[vendors[i] + 'CancelRequestAnimationFrame'] || cancel;
310
                }
311
312
                request = request || function (callback, element) {
313
                    var currTime = new Date().getTime();
314
                    var timeToCall = Math.max(0, 16 - (currTime - lastTime));
315
                    var id = root.setTimeout(function () { callback(currTime + timeToCall); }, timeToCall);
316
                    lastTime = currTime + timeToCall;
317
                    return id;
318
                };
319
                // cancel = cancel || function(id) {
320
                //   clearTimeout(id);
321
                // };
322
            }
323
324
            request.init = _.once(loop);
325
326
            return request;
327
328
        }
329
330
    };
331
332
    /**
333
        * @class
334
        */
335
    var Two = root.Two = function (options) {
336
337
        // Determine what Renderer to use and setup a scene.
338
339
        var params = _.defaults(options || {}, {
340
            fullscreen: false,
341
            width: 640,
342
            height: 480,
343
            type: Two.Types.svg,
344
            autostart: false
345
        });
346
347
        _.each(params, function (v, k) {
348
            if (k === 'fullscreen' || k === 'autostart') {
349
                return;
350
            }
351
            this[k] = v;
352
        }, this);
353
354
        // Specified domElement overrides type declaration only if the element does not support declared renderer type.
355
        if (_.isElement(params.domElement)) {
356
            var tagName = params.domElement.tagName.toLowerCase();
357
            // TODO: Reconsider this if statement's logic.
358
            if (!/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test(this.type + '-' + tagName)) {
359
                this.type = Two.Types[tagName];
360
            }
361
        }
362
363
        this.renderer = new Two[this.type](this);
364
        Two.Utils.setPlaying.call(this, params.autostart);
365
        this.frameCount = 0;
366
367
        if (params.fullscreen) {
368
369
            var fitted = _.bind(fitToWindow, this);
370
            _.extend(document.body.style, {
371
                overflow: 'hidden',
372
                margin: 0,
373
                padding: 0,
374
                top: 0,
375
                left: 0,
376
                right: 0,
377
                bottom: 0,
378
                position: 'fixed'
379
            });
380
            _.extend(this.renderer.domElement.style, {
381
                display: 'block',
382
                top: 0,
383
                left: 0,
384
                right: 0,
385
                bottom: 0,
386
                position: 'fixed'
387
            });
388
            dom.bind(root, 'resize', fitted);
389
            fitted();
390
391
392
        } else if (!_.isElement(params.domElement)) {
393
394
            this.renderer.setSize(params.width, params.height, this.ratio);
395
            this.width = params.width;
396
            this.height = params.height;
397
398
        }
399
400
        this.scene = this.renderer.scene;
401
402
        Two.Instances.push(this);
403
        raf.init();
404
405
    };
406
407
    _.extend(Two, {
408
409
        /**
410
            * Access to root in other files.
411
            */
412
413
        root: root,
414
415
        /**
416
            * Primitive
417
            */
418
419
        Array: root.Float32Array || Array,
420
421
        Types: {
422
            webgl: 'WebGLRenderer',
423
            svg: 'SVGRenderer',
424
            canvas: 'CanvasRenderer'
425
        },
426
427
        Version: 'v0.7.0',
428
429
        Identifier: 'two_',
430
431
        Properties: {
432
            hierarchy: 'hierarchy',
433
            demotion: 'demotion'
434
        },
435
436
        Events: {
437
            play: 'play',
438
            pause: 'pause',
439
            update: 'update',
440
            render: 'render',
441
            resize: 'resize',
442
            change: 'change',
443
            remove: 'remove',
444
            insert: 'insert',
445
            order: 'order',
446
            load: 'load'
447
        },
448
449
        Commands: {
450
            move: 'M',
451
            line: 'L',
452
            curve: 'C',
453
            close: 'Z'
454
        },
455
456
        Resolution: 8,
457
458
        Instances: [],
459
460
        noConflict: function () {
461
            root.Two = previousTwo;
462
            return this;
463
        },
464
465
        uniqueId: function () {
466
            var id = count;
467
            count++;
468
            return id;
469
        },
470
471
        Utils: _.extend(_, {
472
473
            performance: perf,
474
475
            defineProperty: function (property) {
476
477
                var object = this;
478
                var secret = '_' + property;
479
                var flag = '_flag' + property.charAt(0).toUpperCase() + property.slice(1);
480
481
                Object.defineProperty(object, property, {
482
                    enumerable: true,
483
                    get: function () {
484
                        return this[secret];
485
                    },
486
                    set: function (v) {
487
                        this[secret] = v;
488
                        this[flag] = true;
489
                    }
490
                });
491
492
            },
493
494
            /**
495
                * Release an arbitrary class' events from the two.js corpus and recurse
496
                * through its children and or vertices.
497
                */
498
            release: function (obj) {
499
500
                if (!_.isObject(obj)) {
501
                    return;
502
                }
503
504
                if (_.isFunction(obj.unbind)) {
505
                    obj.unbind();
506
                }
507
508
                if (obj.vertices) {
509
                    if (_.isFunction(obj.vertices.unbind)) {
510
                        obj.vertices.unbind();
511
                    }
512
                    _.each(obj.vertices, function (v) {
513
                        if (_.isFunction(v.unbind)) {
514
                            v.unbind();
515
                        }
516
                    });
517
                }
518
519
                if (obj.children) {
520
                    _.each(obj.children, function (obj) {
521
                        Two.Utils.release(obj);
522
                    });
523
                }
524
525
            },
526
527
            xhr: function (path, callback) {
528
529
                var xhr = new XMLHttpRequest();
530
                xhr.open('GET', path);
531
532
                xhr.onreadystatechange = function () {
533
                    if (xhr.readyState === 4 && xhr.status === 200) {
534
                        callback(xhr.responseText);
535
                    }
536
                };
537
538
                xhr.send();
539
                return xhr;
540
541
            },
542
543
            Curve: {
544
545
                CollinearityEpsilon: pow(10, -30),
546
547
                RecursionLimit: 16,
548
549
                CuspLimit: 0,
550
551
                Tolerance: {
552
                    distance: 0.25,
553
                    angle: 0,
554
                    epsilon: 0.01
555
                },
556
557
                // Lookup tables for abscissas and weights with values for n = 2 .. 16.
558
                // As values are symmetric, only store half of them and adapt algorithm
559
                // to factor in symmetry.
560
                abscissas: [
561
                    [0.5773502691896257645091488],
562
                    [0, 0.7745966692414833770358531],
563
                    [0.3399810435848562648026658, 0.8611363115940525752239465],
564
                    [0, 0.5384693101056830910363144, 0.9061798459386639927976269],
565
                    [0.2386191860831969086305017, 0.6612093864662645136613996, 0.9324695142031520278123016],
566
                    [0, 0.4058451513773971669066064, 0.7415311855993944398638648, 0.9491079123427585245261897],
567
                    [0.1834346424956498049394761, 0.5255324099163289858177390, 0.7966664774136267395915539, 0.9602898564975362316835609],
568
                    [0, 0.3242534234038089290385380, 0.6133714327005903973087020, 0.8360311073266357942994298, 0.9681602395076260898355762],
569
                    [0.1488743389816312108848260, 0.4333953941292471907992659, 0.6794095682990244062343274, 0.8650633666889845107320967, 0.9739065285171717200779640],
570
                    [0, 0.2695431559523449723315320, 0.5190961292068118159257257, 0.7301520055740493240934163, 0.8870625997680952990751578, 0.9782286581460569928039380],
571
                    [0.1252334085114689154724414, 0.3678314989981801937526915, 0.5873179542866174472967024, 0.7699026741943046870368938, 0.9041172563704748566784659, 0.9815606342467192506905491],
572
                    [0, 0.2304583159551347940655281, 0.4484927510364468528779129, 0.6423493394403402206439846, 0.8015780907333099127942065, 0.9175983992229779652065478, 0.9841830547185881494728294],
573
                    [0.1080549487073436620662447, 0.3191123689278897604356718, 0.5152486363581540919652907, 0.6872929048116854701480198, 0.8272013150697649931897947, 0.9284348836635735173363911, 0.9862838086968123388415973],
574
                    [0, 0.2011940939974345223006283, 0.3941513470775633698972074, 0.5709721726085388475372267, 0.7244177313601700474161861, 0.8482065834104272162006483, 0.9372733924007059043077589, 0.9879925180204854284895657],
575
                    [0.0950125098376374401853193, 0.2816035507792589132304605, 0.4580167776572273863424194, 0.6178762444026437484466718, 0.7554044083550030338951012, 0.8656312023878317438804679, 0.9445750230732325760779884, 0.9894009349916499325961542]
576
                ],
577
578
                weights: [
579
                    [1],
580
                    [0.8888888888888888888888889, 0.5555555555555555555555556],
581
                    [0.6521451548625461426269361, 0.3478548451374538573730639],
582
                    [0.5688888888888888888888889, 0.4786286704993664680412915, 0.2369268850561890875142640],
583
                    [0.4679139345726910473898703, 0.3607615730481386075698335, 0.1713244923791703450402961],
584
                    [0.4179591836734693877551020, 0.3818300505051189449503698, 0.2797053914892766679014678, 0.1294849661688696932706114],
585
                    [0.3626837833783619829651504, 0.3137066458778872873379622, 0.2223810344533744705443560, 0.1012285362903762591525314],
586
                    [0.3302393550012597631645251, 0.3123470770400028400686304, 0.2606106964029354623187429, 0.1806481606948574040584720, 0.0812743883615744119718922],
587
                    [0.2955242247147528701738930, 0.2692667193099963550912269, 0.2190863625159820439955349, 0.1494513491505805931457763, 0.0666713443086881375935688],
588
                    [0.2729250867779006307144835, 0.2628045445102466621806889, 0.2331937645919904799185237, 0.1862902109277342514260976, 0.1255803694649046246346943, 0.0556685671161736664827537],
589
                    [0.2491470458134027850005624, 0.2334925365383548087608499, 0.2031674267230659217490645, 0.1600783285433462263346525, 0.1069393259953184309602547, 0.0471753363865118271946160],
590
                    [0.2325515532308739101945895, 0.2262831802628972384120902, 0.2078160475368885023125232, 0.1781459807619457382800467, 0.1388735102197872384636018, 0.0921214998377284479144218, 0.0404840047653158795200216],
591
                    [0.2152638534631577901958764, 0.2051984637212956039659241, 0.1855383974779378137417166, 0.1572031671581935345696019, 0.1215185706879031846894148, 0.0801580871597602098056333, 0.0351194603317518630318329],
592
                    [0.2025782419255612728806202, 0.1984314853271115764561183, 0.1861610000155622110268006, 0.1662692058169939335532009, 0.1395706779261543144478048, 0.1071592204671719350118695, 0.0703660474881081247092674, 0.0307532419961172683546284],
593
                    [0.1894506104550684962853967, 0.1826034150449235888667637, 0.1691565193950025381893121, 0.1495959888165767320815017, 0.1246289712555338720524763, 0.0951585116824927848099251, 0.0622535239386478928628438, 0.0271524594117540948517806]
594
                ]
595
596
            },
597
598
            /**
599
                * Account for high dpi rendering.
600
                * http://www.html5rocks.com/en/tutorials/canvas/hidpi/
601
                */
602
603
            devicePixelRatio: root.devicePixelRatio || 1,
604
605
            getBackingStoreRatio: function (ctx) {
606
                return ctx.webkitBackingStorePixelRatio ||
607
                    ctx.mozBackingStorePixelRatio ||
608
                    ctx.msBackingStorePixelRatio ||
609
                    ctx.oBackingStorePixelRatio ||
610
                    ctx.backingStorePixelRatio || 1;
611
            },
612
613
            getRatio: function (ctx) {
614
                return Two.Utils.devicePixelRatio / getBackingStoreRatio(ctx);
615
            },
616
617
            /**
618
                * Properly defer play calling until after all objects
619
                * have been updated with their newest styles.
620
                */
621
            setPlaying: function (b) {
622
623
                this.playing = !!b;
624
                return this;
625
626
            },
627
628
            /**
629
                * Return the computed matrix of a nested object.
630
                * TODO: Optimize traversal.
631
                */
632
            getComputedMatrix: function (object, matrix) {
633
634
                matrix = (matrix && matrix.identity()) || new Two.Matrix();
635
                var parent = object, matrices = [];
636
637
                while (parent && parent._matrix) {
638
                    matrices.push(parent._matrix);
639
                    parent = parent.parent;
640
                }
641
642
                matrices.reverse();
643
644
                _.each(matrices, function (m) {
645
646
                    var e = m.elements;
647
                    matrix.multiply(
648
                        e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9]);
649
650
                });
651
652
                return matrix;
653
654
            },
655
656
            deltaTransformPoint: function (matrix, x, y) {
657
658
                var dx = x * matrix.a + y * matrix.c + 0;
659
                var dy = x * matrix.b + y * matrix.d + 0;
660
661
                return new Two.Vector(dx, dy);
662
663
            },
664
665
            /**
666
                * https://gist.github.com/2052247
667
                */
668
            decomposeMatrix: function (matrix) {
669
670
                // calculate delta transform point
671
                var px = Two.Utils.deltaTransformPoint(matrix, 0, 1);
672
                var py = Two.Utils.deltaTransformPoint(matrix, 1, 0);
673
674
                // calculate skew
675
                var skewX = ((180 / Math.PI) * Math.atan2(px.y, px.x) - 90);
676
                var skewY = ((180 / Math.PI) * Math.atan2(py.y, py.x));
677
678
                return {
679
                    translateX: matrix.e,
680
                    translateY: matrix.f,
681
                    scaleX: Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b),
682
                    scaleY: Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d),
683
                    skewX: skewX,
684
                    skewY: skewY,
685
                    rotation: skewX // rotation is the same as skew x
686
                };
687
688
            },
689
690
            /**
691
                * Walk through item properties and pick the ones of interest.
692
                * Will try to resolve styles applied via CSS
693
                *
694
                * TODO: Reverse calculate `Two.Gradient`s for fill / stroke
695
                * of any given path.
696
                */
697
            applySvgAttributes: function (node, elem) {
698
699
                var attributes = {}, styles = {}, i, key, value, attr;
700
701
                // Not available in non browser environments
702
                if (getComputedStyle) {
703
                    // Convert CSSStyleDeclaration to a normal object
704
                    var computedStyles = getComputedStyle(node);
705
                    i = computedStyles.length;
706
707
                    while (i--) {
708
                        key = computedStyles[i];
709
                        value = computedStyles[key];
710
                        // Gecko returns undefined for unset properties
711
                        // Webkit returns the default value
712
                        if (value !== undefined) {
713
                            styles[key] = value;
714
                        }
715
                    }
716
                }
717
718
                // Convert NodeMap to a normal object
719
                i = node.attributes.length;
720
                while (i--) {
721
                    attr = node.attributes[i];
722
                    attributes[attr.nodeName] = attr.value;
723
                }
724
725
                // Getting the correct opacity is a bit tricky, since SVG path elements don't
726
                // support opacity as an attribute, but you can apply it via CSS.
727
                // So we take the opacity and set (stroke/fill)-opacity to the same value.
728
                if (!_.isUndefined(styles.opacity)) {
729
                    styles['stroke-opacity'] = styles.opacity;
730
                    styles['fill-opacity'] = styles.opacity;
731
                }
732
733
                // Merge attributes and applied styles (attributes take precedence)
734
                _.extend(styles, attributes);
735
736
                // Similarly visibility is influenced by the value of both display and visibility.
737
                // Calculate a unified value here which defaults to `true`.
738
                styles.visible = !(_.isUndefined(styles.display) && styles.display === 'none')
739
                    || (_.isUndefined(styles.visibility) && styles.visibility === 'hidden');
740
741
                // Now iterate the whole thing
742
                for (key in styles) {
743
                    value = styles[key];
744
745
                    switch (key) {
746
                        case 'transform':
747
                            // TODO: Check this out https://github.com/paperjs/paper.js/blob/master/src/svg/SVGImport.js#L313
748
                            if (value === 'none') break;
749
                            var m = node.getCTM ? node.getCTM() : null;
750
751
                            // Might happen when transform string is empty or not valid.
752
                            if (m === null) break;
753
754
                            // // Option 1: edit the underlying matrix and don't force an auto calc.
755
                            // var m = node.getCTM();
756
                            // elem._matrix.manual = true;
757
                            // elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f);
758
759
                            // Option 2: Decompose and infer Two.js related properties.
760
                            var transforms = Two.Utils.decomposeMatrix(node.getCTM());
761
762
                            elem.translation.set(transforms.translateX, transforms.translateY);
763
                            elem.rotation = transforms.rotation;
764
                            // Warning: Two.js elements only support uniform scalars...
765
                            elem.scale = transforms.scaleX;
766
767
                            var x = parseFloat((styles.x + '').replace('px'));
768
                            var y = parseFloat((styles.y + '').replace('px'));
769
770
                            // Override based on attributes.
771
                            if (x) {
772
                                elem.translation.x = x;
773
                            }
774
775
                            if (y) {
776
                                elem.translation.y = y;
777
                            }
778
779
                            break;
780
                        case 'visible':
781
                            elem.visible = value;
782
                            break;
783
                        case 'stroke-linecap':
784
                            elem.cap = value;
785
                            break;
786
                        case 'stroke-linejoin':
787
                            elem.join = value;
788
                            break;
789
                        case 'stroke-miterlimit':
790
                            elem.miter = value;
791
                            break;
792
                        case 'stroke-width':
793
                            elem.linewidth = parseFloat(value);
794
                            break;
795
                        case 'stroke-opacity':
796
                        case 'fill-opacity':
797
                        case 'opacity':
798
                            elem.opacity = parseFloat(value);
799
                            break;
800
                        case 'fill':
801
                        case 'stroke':
802
                            if (/url\(\#.*\)/i.test(value)) {
803
                                elem[key] = this.getById(
804
                                    value.replace(/url\(\#(.*)\)/i, '$1'));
805
                            } else {
806
                                elem[key] = (value === 'none') ? 'transparent' : value;
807
                            }
808
                            break;
809
                        case 'id':
810
                            elem.id = value;
811
                            break;
812
                        case 'class':
813
                            elem.classList = value.split(' ');
814
                            break;
815
                    }
816
                }
817
818
                return elem;
819
820
            },
821
822
            /**
823
                * Read any number of SVG node types and create Two equivalents of them.
824
                */
825
            read: {
826
827
                svg: function () {
828
                    return Two.Utils.read.g.apply(this, arguments);
829
                },
830
831
                g: function (node) {
832
833
                    var group = new Two.Group();
834
835
                    // Switched up order to inherit more specific styles
836
                    Two.Utils.applySvgAttributes.call(this, node, group);
837
838
                    for (var i = 0, l = node.childNodes.length; i < l; i++) {
839
                        var n = node.childNodes[i];
840
                        var tag = n.nodeName;
841
                        if (!tag) return;
842
843
                        var tagName = tag.replace(/svg\:/ig, '').toLowerCase();
844
845
                        if (tagName in Two.Utils.read) {
846
                            var o = Two.Utils.read[tagName].call(group, n);
847
                            group.add(o);
848
                        }
849
                    }
850
851
                    return group;
852
853
                },
854
855
                polygon: function (node, open) {
856
857
                    var points = node.getAttribute('points');
858
859
                    var verts = [];
860
                    points.replace(/(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g, function (match, p1, p2) {
861
                        verts.push(new Two.Anchor(parseFloat(p1), parseFloat(p2)));
862
                    });
863
864
                    var poly = new Two.Path(verts, !open).noStroke();
865
                    poly.fill = 'black';
866
867
                    return Two.Utils.applySvgAttributes.call(this, node, poly);
868
869
                },
870
871
                polyline: function (node) {
872
                    return Two.Utils.read.polygon.call(this, node, true);
873
                },
874
875
                path: function (node) {
876
877
                    var path = node.getAttribute('d');
878
879
                    // Create a Two.Path from the paths.
880
881
                    var coord = new Two.Anchor();
882
                    var control, coords;
883
                    var closed = false, relative = false;
884
                    var commands = path.match(/[a-df-z][^a-df-z]*/ig);
885
                    var last = commands.length - 1;
886
887
                    // Split up polybeziers
888
889
                    _.each(commands.slice(0), function (command, i) {
890
891
                        var type = command[0];
892
                        var lower = type.toLowerCase();
893
                        var items = command.slice(1).trim().split(/[\s,]+|(?=\s?[+\-])/);
894
                        var pre, post, result = [], bin;
895
896
                        if (i <= 0) {
897
                            commands = [];
898
                        }
899
900
                        switch (lower) {
901
                            case 'h':
902
                            case 'v':
903
                                if (items.length > 1) {
904
                                    bin = 1;
905
                                }
906
                                break;
907
                            case 'm':
908
                            case 'l':
909
                            case 't':
910
                                if (items.length > 2) {
911
                                    bin = 2;
912
                                }
913
                                break;
914
                            case 's':
915
                            case 'q':
916
                                if (items.length > 4) {
917
                                    bin = 4;
918
                                }
919
                                break;
920
                            case 'c':
921
                                if (items.length > 6) {
922
                                    bin = 6;
923
                                }
924
                                break;
925
                            case 'a':
926
                                // TODO: Handle Ellipses
927
                                break;
928
                        }
929
930
                        if (bin) {
931
932
                            for (var j = 0, l = items.length, times = 0; j < l; j += bin) {
933
934
                                var ct = type;
935
                                if (times > 0) {
936
937
                                    switch (type) {
938
                                        case 'm':
939
                                            ct = 'l';
940
                                            break;
941
                                        case 'M':
942
                                            ct = 'L';
943
                                            break;
944
                                    }
945
946
                                }
947
948
                                result.push([ct].concat(items.slice(j, j + bin)).join(' '));
949
                                times++;
950
951
                            }
952
953
                            commands = Array.prototype.concat.apply(commands, result);
954
955
                        } else {
956
957
                            commands.push(command);
958
959
                        }
960
961
                    });
962
963
                    // Create the vertices for our Two.Path
964
965
                    var points = [];
966
                    _.each(commands, function (command, i) {
967
968
                        var result, x, y;
969
                        var type = command[0];
970
                        var lower = type.toLowerCase();
971
972
                        coords = command.slice(1).trim();
973
                        coords = coords.replace(/(-?\d+(?:\.\d*)?)[eE]([+\-]?\d+)/g, function (match, n1, n2) {
974
                            return parseFloat(n1) * pow(10, n2);
975
                        });
976
                        coords = coords.split(/[\s,]+|(?=\s?[+\-])/);
977
                        relative = type === lower;
978
979
                        var x1, y1, x2, y2, x3, y3, x4, y4, reflection;
980
981
                        switch (lower) {
982
983
                            case 'z':
984
                                if (i >= last) {
985
                                    closed = true;
986
                                } else {
987
                                    x = coord.x;
988
                                    y = coord.y;
989
                                    result = new Two.Anchor(
990
                                        x, y,
991
                                        undefined, undefined,
992
                                        undefined, undefined,
993
                                        Two.Commands.close
994
                                    );
995
                                }
996
                                break;
997
998
                            case 'm':
999
                            case 'l':
1000
1001
                                x = parseFloat(coords[0]);
1002
                                y = parseFloat(coords[1]);
1003
1004
                                result = new Two.Anchor(
1005
                                    x, y,
1006
                                    undefined, undefined,
1007
                                    undefined, undefined,
1008
                                    lower === 'm' ? Two.Commands.move : Two.Commands.line
1009
                                );
1010
1011
                                if (relative) {
1012
                                    result.addSelf(coord);
1013
                                }
1014
1015
                                // result.controls.left.copy(result);
1016
                                // result.controls.right.copy(result);
1017
1018
                                coord = result;
1019
                                break;
1020
1021
                            case 'h':
1022
                            case 'v':
1023
1024
                                var a = lower === 'h' ? 'x' : 'y';
1025
                                var b = a === 'x' ? 'y' : 'x';
1026
1027
                                result = new Two.Anchor(
1028
                                    undefined, undefined,
1029
                                    undefined, undefined,
1030
                                    undefined, undefined,
1031
                                    Two.Commands.line
1032
                                );
1033
                                result[a] = parseFloat(coords[0]);
1034
                                result[b] = coord[b];
1035
1036
                                if (relative) {
1037
                                    result[a] += coord[a];
1038
                                }
1039
1040
                                // result.controls.left.copy(result);
1041
                                // result.controls.right.copy(result);
1042
1043
                                coord = result;
1044
                                break;
1045
1046
                            case 'c':
1047
                            case 's':
1048
1049
                                x1 = coord.x;
1050
                                y1 = coord.y;
1051
1052
                                if (!control) {
1053
                                    control = new Two.Vector();//.copy(coord);
1054
                                }
1055
1056
                                if (lower === 'c') {
1057
1058
                                    x2 = parseFloat(coords[0]);
1059
                                    y2 = parseFloat(coords[1]);
1060
                                    x3 = parseFloat(coords[2]);
1061
                                    y3 = parseFloat(coords[3]);
1062
                                    x4 = parseFloat(coords[4]);
1063
                                    y4 = parseFloat(coords[5]);
1064
1065
                                } else {
1066
1067
                                    // Calculate reflection control point for proper x2, y2
1068
                                    // inclusion.
1069
1070
                                    reflection = getReflection(coord, control, relative);
1071
1072
                                    x2 = reflection.x;
1073
                                    y2 = reflection.y;
1074
                                    x3 = parseFloat(coords[0]);
1075
                                    y3 = parseFloat(coords[1]);
1076
                                    x4 = parseFloat(coords[2]);
1077
                                    y4 = parseFloat(coords[3]);
1078
1079
                                }
1080
1081
                                if (relative) {
1082
                                    x2 += x1;
1083
                                    y2 += y1;
1084
                                    x3 += x1;
1085
                                    y3 += y1;
1086
                                    x4 += x1;
1087
                                    y4 += y1;
1088
                                }
1089
1090
                                if (!_.isObject(coord.controls)) {
1091
                                    Two.Anchor.AppendCurveProperties(coord);
1092
                                }
1093
1094
                                coord.controls.right.set(x2 - coord.x, y2 - coord.y);
1095
                                result = new Two.Anchor(
1096
                                    x4, y4,
1097
                                    x3 - x4, y3 - y4,
1098
                                    undefined, undefined,
1099
                                    Two.Commands.curve
1100
                                );
1101
1102
                                coord = result;
1103
                                control = result.controls.left;
1104
1105
                                break;
1106
1107
                            case 't':
1108
                            case 'q':
1109
1110
                                x1 = coord.x;
1111
                                y1 = coord.y;
1112
1113
                                if (!control) {
1114
                                    control = new Two.Vector();//.copy(coord);
1115
                                }
1116
1117
                                if (control.isZero()) {
1118
                                    x2 = x1;
1119
                                    y2 = y1;
1120
                                } else {
1121
                                    x2 = control.x;
1122
                                    y1 = control.y;
1123
                                }
1124
1125
                                if (lower === 'q') {
1126
1127
                                    x3 = parseFloat(coords[0]);
1128
                                    y3 = parseFloat(coords[1]);
1129
                                    x4 = parseFloat(coords[1]);
1130
                                    y4 = parseFloat(coords[2]);
1131
1132
                                } else {
1133
1134
                                    reflection = getReflection(coord, control, relative);
1135
1136
                                    x3 = reflection.x;
1137
                                    y3 = reflection.y;
1138
                                    x4 = parseFloat(coords[0]);
1139
                                    y4 = parseFloat(coords[1]);
1140
1141
                                }
1142
1143
                                if (relative) {
1144
                                    x2 += x1;
1145
                                    y2 += y1;
1146
                                    x3 += x1;
1147
                                    y3 += y1;
1148
                                    x4 += x1;
1149
                                    y4 += y1;
1150
                                }
1151
1152
                                if (!_.isObject(coord.controls)) {
1153
                                    Two.Anchor.AppendCurveProperties(coord);
1154
                                }
1155
1156
                                coord.controls.right.set(x2 - coord.x, y2 - coord.y);
1157
                                result = new Two.Anchor(
1158
                                    x4, y4,
1159
                                    x3 - x4, y3 - y4,
1160
                                    undefined, undefined,
1161
                                    Two.Commands.curve
1162
                                );
1163
1164
                                coord = result;
1165
                                control = result.controls.left;
1166
1167
                                break;
1168
1169
                            case 'a':
1170
1171
                                // throw new Two.Utils.Error('not yet able to interpret Elliptical Arcs.');
1172
                                x1 = coord.x;
1173
                                y1 = coord.y;
1174
1175
                                var rx = parseFloat(coords[0]);
1176
                                var ry = parseFloat(coords[1]);
1177
                                var xAxisRotation = parseFloat(coords[2]) * Math.PI / 180;
1178
                                var largeArcFlag = parseFloat(coords[3]);
1179
                                var sweepFlag = parseFloat(coords[4]);
1180
1181
                                x4 = parseFloat(coords[5]);
1182
                                y4 = parseFloat(coords[6]);
1183
1184
                                if (relative) {
1185
                                    x4 += x1;
1186
                                    y4 += y1;
1187
                                }
1188
1189
                                // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
1190
1191
                                // Calculate midpoint mx my
1192
                                var mx = (x4 - x1) / 2;
1193
                                var my = (y4 - y1) / 2;
1194
1195
                                // Calculate x1' y1' F.6.5.1
1196
                                var _x = mx * Math.cos(xAxisRotation) + my * Math.sin(xAxisRotation);
1197
                                var _y = - mx * Math.sin(xAxisRotation) + my * Math.cos(xAxisRotation);
1198
1199
                                var rx2 = rx * rx;
1200
                                var ry2 = ry * ry;
1201
                                var _x2 = _x * _x;
1202
                                var _y2 = _y * _y;
1203
1204
                                // adjust radii
1205
                                var l = _x2 / rx2 + _y2 / ry2;
1206
                                if (l > 1) {
1207
                                    rx *= Math.sqrt(l);
1208
                                    ry *= Math.sqrt(l);
1209
                                }
1210
1211
                                var amp = Math.sqrt((rx2 * ry2 - rx2 * _y2 - ry2 * _x2) / (rx2 * _y2 + ry2 * _x2));
1212
1213
                                if (_.isNaN(amp)) {
1214
                                    amp = 0;
1215
                                } else if (largeArcFlag != sweepFlag && amp > 0) {
1216
                                    amp *= -1;
1217
                                }
1218
1219
                                // Calculate cx' cy' F.6.5.2
1220
                                var _cx = amp * rx * _y / ry;
1221
                                var _cy = - amp * ry * _x / rx;
1222
1223
                                // Calculate cx cy F.6.5.3
1224
                                var cx = _cx * Math.cos(xAxisRotation) - _cy * Math.sin(xAxisRotation) + (x1 + x4) / 2;
1225
                                var cy = _cx * Math.sin(xAxisRotation) + _cy * Math.cos(xAxisRotation) + (y1 + y4) / 2;
1226
1227
                                // vector magnitude
1228
                                var m = function (v) { return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2)); }
1229
                                // ratio between two vectors
1230
                                var r = function (u, v) { return (u[0] * v[0] + u[1] * v[1]) / (m(u) * m(v)) }
1231
                                // angle between two vectors
1232
                                var a = function (u, v) { return (u[0] * v[1] < u[1] * v[0] ? - 1 : 1) * Math.acos(r(u, v)); }
1233
1234
                                // Calculate theta1 and delta theta F.6.5.4 + F.6.5.5
1235
                                var t1 = a([1, 0], [(_x - _cx) / rx, (_y - _cy) / ry]);
1236
                                var u = [(_x - _cx) / rx, (_y - _cy) / ry];
1237
                                var v = [(- _x - _cx) / rx, (- _y - _cy) / ry];
1238
                                var dt = a(u, v);
1239
1240
                                if (r(u, v) <= -1) dt = Math.PI;
1241
                                if (r(u, v) >= 1) dt = 0;
1242
1243
                                // F.6.5.6
1244
                                if (largeArcFlag) {
1245
                                    dt = mod(dt, Math.PI * 2);
1246
                                }
1247
1248
                                if (sweepFlag && dt > 0) {
1249
                                    dt -= Math.PI * 2;
1250
                                }
1251
1252
                                var length = Two.Resolution;
1253
1254
                                // Save a projection of our rotation and translation to apply
1255
                                // to the set of points.
1256
                                var projection = new Two.Matrix()
1257
                                    .translate(cx, cy)
1258
                                    .rotate(xAxisRotation);
1259
1260
                                // Create a resulting array of Two.Anchor's to export to the
1261
                                // the path.
1262
                                result = _.map(_.range(length), function (i) {
1263
                                    var pct = 1 - (i / (length - 1));
1264
                                    var theta = pct * dt + t1;
1265
                                    var x = rx * Math.cos(theta);
1266
                                    var y = ry * Math.sin(theta);
1267
                                    var projected = projection.multiply(x, y, 1);
1268
                                    return new Two.Anchor(projected.x, projected.y, false, false, false, false, Two.Commands.line);;
1269
                                });
1270
1271
                                result.push(new Two.Anchor(x4, y4, false, false, false, false, Two.Commands.line));
1272
1273
                                coord = result[result.length - 1];
1274
                                control = coord.controls.left;
1275
1276
                                break;
1277
1278
                        }
1279
1280
                        if (result) {
1281
                            if (_.isArray(result)) {
1282
                                points = points.concat(result);
1283
                            } else {
1284
                                points.push(result);
1285
                            }
1286
                        }
1287
1288
                    });
1289
1290
                    if (points.length <= 1) {
1291
                        return;
1292
                    }
1293
1294
                    var path = new Two.Path(points, closed, undefined, true).noStroke();
1295
                    path.fill = 'black';
1296
1297
                    var rect = path.getBoundingClientRect(true);
1298
1299
                    // Center objects to stay consistent
1300
                    // with the rest of the Two.js API.
1301
                    rect.centroid = {
1302
                        x: rect.left + rect.width / 2,
1303
                        y: rect.top + rect.height / 2
1304
                    };
1305
1306
                    _.each(path.vertices, function (v) {
1307
                        v.subSelf(rect.centroid);
1308
                    });
1309
1310
                    path.translation.addSelf(rect.centroid);
1311
1312
                    return Two.Utils.applySvgAttributes.call(this, node, path);
1313
1314
                },
1315
1316
                circle: function (node) {
1317
1318
                    var x = parseFloat(node.getAttribute('cx'));
1319
                    var y = parseFloat(node.getAttribute('cy'));
1320
                    var r = parseFloat(node.getAttribute('r'));
1321
1322
                    var circle = new Two.Circle(x, y, r).noStroke();
1323
                    circle.fill = 'black';
1324
1325
                    return Two.Utils.applySvgAttributes.call(this, node, circle);
1326
1327
                },
1328
1329
                ellipse: function (node) {
1330
1331
                    var x = parseFloat(node.getAttribute('cx'));
1332
                    var y = parseFloat(node.getAttribute('cy'));
1333
                    var width = parseFloat(node.getAttribute('rx'));
1334
                    var height = parseFloat(node.getAttribute('ry'));
1335
1336
                    var ellipse = new Two.Ellipse(x, y, width, height).noStroke();
1337
                    ellipse.fill = 'black';
1338
1339
                    return Two.Utils.applySvgAttributes.call(this, node, ellipse);
1340
1341
                },
1342
1343
                rect: function (node) {
1344
1345
                    var x = parseFloat(node.getAttribute('x')) || 0;
1346
                    var y = parseFloat(node.getAttribute('y')) || 0;
1347
                    var width = parseFloat(node.getAttribute('width'));
1348
                    var height = parseFloat(node.getAttribute('height'));
1349
1350
                    var w2 = width / 2;
1351
                    var h2 = height / 2;
1352
1353
                    var rect = new Two.Rectangle(x + w2, y + h2, width, height)
1354
                        .noStroke();
1355
                    rect.fill = 'black';
1356
1357
                    return Two.Utils.applySvgAttributes.call(this, node, rect);
1358
1359
                },
1360
1361
                line: function (node) {
1362
1363
                    var x1 = parseFloat(node.getAttribute('x1'));
1364
                    var y1 = parseFloat(node.getAttribute('y1'));
1365
                    var x2 = parseFloat(node.getAttribute('x2'));
1366
                    var y2 = parseFloat(node.getAttribute('y2'));
1367
1368
                    var line = new Two.Line(x1, y1, x2, y2).noFill();
1369
1370
                    return Two.Utils.applySvgAttributes.call(this, node, line);
1371
1372
                },
1373
1374
                lineargradient: function (node) {
1375
1376
                    var x1 = parseFloat(node.getAttribute('x1'));
1377
                    var y1 = parseFloat(node.getAttribute('y1'));
1378
                    var x2 = parseFloat(node.getAttribute('x2'));
1379
                    var y2 = parseFloat(node.getAttribute('y2'));
1380
1381
                    var ox = (x2 + x1) / 2;
1382
                    var oy = (y2 + y1) / 2;
1383
1384
                    var stops = [];
1385
                    for (var i = 0; i < node.children.length; i++) {
1386
1387
                        var child = node.children[i];
1388
1389
                        var offset = parseFloat(child.getAttribute('offset'));
1390
                        var color = child.getAttribute('stop-color');
1391
                        var opacity = child.getAttribute('stop-opacity');
1392
                        var style = child.getAttribute('style');
1393
1394
                        if (_.isNull(color)) {
1395
                            var matches = style ? style.match(/stop\-color\:\s?([\#a-fA-F0-9]*)/) : false;
1396
                            color = matches && matches.length > 1 ? matches[1] : undefined;
1397
                        }
1398
1399
                        if (_.isNull(opacity)) {
1400
                            var matches = style ? style.match(/stop\-opacity\:\s?([0-9\.\-]*)/) : false;
1401
                            opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;
1402
                        }
1403
1404
                        stops.push(new Two.Gradient.Stop(offset, color, opacity));
1405
1406
                    }
1407
1408
                    var gradient = new Two.LinearGradient(x1 - ox, y1 - oy, x2 - ox,
1409
                        y2 - oy, stops);
1410
1411
                    return Two.Utils.applySvgAttributes.call(this, node, gradient);
1412
1413
                },
1414
1415
                radialgradient: function (node) {
1416
1417
                    var cx = parseFloat(node.getAttribute('cx')) || 0;
1418
                    var cy = parseFloat(node.getAttribute('cy')) || 0;
1419
                    var r = parseFloat(node.getAttribute('r'));
1420
1421
                    var fx = parseFloat(node.getAttribute('fx'));
1422
                    var fy = parseFloat(node.getAttribute('fy'));
1423
1424
                    if (_.isNaN(fx)) {
1425
                        fx = cx;
1426
                    }
1427
1428
                    if (_.isNaN(fy)) {
1429
                        fy = cy;
1430
                    }
1431
1432
                    var ox = Math.abs(cx + fx) / 2;
1433
                    var oy = Math.abs(cy + fy) / 2;
1434
1435
                    var stops = [];
1436
                    for (var i = 0; i < node.children.length; i++) {
1437
1438
                        var child = node.children[i];
1439
1440
                        var offset = parseFloat(child.getAttribute('offset'));
1441
                        var color = child.getAttribute('stop-color');
1442
                        var opacity = child.getAttribute('stop-opacity');
1443
                        var style = child.getAttribute('style');
1444
1445
                        if (_.isNull(color)) {
1446
                            var matches = style ? style.match(/stop\-color\:\s?([\#a-fA-F0-9]*)/) : false;
1447
                            color = matches && matches.length > 1 ? matches[1] : undefined;
1448
                        }
1449
1450
                        if (_.isNull(opacity)) {
1451
                            var matches = style ? style.match(/stop\-opacity\:\s?([0-9\.\-]*)/) : false;
1452
                            opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;
1453
                        }
1454
1455
                        stops.push(new Two.Gradient.Stop(offset, color, opacity));
1456
1457
                    }
1458
1459
                    var gradient = new Two.RadialGradient(cx - ox, cy - oy, r,
1460
                        stops, fx - ox, fy - oy);
1461
1462
                    return Two.Utils.applySvgAttributes.call(this, node, gradient);
1463
1464
                }
1465
1466
            },
1467
1468
            /**
1469
                * Given 2 points (a, b) and corresponding control point for each
1470
                * return an array of points that represent points plotted along
1471
                * the curve. Number points determined by limit.
1472
                */
1473
            subdivide: function (x1, y1, x2, y2, x3, y3, x4, y4, limit) {
1474
1475
                limit = limit || Two.Utils.Curve.RecursionLimit;
1476
                var amount = limit + 1;
1477
1478
                // TODO: Issue 73
1479
                // Don't recurse if the end points are identical
1480
                if (x1 === x4 && y1 === y4) {
1481
                    return [new Two.Anchor(x4, y4)];
1482
                }
1483
1484
                return _.map(_.range(0, amount), function (i) {
1485
1486
                    var t = i / amount;
1487
                    var x = getPointOnCubicBezier(t, x1, x2, x3, x4);
1488
                    var y = getPointOnCubicBezier(t, y1, y2, y3, y4);
1489
1490
                    return new Two.Anchor(x, y);
1491
1492
                });
1493
1494
            },
1495
1496
            getPointOnCubicBezier: function (t, a, b, c, d) {
1497
                var k = 1 - t;
1498
                return (k * k * k * a) + (3 * k * k * t * b) + (3 * k * t * t * c) +
1499
                    (t * t * t * d);
1500
            },
1501
1502
            /**
1503
                * Given 2 points (a, b) and corresponding control point for each
1504
                * return a float that represents the length of the curve using
1505
                * Gauss-Legendre algorithm. Limit iterations of calculation by `limit`.
1506
                */
1507
            getCurveLength: function (x1, y1, x2, y2, x3, y3, x4, y4, limit) {
1508
1509
                // TODO: Better / fuzzier equality check
1510
                // Linear calculation
1511
                if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) {
1512
                    var dx = x4 - x1;
1513
                    var dy = y4 - y1;
1514
                    return sqrt(dx * dx + dy * dy);
1515
                }
1516
1517
                // Calculate the coefficients of a Bezier derivative.
1518
                var ax = 9 * (x2 - x3) + 3 * (x4 - x1),
1519
                    bx = 6 * (x1 + x3) - 12 * x2,
1520
                    cx = 3 * (x2 - x1),
1521
1522
                    ay = 9 * (y2 - y3) + 3 * (y4 - y1),
1523
                    by = 6 * (y1 + y3) - 12 * y2,
1524
                    cy = 3 * (y2 - y1);
1525
1526
                var integrand = function (t) {
1527
                    // Calculate quadratic equations of derivatives for x and y
1528
                    var dx = (ax * t + bx) * t + cx,
1529
                        dy = (ay * t + by) * t + cy;
1530
                    return sqrt(dx * dx + dy * dy);
1531
                };
1532
1533
                return integrate(
1534
                    integrand, 0, 1, limit || Two.Utils.Curve.RecursionLimit
1535
                );
1536
1537
            },
1538
1539
            /**
1540
                * Integration for `getCurveLength` calculations. Referenced from
1541
                * Paper.js: https://github.com/paperjs/paper.js/blob/master/src/util/Numerical.js#L101
1542
                */
1543
            integrate: function (f, a, b, n) {
1544
                var x = Two.Utils.Curve.abscissas[n - 2],
1545
                    w = Two.Utils.Curve.weights[n - 2],
1546
                    A = 0.5 * (b - a),
1547
                    B = A + a,
1548
                    i = 0,
1549
                    m = (n + 1) >> 1,
1550
                    sum = n & 1 ? w[i++] * f(B) : 0; // Handle odd n
1551
                while (i < m) {
1552
                    var Ax = A * x[i];
1553
                    sum += w[i++] * (f(B + Ax) + f(B - Ax));
1554
                }
1555
                return A * sum;
1556
            },
1557
1558
            /**
1559
                * Creates a set of points that have u, v values for anchor positions
1560
                */
1561
            getCurveFromPoints: function (points, closed) {
1562
1563
                var l = points.length, last = l - 1;
1564
1565
                for (var i = 0; i < l; i++) {
1566
1567
                    var point = points[i];
1568
1569
                    if (!_.isObject(point.controls)) {
1570
                        Two.Anchor.AppendCurveProperties(point);
1571
                    }
1572
1573
                    var prev = closed ? mod(i - 1, l) : max(i - 1, 0);
1574
                    var next = closed ? mod(i + 1, l) : min(i + 1, last);
1575
1576
                    var a = points[prev];
1577
                    var b = point;
1578
                    var c = points[next];
1579
                    getControlPoints(a, b, c);
1580
1581
                    b._command = i === 0 ? Two.Commands.move : Two.Commands.curve;
1582
1583
                    b.controls.left.x = _.isNumber(b.controls.left.x) ? b.controls.left.x : b.x;
1584
                    b.controls.left.y = _.isNumber(b.controls.left.y) ? b.controls.left.y : b.y;
1585
1586
                    b.controls.right.x = _.isNumber(b.controls.right.x) ? b.controls.right.x : b.x;
1587
                    b.controls.right.y = _.isNumber(b.controls.right.y) ? b.controls.right.y : b.y;
1588
1589
                }
1590
1591
            },
1592
1593
            /**
1594
                * Given three coordinates return the control points for the middle, b,
1595
                * vertex.
1596
                */
1597
            getControlPoints: function (a, b, c) {
1598
1599
                var a1 = angleBetween(a, b);
1600
                var a2 = angleBetween(c, b);
1601
1602
                var d1 = distanceBetween(a, b);
1603
                var d2 = distanceBetween(c, b);
1604
1605
                var mid = (a1 + a2) / 2;
1606
1607
                // So we know which angle corresponds to which side.
1608
1609
                b.u = _.isObject(b.controls.left) ? b.controls.left : new Two.Vector(0, 0);
1610
                b.v = _.isObject(b.controls.right) ? b.controls.right : new Two.Vector(0, 0);
1611
1612
                // TODO: Issue 73
1613
                if (d1 < 0.0001 || d2 < 0.0001) {
1614
                    if (!b._relative) {
1615
                        b.controls.left.copy(b);
1616
                        b.controls.right.copy(b);
1617
                    }
1618
                    return b;
1619
                }
1620
1621
                d1 *= 0.33; // Why 0.33?
1622
                d2 *= 0.33;
1623
1624
                if (a2 < a1) {
1625
                    mid += HALF_PI;
1626
                } else {
1627
                    mid -= HALF_PI;
1628
                }
1629
1630
                b.controls.left.x = cos(mid) * d1;
1631
                b.controls.left.y = sin(mid) * d1;
1632
1633
                mid -= PI;
1634
1635
                b.controls.right.x = cos(mid) * d2;
1636
                b.controls.right.y = sin(mid) * d2;
1637
1638
                if (!b._relative) {
1639
                    b.controls.left.x += b.x;
1640
                    b.controls.left.y += b.y;
1641
                    b.controls.right.x += b.x;
1642
                    b.controls.right.y += b.y;
1643
                }
1644
1645
                return b;
1646
1647
            },
1648
1649
            /**
1650
                * Get the reflection of a point "b" about point "a". Where "a" is in
1651
                * absolute space and "b" is relative to "a".
1652
                *
1653
                * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
1654
                */
1655
            getReflection: function (a, b, relative) {
1656
1657
                return new Two.Vector(
1658
                    2 * a.x - (b.x + a.x) - (relative ? a.x : 0),
1659
                    2 * a.y - (b.y + a.y) - (relative ? a.y : 0)
1660
                );
1661
1662
            },
1663
1664
            getAnchorsFromArcData: function (center, xAxisRotation, rx, ry, ts, td, ccw) {
1665
1666
                var matrix = new Two.Matrix()
1667
                    .translate(center.x, center.y)
1668
                    .rotate(xAxisRotation);
1669
1670
                var l = Two.Resolution;
1671
1672
                return _.map(_.range(l), function (i) {
1673
1674
                    var pct = (i + 1) / l;
1675
                    if (!!ccw) {
1676
                        pct = 1 - pct;
1677
                    }
1678
1679
                    var theta = pct * td + ts;
1680
                    var x = rx * Math.cos(theta);
1681
                    var y = ry * Math.sin(theta);
1682
1683
                    // x += center.x;
1684
                    // y += center.y;
1685
1686
                    var anchor = new Two.Anchor(x, y);
1687
                    Two.Anchor.AppendCurveProperties(anchor);
1688
                    anchor.command = Two.Commands.line;
1689
1690
                    // TODO: Calculate control points here...
1691
1692
                    return anchor;
1693
1694
                });
1695
1696
            },
1697
1698
            ratioBetween: function (A, B) {
1699
1700
                return (A.x * B.x + A.y * B.y) / (A.length() * B.length());
1701
1702
            },
1703
1704
            angleBetween: function (A, B) {
1705
1706
                var dx, dy;
1707
1708
                if (arguments.length >= 4) {
1709
1710
                    dx = arguments[0] - arguments[2];
1711
                    dy = arguments[1] - arguments[3];
1712
1713
                    return atan2(dy, dx);
1714
1715
                }
1716
1717
                dx = A.x - B.x;
1718
                dy = A.y - B.y;
1719
1720
                return atan2(dy, dx);
1721
1722
            },
1723
1724
            distanceBetweenSquared: function (p1, p2) {
1725
1726
                var dx = p1.x - p2.x;
1727
                var dy = p1.y - p2.y;
1728
1729
                return dx * dx + dy * dy;
1730
1731
            },
1732
1733
            distanceBetween: function (p1, p2) {
1734
1735
                return sqrt(distanceBetweenSquared(p1, p2));
1736
1737
            },
1738
1739
            lerp: function (a, b, t) {
1740
                return t * (b - a) + a;
1741
            },
1742
1743
            // A pretty fast toFixed(3) alternative
1744
            // See http://jsperf.com/parsefloat-tofixed-vs-math-round/18
1745
            toFixed: function (v) {
1746
                return Math.floor(v * 1000) / 1000;
1747
            },
1748
1749
            mod: function (v, l) {
1750
1751
                while (v < 0) {
1752
                    v += l;
1753
                }
1754
1755
                return v % l;
1756
1757
            },
1758
1759
            /**
1760
                * Array like collection that triggers inserted and removed events
1761
                * removed : pop / shift / splice
1762
                * inserted : push / unshift / splice (with > 2 arguments)
1763
                */
1764
            Collection: function () {
1765
1766
                Array.call(this);
1767
1768
                if (arguments.length > 1) {
1769
                    Array.prototype.push.apply(this, arguments);
1770
                } else if (arguments[0] && Array.isArray(arguments[0])) {
1771
                    Array.prototype.push.apply(this, arguments[0]);
1772
                }
1773
1774
            },
1775
1776
            // Custom Error Throwing for Two.js
1777
1778
            Error: function (message) {
1779
                this.name = 'two.js';
1780
                this.message = message;
1781
            },
1782
1783
            Events: {
1784
1785
                on: function (name, callback) {
1786
1787
                    this._events || (this._events = {});
1788
                    var list = this._events[name] || (this._events[name] = []);
1789
1790
                    list.push(callback);
1791
1792
                    return this;
1793
1794
                },
1795
1796
                off: function (name, callback) {
1797
1798
                    if (!this._events) {
1799
                        return this;
1800
                    }
1801
                    if (!name && !callback) {
1802
                        this._events = {};
1803
                        return this;
1804
                    }
1805
1806
                    var names = name ? [name] : _.keys(this._events);
1807
                    for (var i = 0, l = names.length; i < l; i++) {
1808
1809
                        var name = names[i];
1810
                        var list = this._events[name];
1811
1812
                        if (!!list) {
1813
                            var events = [];
1814
                            if (callback) {
1815
                                for (var j = 0, k = list.length; j < k; j++) {
1816
                                    var ev = list[j];
1817
                                    ev = ev.callback ? ev.callback : ev;
1818
                                    if (callback && callback !== ev) {
1819
                                        events.push(ev);
1820
                                    }
1821
                                }
1822
                            }
1823
                            this._events[name] = events;
1824
                        }
1825
                    }
1826
1827
                    return this;
1828
                },
1829
1830
                trigger: function (name) {
1831
                    if (!this._events) return this;
1832
                    var args = slice.call(arguments, 1);
1833
                    var events = this._events[name];
1834
                    if (events) trigger(this, events, args);
1835
                    return this;
1836
                },
1837
1838
                listen: function (obj, name, callback) {
1839
1840
                    var bound = this;
1841
1842
                    if (obj) {
1843
                        var ev = function () {
1844
                            callback.apply(bound, arguments);
1845
                        };
1846
1847
                        // add references about the object that assigned this listener
1848
                        ev.obj = obj;
1849
                        ev.name = name;
1850
                        ev.callback = callback;
1851
1852
                        obj.on(name, ev);
1853
                    }
1854
1855
                    return this;
1856
1857
                },
1858
1859
                ignore: function (obj, name, callback) {
1860
1861
                    obj.off(name, callback);
1862
1863
                    return this;
1864
1865
                }
1866
1867
            }
1868
1869
        })
1870
1871
    });
1872
1873
    Two.Utils.Events.bind = Two.Utils.Events.on;
1874
    Two.Utils.Events.unbind = Two.Utils.Events.off;
1875
1876
    var trigger = function (obj, events, args) {
1877
        var method;
1878
        switch (args.length) {
1879
            case 0:
1880
                method = function (i) {
1881
                    events[i].call(obj, args[0]);
1882
                };
1883
                break;
1884
            case 1:
1885
                method = function (i) {
1886
                    events[i].call(obj, args[0], args[1]);
1887
                };
1888
                break;
1889
            case 2:
1890
                method = function (i) {
1891
                    events[i].call(obj, args[0], args[1], args[2]);
1892
                };
1893
                break;
1894
            case 3:
1895
                method = function (i) {
1896
                    events[i].call(obj, args[0], args[1], args[2], args[3]);
1897
                };
1898
                break;
1899
            default:
1900
                method = function (i) {
1901
                    events[i].apply(obj, args);
1902
                };
1903
        }
1904
        for (var i = 0; i < events.length; i++) {
1905
            method(i);
1906
        }
1907
    };
1908
1909
    Two.Utils.Error.prototype = new Error();
1910
    Two.Utils.Error.prototype.constructor = Two.Utils.Error;
1911
1912
    Two.Utils.Collection.prototype = new Array();
1913
    Two.Utils.Collection.prototype.constructor = Two.Utils.Collection;
1914
1915
    _.extend(Two.Utils.Collection.prototype, Two.Utils.Events, {
1916
1917
        pop: function () {
1918
            var popped = Array.prototype.pop.apply(this, arguments);
1919
            this.trigger(Two.Events.remove, [popped]);
1920
            return popped;
1921
        },
1922
1923
        shift: function () {
1924
            var shifted = Array.prototype.shift.apply(this, arguments);
1925
            this.trigger(Two.Events.remove, [shifted]);
1926
            return shifted;
1927
        },
1928
1929
        push: function () {
1930
            var pushed = Array.prototype.push.apply(this, arguments);
1931
            this.trigger(Two.Events.insert, arguments);
1932
            return pushed;
1933
        },
1934
1935
        unshift: function () {
1936
            var unshifted = Array.prototype.unshift.apply(this, arguments);
1937
            this.trigger(Two.Events.insert, arguments);
1938
            return unshifted;
1939
        },
1940
1941
        splice: function () {
1942
            var spliced = Array.prototype.splice.apply(this, arguments);
1943
            var inserted;
1944
1945
            this.trigger(Two.Events.remove, spliced);
1946
1947
            if (arguments.length > 2) {
1948
                inserted = this.slice(arguments[0], arguments[0] + arguments.length - 2);
1949
                this.trigger(Two.Events.insert, inserted);
1950
                this.trigger(Two.Events.order);
1951
            }
1952
            return spliced;
1953
        },
1954
1955
        sort: function () {
1956
            Array.prototype.sort.apply(this, arguments);
1957
            this.trigger(Two.Events.order);
1958
            return this;
1959
        },
1960
1961
        reverse: function () {
1962
            Array.prototype.reverse.apply(this, arguments);
1963
            this.trigger(Two.Events.order);
1964
            return this;
1965
        }
1966
1967
    });
1968
1969
    // Localize utils
1970
1971
    var distanceBetween = Two.Utils.distanceBetween,
1972
        getAnchorsFromArcData = Two.Utils.getAnchorsFromArcData,
1973
        distanceBetweenSquared = Two.Utils.distanceBetweenSquared,
1974
        ratioBetween = Two.Utils.ratioBetween,
1975
        angleBetween = Two.Utils.angleBetween,
1976
        getControlPoints = Two.Utils.getControlPoints,
1977
        getCurveFromPoints = Two.Utils.getCurveFromPoints,
1978
        solveSegmentIntersection = Two.Utils.solveSegmentIntersection,
1979
        decoupleShapes = Two.Utils.decoupleShapes,
1980
        mod = Two.Utils.mod,
1981
        getBackingStoreRatio = Two.Utils.getBackingStoreRatio,
1982
        getPointOnCubicBezier = Two.Utils.getPointOnCubicBezier,
1983
        getCurveLength = Two.Utils.getCurveLength,
1984
        integrate = Two.Utils.integrate,
1985
        getReflection = Two.Utils.getReflection;
1986
1987
    _.extend(Two.prototype, Two.Utils.Events, {
1988
1989
        appendTo: function (elem) {
1990
1991
            elem.appendChild(this.renderer.domElement);
1992
            return this;
1993
1994
        },
1995
1996
        play: function () {
1997
1998
            Two.Utils.setPlaying.call(this, true);
1999
            return this.trigger(Two.Events.play);
2000
2001
        },
2002
2003
        pause: function () {
2004
2005
            this.playing = false;
2006
            return this.trigger(Two.Events.pause);
2007
2008
        },
2009
2010
        /**
2011
            * Update positions and calculations in one pass before rendering.
2012
            */
2013
        update: function () {
2014
2015
            var animated = !!this._lastFrame;
2016
            var now = perf.now();
2017
2018
            this.frameCount++;
2019
2020
            if (animated) {
2021
                this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3));
2022
            }
2023
            this._lastFrame = now;
2024
2025
            var width = this.width;
2026
            var height = this.height;
2027
            var renderer = this.renderer;
2028
2029
            // Update width / height for the renderer
2030
            if (width !== renderer.width || height !== renderer.height) {
2031
                renderer.setSize(width, height, this.ratio);
2032
            }
2033
2034
            this.trigger(Two.Events.update, this.frameCount, this.timeDelta);
2035
2036
            return this.render();
2037
2038
        },
2039
2040
        /**
2041
            * Render all drawable - visible objects of the scene.
2042
            */
2043
        render: function () {
2044
2045
            this.renderer.render();
2046
            return this.trigger(Two.Events.render, this.frameCount);
2047
2048
        },
2049
2050
        /**
2051
            * Convenience Methods
2052
            */
2053
2054
        add: function (o) {
2055
2056
            var objects = o;
2057
            if (!(objects instanceof Array)) {
2058
                objects = _.toArray(arguments);
2059
            }
2060
2061
            this.scene.add(objects);
2062
            return this;
2063
2064
        },
2065
2066
        remove: function (o) {
2067
2068
            var objects = o;
2069
            if (!(objects instanceof Array)) {
2070
                objects = _.toArray(arguments);
2071
            }
2072
2073
            this.scene.remove(objects);
2074
2075
            return this;
2076
2077
        },
2078
2079
        clear: function () {
2080
2081
            this.scene.remove(_.toArray(this.scene.children));
2082
            return this;
2083
2084
        },
2085
2086
        makeLine: function (x1, y1, x2, y2) {
2087
2088
            var line = new Two.Line(x1, y1, x2, y2);
2089
            this.scene.add(line);
2090
2091
            return line;
2092
2093
        },
2094
2095
        makeRectangle: function (x, y, width, height) {
2096
2097
            var rect = new Two.Rectangle(x, y, width, height);
2098
            this.scene.add(rect);
2099
2100
            return rect;
2101
2102
        },
2103
2104
        makeRoundedRectangle: function (x, y, width, height, sides) {
2105
2106
            var rect = new Two.RoundedRectangle(x, y, width, height, sides);
2107
            this.scene.add(rect);
2108
2109
            return rect;
2110
2111
        },
2112
2113
        makeCircle: function (ox, oy, r) {
2114
2115
            var circle = new Two.Circle(ox, oy, r);
2116
            this.scene.add(circle);
2117
2118
            return circle;
2119
2120
        },
2121
2122
        makeEllipse: function (ox, oy, rx, ry) {
2123
2124
            var ellipse = new Two.Ellipse(ox, oy, rx, ry);
2125
            this.scene.add(ellipse);
2126
2127
            return ellipse;
2128
2129
        },
2130
2131
        makeStar: function (ox, oy, or, ir, sides) {
2132
2133
            var star = new Two.Star(ox, oy, or, ir, sides);
2134
            this.scene.add(star);
2135
2136
            return star;
2137
2138
        },
2139
2140
        makeCurve: function (p) {
2141
2142
            var l = arguments.length, points = p;
2143
            if (!_.isArray(p)) {
2144
                points = [];
2145
                for (var i = 0; i < l; i += 2) {
2146
                    var x = arguments[i];
2147
                    if (!_.isNumber(x)) {
2148
                        break;
2149
                    }
2150
                    var y = arguments[i + 1];
2151
                    points.push(new Two.Anchor(x, y));
2152
                }
2153
            }
2154
2155
            var last = arguments[l - 1];
2156
            var curve = new Two.Path(points, !(_.isBoolean(last) ? last : undefined), true);
2157
            var rect = curve.getBoundingClientRect();
2158
            curve.center().translation
2159
                .set(rect.left + rect.width / 2, rect.top + rect.height / 2);
2160
2161
            this.scene.add(curve);
2162
2163
            return curve;
2164
2165
        },
2166
2167
        makePolygon: function (ox, oy, r, sides) {
2168
2169
            var poly = new Two.Polygon(ox, oy, r, sides);
2170
            this.scene.add(poly);
2171
2172
            return poly;
2173
2174
        },
2175
2176
        /*
2177
        * Make an Arc Segment
2178
        */
2179
2180
        makeArcSegment: function (ox, oy, ir, or, sa, ea, res) {
2181
            var arcSegment = new Two.ArcSegment(ox, oy, ir, or, sa, ea, res);
2182
            this.scene.add(arcSegment);
2183
            return arcSegment;
2184
        },
2185
2186
        /**
2187
            * Convenience method to make and draw a Two.Path.
2188
            */
2189
        makePath: function (p) {
2190
2191
            var l = arguments.length, points = p;
2192
            if (!_.isArray(p)) {
2193
                points = [];
2194
                for (var i = 0; i < l; i += 2) {
2195
                    var x = arguments[i];
2196
                    if (!_.isNumber(x)) {
2197
                        break;
2198
                    }
2199
                    var y = arguments[i + 1];
2200
                    points.push(new Two.Anchor(x, y));
2201
                }
2202
            }
2203
2204
            var last = arguments[l - 1];
2205
            var path = new Two.Path(points, !(_.isBoolean(last) ? last : undefined));
2206
            var rect = path.getBoundingClientRect();
2207
            path.center().translation
2208
                .set(rect.left + rect.width / 2, rect.top + rect.height / 2);
2209
2210
            this.scene.add(path);
2211
2212
            return path;
2213
2214
        },
2215
2216
        /**
2217
            * Convenience method to make and add a Two.Text.
2218
            */
2219
        makeText: function (message, x, y, styles) {
2220
            var text = new Two.Text(message, x, y, styles);
2221
            this.add(text);
2222
            return text;
2223
        },
2224
2225
        /**
2226
            * Convenience method to make and add a Two.LinearGradient.
2227
            */
2228
        makeLinearGradient: function (x1, y1, x2, y2 /* stops */) {
2229
2230
            var stops = slice.call(arguments, 4);
2231
            var gradient = new Two.LinearGradient(x1, y1, x2, y2, stops);
2232
2233
            this.add(gradient);
2234
2235
            return gradient;
2236
2237
        },
2238
2239
        /**
2240
            * Convenience method to make and add a Two.RadialGradient.
2241
            */
2242
        makeRadialGradient: function (x1, y1, r /* stops */) {
2243
2244
            var stops = slice.call(arguments, 3);
2245
            var gradient = new Two.RadialGradient(x1, y1, r, stops);
2246
2247
            this.add(gradient);
2248
2249
            return gradient;
2250
2251
        },
2252
2253
        makeSprite: function (path, x, y, cols, rows, frameRate, autostart) {
2254
2255
            var sprite = new Two.Sprite(path, x, y, cols, rows, frameRate);
2256
            if (!!autostart) {
2257
                sprite.play();
2258
            }
2259
            this.add(sprite);
2260
2261
            return sprite;
2262
2263
        },
2264
2265
        makeImageSequence: function (paths, x, y, frameRate, autostart) {
2266
2267
            var imageSequence = new Two.ImageSequence(paths, x, y, frameRate);
2268
            if (!!autostart) {
2269
                imageSequence.play();
2270
            }
2271
            this.add(imageSequence);
2272
2273
            return imageSequence;
2274
2275
        },
2276
2277
        makeTexture: function (path, callback) {
2278
2279
            var texture = new Two.Texture(path, callback);
2280
            return texture;
2281
2282
        },
2283
2284
        makeGroup: function (o) {
2285
2286
            var objects = o;
2287
            if (!(objects instanceof Array)) {
2288
                objects = _.toArray(arguments);
2289
            }
2290
2291
            var group = new Two.Group();
2292
            this.scene.add(group);
2293
            group.add(objects);
2294
2295
            return group;
2296
2297
        },
2298
2299
        /**
2300
            * Interpret an SVG Node and add it to this instance's scene. The
2301
            * distinction should be made that this doesn't `import` svg's, it solely
2302
            * interprets them into something compatible for Two.js — this is slightly
2303
            * different than a direct transcription.
2304
            *
2305
            * @param {Object} svgNode - The node to be parsed
2306
            * @param {Boolean} shallow - Don't create a top-most group but
2307
            *                                    append all contents directly
2308
            */
2309
        interpret: function (svgNode, shallow) {
2310
2311
            var tag = svgNode.tagName.toLowerCase();
2312
2313
            if (!(tag in Two.Utils.read)) {
2314
                return null;
2315
            }
2316
2317
            var node = Two.Utils.read[tag].call(this, svgNode);
2318
2319
            if (shallow && node instanceof Two.Group) {
2320
                this.add(node.children);
2321
            } else {
2322
                this.add(node);
2323
            }
2324
2325
            return node;
2326
2327
        },
2328
2329
        /**
2330
            * Load an SVG file / text and interpret.
2331
            */
2332
        load: function (text, callback) {
2333
2334
            var nodes = [], elem, i;
2335
2336
            if (/.*\.svg/ig.test(text)) {
2337
2338
                Two.Utils.xhr(text, _.bind(function (data) {
2339
2340
                    dom.temp.innerHTML = data;
2341
                    for (i = 0; i < dom.temp.children.length; i++) {
2342
                        elem = dom.temp.children[i];
2343
                        nodes.push(this.interpret(elem));
2344
                    }
2345
2346
                    callback(nodes.length <= 1 ? nodes[0] : nodes,
2347
                        dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children);
2348
2349
                }, this));
2350
2351
                return this;
2352
2353
            }
2354
2355
            dom.temp.innerHTML = text;
2356
            for (i = 0; i < dom.temp.children.length; i++) {
2357
                elem = dom.temp.children[i];
2358
                nodes.push(this.interpret(elem));
2359
            }
2360
2361
            callback(nodes.length <= 1 ? nodes[0] : nodes,
2362
                dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children);
2363
2364
            return this;
2365
2366
        }
2367
2368
    });
2369
2370
    function fitToWindow() {
2371
2372
        var wr = document.body.getBoundingClientRect();
2373
2374
        var width = this.width = wr.width;
2375
        var height = this.height = wr.height;
2376
2377
        this.renderer.setSize(width, height, this.ratio);
2378
        this.trigger(Two.Events.resize, width, height);
2379
2380
    }
2381
2382
    // Request Animation Frame
2383
2384
    var raf = dom.getRequestAnimationFrame();
2385
2386
    function loop() {
2387
2388
        raf(loop);
2389
2390
        for (var i = 0; i < Two.Instances.length; i++) {
2391
            var t = Two.Instances[i];
2392
            if (t.playing) {
2393
                t.update();
2394
            }
2395
        }
2396
2397
    }
2398
2399
    if (typeof define === 'function' && define.amd) {
2400
        define('two', [], function () {
2401
            return Two;
2402
        });
2403
    } else if (typeof module != 'undefined' && module.exports) {
2404
        module.exports = Two;
2405
    }
2406
2407
    return Two;
2408
2409
})((typeof global !== 'undefined' ? global : this).Two);
2410
2411
(function (Two) {
2412
2413
    var _ = Two.Utils;
2414
2415
    var Registry = Two.Registry = function () {
2416
2417
        this.map = {};
2418
2419
    };
2420
2421
    _.extend(Registry, {
2422
2423
    });
2424
2425
    _.extend(Registry.prototype, {
2426
2427
        add: function (id, obj) {
2428
            this.map[id] = obj;
2429
            return this;
2430
        },
2431
2432
        remove: function (id) {
2433
            delete this.map[id];
2434
            return this;
2435
        },
2436
2437
        get: function (id) {
2438
            return this.map[id];
2439
        },
2440
2441
        contains: function (id) {
2442
            return id in this.map;
2443
        }
2444
2445
    });
2446
2447
})((typeof global !== 'undefined' ? global : this).Two);
2448
2449
(function (Two) {
2450
2451
    var _ = Two.Utils;
2452
2453
    var Vector = Two.Vector = function (x, y) {
2454
2455
        this.x = x || 0;
2456
        this.y = y || 0;
2457
2458
    };
2459
2460
    _.extend(Vector, {
2461
2462
        zero: new Two.Vector()
2463
2464
    });
2465
2466
    _.extend(Vector.prototype, Two.Utils.Events, {
2467
2468
        set: function (x, y) {
2469
            this.x = x;
2470
            this.y = y;
2471
            return this;
2472
        },
2473
2474
        copy: function (v) {
2475
            this.x = v.x;
2476
            this.y = v.y;
2477
            return this;
2478
        },
2479
2480
        clear: function () {
2481
            this.x = 0;
2482
            this.y = 0;
2483
            return this;
2484
        },
2485
2486
        clone: function () {
2487
            return new Vector(this.x, this.y);
2488
        },
2489
2490
        add: function (v1, v2) {
2491
            this.x = v1.x + v2.x;
2492
            this.y = v1.y + v2.y;
2493
            return this;
2494
        },
2495
2496
        addSelf: function (v) {
2497
            this.x += v.x;
2498
            this.y += v.y;
2499
            return this;
2500
        },
2501
2502
        sub: function (v1, v2) {
2503
            this.x = v1.x - v2.x;
2504
            this.y = v1.y - v2.y;
2505
            return this;
2506
        },
2507
2508
        subSelf: function (v) {
2509
            this.x -= v.x;
2510
            this.y -= v.y;
2511
            return this;
2512
        },
2513
2514
        multiplySelf: function (v) {
2515
            this.x *= v.x;
2516
            this.y *= v.y;
2517
            return this;
2518
        },
2519
2520
        multiplyScalar: function (s) {
2521
            this.x *= s;
2522
            this.y *= s;
2523
            return this;
2524
        },
2525
2526
        divideScalar: function (s) {
2527
            if (s) {
2528
                this.x /= s;
2529
                this.y /= s;
2530
            } else {
2531
                this.set(0, 0);
2532
            }
2533
            return this;
2534
        },
2535
2536
        negate: function () {
2537
            return this.multiplyScalar(-1);
2538
        },
2539
2540
        dot: function (v) {
2541
            return this.x * v.x + this.y * v.y;
2542
        },
2543
2544
        lengthSquared: function () {
2545
            return this.x * this.x + this.y * this.y;
2546
        },
2547
2548
        length: function () {
2549
            return Math.sqrt(this.lengthSquared());
2550
        },
2551
2552
        normalize: function () {
2553
            return this.divideScalar(this.length());
2554
        },
2555
2556
        distanceTo: function (v) {
2557
            return Math.sqrt(this.distanceToSquared(v));
2558
        },
2559
2560
        distanceToSquared: function (v) {
2561
            var dx = this.x - v.x,
2562
                dy = this.y - v.y;
2563
            return dx * dx + dy * dy;
2564
        },
2565
2566
        setLength: function (l) {
2567
            return this.normalize().multiplyScalar(l);
2568
        },
2569
2570
        equals: function (v, eps) {
2571
            eps = (typeof eps === 'undefined') ? 0.0001 : eps;
2572
            return (this.distanceTo(v) < eps);
2573
        },
2574
2575
        lerp: function (v, t) {
2576
            var x = (v.x - this.x) * t + this.x;
2577
            var y = (v.y - this.y) * t + this.y;
2578
            return this.set(x, y);
2579
        },
2580
2581
        isZero: function (eps) {
2582
            eps = (typeof eps === 'undefined') ? 0.0001 : eps;
2583
            return (this.length() < eps);
2584
        },
2585
2586
        toString: function () {
2587
            return this.x + ', ' + this.y;
2588
        },
2589
2590
        toObject: function () {
2591
            return { x: this.x, y: this.y };
2592
        },
2593
2594
        rotate: function (radians) {
2595
            var cos = Math.cos(radians);
2596
            var sin = Math.sin(radians);
2597
            this.x = this.x * cos - this.y * sin;
2598
            this.y = this.x * sin + this.y * cos;
2599
            return this;
2600
        }
2601
2602
    });
2603
2604
    var BoundProto = {
2605
2606
        set: function (x, y) {
2607
            this._x = x;
2608
            this._y = y;
2609
            return this.trigger(Two.Events.change);
2610
        },
2611
2612
        copy: function (v) {
2613
            this._x = v.x;
2614
            this._y = v.y;
2615
            return this.trigger(Two.Events.change);
2616
        },
2617
2618
        clear: function () {
2619
            this._x = 0;
2620
            this._y = 0;
2621
            return this.trigger(Two.Events.change);
2622
        },
2623
2624
        clone: function () {
2625
            return new Vector(this._x, this._y);
2626
        },
2627
2628
        add: function (v1, v2) {
2629
            this._x = v1.x + v2.x;
2630
            this._y = v1.y + v2.y;
2631
            return this.trigger(Two.Events.change);
2632
        },
2633
2634
        addSelf: function (v) {
2635
            this._x += v.x;
2636
            this._y += v.y;
2637
            return this.trigger(Two.Events.change);
2638
        },
2639
2640
        sub: function (v1, v2) {
2641
            this._x = v1.x - v2.x;
2642
            this._y = v1.y - v2.y;
2643
            return this.trigger(Two.Events.change);
2644
        },
2645
2646
        subSelf: function (v) {
2647
            this._x -= v.x;
2648
            this._y -= v.y;
2649
            return this.trigger(Two.Events.change);
2650
        },
2651
2652
        multiplySelf: function (v) {
2653
            this._x *= v.x;
2654
            this._y *= v.y;
2655
            return this.trigger(Two.Events.change);
2656
        },
2657
2658
        multiplyScalar: function (s) {
2659
            this._x *= s;
2660
            this._y *= s;
2661
            return this.trigger(Two.Events.change);
2662
        },
2663
2664
        divideScalar: function (s) {
2665
            if (s) {
2666
                this._x /= s;
2667
                this._y /= s;
2668
                return this.trigger(Two.Events.change);
2669
            }
2670
            return this.clear();
2671
        },
2672
2673
        negate: function () {
2674
            return this.multiplyScalar(-1);
2675
        },
2676
2677
        dot: function (v) {
2678
            return this._x * v.x + this._y * v.y;
2679
        },
2680
2681
        lengthSquared: function () {
2682
            return this._x * this._x + this._y * this._y;
2683
        },
2684
2685
        length: function () {
2686
            return Math.sqrt(this.lengthSquared());
2687
        },
2688
2689
        normalize: function () {
2690
            return this.divideScalar(this.length());
2691
        },
2692
2693
        distanceTo: function (v) {
2694
            return Math.sqrt(this.distanceToSquared(v));
2695
        },
2696
2697
        distanceToSquared: function (v) {
2698
            var dx = this._x - v.x,
2699
                dy = this._y - v.y;
2700
            return dx * dx + dy * dy;
2701
        },
2702
2703
        setLength: function (l) {
2704
            return this.normalize().multiplyScalar(l);
2705
        },
2706
2707
        equals: function (v, eps) {
2708
            eps = (typeof eps === 'undefined') ? 0.0001 : eps;
2709
            return (this.distanceTo(v) < eps);
2710
        },
2711
2712
        lerp: function (v, t) {
2713
            var x = (v.x - this._x) * t + this._x;
2714
            var y = (v.y - this._y) * t + this._y;
2715
            return this.set(x, y);
2716
        },
2717
2718
        isZero: function (eps) {
2719
            eps = (typeof eps === 'undefined') ? 0.0001 : eps;
2720
            return (this.length() < eps);
2721
        },
2722
2723
        toString: function () {
2724
            return this._x + ', ' + this._y;
2725
        },
2726
2727
        toObject: function () {
2728
            return { x: this._x, y: this._y };
2729
        },
2730
2731
        rotate: function (radians) {
2732
            var cos = Math.cos(radians);
2733
            var sin = Math.sin(radians);
2734
            this._x = this._x * cos - this._y * sin;
2735
            this._y = this._x * sin + this._y * cos;
2736
            return this;
2737
        }
2738
2739
    };
2740
2741
    var xgs = {
2742
        enumerable: true,
2743
        get: function () {
2744
            return this._x;
2745
        },
2746
        set: function (v) {
2747
            this._x = v;
2748
            this.trigger(Two.Events.change, 'x');
2749
        }
2750
    };
2751
2752
    var ygs = {
2753
        enumerable: true,
2754
        get: function () {
2755
            return this._y;
2756
        },
2757
        set: function (v) {
2758
            this._y = v;
2759
            this.trigger(Two.Events.change, 'y');
2760
        }
2761
    };
2762
2763
    /**
2764
        * Override Backbone bind / on in order to add properly broadcasting.
2765
        * This allows Two.Vector to not broadcast events unless event listeners
2766
        * are explicity bound to it.
2767
        */
2768
2769
    Two.Vector.prototype.bind = Two.Vector.prototype.on = function () {
2770
2771
        if (!this._bound) {
2772
            this._x = this.x;
2773
            this._y = this.y;
2774
            Object.defineProperty(this, 'x', xgs);
2775
            Object.defineProperty(this, 'y', ygs);
2776
            _.extend(this, BoundProto);
2777
            this._bound = true; // Reserved for event initialization check
2778
        }
2779
2780
        Two.Utils.Events.bind.apply(this, arguments);
2781
2782
        return this;
2783
2784
    };
2785
2786
})((typeof global !== 'undefined' ? global : this).Two);
2787
2788
(function (Two) {
2789
2790
    // Localized variables
2791
    var commands = Two.Commands;
2792
    var _ = Two.Utils;
2793
2794
    /**
2795
        * An object that holds 3 `Two.Vector`s, the anchor point and its
2796
        * corresponding handles: `left` and `right`.
2797
        */
2798
    var Anchor = Two.Anchor = function (x, y, ux, uy, vx, vy, command) {
2799
2800
        Two.Vector.call(this, x, y);
2801
2802
        this._broadcast = _.bind(function () {
2803
            this.trigger(Two.Events.change);
2804
        }, this);
2805
2806
        this._command = command || commands.move;
2807
        this._relative = true;
2808
2809
        if (!command) {
2810
            return this;
2811
        }
2812
2813
        Anchor.AppendCurveProperties(this);
2814
2815
        if (_.isNumber(ux)) {
2816
            this.controls.left.x = ux;
2817
        }
2818
        if (_.isNumber(uy)) {
2819
            this.controls.left.y = uy;
2820
        }
2821
        if (_.isNumber(vx)) {
2822
            this.controls.right.x = vx;
2823
        }
2824
        if (_.isNumber(vy)) {
2825
            this.controls.right.y = vy;
2826
        }
2827
2828
    };
2829
2830
    _.extend(Anchor, {
2831
2832
        AppendCurveProperties: function (anchor) {
2833
            anchor.controls = {
2834
                left: new Two.Vector(0, 0),
2835
                right: new Two.Vector(0, 0)
2836
            };
2837
        }
2838
2839
    });
2840
2841
    var AnchorProto = {
2842
2843
        listen: function () {
2844
2845
            if (!_.isObject(this.controls)) {
2846
                Anchor.AppendCurveProperties(this);
2847
            }
2848
2849
            this.controls.left.bind(Two.Events.change, this._broadcast);
2850
            this.controls.right.bind(Two.Events.change, this._broadcast);
2851
2852
            return this;
2853
2854
        },
2855
2856
        ignore: function () {
2857
2858
            this.controls.left.unbind(Two.Events.change, this._broadcast);
2859
            this.controls.right.unbind(Two.Events.change, this._broadcast);
2860
2861
            return this;
2862
2863
        },
2864
2865
        clone: function () {
2866
2867
            var controls = this.controls;
2868
2869
            var clone = new Two.Anchor(
2870
                this.x,
2871
                this.y,
2872
                controls && controls.left.x,
2873
                controls && controls.left.y,
2874
                controls && controls.right.x,
2875
                controls && controls.right.y,
2876
                this.command
2877
            );
2878
            clone.relative = this._relative;
2879
            return clone;
2880
2881
        },
2882
2883
        toObject: function () {
2884
            var o = {
2885
                x: this.x,
2886
                y: this.y
2887
            };
2888
            if (this._command) {
2889
                o.command = this._command;
2890
            }
2891
            if (this._relative) {
2892
                o.relative = this._relative;
2893
            }
2894
            if (this.controls) {
2895
                o.controls = {
2896
                    left: this.controls.left.toObject(),
2897
                    right: this.controls.right.toObject()
2898
                };
2899
            }
2900
            return o;
2901
        },
2902
2903
        toString: function () {
2904
            if (!this.controls) {
2905
                return [this._x, this._y].join(', ');
2906
            }
2907
            return [this._x, this._y, this.controls.left.x, this.controls.left.y,
2908
            this.controls.right.x, this.controls.right.y].join(', ');
2909
        }
2910
2911
    };
2912
2913
    Object.defineProperty(Anchor.prototype, 'command', {
2914
2915
        enumerable: true,
2916
2917
        get: function () {
2918
            return this._command;
2919
        },
2920
2921
        set: function (c) {
2922
            this._command = c;
2923
            if (this._command === commands.curve && !_.isObject(this.controls)) {
2924
                Anchor.AppendCurveProperties(this);
2925
            }
2926
            return this.trigger(Two.Events.change);
2927
        }
2928
2929
    });
2930
2931
    Object.defineProperty(Anchor.prototype, 'relative', {
2932
2933
        enumerable: true,
2934
2935
        get: function () {
2936
            return this._relative;
2937
        },
2938
2939
        set: function (b) {
2940
            if (this._relative == b) {
2941
                return this;
2942
            }
2943
            this._relative = !!b;
2944
            return this.trigger(Two.Events.change);
2945
        }
2946
2947
    });
2948
2949
    _.extend(Anchor.prototype, Two.Vector.prototype, AnchorProto);
2950
2951
    // Make it possible to bind and still have the Anchor specific
2952
    // inheritance from Two.Vector
2953
    Two.Anchor.prototype.bind = Two.Anchor.prototype.on = function () {
2954
        Two.Vector.prototype.bind.apply(this, arguments);
2955
        _.extend(this, AnchorProto);
2956
    };
2957
2958
    Two.Anchor.prototype.unbind = Two.Anchor.prototype.off = function () {
2959
        Two.Vector.prototype.unbind.apply(this, arguments);
2960
        _.extend(this, AnchorProto);
2961
    };
2962
2963
})((typeof global !== 'undefined' ? global : this).Two);
2964
2965
(function (Two) {
2966
2967
    /**
2968
        * Constants
2969
        */
2970
    var cos = Math.cos, sin = Math.sin, tan = Math.tan;
2971
    var _ = Two.Utils;
2972
2973
    /**
2974
        * Two.Matrix contains an array of elements that represent
2975
        * the two dimensional 3 x 3 matrix as illustrated below:
2976
        *
2977
        * =====
2978
        * a b c
2979
        * d e f
2980
        * g h i  // this row is not really used in 2d transformations
2981
        * =====
2982
        *
2983
        * String order is for transform strings: a, d, b, e, c, f
2984
        *
2985
        * @class
2986
        */
2987
    var Matrix = Two.Matrix = function (a, b, c, d, e, f) {
2988
2989
        this.elements = new Two.Array(9);
2990
2991
        var elements = a;
2992
        if (!_.isArray(elements)) {
2993
            elements = _.toArray(arguments);
2994
        }
2995
2996
        // initialize the elements with default values.
2997
2998
        this.identity().set(elements);
2999
3000
    };
3001
3002
    _.extend(Matrix, {
3003
3004
        Identity: [
3005
            1, 0, 0,
3006
            0, 1, 0,
3007
            0, 0, 1
3008
        ],
3009
3010
        /**
3011
            * Multiply two matrix 3x3 arrays
3012
            */
3013
        Multiply: function (A, B, C) {
3014
3015
            if (B.length <= 3) { // Multiply Vector
3016
3017
                var x, y, z, e = A;
3018
3019
                var a = B[0] || 0,
3020
                    b = B[1] || 0,
3021
                    c = B[2] || 0;
3022
3023
                // Go down rows first
3024
                // a, d, g, b, e, h, c, f, i
3025
3026
                x = e[0] * a + e[1] * b + e[2] * c;
3027
                y = e[3] * a + e[4] * b + e[5] * c;
3028
                z = e[6] * a + e[7] * b + e[8] * c;
3029
3030
                return { x: x, y: y, z: z };
3031
3032
            }
3033
3034
            var A0 = A[0], A1 = A[1], A2 = A[2];
3035
            var A3 = A[3], A4 = A[4], A5 = A[5];
3036
            var A6 = A[6], A7 = A[7], A8 = A[8];
3037
3038
            var B0 = B[0], B1 = B[1], B2 = B[2];
3039
            var B3 = B[3], B4 = B[4], B5 = B[5];
3040
            var B6 = B[6], B7 = B[7], B8 = B[8];
3041
3042
            C = C || new Two.Array(9);
3043
3044
            C[0] = A0 * B0 + A1 * B3 + A2 * B6;
3045
            C[1] = A0 * B1 + A1 * B4 + A2 * B7;
3046
            C[2] = A0 * B2 + A1 * B5 + A2 * B8;
3047
            C[3] = A3 * B0 + A4 * B3 + A5 * B6;
3048
            C[4] = A3 * B1 + A4 * B4 + A5 * B7;
3049
            C[5] = A3 * B2 + A4 * B5 + A5 * B8;
3050
            C[6] = A6 * B0 + A7 * B3 + A8 * B6;
3051
            C[7] = A6 * B1 + A7 * B4 + A8 * B7;
3052
            C[8] = A6 * B2 + A7 * B5 + A8 * B8;
3053
3054
            return C;
3055
3056
        }
3057
3058
    });
3059
3060
    _.extend(Matrix.prototype, Two.Utils.Events, {
3061
3062
        /**
3063
            * Takes an array of elements or the arguments list itself to
3064
            * set and update the current matrix's elements. Only updates
3065
            * specified values.
3066
            */
3067
        set: function (a) {
3068
3069
            var elements = a;
3070
            if (!_.isArray(elements)) {
3071
                elements = _.toArray(arguments);
3072
            }
3073
3074
            _.extend(this.elements, elements);
3075
3076
            return this.trigger(Two.Events.change);
3077
3078
        },
3079
3080
        /**
3081
            * Turn matrix to identity, like resetting.
3082
            */
3083
        identity: function () {
3084
3085
            this.set(Matrix.Identity);
3086
3087
            return this;
3088
3089
        },
3090
3091
        /**
3092
            * Multiply scalar or multiply by another matrix.
3093
            */
3094
        multiply: function (a, b, c, d, e, f, g, h, i) {
3095
3096
            var elements = arguments, l = elements.length;
3097
3098
            // Multiply scalar
3099
3100
            if (l <= 1) {
3101
3102
                _.each(this.elements, function (v, i) {
3103
                    this.elements[i] = v * a;
3104
                }, this);
3105
3106
                return this.trigger(Two.Events.change);
3107
3108
            }
3109
3110
            if (l <= 3) { // Multiply Vector
3111
3112
                var x, y, z;
3113
                a = a || 0;
3114
                b = b || 0;
3115
                c = c || 0;
3116
                e = this.elements;
3117
3118
                // Go down rows first
3119
                // a, d, g, b, e, h, c, f, i
3120
3121
                x = e[0] * a + e[1] * b + e[2] * c;
3122
                y = e[3] * a + e[4] * b + e[5] * c;
3123
                z = e[6] * a + e[7] * b + e[8] * c;
3124
3125
                return { x: x, y: y, z: z };
3126
3127
            }
3128
3129
            // Multiple matrix
3130
3131
            var A = this.elements;
3132
            var B = elements;
3133
3134
            var A0 = A[0], A1 = A[1], A2 = A[2];
3135
            var A3 = A[3], A4 = A[4], A5 = A[5];
3136
            var A6 = A[6], A7 = A[7], A8 = A[8];
3137
3138
            var B0 = B[0], B1 = B[1], B2 = B[2];
3139
            var B3 = B[3], B4 = B[4], B5 = B[5];
3140
            var B6 = B[6], B7 = B[7], B8 = B[8];
3141
3142
            this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6;
3143
            this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7;
3144
            this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8;
3145
3146
            this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6;
3147
            this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7;
3148
            this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8;
3149
3150
            this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6;
3151
            this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7;
3152
            this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8;
3153
3154
            return this.trigger(Two.Events.change);
3155
3156
        },
3157
3158
        inverse: function (out) {
3159
3160
            var a = this.elements;
3161
            out = out || new Two.Matrix();
3162
3163
            var a00 = a[0], a01 = a[1], a02 = a[2];
3164
            var a10 = a[3], a11 = a[4], a12 = a[5];
3165
            var a20 = a[6], a21 = a[7], a22 = a[8];
3166
3167
            var b01 = a22 * a11 - a12 * a21;
3168
            var b11 = -a22 * a10 + a12 * a20;
3169
            var b21 = a21 * a10 - a11 * a20;
3170
3171
            // Calculate the determinant
3172
            var det = a00 * b01 + a01 * b11 + a02 * b21;
3173
3174
            if (!det) {
3175
                return null;
3176
            }
3177
3178
            det = 1.0 / det;
3179
3180
            out.elements[0] = b01 * det;
3181
            out.elements[1] = (-a22 * a01 + a02 * a21) * det;
3182
            out.elements[2] = (a12 * a01 - a02 * a11) * det;
3183
            out.elements[3] = b11 * det;
3184
            out.elements[4] = (a22 * a00 - a02 * a20) * det;
3185
            out.elements[5] = (-a12 * a00 + a02 * a10) * det;
3186
            out.elements[6] = b21 * det;
3187
            out.elements[7] = (-a21 * a00 + a01 * a20) * det;
3188
            out.elements[8] = (a11 * a00 - a01 * a10) * det;
3189
3190
            return out;
3191
3192
        },
3193
3194
        /**
3195
            * Set a scalar onto the matrix.
3196
            */
3197
        scale: function (sx, sy) {
3198
3199
            var l = arguments.length;
3200
            if (l <= 1) {
3201
                sy = sx;
3202
            }
3203
3204
            return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1);
3205
3206
        },
3207
3208
        /**
3209
            * Rotate the matrix.
3210
            */
3211
        rotate: function (radians) {
3212
3213
            var c = cos(radians);
3214
            var s = sin(radians);
3215
3216
            return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1);
3217
3218
        },
3219
3220
        /**
3221
            * Translate the matrix.
3222
            */
3223
        translate: function (x, y) {
3224
3225
            return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1);
3226
3227
        },
3228
3229
        /*
3230
            * Skew the matrix by an angle in the x axis direction.
3231
            */
3232
        skewX: function (radians) {
3233
3234
            var a = tan(radians);
3235
3236
            return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1);
3237
3238
        },
3239
3240
        /*
3241
            * Skew the matrix by an angle in the y axis direction.
3242
            */
3243
        skewY: function (radians) {
3244
3245
            var a = tan(radians);
3246
3247
            return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1);
3248
3249
        },
3250
3251
        /**
3252
            * Create a transform string to be used with rendering apis.
3253
            */
3254
        toString: function (fullMatrix) {
3255
            var temp = [];
3256
3257
            this.toArray(fullMatrix, temp);
3258
3259
            return temp.join(' ');
3260
3261
        },
3262
3263
        /**
3264
            * Create a transform array to be used with rendering apis.
3265
            */
3266
        toArray: function (fullMatrix, output) {
3267
3268
            var elements = this.elements;
3269
            var hasOutput = !!output;
3270
3271
            var a = parseFloat(elements[0].toFixed(3));
3272
            var b = parseFloat(elements[1].toFixed(3));
3273
            var c = parseFloat(elements[2].toFixed(3));
3274
            var d = parseFloat(elements[3].toFixed(3));
3275
            var e = parseFloat(elements[4].toFixed(3));
3276
            var f = parseFloat(elements[5].toFixed(3));
3277
3278
            if (!!fullMatrix) {
3279
3280
                var g = parseFloat(elements[6].toFixed(3));
3281
                var h = parseFloat(elements[7].toFixed(3));
3282
                var i = parseFloat(elements[8].toFixed(3));
3283
3284
                if (hasOutput) {
3285
                    output[0] = a;
3286
                    output[1] = d;
3287
                    output[2] = g;
3288
                    output[3] = b;
3289
                    output[4] = e;
3290
                    output[5] = h;
3291
                    output[6] = c;
3292
                    output[7] = f;
3293
                    output[8] = i;
3294
                    return;
3295
                }
3296
3297
                return [
3298
                    a, d, g, b, e, h, c, f, i
3299
                ];
3300
            }
3301
3302
            if (hasOutput) {
3303
                output[0] = a;
3304
                output[1] = d;
3305
                output[2] = b;
3306
                output[3] = e;
3307
                output[4] = c;
3308
                output[5] = f;
3309
                return;
3310
            }
3311
3312
            return [
3313
                a, d, b, e, c, f  // Specific format see LN:19
3314
            ];
3315
3316
        },
3317
3318
        /**
3319
            * Clone the current matrix.
3320
            */
3321
        clone: function () {
3322
            var a, b, c, d, e, f, g, h, i;
3323
3324
            a = this.elements[0];
3325
            b = this.elements[1];
3326
            c = this.elements[2];
3327
            d = this.elements[3];
3328
            e = this.elements[4];
3329
            f = this.elements[5];
3330
            g = this.elements[6];
3331
            h = this.elements[7];
3332
            i = this.elements[8];
3333
3334
            return new Two.Matrix(a, b, c, d, e, f, g, h, i);
3335
3336
        }
3337
3338
    });
3339
3340
})((typeof global !== 'undefined' ? global : this).Two);
3341
3342
(function (Two) {
3343
3344
    // Localize variables
3345
    var mod = Two.Utils.mod, toFixed = Two.Utils.toFixed;
3346
    var _ = Two.Utils;
3347
3348
    var svg = {
3349
3350
        version: 1.1,
3351
3352
        ns: 'http://www.w3.org/2000/svg',
3353
        xlink: 'http://www.w3.org/1999/xlink',
3354
3355
        alignments: {
3356
            left: 'start',
3357
            center: 'middle',
3358
            right: 'end'
3359
        },
3360
3361
        /**
3362
            * Create an svg namespaced element.
3363
            */
3364
        createElement: function (name, attrs) {
3365
            var tag = name;
3366
            var elem = document.createElementNS(svg.ns, tag);
3367
            if (tag === 'svg') {
3368
                attrs = _.defaults(attrs || {}, {
3369
                    version: svg.version
3370
                });
3371
            }
3372
            if (!_.isEmpty(attrs)) {
3373
                svg.setAttributes(elem, attrs);
3374
            }
3375
            return elem;
3376
        },
3377
3378
        /**
3379
            * Add attributes from an svg element.
3380
            */
3381
        setAttributes: function (elem, attrs) {
3382
            var keys = Object.keys(attrs);
3383
            for (var i = 0; i < keys.length; i++) {
3384
                if (/href/.test(keys[i])) {
3385
                    elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]);
3386
                } else {
3387
                    elem.setAttribute(keys[i], attrs[keys[i]]);
3388
                }
3389
            }
3390
            return this;
3391
        },
3392
3393
        /**
3394
            * Remove attributes from an svg element.
3395
            */
3396
        removeAttributes: function (elem, attrs) {
3397
            for (var key in attrs) {
3398
                elem.removeAttribute(key);
3399
            }
3400
            return this;
3401
        },
3402
3403
        /**
3404
            * Turn a set of vertices into a string for the d property of a path
3405
            * element. It is imperative that the string collation is as fast as
3406
            * possible, because this call will be happening multiple times a
3407
            * second.
3408
            */
3409
        toString: function (points, closed) {
3410
3411
            var l = points.length,
3412
                last = l - 1,
3413
                d, // The elusive last Two.Commands.move point
3414
                ret = '';
3415
3416
            for (var i = 0; i < l; i++) {
3417
                var b = points[i];
3418
                var command;
3419
                var prev = closed ? mod(i - 1, l) : Math.max(i - 1, 0);
3420
                var next = closed ? mod(i + 1, l) : Math.min(i + 1, last);
3421
3422
                var a = points[prev];
3423
                var c = points[next];
3424
3425
                var vx, vy, ux, uy, ar, bl, br, cl;
3426
3427
                // Access x and y directly,
3428
                // bypassing the getter
3429
                var x = toFixed(b._x);
3430
                var y = toFixed(b._y);
3431
3432
                switch (b._command) {
3433
3434
                    case Two.Commands.close:
3435
                        command = Two.Commands.close;
3436
                        break;
3437
3438
                    case Two.Commands.curve:
3439
3440
                        ar = (a.controls && a.controls.right) || Two.Vector.zero;
3441
                        bl = (b.controls && b.controls.left) || Two.Vector.zero;
3442
3443
                        if (a._relative) {
3444
                            vx = toFixed((ar.x + a.x));
3445
                            vy = toFixed((ar.y + a.y));
3446
                        } else {
3447
                            vx = toFixed(ar.x);
3448
                            vy = toFixed(ar.y);
3449
                        }
3450
3451
                        if (b._relative) {
3452
                            ux = toFixed((bl.x + b.x));
3453
                            uy = toFixed((bl.y + b.y));
3454
                        } else {
3455
                            ux = toFixed(bl.x);
3456
                            uy = toFixed(bl.y);
3457
                        }
3458
3459
                        command = ((i === 0) ? Two.Commands.move : Two.Commands.curve) +
3460
                            ' ' + vx + ' ' + vy + ' ' + ux + ' ' + uy + ' ' + x + ' ' + y;
3461
                        break;
3462
3463
                    case Two.Commands.move:
3464
                        d = b;
3465
                        command = Two.Commands.move + ' ' + x + ' ' + y;
3466
                        break;
3467
3468
                    default:
3469
                        command = b._command + ' ' + x + ' ' + y;
3470
3471
                }
3472
3473
                // Add a final point and close it off
3474
3475
                if (i >= last && closed) {
3476
3477
                    if (b._command === Two.Commands.curve) {
3478
3479
                        // Make sure we close to the most previous Two.Commands.move
3480
                        c = d;
3481
3482
                        br = (b.controls && b.controls.right) || b;
3483
                        cl = (c.controls && c.controls.left) || c;
3484
3485
                        if (b._relative) {
3486
                            vx = toFixed((br.x + b.x));
3487
                            vy = toFixed((br.y + b.y));
3488
                        } else {
3489
                            vx = toFixed(br.x);
3490
                            vy = toFixed(br.y);
3491
                        }
3492
3493
                        if (c._relative) {
3494
                            ux = toFixed((cl.x + c.x));
3495
                            uy = toFixed((cl.y + c.y));
3496
                        } else {
3497
                            ux = toFixed(cl.x);
3498
                            uy = toFixed(cl.y);
3499
                        }
3500
3501
                        x = toFixed(c.x);
3502
                        y = toFixed(c.y);
3503
3504
                        command +=
3505
                            ' C ' + vx + ' ' + vy + ' ' + ux + ' ' + uy + ' ' + x + ' ' + y;
3506
                    }
3507
3508
                    command += ' Z';
3509
3510
                }
3511
3512
                ret += command + ' ';
3513
3514
            }
3515
3516
            return ret;
3517
3518
        },
3519
3520
        getClip: function (shape) {
3521
3522
            var clip = shape._renderer.clip;
3523
3524
            if (!clip) {
3525
3526
                var root = shape;
3527
3528
                while (root.parent) {
3529
                    root = root.parent;
3530
                }
3531
3532
                clip = shape._renderer.clip = svg.createElement('clipPath');
3533
                root.defs.appendChild(clip);
3534
3535
            }
3536
3537
            return clip;
3538
3539
        },
3540
3541
        group: {
3542
3543
            // TODO: Can speed up.
3544
            // TODO: How does this effect a f
3545
            appendChild: function (object) {
3546
3547
                var elem = object._renderer.elem;
3548
3549
                if (!elem) {
3550
                    return;
3551
                }
3552
3553
                var tag = elem.nodeName;
3554
3555
                if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) {
3556
                    return;
3557
                }
3558
3559
                this.elem.appendChild(elem);
3560
3561
            },
3562
3563
            removeChild: function (object) {
3564
3565
                var elem = object._renderer.elem;
3566
3567
                if (!elem || elem.parentNode != this.elem) {
3568
                    return;
3569
                }
3570
3571
                var tag = elem.nodeName;
3572
3573
                if (!tag) {
3574
                    return;
3575
                }
3576
3577
                // Defer subtractions while clipping.
3578
                if (object._clip) {
3579
                    return;
3580
                }
3581
3582
                this.elem.removeChild(elem);
3583
3584
            },
3585
3586
            orderChild: function (object) {
3587
                this.elem.appendChild(object._renderer.elem);
3588
            },
3589
3590
            renderChild: function (child) {
3591
                svg[child._renderer.type].render.call(child, this);
3592
            },
3593
3594
            render: function (domElement) {
3595
3596
                this._update();
3597
3598
                // Shortcut for hidden objects.
3599
                // Doesn't reset the flags, so changes are stored and
3600
                // applied once the object is visible again
3601
                if (this._opacity === 0 && !this._flagOpacity) {
3602
                    return this;
3603
                }
3604
3605
                if (!this._renderer.elem) {
3606
                    this._renderer.elem = svg.createElement('g', {
3607
                        id: this.id
3608
                    });
3609
                    domElement.appendChild(this._renderer.elem);
3610
                }
3611
3612
                // _Update styles for the <g>
3613
                var flagMatrix = this._matrix.manual || this._flagMatrix;
3614
                var context = {
3615
                    domElement: domElement,
3616
                    elem: this._renderer.elem
3617
                };
3618
3619
                if (flagMatrix) {
3620
                    this._renderer.elem.setAttribute('transform', 'matrix(' + this._matrix.toString() + ')');
3621
                }
3622
3623
                for (var i = 0; i < this.children.length; i++) {
3624
                    var child = this.children[i];
3625
                    svg[child._renderer.type].render.call(child, domElement);
3626
                }
3627
3628
                if (this._flagOpacity) {
3629
                    this._renderer.elem.setAttribute('opacity', this._opacity);
3630
                }
3631
3632
                if (this._flagAdditions) {
3633
                    this.additions.forEach(svg.group.appendChild, context);
3634
                }
3635
3636
                if (this._flagSubtractions) {
3637
                    this.subtractions.forEach(svg.group.removeChild, context);
3638
                }
3639
3640
                if (this._flagOrder) {
3641
                    this.children.forEach(svg.group.orderChild, context);
3642
                }
3643
3644
                /**
3645
                    * Commented two-way functionality of clips / masks with groups and
3646
                    * polygons. Uncomment when this bug is fixed:
3647
                    * https://code.google.com/p/chromium/issues/detail?id=370951
3648
                    */
3649
3650
                // if (this._flagClip) {
3651
3652
                //   clip = svg.getClip(this);
3653
                //   elem = this._renderer.elem;
3654
3655
                //   if (this._clip) {
3656
                //     elem.removeAttribute('id');
3657
                //     clip.setAttribute('id', this.id);
3658
                //     clip.appendChild(elem);
3659
                //   } else {
3660
                //     clip.removeAttribute('id');
3661
                //     elem.setAttribute('id', this.id);
3662
                //     this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore
3663
                //   }
3664
3665
                // }
3666
3667
                if (this._flagMask) {
3668
                    if (this._mask) {
3669
                        this._renderer.elem.setAttribute('clip-path', 'url(#' + this._mask.id + ')');
3670
                    } else {
3671
                        this._renderer.elem.removeAttribute('clip-path');
3672
                    }
3673
                }
3674
3675
                return this.flagReset();
3676
3677
            }
3678
3679
        },
3680
3681
        path: {
3682
3683
            render: function (domElement) {
3684
3685
                this._update();
3686
3687
                // Shortcut for hidden objects.
3688
                // Doesn't reset the flags, so changes are stored and
3689
                // applied once the object is visible again
3690
                if (this._opacity === 0 && !this._flagOpacity) {
3691
                    return this;
3692
                }
3693
3694
                // Collect any attribute that needs to be changed here
3695
                var changed = {};
3696
3697
                var flagMatrix = this._matrix.manual || this._flagMatrix;
3698
3699
                if (flagMatrix) {
3700
                    changed.transform = 'matrix(' + this._matrix.toString() + ')';
3701
                }
3702
3703
                if (this._flagVertices) {
3704
                    var vertices = svg.toString(this._vertices, this._closed);
3705
                    changed.d = vertices;
3706
                }
3707
3708
                if (this._fill && this._fill._renderer) {
3709
                    this._fill._update();
3710
                    svg[this._fill._renderer.type].render.call(this._fill, domElement, true);
3711
                }
3712
3713
                if (this._flagFill) {
3714
                    changed.fill = this._fill && this._fill.id
3715
                        ? 'url(#' + this._fill.id + ')' : this._fill;
3716
                }
3717
3718
                if (this._stroke && this._stroke._renderer) {
3719
                    this._stroke._update();
3720
                    svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true);
3721
                }
3722
3723
                if (this._flagStroke) {
3724
                    changed.stroke = this._stroke && this._stroke.id
3725
                        ? 'url(#' + this._stroke.id + ')' : this._stroke;
3726
                }
3727
3728
                if (this._flagLinewidth) {
3729
                    changed['stroke-width'] = this._linewidth;
3730
                }
3731
3732
                if (this._flagOpacity) {
3733
                    changed['stroke-opacity'] = this._opacity;
3734
                    changed['fill-opacity'] = this._opacity;
3735
                }
3736
3737
                if (this._flagVisible) {
3738
                    changed.visibility = this._visible ? 'visible' : 'hidden';
3739
                }
3740
3741
                if (this._flagCap) {
3742
                    changed['stroke-linecap'] = this._cap;
3743
                }
3744
3745
                if (this._flagJoin) {
3746
                    changed['stroke-linejoin'] = this._join;
3747
                }
3748
3749
                if (this._flagMiter) {
3750
                    changed['stroke-miterlimit'] = this._miter;
3751
                }
3752
3753
                // If there is no attached DOM element yet,
3754
                // create it with all necessary attributes.
3755
                if (!this._renderer.elem) {
3756
3757
                    changed.id = this.id;
3758
                    this._renderer.elem = svg.createElement('path', changed);
3759
                    domElement.appendChild(this._renderer.elem);
3760
3761
                    // Otherwise apply all pending attributes
3762
                } else {
3763
                    svg.setAttributes(this._renderer.elem, changed);
3764
                }
3765
3766
                if (this._flagClip) {
3767
3768
                    var clip = svg.getClip(this);
3769
                    var elem = this._renderer.elem;
3770
3771
                    if (this._clip) {
3772
                        elem.removeAttribute('id');
3773
                        clip.setAttribute('id', this.id);
3774
                        clip.appendChild(elem);
3775
                    } else {
3776
                        clip.removeAttribute('id');
3777
                        elem.setAttribute('id', this.id);
3778
                        this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore
3779
                    }
3780
3781
                }
3782
3783
                /**
3784
                    * Commented two-way functionality of clips / masks with groups and
3785
                    * polygons. Uncomment when this bug is fixed:
3786
                    * https://code.google.com/p/chromium/issues/detail?id=370951
3787
                    */
3788
3789
                // if (this._flagMask) {
3790
                //   if (this._mask) {
3791
                //     elem.setAttribute('clip-path', 'url(#' + this._mask.id + ')');
3792
                //   } else {
3793
                //     elem.removeAttribute('clip-path');
3794
                //   }
3795
                // }
3796
3797
                return this.flagReset();
3798
3799
            }
3800
3801
        },
3802
3803
        text: {
3804
3805
            render: function (domElement) {
3806
3807
                this._update();
3808
3809
                var changed = {};
3810
3811
                var flagMatrix = this._matrix.manual || this._flagMatrix;
3812
3813
                if (flagMatrix) {
3814
                    changed.transform = 'matrix(' + this._matrix.toString() + ')';
3815
                }
3816
3817
                if (this._flagFamily) {
3818
                    changed['font-family'] = this._family;
3819
                }
3820
                if (this._flagSize) {
3821
                    changed['font-size'] = this._size;
3822
                }
3823
                if (this._flagLeading) {
3824
                    changed['line-height'] = this._leading;
3825
                }
3826
                if (this._flagAlignment) {
3827
                    changed['text-anchor'] = svg.alignments[this._alignment] || this._alignment;
3828
                }
3829
                if (this._flagBaseline) {
3830
                    changed['alignment-baseline'] = changed['dominant-baseline'] = this._baseline;
3831
                }
3832
                if (this._flagStyle) {
3833
                    changed['font-style'] = this._style;
3834
                }
3835
                if (this._flagWeight) {
3836
                    changed['font-weight'] = this._weight;
3837
                }
3838
                if (this._flagDecoration) {
3839
                    changed['text-decoration'] = this._decoration;
3840
                }
3841
                if (this._fill && this._fill._renderer) {
3842
                    this._fill._update();
3843
                    svg[this._fill._renderer.type].render.call(this._fill, domElement, true);
3844
                }
3845
                if (this._flagFill) {
3846
                    changed.fill = this._fill && this._fill.id
3847
                        ? 'url(#' + this._fill.id + ')' : this._fill;
3848
                }
3849
                if (this._stroke && this._stroke._renderer) {
3850
                    this._stroke._update();
3851
                    svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true);
3852
                }
3853
                if (this._flagStroke) {
3854
                    changed.stroke = this._stroke && this._stroke.id
3855
                        ? 'url(#' + this._stroke.id + ')' : this._stroke;
3856
                }
3857
                if (this._flagLinewidth) {
3858
                    changed['stroke-width'] = this._linewidth;
3859
                }
3860
                if (this._flagOpacity) {
3861
                    changed.opacity = this._opacity;
3862
                }
3863
                if (this._flagVisible) {
3864
                    changed.visibility = this._visible ? 'visible' : 'hidden';
3865
                }
3866
3867
                if (!this._renderer.elem) {
3868
3869
                    changed.id = this.id;
3870
3871
                    this._renderer.elem = svg.createElement('text', changed);
3872
                    domElement.defs.appendChild(this._renderer.elem);
3873
3874
                } else {
3875
3876
                    svg.setAttributes(this._renderer.elem, changed);
3877
3878
                }
3879
3880
                if (this._flagClip) {
3881
3882
                    var clip = svg.getClip(this);
3883
                    var elem = this._renderer.elem;
3884
3885
                    if (this._clip) {
3886
                        elem.removeAttribute('id');
3887
                        clip.setAttribute('id', this.id);
3888
                        clip.appendChild(elem);
3889
                    } else {
3890
                        clip.removeAttribute('id');
3891
                        elem.setAttribute('id', this.id);
3892
                        this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore
3893
                    }
3894
3895
                }
3896
3897
                if (this._flagValue) {
3898
                    this._renderer.elem.textContent = this._value;
3899
                }
3900
3901
                return this.flagReset();
3902
3903
            }
3904
3905
        },
3906
3907
        'linear-gradient': {
3908
3909
            render: function (domElement, silent) {
3910
3911
                if (!silent) {
3912
                    this._update();
3913
                }
3914
3915
                var changed = {};
3916
3917
                if (this._flagEndPoints) {
3918
                    changed.x1 = this.left._x;
3919
                    changed.y1 = this.left._y;
3920
                    changed.x2 = this.right._x;
3921
                    changed.y2 = this.right._y;
3922
                }
3923
3924
                if (this._flagSpread) {
3925
                    changed.spreadMethod = this._spread;
3926
                }
3927
3928
                // If there is no attached DOM element yet,
3929
                // create it with all necessary attributes.
3930
                if (!this._renderer.elem) {
3931
3932
                    changed.id = this.id;
3933
                    changed.gradientUnits = 'userSpaceOnUse';
3934
                    this._renderer.elem = svg.createElement('linearGradient', changed);
3935
                    domElement.defs.appendChild(this._renderer.elem);
3936
3937
                    // Otherwise apply all pending attributes
3938
                } else {
3939
3940
                    svg.setAttributes(this._renderer.elem, changed);
3941
3942
                }
3943
3944
                if (this._flagStops) {
3945
3946
                    var lengthChanged = this._renderer.elem.childNodes.length
3947
                        !== this.stops.length;
3948
3949
                    if (lengthChanged) {
3950
                        this._renderer.elem.childNodes.length = 0;
3951
                    }
3952
3953
                    for (var i = 0; i < this.stops.length; i++) {
3954
3955
                        var stop = this.stops[i];
3956
                        var attrs = {};
3957
3958
                        if (stop._flagOffset) {
3959
                            attrs.offset = 100 * stop._offset + '%';
3960
                        }
3961
                        if (stop._flagColor) {
3962
                            attrs['stop-color'] = stop._color;
3963
                        }
3964
                        if (stop._flagOpacity) {
3965
                            attrs['stop-opacity'] = stop._opacity;
3966
                        }
3967
3968
                        if (!stop._renderer.elem) {
3969
                            stop._renderer.elem = svg.createElement('stop', attrs);
3970
                        } else {
3971
                            svg.setAttributes(stop._renderer.elem, attrs);
3972
                        }
3973
3974
                        if (lengthChanged) {
3975
                            this._renderer.elem.appendChild(stop._renderer.elem);
3976
                        }
3977
                        stop.flagReset();
3978
3979
                    }
3980
3981
                }
3982
3983
                return this.flagReset();
3984
3985
            }
3986
3987
        },
3988
3989
        'radial-gradient': {
3990
3991
            render: function (domElement, silent) {
3992
3993
                if (!silent) {
3994
                    this._update();
3995
                }
3996
3997
                var changed = {};
3998
3999
                if (this._flagCenter) {
4000
                    changed.cx = this.center._x;
4001
                    changed.cy = this.center._y;
4002
                }
4003
                if (this._flagFocal) {
4004
                    changed.fx = this.focal._x;
4005
                    changed.fy = this.focal._y;
4006
                }
4007
4008
                if (this._flagRadius) {
4009
                    changed.r = this._radius;
4010
                }
4011
4012
                if (this._flagSpread) {
4013
                    changed.spreadMethod = this._spread;
4014
                }
4015
4016
                // If there is no attached DOM element yet,
4017
                // create it with all necessary attributes.
4018
                if (!this._renderer.elem) {
4019
4020
                    changed.id = this.id;
4021
                    changed.gradientUnits = 'userSpaceOnUse';
4022
                    this._renderer.elem = svg.createElement('radialGradient', changed);
4023
                    domElement.defs.appendChild(this._renderer.elem);
4024
4025
                    // Otherwise apply all pending attributes
4026
                } else {
4027
4028
                    svg.setAttributes(this._renderer.elem, changed);
4029
4030
                }
4031
4032
                if (this._flagStops) {
4033
4034
                    var lengthChanged = this._renderer.elem.childNodes.length
4035
                        !== this.stops.length;
4036
4037
                    if (lengthChanged) {
4038
                        this._renderer.elem.childNodes.length = 0;
4039
                    }
4040
4041
                    for (var i = 0; i < this.stops.length; i++) {
4042
4043
                        var stop = this.stops[i];
4044
                        var attrs = {};
4045
4046
                        if (stop._flagOffset) {
4047
                            attrs.offset = 100 * stop._offset + '%';
4048
                        }
4049
                        if (stop._flagColor) {
4050
                            attrs['stop-color'] = stop._color;
4051
                        }
4052
                        if (stop._flagOpacity) {
4053
                            attrs['stop-opacity'] = stop._opacity;
4054
                        }
4055
4056
                        if (!stop._renderer.elem) {
4057
                            stop._renderer.elem = svg.createElement('stop', attrs);
4058
                        } else {
4059
                            svg.setAttributes(stop._renderer.elem, attrs);
4060
                        }
4061
4062
                        if (lengthChanged) {
4063
                            this._renderer.elem.appendChild(stop._renderer.elem);
4064
                        }
4065
                        stop.flagReset();
4066
4067
                    }
4068
4069
                }
4070
4071
                return this.flagReset();
4072
4073
            }
4074
4075
        },
4076
4077
        texture: {
4078
4079
            render: function (domElement, silent) {
4080
4081
                if (!silent) {
4082
                    this._update();
4083
                }
4084
4085
                var changed = {};
4086
                var styles = { x: 0, y: 0 };
4087
                var image = this.image;
4088
4089
                if (this._flagLoaded && this.loaded) {
4090
4091
                    switch (image.nodeName.toLowerCase()) {
4092
4093
                        case 'canvas':
4094
                            styles.href = styles['xlink:href'] = image.toDataURL('image/png');
4095
                            break;
4096
                        case 'img':
4097
                        case 'image':
4098
                            styles.href = styles['xlink:href'] = this.src;
4099
                            break;
4100
4101
                    }
4102
4103
                }
4104
4105
                if (this._flagOffset || this._flagLoaded || this._flagScale) {
4106
4107
                    changed.x = this._offset.x;
4108
                    changed.y = this._offset.y;
4109
4110
                    if (image) {
4111
4112
                        changed.x -= image.width / 2;
4113
                        changed.y -= image.height / 2;
4114
4115
                        if (this._scale instanceof Two.Vector) {
4116
                            changed.x *= this._scale.x;
4117
                            changed.y *= this._scale.y;
4118
                        } else {
4119
                            changed.x *= this._scale;
4120
                            changed.y *= this._scale;
4121
                        }
4122
                    }
4123
4124
                    if (changed.x > 0) {
4125
                        changed.x *= - 1;
4126
                    }
4127
                    if (changed.y > 0) {
4128
                        changed.y *= - 1;
4129
                    }
4130
4131
                }
4132
4133
                if (this._flagScale || this._flagLoaded || this._flagRepeat) {
4134
4135
                    changed.width = 0;
4136
                    changed.height = 0;
4137
4138
                    if (image) {
4139
4140
                        styles.width = changed.width = image.width;
4141
                        styles.height = changed.height = image.height;
4142
4143
                        // TODO: Hack / Bandaid
4144
                        switch (this._repeat) {
4145
                            case 'no-repeat':
4146
                                changed.width += 1;
4147
                                changed.height += 1;
4148
                                break;
4149
                        }
4150
4151
                        if (this._scale instanceof Two.Vector) {
4152
                            changed.width *= this._scale.x;
4153
                            changed.height *= this._scale.y;
4154
                        } else {
4155
                            changed.width *= this._scale;
4156
                            changed.height *= this._scale;
4157
                        }
4158
                    }
4159
4160
                }
4161
4162
                if (this._flagScale || this._flagLoaded) {
4163
                    if (!this._renderer.image) {
4164
                        this._renderer.image = svg.createElement('image', styles);
4165
                    } else if (!_.isEmpty(styles)) {
4166
                        svg.setAttributes(this._renderer.image, styles);
4167
                    }
4168
                }
4169
4170
                if (!this._renderer.elem) {
4171
4172
                    changed.id = this.id;
4173
                    changed.patternUnits = 'userSpaceOnUse';
4174
                    this._renderer.elem = svg.createElement('pattern', changed);
4175
                    domElement.defs.appendChild(this._renderer.elem);
4176
4177
                } else if (!_.isEmpty(changed)) {
4178
4179
                    svg.setAttributes(this._renderer.elem, changed);
4180
4181
                }
4182
4183
                if (this._renderer.elem && this._renderer.image && !this._renderer.appended) {
4184
                    this._renderer.elem.appendChild(this._renderer.image);
4185
                    this._renderer.appended = true;
4186
                }
4187
4188
                return this.flagReset();
4189
4190
            }
4191
4192
        }
4193
4194
    };
4195
4196
    /**
4197
        * @class
4198
        */
4199
    var Renderer = Two[Two.Types.svg] = function (params) {
4200
4201
        this.domElement = params.domElement || svg.createElement('svg');
4202
4203
        this.scene = new Two.Group();
4204
        this.scene.parent = this;
4205
4206
        this.defs = svg.createElement('defs');
4207
        this.domElement.appendChild(this.defs);
4208
        this.domElement.defs = this.defs;
4209
        this.domElement.style.overflow = 'hidden';
4210
4211
    };
4212
4213
    _.extend(Renderer, {
4214
4215
        Utils: svg
4216
4217
    });
4218
4219
    _.extend(Renderer.prototype, Two.Utils.Events, {
4220
4221
        setSize: function (width, height) {
4222
4223
            this.width = width;
4224
            this.height = height;
4225
4226
            svg.setAttributes(this.domElement, {
4227
                width: width,
4228
                height: height
4229
            });
4230
4231
            return this;
4232
4233
        },
4234
4235
        render: function () {
4236
4237
            svg.group.render.call(this.scene, this.domElement);
4238
4239
            return this;
4240
4241
        }
4242
4243
    });
4244
4245
})((typeof global !== 'undefined' ? global : this).Two);
4246
4247
(function (Two) {
4248
4249
    /**
4250
        * Constants
4251
        */
4252
    var mod = Two.Utils.mod, toFixed = Two.Utils.toFixed;
4253
    var getRatio = Two.Utils.getRatio;
4254
    var _ = Two.Utils;
4255
4256
    // Returns true if this is a non-transforming matrix
4257
    var isDefaultMatrix = function (m) {
4258
        return (m[0] == 1 && m[3] == 0 && m[1] == 0 && m[4] == 1 && m[2] == 0 && m[5] == 0);
4259
    };
4260
4261
    var canvas = {
4262
4263
        isHidden: /(none|transparent)/i,
4264
4265
        alignments: {
4266
            left: 'start',
4267
            middle: 'center',
4268
            right: 'end'
4269
        },
4270
4271
        shim: function (elem) {
4272
            elem.tagName = 'canvas';
4273
            elem.nodeType = 1;
4274
            return elem;
4275
        },
4276
4277
        group: {
4278
4279
            renderChild: function (child) {
4280
                canvas[child._renderer.type].render.call(child, this.ctx, true, this.clip);
4281
            },
4282
4283
            render: function (ctx) {
4284
4285
                // TODO: Add a check here to only invoke _update if need be.
4286
                this._update();
4287
4288
                var matrix = this._matrix.elements;
4289
                var parent = this.parent;
4290
                this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1);
4291
4292
                var defaultMatrix = isDefaultMatrix(matrix);
4293
4294
                var mask = this._mask;
4295
                // var clip = this._clip;
4296
4297
                if (!this._renderer.context) {
4298
                    this._renderer.context = {};
4299
                }
4300
4301
                this._renderer.context.ctx = ctx;
4302
                // this._renderer.context.clip = clip;
4303
4304
                if (!defaultMatrix) {
4305
                    ctx.save();
4306
                    ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]);
4307
                }
4308
4309
                if (mask) {
4310
                    canvas[mask._renderer.type].render.call(mask, ctx, true);
4311
                }
4312
4313
                if (this.opacity > 0 && this.scale !== 0) {
4314
                    for (var i = 0; i < this.children.length; i++) {
4315
                        var child = this.children[i];
4316
                        canvas[child._renderer.type].render.call(child, ctx);
4317
                    }
4318
                }
4319
4320
                if (!defaultMatrix) {
4321
                    ctx.restore();
4322
                }
4323
4324
                /**
4325
                    * Commented two-way functionality of clips / masks with groups and
4326
                    * polygons. Uncomment when this bug is fixed:
4327
                    * https://code.google.com/p/chromium/issues/detail?id=370951
4328
                    */
4329
4330
                // if (clip) {
4331
                //   ctx.clip();
4332
                // }
4333
4334
                return this.flagReset();
4335
4336
            }
4337
4338
        },
4339
4340
        path: {
4341
4342
            render: function (ctx, forced, parentClipped) {
4343
4344
                var matrix, stroke, linewidth, fill, opacity, visible, cap, join, miter,
4345
                    closed, commands, length, last, next, prev, a, b, c, d, ux, uy, vx, vy,
4346
                    ar, bl, br, cl, x, y, mask, clip, defaultMatrix, isOffset;
4347
4348
                // TODO: Add a check here to only invoke _update if need be.
4349
                this._update();
4350
4351
                matrix = this._matrix.elements;
4352
                stroke = this._stroke;
4353
                linewidth = this._linewidth;
4354
                fill = this._fill;
4355
                opacity = this._opacity * this.parent._renderer.opacity;
4356
                visible = this._visible;
4357
                cap = this._cap;
4358
                join = this._join;
4359
                miter = this._miter;
4360
                closed = this._closed;
4361
                commands = this._vertices; // Commands
4362
                length = commands.length;
4363
                last = length - 1;
4364
                defaultMatrix = isDefaultMatrix(matrix);
4365
4366
                // mask = this._mask;
4367
                clip = this._clip;
4368
4369
                if (!forced && (!visible || clip)) {
4370
                    return this;
4371
                }
4372
4373
                // Transform
4374
                if (!defaultMatrix) {
4375
                    ctx.save();
4376
                    ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]);
4377
                }
4378
4379
                /**
4380
                    * Commented two-way functionality of clips / masks with groups and
4381
                    * polygons. Uncomment when this bug is fixed:
4382
                    * https://code.google.com/p/chromium/issues/detail?id=370951
4383
                    */
4384
4385
                // if (mask) {
4386
                //   canvas[mask._renderer.type].render.call(mask, ctx, true);
4387
                // }
4388
4389
                // Styles
4390
                if (fill) {
4391
                    if (_.isString(fill)) {
4392
                        ctx.fillStyle = fill;
4393
                    } else {
4394
                        canvas[fill._renderer.type].render.call(fill, ctx);
4395
                        ctx.fillStyle = fill._renderer.effect;
4396
                    }
4397
                }
4398
                if (stroke) {
4399
                    if (_.isString(stroke)) {
4400
                        ctx.strokeStyle = stroke;
4401
                    } else {
4402
                        canvas[stroke._renderer.type].render.call(stroke, ctx);
4403
                        ctx.strokeStyle = stroke._renderer.effect;
4404
                    }
4405
                }
4406
                if (linewidth) {
4407
                    ctx.lineWidth = linewidth;
4408
                }
4409
                if (miter) {
4410
                    ctx.miterLimit = miter;
4411
                }
4412
                if (join) {
4413
                    ctx.lineJoin = join;
4414
                }
4415
                if (cap) {
4416
                    ctx.lineCap = cap;
4417
                }
4418
                if (_.isNumber(opacity)) {
4419
                    ctx.globalAlpha = opacity;
4420
                }
4421
4422
                ctx.beginPath();
4423
4424
                for (var i = 0; i < commands.length; i++) {
4425
4426
                    b = commands[i];
4427
4428
                    x = toFixed(b._x);
4429
                    y = toFixed(b._y);
4430
4431
                    switch (b._command) {
4432
4433
                        case Two.Commands.close:
4434
                            ctx.closePath();
4435
                            break;
4436
4437
                        case Two.Commands.curve:
4438
4439
                            prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0);
4440
                            next = closed ? mod(i + 1, length) : Math.min(i + 1, last);
4441
4442
                            a = commands[prev];
4443
                            c = commands[next];
4444
                            ar = (a.controls && a.controls.right) || Two.Vector.zero;
4445
                            bl = (b.controls && b.controls.left) || Two.Vector.zero;
4446
4447
                            if (a._relative) {
4448
                                vx = (ar.x + toFixed(a._x));
4449
                                vy = (ar.y + toFixed(a._y));
4450
                            } else {
4451
                                vx = toFixed(ar.x);
4452
                                vy = toFixed(ar.y);
4453
                            }
4454
4455
                            if (b._relative) {
4456
                                ux = (bl.x + toFixed(b._x));
4457
                                uy = (bl.y + toFixed(b._y));
4458
                            } else {
4459
                                ux = toFixed(bl.x);
4460
                                uy = toFixed(bl.y);
4461
                            }
4462
4463
                            ctx.bezierCurveTo(vx, vy, ux, uy, x, y);
4464
4465
                            if (i >= last && closed) {
4466
4467
                                c = d;
4468
4469
                                br = (b.controls && b.controls.right) || Two.Vector.zero;
4470
                                cl = (c.controls && c.controls.left) || Two.Vector.zero;
4471
4472
                                if (b._relative) {
4473
                                    vx = (br.x + toFixed(b._x));
4474
                                    vy = (br.y + toFixed(b._y));
4475
                                } else {
4476
                                    vx = toFixed(br.x);
4477
                                    vy = toFixed(br.y);
4478
                                }
4479
4480
                                if (c._relative) {
4481
                                    ux = (cl.x + toFixed(c._x));
4482
                                    uy = (cl.y + toFixed(c._y));
4483
                                } else {
4484
                                    ux = toFixed(cl.x);
4485
                                    uy = toFixed(cl.y);
4486
                                }
4487
4488
                                x = toFixed(c._x);
4489
                                y = toFixed(c._y);
4490
4491
                                ctx.bezierCurveTo(vx, vy, ux, uy, x, y);
4492
4493
                            }
4494
4495
                            break;
4496
4497
                        case Two.Commands.line:
4498
                            ctx.lineTo(x, y);
4499
                            break;
4500
4501
                        case Two.Commands.move:
4502
                            d = b;
4503
                            ctx.moveTo(x, y);
4504
                            break;
4505
4506
                    }
4507
                }
4508
4509
                // Loose ends
4510
4511
                if (closed) {
4512
                    ctx.closePath();
4513
                }
4514
4515
                if (!clip && !parentClipped) {
4516
                    if (!canvas.isHidden.test(fill)) {
4517
                        isOffset = fill._renderer && fill._renderer.offset
4518
                        if (isOffset) {
4519
                            ctx.save();
4520
                            ctx.translate(
4521
                                - fill._renderer.offset.x, - fill._renderer.offset.y);
4522
                            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);
4523
                        }
4524
                        ctx.fill();
4525
                        if (isOffset) {
4526
                            ctx.restore();
4527
                        }
4528
                    }
4529
                    if (!canvas.isHidden.test(stroke)) {
4530
                        isOffset = stroke._renderer && stroke._renderer.offset;
4531
                        if (isOffset) {
4532
                            ctx.save();
4533
                            ctx.translate(
4534
                                - stroke._renderer.offset.x, - stroke._renderer.offset.y);
4535
                            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);
4536
                            ctx.lineWidth = linewidth / stroke._renderer.scale.x;
4537
                        }
4538
                        ctx.stroke();
4539
                        if (isOffset) {
4540
                            ctx.restore();
4541
                        }
4542
                    }
4543
                }
4544
4545
                if (!defaultMatrix) {
4546
                    ctx.restore();
4547
                }
4548
4549
                if (clip && !parentClipped) {
4550
                    ctx.clip();
4551
                }
4552
4553
                return this.flagReset();
4554
4555
            }
4556
4557
        },
4558
4559
        text: {
4560
4561
            render: function (ctx, forced, parentClipped) {
4562
4563
                // TODO: Add a check here to only invoke _update if need be.
4564
                this._update();
4565
4566
                var matrix = this._matrix.elements;
4567
                var stroke = this._stroke;
4568
                var linewidth = this._linewidth;
4569
                var fill = this._fill;
4570
                var opacity = this._opacity * this.parent._renderer.opacity;
4571
                var visible = this._visible;
4572
                var defaultMatrix = isDefaultMatrix(matrix);
4573
                var isOffset = fill._renderer && fill._renderer.offset
4574
                    && stroke._renderer && stroke._renderer.offset;
4575
4576
                var a, b, c, d, e, sx, sy;
4577
4578
                // mask = this._mask;
4579
                var clip = this._clip;
4580
4581
                if (!forced && (!visible || clip)) {
4582
                    return this;
4583
                }
4584
4585
                // Transform
4586
                if (!defaultMatrix) {
4587
                    ctx.save();
4588
                    ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]);
4589
                }
4590
4591
                /**
4592
                    * Commented two-way functionality of clips / masks with groups and
4593
                    * polygons. Uncomment when this bug is fixed:
4594
                    * https://code.google.com/p/chromium/issues/detail?id=370951
4595
                    */
4596
4597
                // if (mask) {
4598
                //   canvas[mask._renderer.type].render.call(mask, ctx, true);
4599
                // }
4600
4601
                if (!isOffset) {
4602
                    ctx.font = [this._style, this._weight, this._size + 'px/' +
4603
                        this._leading + 'px', this._family].join(' ');
4604
                }
4605
4606
                ctx.textAlign = canvas.alignments[this._alignment] || this._alignment;
4607
                ctx.textBaseline = this._baseline;
4608
4609
                // Styles
4610
                if (fill) {
4611
                    if (_.isString(fill)) {
4612
                        ctx.fillStyle = fill;
4613
                    } else {
4614
                        canvas[fill._renderer.type].render.call(fill, ctx);
4615
                        ctx.fillStyle = fill._renderer.effect;
4616
                    }
4617
                }
4618
                if (stroke) {
4619
                    if (_.isString(stroke)) {
4620
                        ctx.strokeStyle = stroke;
4621
                    } else {
4622
                        canvas[stroke._renderer.type].render.call(stroke, ctx);
4623
                        ctx.strokeStyle = stroke._renderer.effect;
4624
                    }
4625
                }
4626
                if (linewidth) {
4627
                    ctx.lineWidth = linewidth;
4628
                }
4629
                if (_.isNumber(opacity)) {
4630
                    ctx.globalAlpha = opacity;
4631
                }
4632
4633
                if (!clip && !parentClipped) {
4634
4635
                    if (!canvas.isHidden.test(fill)) {
4636
4637
                        if (fill._renderer && fill._renderer.offset) {
4638
4639
                            sx = toFixed(fill._renderer.scale.x);
4640
                            sy = toFixed(fill._renderer.scale.y);
4641
4642
                            ctx.save();
4643
                            ctx.translate(- toFixed(fill._renderer.offset.x),
4644
                                - toFixed(fill._renderer.offset.y));
4645
                            ctx.scale(sx, sy);
4646
4647
                            a = this._size / fill._renderer.scale.y;
4648
                            b = this._leading / fill._renderer.scale.y;
4649
                            ctx.font = [this._style, this._weight, toFixed(a) + 'px/',
4650
                            toFixed(b) + 'px', this._family].join(' ');
4651
4652
                            c = fill._renderer.offset.x / fill._renderer.scale.x;
4653
                            d = fill._renderer.offset.y / fill._renderer.scale.y;
4654
4655
                            ctx.fillText(this.value, toFixed(c), toFixed(d));
4656
                            ctx.restore();
4657
4658
                        } else {
4659
                            ctx.fillText(this.value, 0, 0);
4660
                        }
4661
4662
                    }
4663
4664
                    if (!canvas.isHidden.test(stroke)) {
4665
4666
                        if (stroke._renderer && stroke._renderer.offset) {
4667
4668
                            sx = toFixed(stroke._renderer.scale.x);
4669
                            sy = toFixed(stroke._renderer.scale.y);
4670
4671
                            ctx.save();
4672
                            ctx.translate(- toFixed(stroke._renderer.offset.x),
4673
                                - toFixed(stroke._renderer.offset.y));
4674
                            ctx.scale(sx, sy);
4675
4676
                            a = this._size / stroke._renderer.scale.y;
4677
                            b = this._leading / stroke._renderer.scale.y;
4678
                            ctx.font = [this._style, this._weight, toFixed(a) + 'px/',
4679
                            toFixed(b) + 'px', this._family].join(' ');
4680
4681
                            c = stroke._renderer.offset.x / stroke._renderer.scale.x;
4682
                            d = stroke._renderer.offset.y / stroke._renderer.scale.y;
4683
                            e = linewidth / stroke._renderer.scale.x;
4684
4685
                            ctx.lineWidth = toFixed(e);
4686
                            ctx.strokeText(this.value, toFixed(c), toFixed(d));
4687
                            ctx.restore();
4688
4689
                        } else {
4690
                            ctx.strokeText(this.value, 0, 0);
4691
                        }
4692
                    }
4693
                }
4694
4695
                if (!defaultMatrix) {
4696
                    ctx.restore();
4697
                }
4698
4699
                // TODO: Test for text
4700
                if (clip && !parentClipped) {
4701
                    ctx.clip();
4702
                }
4703
4704
                return this.flagReset();
4705
4706
            }
4707
4708
        },
4709
4710
        'linear-gradient': {
4711
4712
            render: function (ctx) {
4713
4714
                this._update();
4715
4716
                if (!this._renderer.effect || this._flagEndPoints || this._flagStops) {
4717
4718
                    this._renderer.effect = ctx.createLinearGradient(
4719
                        this.left._x, this.left._y,
4720
                        this.right._x, this.right._y
4721
                    );
4722
4723
                    for (var i = 0; i < this.stops.length; i++) {
4724
                        var stop = this.stops[i];
4725
                        this._renderer.effect.addColorStop(stop._offset, stop._color);
4726
                    }
4727
4728
                }
4729
4730
                return this.flagReset();
4731
4732
            }
4733
4734
        },
4735
4736
        'radial-gradient': {
4737
4738
            render: function (ctx) {
4739
4740
                this._update();
4741
4742
                if (!this._renderer.effect || this._flagCenter || this._flagFocal
4743
                    || this._flagRadius || this._flagStops) {
4744
4745
                    this._renderer.effect = ctx.createRadialGradient(
4746
                        this.center._x, this.center._y, 0,
4747
                        this.focal._x, this.focal._y, this._radius
4748
                    );
4749
4750
                    for (var i = 0; i < this.stops.length; i++) {
4751
                        var stop = this.stops[i];
4752
                        this._renderer.effect.addColorStop(stop._offset, stop._color);
4753
                    }
4754
4755
                }
4756
4757
                return this.flagReset();
4758
4759
            }
4760
4761
        },
4762
4763
        texture: {
4764
4765
            render: function (ctx) {
4766
4767
                this._update();
4768
4769
                var image = this.image;
4770
                var repeat;
4771
4772
                if (!this._renderer.effect || ((this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded)) {
4773
                    this._renderer.effect = ctx.createPattern(this.image, this._repeat);
4774
                }
4775
4776
                if (this._flagOffset || this._flagLoaded || this._flagScale) {
4777
4778
                    if (!(this._renderer.offset instanceof Two.Vector)) {
4779
                        this._renderer.offset = new Two.Vector();
4780
                    }
4781
4782
                    this._renderer.offset.x = - this._offset.x;
4783
                    this._renderer.offset.y = - this._offset.y;
4784
4785
                    if (image) {
4786
4787
                        this._renderer.offset.x += image.width / 2;
4788
                        this._renderer.offset.y += image.height / 2;
4789
4790
                        if (this._scale instanceof Two.Vector) {
4791
                            this._renderer.offset.x *= this._scale.x;
4792
                            this._renderer.offset.y *= this._scale.y;
4793
                        } else {
4794
                            this._renderer.offset.x *= this._scale;
4795
                            this._renderer.offset.y *= this._scale;
4796
                        }
4797
                    }
4798
4799
                }
4800
4801
                if (this._flagScale || this._flagLoaded) {
4802
4803
                    if (!(this._renderer.scale instanceof Two.Vector)) {
4804
                        this._renderer.scale = new Two.Vector();
4805
                    }
4806
4807
                    if (this._scale instanceof Two.Vector) {
4808
                        this._renderer.scale.copy(this._scale);
4809
                    } else {
4810
                        this._renderer.scale.set(this._scale, this._scale);
4811
                    }
4812
4813
                }
4814
4815
                return this.flagReset();
4816
4817
            }
4818
4819
        }
4820
4821
    };
4822
4823
    var Renderer = Two[Two.Types.canvas] = function (params) {
4824
        // Smoothing property. Defaults to true
4825
        // Set it to false when working with pixel art.
4826
        // false can lead to better performance, since it would use a cheaper interpolation algorithm.
4827
        // It might not make a big difference on GPU backed canvases.
4828
        var smoothing = (params.smoothing !== false);
4829
        this.domElement = params.domElement || document.createElement('canvas');
4830
        this.ctx = this.domElement.getContext('2d');
4831
        this.overdraw = params.overdraw || false;
4832
4833
        if (!_.isUndefined(this.ctx.imageSmoothingEnabled)) {
4834
            this.ctx.imageSmoothingEnabled = smoothing;
4835
        }
4836
4837
        // Everything drawn on the canvas needs to be added to the scene.
4838
        this.scene = new Two.Group();
4839
        this.scene.parent = this;
4840
    };
4841
4842
4843
    _.extend(Renderer, {
4844
4845
        Utils: canvas
4846
4847
    });
4848
4849
    _.extend(Renderer.prototype, Two.Utils.Events, {
4850
4851
        setSize: function (width, height, ratio) {
4852
4853
            this.width = width;
4854
            this.height = height;
4855
4856
            this.ratio = _.isUndefined(ratio) ? getRatio(this.ctx) : ratio;
4857
4858
            this.domElement.width = width * this.ratio;
4859
            this.domElement.height = height * this.ratio;
4860
4861
            if (this.domElement.style) {
4862
                _.extend(this.domElement.style, {
4863
                    width: width + 'px',
4864
                    height: height + 'px'
4865
                });
4866
            }
4867
4868
            return this;
4869
4870
        },
4871
4872
        render: function () {
4873
4874
            var isOne = this.ratio === 1;
4875
4876
            if (!isOne) {
4877
                this.ctx.save();
4878
                this.ctx.scale(this.ratio, this.ratio);
4879
            }
4880
4881
            if (!this.overdraw) {
4882
                this.ctx.clearRect(0, 0, this.width, this.height);
4883
            }
4884
4885
            canvas.group.render.call(this.scene, this.ctx);
4886
4887
            if (!isOne) {
4888
                this.ctx.restore();
4889
            }
4890
4891
            return this;
4892
4893
        }
4894
4895
    });
4896
4897
    function resetTransform(ctx) {
4898
        ctx.setTransform(1, 0, 0, 1, 0, 0);
4899
    }
4900
4901
})((typeof global !== 'undefined' ? global : this).Two);
4902
4903
(function (Two) {
4904
4905
    /**
4906
        * Constants
4907
        */
4908
4909
    var root = Two.root,
4910
        multiplyMatrix = Two.Matrix.Multiply,
4911
        mod = Two.Utils.mod,
4912
        identity = [1, 0, 0, 0, 1, 0, 0, 0, 1],
4913
        transformation = new Two.Array(9),
4914
        getRatio = Two.Utils.getRatio,
4915
        getComputedMatrix = Two.Utils.getComputedMatrix,
4916
        toFixed = Two.Utils.toFixed,
4917
        _ = Two.Utils;
4918
4919
    var webgl = {
4920
4921
        isHidden: /(none|transparent)/i,
4922
4923
        canvas: (root.document ? root.document.createElement('canvas') : { getContext: _.identity }),
4924
4925
        alignments: {
4926
            left: 'start',
4927
            middle: 'center',
4928
            right: 'end'
4929
        },
4930
4931
        matrix: new Two.Matrix(),
4932
4933
        uv: new Two.Array([
4934
            0, 0,
4935
            1, 0,
4936
            0, 1,
4937
            0, 1,
4938
            1, 0,
4939
            1, 1
4940
        ]),
4941
4942
        group: {
4943
4944
            removeChild: function (child, gl) {
4945
                if (child.children) {
4946
                    for (var i = 0; i < child.children.length; i++) {
4947
                        webgl.group.removeChild(child.children[i], gl);
4948
                    }
4949
                    return;
4950
                }
4951
                // Deallocate texture to free up gl memory.
4952
                gl.deleteTexture(child._renderer.texture);
4953
                delete child._renderer.texture;
4954
            },
4955
4956
            renderChild: function (child) {
4957
                webgl[child._renderer.type].render.call(child, this.gl, this.program);
4958
            },
4959
4960
            render: function (gl, program) {
4961
4962
                this._update();
4963
4964
                var parent = this.parent;
4965
                var flagParentMatrix = (parent._matrix && parent._matrix.manual) || parent._flagMatrix;
4966
                var flagMatrix = this._matrix.manual || this._flagMatrix;
4967
4968
                if (flagParentMatrix || flagMatrix) {
4969
4970
                    if (!this._renderer.matrix) {
4971
                        this._renderer.matrix = new Two.Array(9);
4972
                    }
4973
4974
                    // Reduce amount of object / array creation / deletion
4975
                    this._matrix.toArray(true, transformation);
4976
4977
                    multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix);
4978
                    this._renderer.scale = this._scale * parent._renderer.scale;
4979
4980
                    if (flagParentMatrix) {
4981
                        this._flagMatrix = true;
4982
                    }
4983
4984
                }
4985
4986
                if (this._mask) {
4987
4988
                    gl.enable(gl.STENCIL_TEST);
4989
                    gl.stencilFunc(gl.ALWAYS, 1, 1);
4990
4991
                    gl.colorMask(false, false, false, true);
4992
                    gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
4993
4994
                    webgl[this._mask._renderer.type].render.call(this._mask, gl, program, this);
4995
4996
                    gl.colorMask(true, true, true, true);
4997
                    gl.stencilFunc(gl.NOTEQUAL, 0, 1);
4998
                    gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
4999
5000
                }
5001
5002
                this._flagOpacity = parent._flagOpacity || this._flagOpacity;
5003
5004
                this._renderer.opacity = this._opacity
5005
                    * (parent && parent._renderer ? parent._renderer.opacity : 1);
5006
5007
                if (this._flagSubtractions) {
5008
                    for (var i = 0; i < this.subtractions.length; i++) {
5009
                        webgl.group.removeChild(this.subtractions[i], gl);
5010
                    }
5011
                }
5012
5013
                this.children.forEach(webgl.group.renderChild, {
5014
                    gl: gl,
5015
                    program: program
5016
                });
5017
5018
                if (this._mask) {
5019
5020
                    gl.colorMask(false, false, false, false);
5021
                    gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR);
5022
5023
                    webgl[this._mask._renderer.type].render.call(this._mask, gl, program, this);
5024
5025
                    gl.colorMask(true, true, true, true);
5026
                    gl.stencilFunc(gl.NOTEQUAL, 0, 1);
5027
                    gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
5028
5029
                    gl.disable(gl.STENCIL_TEST);
5030
5031
                }
5032
5033
                return this.flagReset();
5034
5035
            }
5036
5037
        },
5038
5039
        path: {
5040
5041
            updateCanvas: function (elem) {
5042
5043
                var next, prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y;
5044
                var isOffset;
5045
5046
                var commands = elem._vertices;
5047
                var canvas = this.canvas;
5048
                var ctx = this.ctx;
5049
5050
                // Styles
5051
                var scale = elem._renderer.scale;
5052
                var stroke = elem._stroke;
5053
                var linewidth = elem._linewidth;
5054
                var fill = elem._fill;
5055
                var opacity = elem._renderer.opacity || elem._opacity;
5056
                var cap = elem._cap;
5057
                var join = elem._join;
5058
                var miter = elem._miter;
5059
                var closed = elem._closed;
5060
                var length = commands.length;
5061
                var last = length - 1;
5062
5063
                canvas.width = Math.max(Math.ceil(elem._renderer.rect.width * scale), 1);
5064
                canvas.height = Math.max(Math.ceil(elem._renderer.rect.height * scale), 1);
5065
5066
                var centroid = elem._renderer.rect.centroid;
5067
                var cx = centroid.x;
5068
                var cy = centroid.y;
5069
5070
                ctx.clearRect(0, 0, canvas.width, canvas.height);
5071
5072
                if (fill) {
5073
                    if (_.isString(fill)) {
5074
                        ctx.fillStyle = fill;
5075
                    } else {
5076
                        webgl[fill._renderer.type].render.call(fill, ctx, elem);
5077
                        ctx.fillStyle = fill._renderer.effect;
5078
                    }
5079
                }
5080
                if (stroke) {
5081
                    if (_.isString(stroke)) {
5082
                        ctx.strokeStyle = stroke;
5083
                    } else {
5084
                        webgl[stroke._renderer.type].render.call(stroke, ctx, elem);
5085
                        ctx.strokeStyle = stroke._renderer.effect;
5086
                    }
5087
                }
5088
                if (linewidth) {
5089
                    ctx.lineWidth = linewidth;
5090
                }
5091
                if (miter) {
5092
                    ctx.miterLimit = miter;
5093
                }
5094
                if (join) {
5095
                    ctx.lineJoin = join;
5096
                }
5097
                if (cap) {
5098
                    ctx.lineCap = cap;
5099
                }
5100
                if (_.isNumber(opacity)) {
5101
                    ctx.globalAlpha = opacity;
5102
                }
5103
5104
                var d;
5105
                ctx.save();
5106
                ctx.scale(scale, scale);
5107
                ctx.translate(cx, cy);
5108
5109
                ctx.beginPath();
5110
                for (var i = 0; i < commands.length; i++) {
5111
5112
                    b = commands[i];
5113
5114
                    x = toFixed(b._x);
5115
                    y = toFixed(b._y);
5116
5117
                    switch (b._command) {
5118
5119
                        case Two.Commands.close:
5120
                            ctx.closePath();
5121
                            break;
5122
5123
                        case Two.Commands.curve:
5124
5125
                            prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0);
5126
                            next = closed ? mod(i + 1, length) : Math.min(i + 1, last);
5127
5128
                            a = commands[prev];
5129
                            c = commands[next];
5130
                            ar = (a.controls && a.controls.right) || Two.Vector.zero;
5131
                            bl = (b.controls && b.controls.left) || Two.Vector.zero;
5132
5133
                            if (a._relative) {
5134
                                vx = toFixed((ar.x + a._x));
5135
                                vy = toFixed((ar.y + a._y));
5136
                            } else {
5137
                                vx = toFixed(ar.x);
5138
                                vy = toFixed(ar.y);
5139
                            }
5140
5141
                            if (b._relative) {
5142
                                ux = toFixed((bl.x + b._x));
5143
                                uy = toFixed((bl.y + b._y));
5144
                            } else {
5145
                                ux = toFixed(bl.x);
5146
                                uy = toFixed(bl.y);
5147
                            }
5148
5149
                            ctx.bezierCurveTo(vx, vy, ux, uy, x, y);
5150
5151
                            if (i >= last && closed) {
5152
5153
                                c = d;
5154
5155
                                br = (b.controls && b.controls.right) || Two.Vector.zero;
5156
                                cl = (c.controls && c.controls.left) || Two.Vector.zero;
5157
5158
                                if (b._relative) {
5159
                                    vx = toFixed((br.x + b._x));
5160
                                    vy = toFixed((br.y + b._y));
5161
                                } else {
5162
                                    vx = toFixed(br.x);
5163
                                    vy = toFixed(br.y);
5164
                                }
5165
5166
                                if (c._relative) {
5167
                                    ux = toFixed((cl.x + c._x));
5168
                                    uy = toFixed((cl.y + c._y));
5169
                                } else {
5170
                                    ux = toFixed(cl.x);
5171
                                    uy = toFixed(cl.y);
5172
                                }
5173
5174
                                x = toFixed(c._x);
5175
                                y = toFixed(c._y);
5176
5177
                                ctx.bezierCurveTo(vx, vy, ux, uy, x, y);
5178
5179
                            }
5180
5181
                            break;
5182
5183
                        case Two.Commands.line:
5184
                            ctx.lineTo(x, y);
5185
                            break;
5186
5187
                        case Two.Commands.move:
5188
                            d = b;
5189
                            ctx.moveTo(x, y);
5190
                            break;
5191
5192
                    }
5193
5194
                }
5195
5196
                // Loose ends
5197
5198
                if (closed) {
5199
                    ctx.closePath();
5200
                }
5201
5202
                if (!webgl.isHidden.test(fill)) {
5203
                    isOffset = fill._renderer && fill._renderer.offset
5204
                    if (isOffset) {
5205
                        ctx.save();
5206
                        ctx.translate(
5207
                            - fill._renderer.offset.x, - fill._renderer.offset.y);
5208
                        ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);
5209
                    }
5210
                    ctx.fill();
5211
                    if (isOffset) {
5212
                        ctx.restore();
5213
                    }
5214
                }
5215
5216
                if (!webgl.isHidden.test(stroke)) {
5217
                    isOffset = stroke._renderer && stroke._renderer.offset;
5218
                    if (isOffset) {
5219
                        ctx.save();
5220
                        ctx.translate(
5221
                            - stroke._renderer.offset.x, - stroke._renderer.offset.y);
5222
                        ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);
5223
                        ctx.lineWidth = linewidth / stroke._renderer.scale.x;
5224
                    }
5225
                    ctx.stroke();
5226
                    if (isOffset) {
5227
                        ctx.restore();
5228
                    }
5229
                }
5230
5231
                ctx.restore();
5232
5233
            },
5234
5235
            /**
5236
                * Returns the rect of a set of verts. Typically takes vertices that are
5237
                * "centered" around 0 and returns them to be anchored upper-left.
5238
                */
5239
            getBoundingClientRect: function (vertices, border, rect) {
5240
5241
                var left = Infinity, right = -Infinity,
5242
                    top = Infinity, bottom = -Infinity,
5243
                    width, height;
5244
5245
                vertices.forEach(function (v) {
5246
5247
                    var x = v.x, y = v.y, controls = v.controls;
5248
                    var a, b, c, d, cl, cr;
5249
5250
                    top = Math.min(y, top);
5251
                    left = Math.min(x, left);
5252
                    right = Math.max(x, right);
5253
                    bottom = Math.max(y, bottom);
5254
5255
                    if (!v.controls) {
5256
                        return;
5257
                    }
5258
5259
                    cl = controls.left;
5260
                    cr = controls.right;
5261
5262
                    if (!cl || !cr) {
5263
                        return;
5264
                    }
5265
5266
                    a = v._relative ? cl.x + x : cl.x;
5267
                    b = v._relative ? cl.y + y : cl.y;
5268
                    c = v._relative ? cr.x + x : cr.x;
5269
                    d = v._relative ? cr.y + y : cr.y;
5270
5271
                    if (!a || !b || !c || !d) {
5272
                        return;
5273
                    }
5274
5275
                    top = Math.min(b, d, top);
5276
                    left = Math.min(a, c, left);
5277
                    right = Math.max(a, c, right);
5278
                    bottom = Math.max(b, d, bottom);
5279
5280
                });
5281
5282
                // Expand borders
5283
5284
                if (_.isNumber(border)) {
5285
                    top -= border;
5286
                    left -= border;
5287
                    right += border;
5288
                    bottom += border;
5289
                }
5290
5291
                width = right - left;
5292
                height = bottom - top;
5293
5294
                rect.top = top;
5295
                rect.left = left;
5296
                rect.right = right;
5297
                rect.bottom = bottom;
5298
                rect.width = width;
5299
                rect.height = height;
5300
5301
                if (!rect.centroid) {
5302
                    rect.centroid = {};
5303
                }
5304
5305
                rect.centroid.x = - left;
5306
                rect.centroid.y = - top;
5307
5308
            },
5309
5310
            render: function (gl, program, forcedParent) {
5311
5312
                if (!this._visible || !this._opacity) {
5313
                    return this;
5314
                }
5315
5316
                this._update();
5317
5318
                // Calculate what changed
5319
5320
                var parent = this.parent;
5321
                var flagParentMatrix = parent._matrix.manual || parent._flagMatrix;
5322
                var flagMatrix = this._matrix.manual || this._flagMatrix;
5323
                var flagTexture = this._flagVertices || this._flagFill
5324
                    || (this._fill instanceof Two.LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints))
5325
                    || (this._fill instanceof Two.RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal))
5326
                    || (this._fill instanceof Two.Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagOffset || this._fill._flagScale))
5327
                    || (this._stroke instanceof Two.LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints))
5328
                    || (this._stroke instanceof Two.RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal))
5329
                    || (this._stroke instanceof Two.Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagOffset || this._fill._flagScale))
5330
                    || this._flagStroke || this._flagLinewidth || this._flagOpacity
5331
                    || parent._flagOpacity || this._flagVisible || this._flagCap
5332
                    || this._flagJoin || this._flagMiter || this._flagScale
5333
                    || !this._renderer.texture;
5334
5335
                if (flagParentMatrix || flagMatrix) {
5336
5337
                    if (!this._renderer.matrix) {
5338
                        this._renderer.matrix = new Two.Array(9);
5339
                    }
5340
5341
                    // Reduce amount of object / array creation / deletion
5342
5343
                    this._matrix.toArray(true, transformation);
5344
5345
                    multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix);
5346
                    this._renderer.scale = this._scale * parent._renderer.scale;
5347
5348
                }
5349
5350
                if (flagTexture) {
5351
5352
                    if (!this._renderer.rect) {
5353
                        this._renderer.rect = {};
5354
                    }
5355
5356
                    if (!this._renderer.triangles) {
5357
                        this._renderer.triangles = new Two.Array(12);
5358
                    }
5359
5360
                    this._renderer.opacity = this._opacity * parent._renderer.opacity;
5361
5362
                    webgl.path.getBoundingClientRect(this._vertices, this._linewidth, this._renderer.rect);
5363
                    webgl.getTriangles(this._renderer.rect, this._renderer.triangles);
5364
5365
                    webgl.updateBuffer.call(webgl, gl, this, program);
5366
                    webgl.updateTexture.call(webgl, gl, this);
5367
5368
                }
5369
5370
                // if (this._mask) {
5371
                //   webgl[this._mask._renderer.type].render.call(mask, gl, program, this);
5372
                // }
5373
5374
                if (this._clip && !forcedParent) {
5375
                    return;
5376
                }
5377
5378
                // Draw Texture
5379
5380
                gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.textureCoordsBuffer);
5381
5382
                gl.vertexAttribPointer(program.textureCoords, 2, gl.FLOAT, false, 0, 0);
5383
5384
                gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);
5385
5386
5387
                // Draw Rect
5388
5389
                gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);
5390
5391
                gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.buffer);
5392
5393
                gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);
5394
5395
                gl.drawArrays(gl.TRIANGLES, 0, 6);
5396
5397
                return this.flagReset();
5398
5399
            }
5400
5401
        },
5402
5403
        text: {
5404
5405
            updateCanvas: function (elem) {
5406
5407
                var canvas = this.canvas;
5408
                var ctx = this.ctx;
5409
5410
                // Styles
5411
                var scale = elem._renderer.scale;
5412
                var stroke = elem._stroke;
5413
                var linewidth = elem._linewidth * scale;
5414
                var fill = elem._fill;
5415
                var opacity = elem._renderer.opacity || elem._opacity;
5416
5417
                canvas.width = Math.max(Math.ceil(elem._renderer.rect.width * scale), 1);
5418
                canvas.height = Math.max(Math.ceil(elem._renderer.rect.height * scale), 1);
5419
5420
                var centroid = elem._renderer.rect.centroid;
5421
                var cx = centroid.x;
5422
                var cy = centroid.y;
5423
5424
                var a, b, c, d, e, sx, sy;
5425
                var isOffset = fill._renderer && fill._renderer.offset
5426
                    && stroke._renderer && stroke._renderer.offset;
5427
5428
                ctx.clearRect(0, 0, canvas.width, canvas.height);
5429
5430
                if (!isOffset) {
5431
                    ctx.font = [elem._style, elem._weight, elem._size + 'px/' +
5432
                        elem._leading + 'px', elem._family].join(' ');
5433
                }
5434
5435
                ctx.textAlign = 'center';
5436
                ctx.textBaseline = 'middle';
5437
5438
                // Styles
5439
                if (fill) {
5440
                    if (_.isString(fill)) {
5441
                        ctx.fillStyle = fill;
5442
                    } else {
5443
                        webgl[fill._renderer.type].render.call(fill, ctx, elem);
5444
                        ctx.fillStyle = fill._renderer.effect;
5445
                    }
5446
                }
5447
                if (stroke) {
5448
                    if (_.isString(stroke)) {
5449
                        ctx.strokeStyle = stroke;
5450
                    } else {
5451
                        webgl[stroke._renderer.type].render.call(stroke, ctx, elem);
5452
                        ctx.strokeStyle = stroke._renderer.effect;
5453
                    }
5454
                }
5455
                if (linewidth) {
5456
                    ctx.lineWidth = linewidth;
5457
                }
5458
                if (_.isNumber(opacity)) {
5459
                    ctx.globalAlpha = opacity;
5460
                }
5461
5462
                ctx.save();
5463
                ctx.scale(scale, scale);
5464
                ctx.translate(cx, cy);
5465
5466
                if (!webgl.isHidden.test(fill)) {
5467
5468
                    if (fill._renderer && fill._renderer.offset) {
5469
5470
                        sx = toFixed(fill._renderer.scale.x);
5471
                        sy = toFixed(fill._renderer.scale.y);
5472
5473
                        ctx.save();
5474
                        ctx.translate(- toFixed(fill._renderer.offset.x),
5475
                            - toFixed(fill._renderer.offset.y));
5476
                        ctx.scale(sx, sy);
5477
5478
                        a = elem._size / fill._renderer.scale.y;
5479
                        b = elem._leading / fill._renderer.scale.y;
5480
                        ctx.font = [elem._style, elem._weight, toFixed(a) + 'px/',
5481
                        toFixed(b) + 'px', elem._family].join(' ');
5482
5483
                        c = fill._renderer.offset.x / fill._renderer.scale.x;
5484
                        d = fill._renderer.offset.y / fill._renderer.scale.y;
5485
5486
                        ctx.fillText(elem.value, toFixed(c), toFixed(d));
5487
                        ctx.restore();
5488
5489
                    } else {
5490
                        ctx.fillText(elem.value, 0, 0);
5491
                    }
5492
5493
                }
5494
5495
                if (!webgl.isHidden.test(stroke)) {
5496
5497
                    if (stroke._renderer && stroke._renderer.offset) {
5498
5499
                        sx = toFixed(stroke._renderer.scale.x);
5500
                        sy = toFixed(stroke._renderer.scale.y);
5501
5502
                        ctx.save();
5503
                        ctx.translate(- toFixed(stroke._renderer.offset.x),
5504
                            - toFixed(stroke._renderer.offset.y));
5505
                        ctx.scale(sx, sy);
5506
5507
                        a = elem._size / stroke._renderer.scale.y;
5508
                        b = elem._leading / stroke._renderer.scale.y;
5509
                        ctx.font = [elem._style, elem._weight, toFixed(a) + 'px/',
5510
                        toFixed(b) + 'px', elem._family].join(' ');
5511
5512
                        c = stroke._renderer.offset.x / stroke._renderer.scale.x;
5513
                        d = stroke._renderer.offset.y / stroke._renderer.scale.y;
5514
                        e = linewidth / stroke._renderer.scale.x;
5515
5516
                        ctx.lineWidth = toFixed(e);
5517
                        ctx.strokeText(elem.value, toFixed(c), toFixed(d));
5518
                        ctx.restore();
5519
5520
                    } else {
5521
                        ctx.strokeText(elem.value, 0, 0);
5522
                    }
5523
5524
                }
5525
5526
                ctx.restore();
5527
5528
            },
5529
5530
            getBoundingClientRect: function (elem, rect) {
5531
5532
                var ctx = webgl.ctx;
5533
5534
                ctx.font = [elem._style, elem._weight, elem._size + 'px/' +
5535
                    elem._leading + 'px', elem._family].join(' ');
5536
5537
                ctx.textAlign = 'center';
5538
                ctx.textBaseline = elem._baseline;
5539
5540
                // TODO: Estimate this better
5541
                var width = ctx.measureText(elem._value).width;
5542
                var height = Math.max(elem._size || elem._leading);
5543
5544
                if (this._linewidth && !webgl.isHidden.test(this._stroke)) {
5545
                    // width += this._linewidth; // TODO: Not sure if the `measure` calcs this.
5546
                    height += this._linewidth;
5547
                }
5548
5549
                var w = width / 2;
5550
                var h = height / 2;
5551
5552
                switch (webgl.alignments[elem._alignment] || elem._alignment) {
5553
5554
                    case webgl.alignments.left:
5555
                        rect.left = 0;
5556
                        rect.right = width;
5557
                        break;
5558
                    case webgl.alignments.right:
5559
                        rect.left = - width;
5560
                        rect.right = 0;
5561
                        break;
5562
                    default:
5563
                        rect.left = - w;
5564
                        rect.right = w;
5565
                }
5566
5567
                // TODO: Gradients aren't inherited...
5568
                switch (elem._baseline) {
5569
                    case 'bottom':
5570
                        rect.top = - height;
5571
                        rect.bottom = 0;
5572
                        break;
5573
                    case 'top':
5574
                        rect.top = 0;
5575
                        rect.bottom = height;
5576
                        break;
5577
                    default:
5578
                        rect.top = - h;
5579
                        rect.bottom = h;
5580
                }
5581
5582
                rect.width = width;
5583
                rect.height = height;
5584
5585
                if (!rect.centroid) {
5586
                    rect.centroid = {};
5587
                }
5588
5589
                // TODO:
5590
                rect.centroid.x = w;
5591
                rect.centroid.y = h;
5592
5593
            },
5594
5595
            render: function (gl, program, forcedParent) {
5596
5597
                if (!this._visible || !this._opacity) {
5598
                    return this;
5599
                }
5600
5601
                this._update();
5602
5603
                // Calculate what changed
5604
5605
                var parent = this.parent;
5606
                var flagParentMatrix = parent._matrix.manual || parent._flagMatrix;
5607
                var flagMatrix = this._matrix.manual || this._flagMatrix;
5608
                var flagTexture = this._flagVertices || this._flagFill
5609
                    || (this._fill instanceof Two.LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints))
5610
                    || (this._fill instanceof Two.RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal))
5611
                    || (this._fill instanceof Two.Texture && (this._fill._flagLoaded && this._fill.loaded))
5612
                    || (this._stroke instanceof Two.LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints))
5613
                    || (this._stroke instanceof Two.RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal))
5614
                    || (this._texture instanceof Two.Texture && (this._texture._flagLoaded && this._texture.loaded))
5615
                    || this._flagStroke || this._flagLinewidth || this._flagOpacity
5616
                    || parent._flagOpacity || this._flagVisible || this._flagScale
5617
                    || this._flagValue || this._flagFamily || this._flagSize
5618
                    || this._flagLeading || this._flagAlignment || this._flagBaseline
5619
                    || this._flagStyle || this._flagWeight || this._flagDecoration
5620
                    || !this._renderer.texture;
5621
5622
                if (flagParentMatrix || flagMatrix) {
5623
5624
                    if (!this._renderer.matrix) {
5625
                        this._renderer.matrix = new Two.Array(9);
5626
                    }
5627
5628
                    // Reduce amount of object / array creation / deletion
5629
5630
                    this._matrix.toArray(true, transformation);
5631
5632
                    multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix);
5633
                    this._renderer.scale = this._scale * parent._renderer.scale;
5634
5635
                }
5636
5637
                if (flagTexture) {
5638
5639
                    if (!this._renderer.rect) {
5640
                        this._renderer.rect = {};
5641
                    }
5642
5643
                    if (!this._renderer.triangles) {
5644
                        this._renderer.triangles = new Two.Array(12);
5645
                    }
5646
5647
                    this._renderer.opacity = this._opacity * parent._renderer.opacity;
5648
5649
                    webgl.text.getBoundingClientRect(this, this._renderer.rect);
5650
                    webgl.getTriangles(this._renderer.rect, this._renderer.triangles);
5651
5652
                    webgl.updateBuffer.call(webgl, gl, this, program);
5653
                    webgl.updateTexture.call(webgl, gl, this);
5654
5655
                }
5656
5657
                // if (this._mask) {
5658
                //   webgl[this._mask._renderer.type].render.call(mask, gl, program, this);
5659
                // }
5660
5661
                if (this._clip && !forcedParent) {
5662
                    return;
5663
                }
5664
5665
                // Draw Texture
5666
5667
                gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.textureCoordsBuffer);
5668
5669
                gl.vertexAttribPointer(program.textureCoords, 2, gl.FLOAT, false, 0, 0);
5670
5671
                gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);
5672
5673
5674
                // Draw Rect
5675
5676
                gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);
5677
5678
                gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.buffer);
5679
5680
                gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);
5681
5682
                gl.drawArrays(gl.TRIANGLES, 0, 6);
5683
5684
                return this.flagReset();
5685
5686
            }
5687
5688
        },
5689
5690
        'linear-gradient': {
5691
5692
            render: function (ctx, elem) {
5693
5694
                if (!ctx.canvas.getContext('2d')) {
5695
                    return;
5696
                }
5697
5698
                this._update();
5699
5700
                if (!this._renderer.effect || this._flagEndPoints || this._flagStops) {
5701
5702
                    this._renderer.effect = ctx.createLinearGradient(
5703
                        this.left._x, this.left._y,
5704
                        this.right._x, this.right._y
5705
                    );
5706
5707
                    for (var i = 0; i < this.stops.length; i++) {
5708
                        var stop = this.stops[i];
5709
                        this._renderer.effect.addColorStop(stop._offset, stop._color);
5710
                    }
5711
5712
                }
5713
5714
                return this.flagReset();
5715
5716
            }
5717
5718
        },
5719
5720
        'radial-gradient': {
5721
5722
            render: function (ctx, elem) {
5723
5724
                if (!ctx.canvas.getContext('2d')) {
5725
                    return;
5726
                }
5727
5728
                this._update();
5729
5730
                if (!this._renderer.effect || this._flagCenter || this._flagFocal
5731
                    || this._flagRadius || this._flagStops) {
5732
5733
                    this._renderer.effect = ctx.createRadialGradient(
5734
                        this.center._x, this.center._y, 0,
5735
                        this.focal._x, this.focal._y, this._radius
5736
                    );
5737
5738
                    for (var i = 0; i < this.stops.length; i++) {
5739
                        var stop = this.stops[i];
5740
                        this._renderer.effect.addColorStop(stop._offset, stop._color);
5741
                    }
5742
5743
                }
5744
5745
                return this.flagReset();
5746
5747
            }
5748
5749
        },
5750
5751
        texture: {
5752
5753
            render: function (ctx, elem) {
5754
5755
                if (!ctx.canvas.getContext('2d')) {
5756
                    return;
5757
                }
5758
5759
                this._update();
5760
5761
                var image = this.image;
5762
                var repeat;
5763
5764
                if (!this._renderer.effect || ((this._flagLoaded || this._flagRepeat) && this.loaded)) {
5765
                    this._renderer.effect = ctx.createPattern(image, this._repeat);
5766
                }
5767
5768
                if (this._flagOffset || this._flagLoaded || this._flagScale) {
5769
5770
                    if (!(this._renderer.offset instanceof Two.Vector)) {
5771
                        this._renderer.offset = new Two.Vector();
5772
                    }
5773
5774
                    this._renderer.offset.x = this._offset.x;
5775
                    this._renderer.offset.y = this._offset.y;
5776
5777
                    if (image) {
5778
5779
                        this._renderer.offset.x -= image.width / 2;
5780
                        this._renderer.offset.y += image.height / 2;
5781
5782
                        if (this._scale instanceof Two.Vector) {
5783
                            this._renderer.offset.x *= this._scale.x;
5784
                            this._renderer.offset.y *= this._scale.y;
5785
                        } else {
5786
                            this._renderer.offset.x *= this._scale;
5787
                            this._renderer.offset.y *= this._scale;
5788
                        }
5789
                    }
5790
5791
                }
5792
5793
                if (this._flagScale || this._flagLoaded) {
5794
5795
                    if (!(this._renderer.scale instanceof Two.Vector)) {
5796
                        this._renderer.scale = new Two.Vector();
5797
                    }
5798
5799
                    if (this._scale instanceof Two.Vector) {
5800
                        this._renderer.scale.copy(this._scale);
5801
                    } else {
5802
                        this._renderer.scale.set(this._scale, this._scale);
5803
                    }
5804
5805
                }
5806
5807
                return this.flagReset();
5808
5809
            }
5810
5811
        },
5812
5813
        getTriangles: function (rect, triangles) {
5814
5815
            var top = rect.top,
5816
                left = rect.left,
5817
                right = rect.right,
5818
                bottom = rect.bottom;
5819
5820
            // First Triangle
5821
5822
            triangles[0] = left;
5823
            triangles[1] = top;
5824
5825
            triangles[2] = right;
5826
            triangles[3] = top;
5827
5828
            triangles[4] = left;
5829
            triangles[5] = bottom;
5830
5831
            // Second Triangle
5832
5833
            triangles[6] = left;
5834
            triangles[7] = bottom;
5835
5836
            triangles[8] = right;
5837
            triangles[9] = top;
5838
5839
            triangles[10] = right;
5840
            triangles[11] = bottom;
5841
5842
        },
5843
5844
        updateTexture: function (gl, elem) {
5845
5846
            this[elem._renderer.type].updateCanvas.call(webgl, elem);
5847
5848
            if (elem._renderer.texture) {
5849
                gl.deleteTexture(elem._renderer.texture);
5850
            }
5851
5852
            gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.textureCoordsBuffer);
5853
5854
            // TODO: Is this necessary every time or can we do once?
5855
            // TODO: Create a registry for textures
5856
            elem._renderer.texture = gl.createTexture();
5857
            gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture);
5858
5859
            // Set the parameters so we can render any size image.
5860
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
5861
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
5862
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
5863
            // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
5864
            // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
5865
            // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
5866
5867
            if (this.canvas.width <= 0 || this.canvas.height <= 0) {
5868
                return;
5869
            }
5870
5871
            // Upload the image into the texture.
5872
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvas);
5873
5874
        },
5875
5876
        updateBuffer: function (gl, elem, program) {
5877
5878
            if (_.isObject(elem._renderer.buffer)) {
5879
                gl.deleteBuffer(elem._renderer.buffer);
5880
            }
5881
5882
            elem._renderer.buffer = gl.createBuffer();
5883
5884
            gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.buffer);
5885
            gl.enableVertexAttribArray(program.position);
5886
5887
            gl.bufferData(gl.ARRAY_BUFFER, elem._renderer.triangles, gl.STATIC_DRAW);
5888
5889
            if (_.isObject(elem._renderer.textureCoordsBuffer)) {
5890
                gl.deleteBuffer(elem._renderer.textureCoordsBuffer);
5891
            }
5892
5893
            elem._renderer.textureCoordsBuffer = gl.createBuffer();
5894
5895
            gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.textureCoordsBuffer);
5896
            gl.enableVertexAttribArray(program.textureCoords);
5897
5898
            gl.bufferData(gl.ARRAY_BUFFER, this.uv, gl.STATIC_DRAW);
5899
5900
        },
5901
5902
        program: {
5903
5904
            create: function (gl, shaders) {
5905
                var program, linked, error;
5906
                program = gl.createProgram();
5907
                _.each(shaders, function (s) {
5908
                    gl.attachShader(program, s);
5909
                });
5910
5911
                gl.linkProgram(program);
5912
                linked = gl.getProgramParameter(program, gl.LINK_STATUS);
5913
                if (!linked) {
5914
                    error = gl.getProgramInfoLog(program);
5915
                    gl.deleteProgram(program);
5916
                    throw new Two.Utils.Error('unable to link program: ' + error);
5917
                }
5918
5919
                return program;
5920
5921
            }
5922
5923
        },
5924
5925
        shaders: {
5926
5927
            create: function (gl, source, type) {
5928
                var shader, compiled, error;
5929
                shader = gl.createShader(gl[type]);
5930
                gl.shaderSource(shader, source);
5931
                gl.compileShader(shader);
5932
5933
                compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
5934
                if (!compiled) {
5935
                    error = gl.getShaderInfoLog(shader);
5936
                    gl.deleteShader(shader);
5937
                    throw new Two.Utils.Error('unable to compile shader ' + shader + ': ' + error);
5938
                }
5939
5940
                return shader;
5941
5942
            },
5943
5944
            types: {
5945
                vertex: 'VERTEX_SHADER',
5946
                fragment: 'FRAGMENT_SHADER'
5947
            },
5948
5949
            vertex: [
5950
                'attribute vec2 a_position;',
5951
                'attribute vec2 a_textureCoords;',
5952
                '',
5953
                'uniform mat3 u_matrix;',
5954
                'uniform vec2 u_resolution;',
5955
                '',
5956
                'varying vec2 v_textureCoords;',
5957
                '',
5958
                'void main() {',
5959
                '   vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy;',
5960
                '   vec2 normal = projected / u_resolution;',
5961
                '   vec2 clipspace = (normal * 2.0) - 1.0;',
5962
                '',
5963
                '   gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);',
5964
                '   v_textureCoords = a_textureCoords;',
5965
                '}'
5966
            ].join('\n'),
5967
5968
            fragment: [
5969
                'precision mediump float;',
5970
                '',
5971
                'uniform sampler2D u_image;',
5972
                'varying vec2 v_textureCoords;',
5973
                '',
5974
                'void main() {',
5975
                '  gl_FragColor = texture2D(u_image, v_textureCoords);',
5976
                '}'
5977
            ].join('\n')
5978
5979
        },
5980
5981
        TextureRegistry: new Two.Registry()
5982
5983
    };
5984
5985
    webgl.ctx = webgl.canvas.getContext('2d');
5986
5987
    var Renderer = Two[Two.Types.webgl] = function (options) {
5988
5989
        var params, gl, vs, fs;
5990
        this.domElement = options.domElement || document.createElement('canvas');
5991
5992
        // Everything drawn on the canvas needs to come from the stage.
5993
        this.scene = new Two.Group();
5994
        this.scene.parent = this;
5995
5996
        this._renderer = {
5997
            matrix: new Two.Array(identity),
5998
            scale: 1,
5999
            opacity: 1
6000
        };
6001
        this._flagMatrix = true;
6002
6003
        // http://games.greggman.com/game/webgl-and-alpha/
6004
        // http://www.khronos.org/registry/webgl/specs/latest/#5.2
6005
        params = _.defaults(options || {}, {
6006
            antialias: false,
6007
            alpha: true,
6008
            premultipliedAlpha: true,
6009
            stencil: true,
6010
            preserveDrawingBuffer: true,
6011
            overdraw: false
6012
        });
6013
6014
        this.overdraw = params.overdraw;
6015
6016
        gl = this.ctx = this.domElement.getContext('webgl', params) ||
6017
            this.domElement.getContext('experimental-webgl', params);
6018
6019
        if (!this.ctx) {
6020
            throw new Two.Utils.Error(
6021
                'unable to create a webgl context. Try using another renderer.');
6022
        }
6023
6024
        // Compile Base Shaders to draw in pixel space.
6025
        vs = webgl.shaders.create(
6026
            gl, webgl.shaders.vertex, webgl.shaders.types.vertex);
6027
        fs = webgl.shaders.create(
6028
            gl, webgl.shaders.fragment, webgl.shaders.types.fragment);
6029
6030
        this.program = webgl.program.create(gl, [vs, fs]);
6031
        gl.useProgram(this.program);
6032
6033
        // Create and bind the drawing buffer
6034
6035
        // look up where the vertex data needs to go.
6036
        this.program.position = gl.getAttribLocation(this.program, 'a_position');
6037
        this.program.matrix = gl.getUniformLocation(this.program, 'u_matrix');
6038
        this.program.textureCoords = gl.getAttribLocation(this.program, 'a_textureCoords');
6039
6040
        // Copied from Three.js WebGLRenderer
6041
        gl.disable(gl.DEPTH_TEST);
6042
6043
        // Setup some initial statements of the gl context
6044
        gl.enable(gl.BLEND);
6045
6046
        // https://code.google.com/p/chromium/issues/detail?id=316393
6047
        // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, gl.TRUE);
6048
6049
        gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
6050
        gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA,
6051
            gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
6052
6053
    };
6054
6055
    _.extend(Renderer, {
6056
6057
        Utils: webgl
6058
6059
    });
6060
6061
    _.extend(Renderer.prototype, Two.Utils.Events, {
6062
6063
        setSize: function (width, height, ratio) {
6064
6065
            this.width = width;
6066
            this.height = height;
6067
6068
            this.ratio = _.isUndefined(ratio) ? getRatio(this.ctx) : ratio;
6069
6070
            this.domElement.width = width * this.ratio;
6071
            this.domElement.height = height * this.ratio;
6072
6073
            _.extend(this.domElement.style, {
6074
                width: width + 'px',
6075
                height: height + 'px'
6076
            });
6077
6078
            width *= this.ratio;
6079
            height *= this.ratio;
6080
6081
            // Set for this.stage parent scaling to account for HDPI
6082
            this._renderer.matrix[0] = this._renderer.matrix[4] = this._renderer.scale = this.ratio;
6083
6084
            this._flagMatrix = true;
6085
6086
            this.ctx.viewport(0, 0, width, height);
6087
6088
            var resolutionLocation = this.ctx.getUniformLocation(
6089
                this.program, 'u_resolution');
6090
            this.ctx.uniform2f(resolutionLocation, width, height);
6091
6092
            return this;
6093
6094
        },
6095
6096
        render: function () {
6097
6098
            var gl = this.ctx;
6099
6100
            if (!this.overdraw) {
6101
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
6102
            }
6103
6104
            webgl.group.render.call(this.scene, gl, this.program);
6105
            this._flagMatrix = false;
6106
6107
            return this;
6108
6109
        }
6110
6111
    });
6112
6113
})((typeof global !== 'undefined' ? global : this).Two);
6114
6115
(function (Two) {
6116
6117
    var _ = Two.Utils;
6118
6119
    var Shape = Two.Shape = function () {
6120
6121
        // Private object for renderer specific variables.
6122
        this._renderer = {};
6123
        this._renderer.flagMatrix = _.bind(Shape.FlagMatrix, this);
6124
        this.isShape = true;
6125
6126
        this.id = Two.Identifier + Two.uniqueId();
6127
        this.classList = [];
6128
6129
        // Define matrix properties which all inherited
6130
        // objects of Shape have.
6131
6132
        this._matrix = new Two.Matrix();
6133
6134
        this.translation = new Two.Vector();
6135
        this.rotation = 0;
6136
        this.scale = 1;
6137
6138
    };
6139
6140
    _.extend(Shape, {
6141
6142
        FlagMatrix: function () {
6143
            this._flagMatrix = true;
6144
        },
6145
6146
        MakeObservable: function (object) {
6147
6148
            Object.defineProperty(object, 'translation', {
6149
                enumerable: true,
6150
                get: function () {
6151
                    return this._translation;
6152
                },
6153
                set: function (v) {
6154
                    if (this._translation) {
6155
                        this._translation.unbind(Two.Events.change, this._renderer.flagMatrix);
6156
                    }
6157
                    this._translation = v;
6158
                    this._translation.bind(Two.Events.change, this._renderer.flagMatrix);
6159
                    Shape.FlagMatrix.call(this);
6160
                }
6161
            });
6162
6163
            Object.defineProperty(object, 'rotation', {
6164
                enumerable: true,
6165
                get: function () {
6166
                    return this._rotation;
6167
                },
6168
                set: function (v) {
6169
                    this._rotation = v;
6170
                    this._flagMatrix = true;
6171
                }
6172
            });
6173
6174
            Object.defineProperty(object, 'scale', {
6175
                enumerable: true,
6176
                get: function () {
6177
                    return this._scale;
6178
                },
6179
                set: function (v) {
6180
6181
                    if (this._scale instanceof Two.Vector) {
6182
                        this._scale.unbind(Two.Events.change, this._renderer.flagMatrix);
6183
                    }
6184
6185
                    this._scale = v;
6186
6187
                    if (this._scale instanceof Two.Vector) {
6188
                        this._scale.bind(Two.Events.change, this._renderer.flagMatrix);
6189
                    }
6190
6191
                    this._flagMatrix = true;
6192
                    this._flagScale = true;
6193
6194
                }
6195
            });
6196
6197
        }
6198
6199
    });
6200
6201
    _.extend(Shape.prototype, Two.Utils.Events, {
6202
6203
        // Flags
6204
6205
        _flagMatrix: true,
6206
        _flagScale: false,
6207
6208
        // _flagMask: false,
6209
        // _flagClip: false,
6210
6211
        // Underlying Properties
6212
6213
        _rotation: 0,
6214
        _scale: 1,
6215
        _translation: null,
6216
6217
        // _mask: null,
6218
        // _clip: false,
6219
6220
        addTo: function (group) {
6221
            group.add(this);
6222
            return this;
6223
        },
6224
6225
        clone: function () {
6226
            var clone = new Shape();
6227
            clone.translation.copy(this.translation);
6228
            clone.rotation = this.rotation;
6229
            clone.scale = this.scale;
6230
            _.each(Shape.Properties, function (k) {
6231
                clone[k] = this[k];
6232
            }, this);
6233
            return clone._update();
6234
        },
6235
6236
        /**
6237
            * To be called before render that calculates and collates all information
6238
            * to be as up-to-date as possible for the render. Called once a frame.
6239
            */
6240
        _update: function (deep) {
6241
6242
            if (!this._matrix.manual && this._flagMatrix) {
6243
6244
                this._matrix
6245
                    .identity()
6246
                    .translate(this.translation.x, this.translation.y);
6247
6248
                if (this._scale instanceof Two.Vector) {
6249
                    this._matrix.scale(this._scale.x, this._scale.y);
6250
                } else {
6251
                    this._matrix.scale(this._scale);
6252
                }
6253
6254
                this._matrix.rotate(this.rotation);
6255
6256
            }
6257
6258
            if (deep) {
6259
                // Bubble up to parents mainly for `getBoundingClientRect` method.
6260
                if (this.parent && this.parent._update) {
6261
                    this.parent._update();
6262
                }
6263
            }
6264
6265
            return this;
6266
6267
        },
6268
6269
        flagReset: function () {
6270
6271
            this._flagMatrix = this._flagScale = false;
6272
6273
            return this;
6274
6275
        }
6276
6277
    });
6278
6279
    Shape.MakeObservable(Shape.prototype);
6280
6281
})((typeof global !== 'undefined' ? global : this).Two);
6282
6283
(function (Two) {
6284
6285
    /**
6286
        * Constants
6287
        */
6288
6289
    var min = Math.min, max = Math.max, round = Math.round,
6290
        getComputedMatrix = Two.Utils.getComputedMatrix;
6291
6292
    var commands = {};
6293
    var _ = Two.Utils;
6294
6295
    _.each(Two.Commands, function (v, k) {
6296
        commands[k] = new RegExp(v);
6297
    });
6298
6299
    var Path = Two.Path = function (vertices, closed, curved, manual) {
6300
6301
        Two.Shape.call(this);
6302
6303
        this._renderer.type = 'path';
6304
        this._renderer.flagVertices = _.bind(Path.FlagVertices, this);
6305
        this._renderer.bindVertices = _.bind(Path.BindVertices, this);
6306
        this._renderer.unbindVertices = _.bind(Path.UnbindVertices, this);
6307
6308
        this._renderer.flagFill = _.bind(Path.FlagFill, this);
6309
        this._renderer.flagStroke = _.bind(Path.FlagStroke, this);
6310
6311
        this._closed = !!closed;
6312
        this._curved = !!curved;
6313
6314
        this.beginning = 0;
6315
        this.ending = 1;
6316
6317
        // Style properties
6318
6319
        this.fill = '#fff';
6320
        this.stroke = '#000';
6321
        this.linewidth = 1.0;
6322
        this.opacity = 1.0;
6323
        this.visible = true;
6324
6325
        this.cap = 'butt';      // Default of Adobe Illustrator
6326
        this.join = 'miter';    // Default of Adobe Illustrator
6327
        this.miter = 4;         // Default of Adobe Illustrator
6328
6329
        this._vertices = [];
6330
        this.vertices = vertices;
6331
6332
        // Determines whether or not two.js should calculate curves, lines, and
6333
        // commands automatically for you or to let the developer manipulate them
6334
        // for themselves.
6335
        this.automatic = !manual;
6336
6337
    };
6338
6339
    _.extend(Path, {
6340
6341
        Properties: [
6342
            'fill',
6343
            'stroke',
6344
            'linewidth',
6345
            'opacity',
6346
            'visible',
6347
            'cap',
6348
            'join',
6349
            'miter',
6350
6351
            'closed',
6352
            'curved',
6353
            'automatic',
6354
            'beginning',
6355
            'ending'
6356
        ],
6357
6358
        FlagVertices: function () {
6359
            this._flagVertices = true;
6360
            this._flagLength = true;
6361
        },
6362
6363
        BindVertices: function (items) {
6364
6365
            // This function is called a lot
6366
            // when importing a large SVG
6367
            var i = items.length;
6368
            while (i--) {
6369
                items[i].bind(Two.Events.change, this._renderer.flagVertices);
6370
            }
6371
6372
            this._renderer.flagVertices();
6373
6374
        },
6375
6376
        UnbindVertices: function (items) {
6377
6378
            var i = items.length;
6379
            while (i--) {
6380
                items[i].unbind(Two.Events.change, this._renderer.flagVertices);
6381
            }
6382
6383
            this._renderer.flagVertices();
6384
6385
        },
6386
6387
        FlagFill: function () {
6388
            this._flagFill = true;
6389
        },
6390
6391
        FlagStroke: function () {
6392
            this._flagStroke = true;
6393
        },
6394
6395
        MakeObservable: function (object) {
6396
6397
            Two.Shape.MakeObservable(object);
6398
6399
            // Only the 6 defined properties are flagged like this. The subsequent
6400
            // properties behave differently and need to be hand written.
6401
            _.each(Path.Properties.slice(2, 8), Two.Utils.defineProperty, object);
6402
6403
            Object.defineProperty(object, 'fill', {
6404
                enumerable: true,
6405
                get: function () {
6406
                    return this._fill;
6407
                },
6408
                set: function (f) {
6409
6410
                    if (this._fill instanceof Two.Gradient
6411
                        || this._fill instanceof Two.LinearGradient
6412
                        || this._fill instanceof Two.RadialGradient
6413
                        || this._fill instanceof Two.Texture) {
6414
                        this._fill.unbind(Two.Events.change, this._renderer.flagFill);
6415
                    }
6416
6417
                    this._fill = f;
6418
                    this._flagFill = true;
6419
6420
                    if (this._fill instanceof Two.Gradient
6421
                        || this._fill instanceof Two.LinearGradient
6422
                        || this._fill instanceof Two.RadialGradient
6423
                        || this._fill instanceof Two.Texture) {
6424
                        this._fill.bind(Two.Events.change, this._renderer.flagFill);
6425
                    }
6426
6427
                }
6428
            });
6429
6430
            Object.defineProperty(object, 'stroke', {
6431
                enumerable: true,
6432
                get: function () {
6433
                    return this._stroke;
6434
                },
6435
                set: function (f) {
6436
6437
                    if (this._stroke instanceof Two.Gradient
6438
                        || this._stroke instanceof Two.LinearGradient
6439
                        || this._stroke instanceof Two.RadialGradient
6440
                        || this._stroke instanceof Two.Texture) {
6441
                        this._stroke.unbind(Two.Events.change, this._renderer.flagStroke);
6442
                    }
6443
6444
                    this._stroke = f;
6445
                    this._flagStroke = true;
6446
6447
                    if (this._stroke instanceof Two.Gradient
6448
                        || this._stroke instanceof Two.LinearGradient
6449
                        || this._stroke instanceof Two.RadialGradient
6450
                        || this._stroke instanceof Two.Texture) {
6451
                        this._stroke.bind(Two.Events.change, this._renderer.flagStroke);
6452
                    }
6453
6454
                }
6455
            });
6456
6457
            Object.defineProperty(object, 'length', {
6458
                get: function () {
6459
                    if (this._flagLength) {
6460
                        this._updateLength();
6461
                    }
6462
                    return this._length;
6463
                }
6464
            });
6465
6466
            Object.defineProperty(object, 'closed', {
6467
                enumerable: true,
6468
                get: function () {
6469
                    return this._closed;
6470
                },
6471
                set: function (v) {
6472
                    this._closed = !!v;
6473
                    this._flagVertices = true;
6474
                }
6475
            });
6476
6477
            Object.defineProperty(object, 'curved', {
6478
                enumerable: true,
6479
                get: function () {
6480
                    return this._curved;
6481
                },
6482
                set: function (v) {
6483
                    this._curved = !!v;
6484
                    this._flagVertices = true;
6485
                }
6486
            });
6487
6488
            Object.defineProperty(object, 'automatic', {
6489
                enumerable: true,
6490
                get: function () {
6491
                    return this._automatic;
6492
                },
6493
                set: function (v) {
6494
                    if (v === this._automatic) {
6495
                        return;
6496
                    }
6497
                    this._automatic = !!v;
6498
                    var method = this._automatic ? 'ignore' : 'listen';
6499
                    _.each(this.vertices, function (v) {
6500
                        v[method]();
6501
                    });
6502
                }
6503
            });
6504
6505
            Object.defineProperty(object, 'beginning', {
6506
                enumerable: true,
6507
                get: function () {
6508
                    return this._beginning;
6509
                },
6510
                set: function (v) {
6511
                    this._beginning = v;
6512
                    this._flagVertices = true;
6513
                }
6514
            });
6515
6516
            Object.defineProperty(object, 'ending', {
6517
                enumerable: true,
6518
                get: function () {
6519
                    return this._ending;
6520
                },
6521
                set: function (v) {
6522
                    this._ending = v;
6523
                    this._flagVertices = true;
6524
                }
6525
            });
6526
6527
            Object.defineProperty(object, 'vertices', {
6528
6529
                enumerable: true,
6530
6531
                get: function () {
6532
                    return this._collection;
6533
                },
6534
6535
                set: function (vertices) {
6536
6537
                    var updateVertices = this._renderer.flagVertices;
6538
                    var bindVertices = this._renderer.bindVertices;
6539
                    var unbindVertices = this._renderer.unbindVertices;
6540
6541
                    // Remove previous listeners
6542
                    if (this._collection) {
6543
                        this._collection
6544
                            .unbind(Two.Events.insert, bindVertices)
6545
                            .unbind(Two.Events.remove, unbindVertices);
6546
                    }
6547
6548
                    // Create new Collection with copy of vertices
6549
                    this._collection = new Two.Utils.Collection((vertices || []).slice(0));
6550
6551
                    // Listen for Collection changes and bind / unbind
6552
                    this._collection
6553
                        .bind(Two.Events.insert, bindVertices)
6554
                        .bind(Two.Events.remove, unbindVertices);
6555
6556
                    // Bind Initial Vertices
6557
                    bindVertices(this._collection);
6558
6559
                }
6560
6561
            });
6562
6563
            Object.defineProperty(object, 'clip', {
6564
                enumerable: true,
6565
                get: function () {
6566
                    return this._clip;
6567
                },
6568
                set: function (v) {
6569
                    this._clip = v;
6570
                    this._flagClip = true;
6571
                }
6572
            });
6573
6574
        }
6575
6576
    });
6577
6578
    _.extend(Path.prototype, Two.Shape.prototype, {
6579
6580
        // Flags
6581
        // http://en.wikipedia.org/wiki/Flag
6582
6583
        _flagVertices: true,
6584
        _flagLength: true,
6585
6586
        _flagFill: true,
6587
        _flagStroke: true,
6588
        _flagLinewidth: true,
6589
        _flagOpacity: true,
6590
        _flagVisible: true,
6591
6592
        _flagCap: true,
6593
        _flagJoin: true,
6594
        _flagMiter: true,
6595
6596
        _flagClip: false,
6597
6598
        // Underlying Properties
6599
6600
        _length: 0,
6601
6602
        _fill: '#fff',
6603
        _stroke: '#000',
6604
        _linewidth: 1.0,
6605
        _opacity: 1.0,
6606
        _visible: true,
6607
6608
        _cap: 'round',
6609
        _join: 'round',
6610
        _miter: 4,
6611
6612
        _closed: true,
6613
        _curved: false,
6614
        _automatic: true,
6615
        _beginning: 0,
6616
        _ending: 1.0,
6617
6618
        _clip: false,
6619
6620
        clone: function (parent) {
6621
6622
            parent = parent || this.parent;
6623
6624
            var points = _.map(this.vertices, function (v) {
6625
                return v.clone();
6626
            });
6627
6628
            var clone = new Path(points, this.closed, this.curved, !this.automatic);
6629
6630
            _.each(Two.Path.Properties, function (k) {
6631
                clone[k] = this[k];
6632
            }, this);
6633
6634
            clone.translation.copy(this.translation);
6635
            clone.rotation = this.rotation;
6636
            clone.scale = this.scale;
6637
6638
            if (parent) {
6639
                parent.add(clone);
6640
            }
6641
6642
            return clone;
6643
6644
        },
6645
6646
        toObject: function () {
6647
6648
            var result = {
6649
                vertices: _.map(this.vertices, function (v) {
6650
                    return v.toObject();
6651
                })
6652
            };
6653
6654
            _.each(Two.Shape.Properties, function (k) {
6655
                result[k] = this[k];
6656
            }, this);
6657
6658
            result.translation = this.translation.toObject;
6659
            result.rotation = this.rotation;
6660
            result.scale = this.scale;
6661
6662
            return result;
6663
6664
        },
6665
6666
        noFill: function () {
6667
            this.fill = 'transparent';
6668
            return this;
6669
        },
6670
6671
        noStroke: function () {
6672
            this.stroke = 'transparent';
6673
            return this;
6674
        },
6675
6676
        /**
6677
            * Orient the vertices of the shape to the upper lefthand
6678
            * corner of the path.
6679
            */
6680
        corner: function () {
6681
6682
            var rect = this.getBoundingClientRect(true);
6683
6684
            rect.centroid = {
6685
                x: rect.left + rect.width / 2,
6686
                y: rect.top + rect.height / 2
6687
            };
6688
6689
            _.each(this.vertices, function (v) {
6690
                v.addSelf(rect.centroid);
6691
            });
6692
6693
            return this;
6694
6695
        },
6696
6697
        /**
6698
            * Orient the vertices of the shape to the center of the
6699
            * path.
6700
            */
6701
        center: function () {
6702
6703
            var rect = this.getBoundingClientRect(true);
6704
6705
            rect.centroid = {
6706
                x: rect.left + rect.width / 2,
6707
                y: rect.top + rect.height / 2
6708
            };
6709
6710
            _.each(this.vertices, function (v) {
6711
                v.subSelf(rect.centroid);
6712
            });
6713
6714
            // this.translation.addSelf(rect.centroid);
6715
6716
            return this;
6717
6718
        },
6719
6720
        /**
6721
            * Remove self from the scene / parent.
6722
            */
6723
        remove: function () {
6724
6725
            if (!this.parent) {
6726
                return this;
6727
            }
6728
6729
            this.parent.remove(this);
6730
6731
            return this;
6732
6733
        },
6734
6735
        /**
6736
            * Return an object with top, left, right, bottom, width, and height
6737
            * parameters of the group.
6738
            */
6739
        getBoundingClientRect: function (shallow) {
6740
            var matrix, border, l, x, y, i, v;
6741
6742
            var left = Infinity, right = -Infinity,
6743
                top = Infinity, bottom = -Infinity;
6744
6745
            // TODO: Update this to not __always__ update. Just when it needs to.
6746
            this._update(true);
6747
6748
            matrix = !!shallow ? this._matrix : getComputedMatrix(this);
6749
6750
            border = this.linewidth / 2;
6751
            l = this._vertices.length;
6752
6753
            if (l <= 0) {
6754
                v = matrix.multiply(0, 0, 1);
6755
                return {
6756
                    top: v.y,
6757
                    left: v.x,
6758
                    right: v.x,
6759
                    bottom: v.y,
6760
                    width: 0,
6761
                    height: 0
6762
                };
6763
            }
6764
6765
            for (i = 0; i < l; i++) {
6766
                v = this._vertices[i];
6767
6768
                x = v.x;
6769
                y = v.y;
6770
6771
                v = matrix.multiply(x, y, 1);
6772
                top = min(v.y - border, top);
6773
                left = min(v.x - border, left);
6774
                right = max(v.x + border, right);
6775
                bottom = max(v.y + border, bottom);
6776
            }
6777
6778
            return {
6779
                top: top,
6780
                left: left,
6781
                right: right,
6782
                bottom: bottom,
6783
                width: right - left,
6784
                height: bottom - top
6785
            };
6786
6787
        },
6788
6789
        /**
6790
            * Given a float `t` from 0 to 1, return a point or assign a passed `obj`'s
6791
            * coordinates to that percentage on this Two.Path's curve.
6792
            */
6793
        getPointAt: function (t, obj) {
6794
            var ia, ib;
6795
            var x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right;
6796
            var target = this.length * Math.min(Math.max(t, 0), 1);
6797
            var length = this.vertices.length;
6798
            var last = length - 1;
6799
6800
            var a = null;
6801
            var b = null;
6802
6803
            for (var i = 0, l = this._lengths.length, sum = 0; i < l; i++) {
6804
6805
                if (sum + this._lengths[i] >= target) {
6806
6807
                    if (this._closed) {
6808
                        ia = Two.Utils.mod(i, length);
6809
                        ib = Two.Utils.mod(i - 1, length);
6810
                        if (i === 0) {
6811
                            ia = ib;
6812
                            ib = i;
6813
                        }
6814
                    } else {
6815
                        ia = i;
6816
                        ib = Math.min(Math.max(i - 1, 0), last);
6817
                    }
6818
6819
                    a = this.vertices[ia];
6820
                    b = this.vertices[ib];
6821
                    target -= sum;
6822
                    if (this._lengths[i] !== 0) {
6823
                        t = target / this._lengths[i];
6824
                    }
6825
6826
                    break;
6827
6828
                }
6829
6830
                sum += this._lengths[i];
6831
6832
            }
6833
6834
            // console.log(sum, a.command, b.command);
6835
6836
            if (_.isNull(a) || _.isNull(b)) {
6837
                return null;
6838
            }
6839
6840
            right = b.controls && b.controls.right;
6841
            left = a.controls && a.controls.left;
6842
6843
            x1 = b.x;
6844
            y1 = b.y;
6845
            x2 = (right || b).x;
6846
            y2 = (right || b).y;
6847
            x3 = (left || a).x;
6848
            y3 = (left || a).y;
6849
            x4 = a.x;
6850
            y4 = a.y;
6851
6852
            if (right && b._relative) {
6853
                x2 += b.x;
6854
                y2 += b.y;
6855
            }
6856
6857
            if (left && a._relative) {
6858
                x3 += a.x;
6859
                y3 += a.y;
6860
            }
6861
6862
            x = Two.Utils.getPointOnCubicBezier(t, x1, x2, x3, x4);
6863
            y = Two.Utils.getPointOnCubicBezier(t, y1, y2, y3, y4);
6864
6865
            if (_.isObject(obj)) {
6866
                obj.x = x;
6867
                obj.y = y;
6868
                return obj;
6869
            }
6870
6871
            return new Two.Vector(x, y);
6872
6873
        },
6874
6875
        /**
6876
            * Based on closed / curved and sorting of vertices plot where all points
6877
            * should be and where the respective handles should be too.
6878
            */
6879
        plot: function () {
6880
6881
            if (this.curved) {
6882
                Two.Utils.getCurveFromPoints(this._vertices, this.closed);
6883
                return this;
6884
            }
6885
6886
            for (var i = 0; i < this._vertices.length; i++) {
6887
                this._vertices[i]._command = i === 0 ? Two.Commands.move : Two.Commands.line;
6888
            }
6889
6890
            return this;
6891
6892
        },
6893
6894
        subdivide: function (limit) {
6895
            //TODO: DRYness (function below)
6896
            this._update();
6897
6898
            var last = this.vertices.length - 1;
6899
            var b = this.vertices[last];
6900
            var closed = this._closed || this.vertices[last]._command === Two.Commands.close;
6901
            var points = [];
6902
            _.each(this.vertices, function (a, i) {
6903
6904
                if (i <= 0 && !closed) {
6905
                    b = a;
6906
                    return;
6907
                }
6908
6909
                if (a.command === Two.Commands.move) {
6910
                    points.push(new Two.Anchor(b.x, b.y));
6911
                    if (i > 0) {
6912
                        points[points.length - 1].command = Two.Commands.line;
6913
                    }
6914
                    b = a;
6915
                    return;
6916
                }
6917
6918
                var verts = getSubdivisions(a, b, limit);
6919
                points = points.concat(verts);
6920
6921
                // Assign commands to all the verts
6922
                _.each(verts, function (v, i) {
6923
                    if (i <= 0 && b.command === Two.Commands.move) {
6924
                        v.command = Two.Commands.move;
6925
                    } else {
6926
                        v.command = Two.Commands.line;
6927
                    }
6928
                });
6929
6930
                if (i >= last) {
6931
6932
                    // TODO: Add check if the two vectors in question are the same values.
6933
                    if (this._closed && this._automatic) {
6934
6935
                        b = a;
6936
6937
                        verts = getSubdivisions(a, b, limit);
6938
                        points = points.concat(verts);
6939
6940
                        // Assign commands to all the verts
6941
                        _.each(verts, function (v, i) {
6942
                            if (i <= 0 && b.command === Two.Commands.move) {
6943
                                v.command = Two.Commands.move;
6944
                            } else {
6945
                                v.command = Two.Commands.line;
6946
                            }
6947
                        });
6948
6949
                    } else if (closed) {
6950
                        points.push(new Two.Anchor(a.x, a.y));
6951
                    }
6952
6953
                    points[points.length - 1].command = closed ? Two.Commands.close : Two.Commands.line;
6954
6955
                }
6956
6957
                b = a;
6958
6959
            }, this);
6960
6961
            this._automatic = false;
6962
            this._curved = false;
6963
            this.vertices = points;
6964
6965
            return this;
6966
6967
        },
6968
6969
        _updateLength: function (limit) {
6970
            //TODO: DRYness (function above)
6971
            this._update();
6972
6973
            var length = this.vertices.length;
6974
            var last = length - 1;
6975
            var b = this.vertices[last];
6976
            var closed = this._closed || this.vertices[last]._command === Two.Commands.close;
6977
            var sum = 0;
6978
6979
            if (_.isUndefined(this._lengths)) {
6980
                this._lengths = [];
6981
            }
6982
6983
            _.each(this.vertices, function (a, i) {
6984
6985
                if ((i <= 0 && !closed) || a.command === Two.Commands.move) {
6986
                    b = a;
6987
                    this._lengths[i] = 0;
6988
                    return;
6989
                }
6990
6991
                this._lengths[i] = getCurveLength(a, b, limit);
6992
                sum += this._lengths[i];
6993
6994
                if (i >= last && closed) {
6995
6996
                    b = this.vertices[(i + 1) % length];
6997
6998
                    this._lengths[i + 1] = getCurveLength(a, b, limit);
6999
                    sum += this._lengths[i + 1];
7000
7001
                }
7002
7003
                b = a;
7004
7005
            }, this);
7006
7007
            this._length = sum;
7008
7009
            return this;
7010
7011
        },
7012
7013
        _update: function () {
7014
7015
            if (this._flagVertices) {
7016
7017
                var l = this.vertices.length;
7018
                var last = l - 1, v;
7019
7020
                // TODO: Should clamp this so that `ia` and `ib`
7021
                // cannot select non-verts.
7022
                var ia = round((this._beginning) * last);
7023
                var ib = round((this._ending) * last);
7024
7025
                this._vertices.length = 0;
7026
7027
                for (var i = ia; i < ib + 1; i++) {
7028
                    v = this.vertices[i];
7029
                    this._vertices.push(v);
7030
                }
7031
7032
                if (this._automatic) {
7033
                    this.plot();
7034
                }
7035
7036
            }
7037
7038
            Two.Shape.prototype._update.apply(this, arguments);
7039
7040
            return this;
7041
7042
        },
7043
7044
        flagReset: function () {
7045
7046
            this._flagVertices = this._flagFill = this._flagStroke =
7047
                this._flagLinewidth = this._flagOpacity = this._flagVisible =
7048
                this._flagCap = this._flagJoin = this._flagMiter =
7049
                this._flagClip = false;
7050
7051
            Two.Shape.prototype.flagReset.call(this);
7052
7053
            return this;
7054
7055
        }
7056
7057
    });
7058
7059
    Path.MakeObservable(Path.prototype);
7060
7061
    /**
7062
        * Utility functions
7063
        */
7064
7065
    function getCurveLength(a, b, limit) {
7066
        // TODO: DRYness
7067
        var x1, x2, x3, x4, y1, y2, y3, y4;
7068
7069
        var right = b.controls && b.controls.right;
7070
        var left = a.controls && a.controls.left;
7071
7072
        x1 = b.x;
7073
        y1 = b.y;
7074
        x2 = (right || b).x;
7075
        y2 = (right || b).y;
7076
        x3 = (left || a).x;
7077
        y3 = (left || a).y;
7078
        x4 = a.x;
7079
        y4 = a.y;
7080
7081
        if (right && b._relative) {
7082
            x2 += b.x;
7083
            y2 += b.y;
7084
        }
7085
7086
        if (left && a._relative) {
7087
            x3 += a.x;
7088
            y3 += a.y;
7089
        }
7090
7091
        return Two.Utils.getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit);
7092
7093
    }
7094
7095
    function getSubdivisions(a, b, limit) {
7096
        // TODO: DRYness
7097
        var x1, x2, x3, x4, y1, y2, y3, y4;
7098
7099
        var right = b.controls && b.controls.right;
7100
        var left = a.controls && a.controls.left;
7101
7102
        x1 = b.x;
7103
        y1 = b.y;
7104
        x2 = (right || b).x;
7105
        y2 = (right || b).y;
7106
        x3 = (left || a).x;
7107
        y3 = (left || a).y;
7108
        x4 = a.x;
7109
        y4 = a.y;
7110
7111
        if (right && b._relative) {
7112
            x2 += b.x;
7113
            y2 += b.y;
7114
        }
7115
7116
        if (left && a._relative) {
7117
            x3 += a.x;
7118
            y3 += a.y;
7119
        }
7120
7121
        return Two.Utils.subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit);
7122
7123
    }
7124
7125
})((typeof global !== 'undefined' ? global : this).Two);
7126
7127
(function (Two) {
7128
7129
    var Path = Two.Path;
7130
    var _ = Two.Utils;
7131
7132
    var Line = Two.Line = function (x1, y1, x2, y2) {
7133
7134
        var width = x2 - x1;
7135
        var height = y2 - y1;
7136
7137
        var w2 = width / 2;
7138
        var h2 = height / 2;
7139
7140
        Path.call(this, [
7141
            new Two.Anchor(- w2, - h2),
7142
            new Two.Anchor(w2, h2)
7143
        ]);
7144
7145
        this.translation.set(x1 + w2, y1 + h2);
7146
7147
    };
7148
7149
    _.extend(Line.prototype, Path.prototype);
7150
7151
    Path.MakeObservable(Line.prototype);
7152
7153
})((typeof global !== 'undefined' ? global : this).Two);
7154
7155
(function (Two) {
7156
7157
    var Path = Two.Path;
7158
    var _ = Two.Utils;
7159
7160
    var Rectangle = Two.Rectangle = function (x, y, width, height) {
7161
7162
        Path.call(this, [
7163
            new Two.Anchor(),
7164
            new Two.Anchor(),
7165
            new Two.Anchor(),
7166
            new Two.Anchor()
7167
        ], true);
7168
7169
        this.width = width;
7170
        this.height = height;
7171
        this._update();
7172
7173
        this.translation.set(x, y);
7174
7175
    };
7176
7177
    _.extend(Rectangle, {
7178
7179
        Properties: ['width', 'height'],
7180
7181
        MakeObservable: function (obj) {
7182
            Path.MakeObservable(obj);
7183
            _.each(Rectangle.Properties, Two.Utils.defineProperty, obj);
7184
        }
7185
7186
    });
7187
7188
    _.extend(Rectangle.prototype, Path.prototype, {
7189
7190
        _width: 0,
7191
        _height: 0,
7192
7193
        _flagWidth: 0,
7194
        _flagHeight: 0,
7195
7196
        _update: function () {
7197
7198
            if (this._flagWidth || this._flagHeight) {
7199
7200
                var xr = this._width / 2;
7201
                var yr = this._height / 2;
7202
7203
                this.vertices[0].set(-xr, -yr);
7204
                this.vertices[1].set(xr, -yr);
7205
                this.vertices[2].set(xr, yr);
7206
                this.vertices[3].set(-xr, yr);
7207
7208
            }
7209
7210
            Path.prototype._update.call(this);
7211
7212
            return this;
7213
7214
        },
7215
7216
        flagReset: function () {
7217
7218
            this._flagWidth = this._flagHeight = false;
7219
            Path.prototype.flagReset.call(this);
7220
7221
            return this;
7222
7223
        }
7224
7225
    });
7226
7227
    Rectangle.MakeObservable(Rectangle.prototype);
7228
7229
})((typeof global !== 'undefined' ? global : this).Two);
7230
7231
(function (Two) {
7232
7233
    var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin;
7234
    var _ = Two.Utils;
7235
7236
    var Ellipse = Two.Ellipse = function (ox, oy, rx, ry) {
7237
7238
        if (!_.isNumber(ry)) {
7239
            ry = rx;
7240
        }
7241
7242
        var amount = Two.Resolution;
7243
7244
        var points = _.map(_.range(amount), function (i) {
7245
            return new Two.Anchor();
7246
        }, this);
7247
7248
        Path.call(this, points, true, true);
7249
7250
        this.width = rx * 2;
7251
        this.height = ry * 2;
7252
7253
        this._update();
7254
        this.translation.set(ox, oy);
7255
7256
    };
7257
7258
    _.extend(Ellipse, {
7259
7260
        Properties: ['width', 'height'],
7261
7262
        MakeObservable: function (obj) {
7263
7264
            Path.MakeObservable(obj);
7265
            _.each(Ellipse.Properties, Two.Utils.defineProperty, obj);
7266
7267
        }
7268
7269
    });
7270
7271
    _.extend(Ellipse.prototype, Path.prototype, {
7272
7273
        _width: 0,
7274
        _height: 0,
7275
7276
        _flagWidth: false,
7277
        _flagHeight: false,
7278
7279
        _update: function () {
7280
7281
            if (this._flagWidth || this._flagHeight) {
7282
                for (var i = 0, l = this.vertices.length; i < l; i++) {
7283
                    var pct = i / l;
7284
                    var theta = pct * TWO_PI;
7285
                    var x = this._width * cos(theta) / 2;
7286
                    var y = this._height * sin(theta) / 2;
7287
                    this.vertices[i].set(x, y);
7288
                }
7289
            }
7290
7291
            Path.prototype._update.call(this);
7292
            return this;
7293
7294
        },
7295
7296
        flagReset: function () {
7297
7298
            this._flagWidth = this._flagHeight = false;
7299
7300
            Path.prototype.flagReset.call(this);
7301
            return this;
7302
7303
        }
7304
7305
    });
7306
7307
    Ellipse.MakeObservable(Ellipse.prototype);
7308
7309
})((typeof global !== 'undefined' ? global : this).Two);
7310
7311
(function (Two) {
7312
7313
    var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin;
7314
    var _ = Two.Utils;
7315
7316
    var Circle = Two.Circle = function (ox, oy, r) {
7317
7318
        var amount = Two.Resolution;
7319
7320
        var points = _.map(_.range(amount), function (i) {
7321
            return new Two.Anchor();
7322
        }, this);
7323
7324
        Path.call(this, points, true, true);
7325
7326
        this.radius = r;
7327
7328
        this._update();
7329
        this.translation.set(ox, oy);
7330
7331
    };
7332
7333
    _.extend(Circle, {
7334
7335
        Properties: ['radius'],
7336
7337
        MakeObservable: function (obj) {
7338
7339
            Path.MakeObservable(obj);
7340
            _.each(Circle.Properties, Two.Utils.defineProperty, obj);
7341
7342
        }
7343
7344
    });
7345
7346
    _.extend(Circle.prototype, Path.prototype, {
7347
7348
        _radius: 0,
7349
        _flagRadius: false,
7350
7351
        _update: function () {
7352
7353
            if (this._flagRadius) {
7354
                for (var i = 0, l = this.vertices.length; i < l; i++) {
7355
                    var pct = i / l;
7356
                    var theta = pct * TWO_PI;
7357
                    var x = this._radius * cos(theta);
7358
                    var y = this._radius * sin(theta);
7359
                    this.vertices[i].set(x, y);
7360
                }
7361
            }
7362
7363
            Path.prototype._update.call(this);
7364
            return this;
7365
7366
        },
7367
7368
        flagReset: function () {
7369
7370
            this._flagRadius = false;
7371
7372
            Path.prototype.flagReset.call(this);
7373
            return this;
7374
7375
        }
7376
7377
    });
7378
7379
    Circle.MakeObservable(Circle.prototype);
7380
7381
})((typeof global !== 'undefined' ? global : this).Two);
7382
7383
(function (Two) {
7384
7385
    var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin;
7386
    var _ = Two.Utils;
7387
7388
    var Polygon = Two.Polygon = function (ox, oy, r, sides) {
7389
7390
        sides = Math.max(sides || 0, 3);
7391
7392
        var points = _.map(_.range(sides), function (i) {
7393
            return new Two.Anchor();
7394
        });
7395
7396
        Path.call(this, points, true);
7397
7398
        this.width = r * 2;
7399
        this.height = r * 2;
7400
        this.sides = sides;
7401
7402
        this._update();
7403
        this.translation.set(ox, oy);
7404
7405
    };
7406
7407
    _.extend(Polygon, {
7408
7409
        Properties: ['width', 'height', 'sides'],
7410
7411
        MakeObservable: function (obj) {
7412
7413
            Path.MakeObservable(obj);
7414
            _.each(Polygon.Properties, Two.Utils.defineProperty, obj);
7415
7416
        }
7417
7418
    });
7419
7420
    _.extend(Polygon.prototype, Path.prototype, {
7421
7422
        _width: 0,
7423
        _height: 0,
7424
        _sides: 0,
7425
7426
        _flagWidth: false,
7427
        _flagHeight: false,
7428
        _flagSides: false,
7429
7430
        _update: function () {
7431
7432
            if (this._flagWidth || this._flagHeight || this._flagSides) {
7433
7434
                var sides = this._sides;
7435
                var amount = this.vertices.length;
7436
7437
                if (amount > sides) {
7438
                    this.vertices.splice(sides - 1, amount - sides);
7439
                }
7440
7441
                for (var i = 0; i < sides; i++) {
7442
7443
                    var pct = (i + 0.5) / sides;
7444
                    var theta = TWO_PI * pct + Math.PI / 2;
7445
                    var x = this._width * cos(theta);
7446
                    var y = this._height * sin(theta);
7447
7448
                    if (i >= amount) {
7449
                        this.vertices.push(new Two.Anchor(x, y));
7450
                    } else {
7451
                        this.vertices[i].set(x, y);
7452
                    }
7453
7454
                }
7455
7456
            }
7457
7458
            Path.prototype._update.call(this);
7459
            return this;
7460
7461
        },
7462
7463
        flagReset: function () {
7464
7465
            this._flagWidth = this._flagHeight = this._flagSides = false;
7466
            Path.prototype.flagReset.call(this);
7467
7468
            return this;
7469
7470
        }
7471
7472
    });
7473
7474
    Polygon.MakeObservable(Polygon.prototype);
7475
7476
})((typeof global !== 'undefined' ? global : this).Two);
7477
7478
(function (Two) {
7479
7480
    var Path = Two.Path, PI = Math.PI, TWO_PI = Math.PI * 2, HALF_PI = Math.PI / 2,
7481
        cos = Math.cos, sin = Math.sin, abs = Math.abs, _ = Two.Utils;
7482
7483
    var ArcSegment = Two.ArcSegment = function (ox, oy, ir, or, sa, ea, res) {
7484
7485
        var points = _.map(_.range(res || (Two.Resolution * 3)), function () {
7486
            return new Two.Anchor();
7487
        });
7488
7489
        Path.call(this, points, false, false, true);
7490
7491
        this.innerRadius = ir;
7492
        this.outerRadius = or;
7493
7494
        this.startAngle = sa;
7495
        this.endAngle = ea;
7496
7497
        this._update();
7498
        this.translation.set(ox, oy);
7499
7500
    }
7501
7502
    _.extend(ArcSegment, {
7503
7504
        Properties: ['startAngle', 'endAngle', 'innerRadius', 'outerRadius'],
7505
7506
        MakeObservable: function (obj) {
7507
7508
            Path.MakeObservable(obj);
7509
            _.each(ArcSegment.Properties, Two.Utils.defineProperty, obj);
7510
7511
        }
7512
7513
    });
7514
7515
    _.extend(ArcSegment.prototype, Path.prototype, {
7516
7517
        _flagStartAngle: false,
7518
        _flagEndAngle: false,
7519
        _flagInnerRadius: false,
7520
        _flagOuterRadius: false,
7521
7522
        _startAngle: 0,
7523
        _endAngle: TWO_PI,
7524
        _innerRadius: 0,
7525
        _outerRadius: 0,
7526
7527
        _update: function () {
7528
7529
            if (this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius
7530
                || this._flagOuterRadius) {
7531
7532
                var sa = this._startAngle;
7533
                var ea = this._endAngle;
7534
7535
                var ir = this._innerRadius;
7536
                var or = this._outerRadius;
7537
7538
                var connected = mod(sa, TWO_PI) === mod(ea, TWO_PI);
7539
                var punctured = ir > 0;
7540
7541
                var vertices = this.vertices;
7542
                var length = (punctured ? vertices.length / 2 : vertices.length);
7543
                var command, id = 0;
7544
7545
                if (connected) {
7546
                    length--;
7547
                } else if (!punctured) {
7548
                    length -= 2;
7549
                }
7550
7551
                /**
7552
                    * Outer Circle
7553
                    */
7554
                for (var i = 0, last = length - 1; i < length; i++) {
7555
7556
                    var pct = i / last;
7557
                    var v = vertices[id];
7558
                    var theta = pct * (ea - sa) + sa;
7559
                    var step = (ea - sa) / length;
7560
7561
                    var x = or * Math.cos(theta);
7562
                    var y = or * Math.sin(theta);
7563
7564
                    switch (i) {
7565
                        case 0:
7566
                            command = Two.Commands.move;
7567
                            break;
7568
                        default:
7569
                            command = Two.Commands.curve;
7570
                    }
7571
7572
                    v.command = command;
7573
                    v.x = x;
7574
                    v.y = y;
7575
                    v.controls.left.clear();
7576
                    v.controls.right.clear();
7577
7578
                    if (v.command === Two.Commands.curve) {
7579
                        var amp = or * step / Math.PI;
7580
                        v.controls.left.x = amp * Math.cos(theta - HALF_PI);
7581
                        v.controls.left.y = amp * Math.sin(theta - HALF_PI);
7582
                        v.controls.right.x = amp * Math.cos(theta + HALF_PI);
7583
                        v.controls.right.y = amp * Math.sin(theta + HALF_PI);
7584
                        if (i === 1) {
7585
                            v.controls.left.multiplyScalar(2);
7586
                        }
7587
                        if (i === last) {
7588
                            v.controls.right.multiplyScalar(2);
7589
                        }
7590
                    }
7591
7592
                    id++;
7593
7594
                }
7595
7596
                if (punctured) {
7597
7598
                    if (connected) {
7599
                        vertices[id].command = Two.Commands.close;
7600
                        id++;
7601
                    } else {
7602
                        length--;
7603
                        last = length - 1;
7604
                    }
7605
7606
                    /**
7607
                        * Inner Circle
7608
                        */
7609
                    for (i = 0; i < length; i++) {
7610
7611
                        pct = i / last;
7612
                        v = vertices[id];
7613
                        theta = (1 - pct) * (ea - sa) + sa;
7614
                        step = (ea - sa) / length;
7615
7616
                        x = ir * Math.cos(theta);
7617
                        y = ir * Math.sin(theta);
7618
                        command = Two.Commands.curve;
7619
                        if (i <= 0) {
7620
                            command = connected ? Two.Commands.move : Two.Commands.line;
7621
                        }
7622
7623
                        v.command = command;
7624
                        v.x = x;
7625
                        v.y = y;
7626
                        v.controls.left.clear();
7627
                        v.controls.right.clear();
7628
7629
                        if (v.command === Two.Commands.curve) {
7630
                            amp = ir * step / Math.PI;
7631
                            v.controls.left.x = amp * Math.cos(theta + HALF_PI);
7632
                            v.controls.left.y = amp * Math.sin(theta + HALF_PI);
7633
                            v.controls.right.x = amp * Math.cos(theta - HALF_PI);
7634
                            v.controls.right.y = amp * Math.sin(theta - HALF_PI);
7635
                            if (i === 1) {
7636
                                v.controls.left.multiplyScalar(2);
7637
                            }
7638
                            if (i === last) {
7639
                                v.controls.right.multiplyScalar(2);
7640
                            }
7641
                        }
7642
7643
                        id++;
7644
7645
                    }
7646
7647
                } else if (!connected) {
7648
7649
                    vertices[id].command = Two.Commands.line;
7650
                    vertices[id].x = 0;
7651
                    vertices[id].y = 0;
7652
                    id++;
7653
7654
                }
7655
7656
                /**
7657
                    * Final Point
7658
                    */
7659
                vertices[id].command = Two.Commands.close;
7660
7661
            }
7662
7663
            Path.prototype._update.call(this);
7664
7665
            return this;
7666
7667
        },
7668
7669
        flagReset: function () {
7670
7671
            Path.prototype.flagReset.call(this);
7672
7673
            this._flagStartAngle = this._flagEndAngle
7674
                = this._flagInnerRadius = this._flagOuterRadius = false;
7675
7676
            return this;
7677
7678
        }
7679
7680
    });
7681
7682
    ArcSegment.MakeObservable(ArcSegment.prototype);
7683
7684
    function mod(v, l) {
7685
        while (v < 0) {
7686
            v += l;
7687
        }
7688
        return v % l;
7689
    }
7690
7691
})((typeof global !== 'undefined' ? global : this).Two);
7692
7693
(function (Two) {
7694
7695
    var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin;
7696
    var _ = Two.Utils;
7697
7698
    var Star = Two.Star = function (ox, oy, or, ir, sides) {
7699
7700
        if (!_.isNumber(ir)) {
7701
            ir = or / 2;
7702
        }
7703
7704
        if (!_.isNumber(sides) || sides <= 0) {
7705
            sides = 5;
7706
        }
7707
7708
        var length = sides * 2;
7709
7710
        var points = _.map(_.range(length), function (i) {
7711
            return new Two.Anchor();
7712
        });
7713
7714
        Path.call(this, points, true);
7715
7716
        this.innerRadius = ir;
7717
        this.outerRadius = or;
7718
        this.sides = sides;
7719
7720
        this._update();
7721
        this.translation.set(ox, oy);
7722
7723
    };
7724
7725
    _.extend(Star, {
7726
7727
        Properties: ['innerRadius', 'outerRadius', 'sides'],
7728
7729
        MakeObservable: function (obj) {
7730
7731
            Path.MakeObservable(obj);
7732
            _.each(Star.Properties, Two.Utils.defineProperty, obj);
7733
7734
        }
7735
7736
    });
7737
7738
    _.extend(Star.prototype, Path.prototype, {
7739
7740
        _innerRadius: 0,
7741
        _outerRadius: 0,
7742
        _sides: 0,
7743
7744
        _flagInnerRadius: false,
7745
        _flagOuterRadius: false,
7746
        _flagSides: false,
7747
7748
        _update: function () {
7749
7750
            if (this._flagInnerRadius || this._flagOuterRadius || this._flagSides) {
7751
7752
                var sides = this._sides * 2;
7753
                var amount = this.vertices.length;
7754
7755
                if (amount > sides) {
7756
                    this.vertices.splice(sides - 1, amount - sides);
7757
                }
7758
7759
                for (var i = 0; i < sides; i++) {
7760
7761
                    var pct = (i + 0.5) / sides;
7762
                    var theta = TWO_PI * pct;
7763
                    var r = (i % 2 ? this._innerRadius : this._outerRadius);
7764
                    var x = r * cos(theta);
7765
                    var y = r * sin(theta);
7766
7767
                    if (i >= amount) {
7768
                        this.vertices.push(new Two.Anchor(x, y));
7769
                    } else {
7770
                        this.vertices[i].set(x, y);
7771
                    }
7772
7773
                }
7774
7775
            }
7776
7777
            Path.prototype._update.call(this);
7778
7779
            return this;
7780
7781
        },
7782
7783
        flagReset: function () {
7784
7785
            this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false;
7786
            Path.prototype.flagReset.call(this);
7787
7788
            return this;
7789
7790
        }
7791
7792
    });
7793
7794
    Star.MakeObservable(Star.prototype);
7795
7796
})((typeof global !== 'undefined' ? global : this).Two);
7797
7798
(function (Two) {
7799
7800
    var Path = Two.Path;
7801
    var _ = Two.Utils;
7802
7803
    var RoundedRectangle = Two.RoundedRectangle = function (ox, oy, width, height, radius) {
7804
7805
        if (!_.isNumber(radius)) {
7806
            radius = Math.floor(Math.min(width, height) / 12);
7807
        }
7808
7809
        var amount = 10;
7810
7811
        var points = _.map(_.range(amount), function (i) {
7812
            return new Two.Anchor(0, 0, 0, 0, 0, 0,
7813
                i === 0 ? Two.Commands.move : Two.Commands.curve);
7814
        });
7815
7816
        points[points.length - 1].command = Two.Commands.close;
7817
7818
        Path.call(this, points, false, false, true);
7819
7820
        this.width = width;
7821
        this.height = height;
7822
        this.radius = radius;
7823
7824
        this._update();
7825
        this.translation.set(ox, oy);
7826
7827
    };
7828
7829
    _.extend(RoundedRectangle, {
7830
7831
        Properties: ['width', 'height', 'radius'],
7832
7833
        MakeObservable: function (obj) {
7834
7835
            Path.MakeObservable(obj);
7836
            _.each(RoundedRectangle.Properties, Two.Utils.defineProperty, obj);
7837
7838
        }
7839
7840
    });
7841
7842
    _.extend(RoundedRectangle.prototype, Path.prototype, {
7843
7844
        _width: 0,
7845
        _height: 0,
7846
        _radius: 0,
7847
7848
        _flagWidth: false,
7849
        _flagHeight: false,
7850
        _flagRadius: false,
7851
7852
        _update: function () {
7853
7854
            if (this._flagWidth || this._flagHeight || this._flagRadius) {
7855
7856
                var width = this._width;
7857
                var height = this._height;
7858
                var radius = Math.min(Math.max(this._radius, 0),
7859
                    Math.min(width, height));
7860
7861
                var v;
7862
                var w = width / 2;
7863
                var h = height / 2;
7864
7865
                v = this.vertices[0];
7866
                v.x = - (w - radius);
7867
                v.y = - h;
7868
7869
                // Upper Right Corner
7870
7871
                v = this.vertices[1];
7872
                v.x = (w - radius);
7873
                v.y = - h;
7874
                v.controls.left.clear();
7875
                v.controls.right.x = radius;
7876
                v.controls.right.y = 0;
7877
7878
                v = this.vertices[2];
7879
                v.x = w;
7880
                v.y = - (h - radius);
7881
                v.controls.right.clear();
7882
                v.controls.left.clear();
7883
7884
                // Bottom Right Corner
7885
7886
                v = this.vertices[3];
7887
                v.x = w;
7888
                v.y = (h - radius);
7889
                v.controls.left.clear();
7890
                v.controls.right.x = 0;
7891
                v.controls.right.y = radius;
7892
7893
                v = this.vertices[4];
7894
                v.x = (w - radius);
7895
                v.y = h;
7896
                v.controls.right.clear();
7897
                v.controls.left.clear();
7898
7899
                // Bottom Left Corner
7900
7901
                v = this.vertices[5];
7902
                v.x = - (w - radius);
7903
                v.y = h;
7904
                v.controls.left.clear();
7905
                v.controls.right.x = - radius;
7906
                v.controls.right.y = 0;
7907
7908
                v = this.vertices[6];
7909
                v.x = - w;
7910
                v.y = (h - radius);
7911
                v.controls.left.clear();
7912
                v.controls.right.clear();
7913
7914
                // Upper Left Corner
7915
7916
                v = this.vertices[7];
7917
                v.x = - w;
7918
                v.y = - (h - radius);
7919
                v.controls.left.clear();
7920
                v.controls.right.x = 0;
7921
                v.controls.right.y = - radius;
7922
7923
                v = this.vertices[8];
7924
                v.x = - (w - radius);
7925
                v.y = - h;
7926
                v.controls.left.clear();
7927
                v.controls.right.clear();
7928
7929
                v = this.vertices[9];
7930
                v.copy(this.vertices[8]);
7931
7932
            }
7933
7934
            Path.prototype._update.call(this);
7935
7936
            return this;
7937
7938
        },
7939
7940
        flagReset: function () {
7941
7942
            this._flagWidth = this._flagHeight = this._flagRadius = false;
7943
            Path.prototype.flagReset.call(this);
7944
7945
            return this;
7946
7947
        }
7948
7949
    });
7950
7951
    RoundedRectangle.MakeObservable(RoundedRectangle.prototype);
7952
7953
})((typeof global !== 'undefined' ? global : this).Two);
7954
7955
(function (Two) {
7956
7957
    var root = Two.root;
7958
    var getComputedMatrix = Two.Utils.getComputedMatrix;
7959
    var _ = Two.Utils;
7960
7961
    var canvas = (root.document ? root.document.createElement('canvas') : { getContext: _.identity });
7962
    var ctx = canvas.getContext('2d');
7963
7964
    var Text = Two.Text = function (message, x, y, styles) {
7965
7966
        Two.Shape.call(this);
7967
7968
        this._renderer.type = 'text';
7969
        this._renderer.flagFill = _.bind(Text.FlagFill, this);
7970
        this._renderer.flagStroke = _.bind(Text.FlagStroke, this);
7971
7972
        this.value = message;
7973
7974
        if (_.isNumber(x)) {
7975
            this.translation.x = x;
7976
        }
7977
        if (_.isNumber(y)) {
7978
            this.translation.y = y;
7979
        }
7980
7981
        if (!_.isObject(styles)) {
7982
            return this;
7983
        }
7984
7985
        _.each(Two.Text.Properties, function (property) {
7986
7987
            if (property in styles) {
7988
                this[property] = styles[property];
7989
            }
7990
7991
        }, this);
7992
7993
    };
7994
7995
    _.extend(Two.Text, {
7996
7997
        Properties: [
7998
            'value', 'family', 'size', 'leading', 'alignment', 'linewidth', 'style',
7999
            'weight', 'decoration', 'baseline', 'opacity', 'visible', 'fill', 'stroke'
8000
        ],
8001
8002
        FlagFill: function () {
8003
            this._flagFill = true;
8004
        },
8005
8006
        FlagStroke: function () {
8007
            this._flagStroke = true;
8008
        },
8009
8010
        MakeObservable: function (object) {
8011
8012
            Two.Shape.MakeObservable(object);
8013
8014
            _.each(Two.Text.Properties.slice(0, 12), Two.Utils.defineProperty, object);
8015
8016
            Object.defineProperty(object, 'fill', {
8017
                enumerable: true,
8018
                get: function () {
8019
                    return this._fill;
8020
                },
8021
                set: function (f) {
8022
8023
                    if (this._fill instanceof Two.Gradient
8024
                        || this._fill instanceof Two.LinearGradient
8025
                        || this._fill instanceof Two.RadialGradient
8026
                        || this._fill instanceof Two.Texture) {
8027
                        this._fill.unbind(Two.Events.change, this._renderer.flagFill);
8028
                    }
8029
8030
                    this._fill = f;
8031
                    this._flagFill = true;
8032
8033
                    if (this._fill instanceof Two.Gradient
8034
                        || this._fill instanceof Two.LinearGradient
8035
                        || this._fill instanceof Two.RadialGradient
8036
                        || this._fill instanceof Two.Texture) {
8037
                        this._fill.bind(Two.Events.change, this._renderer.flagFill);
8038
                    }
8039
8040
                }
8041
            });
8042
8043
            Object.defineProperty(object, 'stroke', {
8044
                enumerable: true,
8045
                get: function () {
8046
                    return this._stroke;
8047
                },
8048
                set: function (f) {
8049
8050
                    if (this._stroke instanceof Two.Gradient
8051
                        || this._stroke instanceof Two.LinearGradient
8052
                        || this._stroke instanceof Two.RadialGradient
8053
                        || this._stroke instanceof Two.Texture) {
8054
                        this._stroke.unbind(Two.Events.change, this._renderer.flagStroke);
8055
                    }
8056
8057
                    this._stroke = f;
8058
                    this._flagStroke = true;
8059
8060
                    if (this._stroke instanceof Two.Gradient
8061
                        || this._stroke instanceof Two.LinearGradient
8062
                        || this._stroke instanceof Two.RadialGradient
8063
                        || this._stroke instanceof Two.Texture) {
8064
                        this._stroke.bind(Two.Events.change, this._renderer.flagStroke);
8065
                    }
8066
8067
                }
8068
            });
8069
8070
            Object.defineProperty(object, 'clip', {
8071
                enumerable: true,
8072
                get: function () {
8073
                    return this._clip;
8074
                },
8075
                set: function (v) {
8076
                    this._clip = v;
8077
                    this._flagClip = true;
8078
                }
8079
            });
8080
8081
        }
8082
8083
    });
8084
8085
    _.extend(Two.Text.prototype, Two.Shape.prototype, {
8086
8087
        // Flags
8088
        // http://en.wikipedia.org/wiki/Flag
8089
8090
        _flagValue: true,
8091
        _flagFamily: true,
8092
        _flagSize: true,
8093
        _flagLeading: true,
8094
        _flagAlignment: true,
8095
        _flagBaseline: true,
8096
        _flagStyle: true,
8097
        _flagWeight: true,
8098
        _flagDecoration: true,
8099
8100
        _flagFill: true,
8101
        _flagStroke: true,
8102
        _flagLinewidth: true,
8103
        _flagOpacity: true,
8104
        _flagVisible: true,
8105
8106
        _flagClip: false,
8107
8108
        // Underlying Properties
8109
8110
        _value: '',
8111
        _family: 'sans-serif',
8112
        _size: 13,
8113
        _leading: 17,
8114
        _alignment: 'center',
8115
        _baseline: 'middle',
8116
        _style: 'normal',
8117
        _weight: 500,
8118
        _decoration: 'none',
8119
8120
        _fill: '#000',
8121
        _stroke: 'transparent',
8122
        _linewidth: 1,
8123
        _opacity: 1,
8124
        _visible: true,
8125
8126
        _clip: false,
8127
8128
        remove: function () {
8129
8130
            if (!this.parent) {
8131
                return this;
8132
            }
8133
8134
            this.parent.remove(this);
8135
8136
            return this;
8137
8138
        },
8139
8140
        clone: function (parent) {
8141
8142
            var parent = parent || this.parent;
8143
8144
            var clone = new Two.Text(this.value);
8145
            clone.translation.copy(this.translation);
8146
            clone.rotation = this.rotation;
8147
            clone.scale = this.scale;
8148
8149
            _.each(Two.Text.Properties, function (property) {
8150
                clone[property] = this[property];
8151
            }, this);
8152
8153
            if (parent) {
8154
                parent.add(clone);
8155
            }
8156
8157
            return clone;
8158
8159
        },
8160
8161
        toObject: function () {
8162
8163
            var result = {
8164
                translation: this.translation.toObject(),
8165
                rotation: this.rotation,
8166
                scale: this.scale
8167
            };
8168
8169
            _.each(Two.Text.Properties, function (property) {
8170
                result[property] = this[property];
8171
            }, this);
8172
8173
            return result;
8174
8175
        },
8176
8177
        noStroke: function () {
8178
            this.stroke = 'transparent';
8179
            return this;
8180
        },
8181
8182
        noFill: function () {
8183
            this.fill = 'transparent';
8184
            return this;
8185
        },
8186
8187
        /**
8188
            * A shim to not break `getBoundingClientRect` calls. TODO: Implement a
8189
            * way to calculate proper bounding boxes of `Two.Text`.
8190
            */
8191
        getBoundingClientRect: function (shallow) {
8192
8193
            var matrix, border, l, x, y, i, v;
8194
8195
            var left = Infinity, right = -Infinity,
8196
                top = Infinity, bottom = -Infinity;
8197
8198
            // TODO: Update this to not __always__ update. Just when it needs to.
8199
            this._update(true);
8200
8201
            matrix = !!shallow ? this._matrix : getComputedMatrix(this);
8202
8203
            v = matrix.multiply(0, 0, 1);
8204
8205
            return {
8206
                top: v.x,
8207
                left: v.y,
8208
                right: v.x,
8209
                bottom: v.y,
8210
                width: 0,
8211
                height: 0
8212
            };
8213
8214
        },
8215
8216
        flagReset: function () {
8217
8218
            this._flagValue = this._flagFamily = this._flagSize =
8219
                this._flagLeading = this._flagAlignment = this._flagFill =
8220
                this._flagStroke = this._flagLinewidth = this._flagOpaicty =
8221
                this._flagVisible = this._flagClip = this._flagDecoration =
8222
                this._flagBaseline = false;
8223
8224
            Two.Shape.prototype.flagReset.call(this);
8225
8226
            return this;
8227
8228
        }
8229
8230
    });
8231
8232
    Two.Text.MakeObservable(Two.Text.prototype);
8233
8234
})((typeof global !== 'undefined' ? global : this).Two);
8235
8236
(function (Two) {
8237
8238
    var _ = Two.Utils;
8239
8240
    var Stop = Two.Stop = function (offset, color, opacity) {
8241
8242
        this._renderer = {};
8243
        this._renderer.type = 'stop';
8244
8245
        this.offset = _.isNumber(offset) ? offset
8246
            : Stop.Index <= 0 ? 0 : 1;
8247
8248
        this.opacity = _.isNumber(opacity) ? opacity : 1;
8249
8250
        this.color = _.isString(color) ? color
8251
            : Stop.Index <= 0 ? '#fff' : '#000';
8252
8253
        Stop.Index = (Stop.Index + 1) % 2;
8254
8255
    };
8256
8257
    _.extend(Stop, {
8258
8259
        Index: 0,
8260
8261
        Properties: [
8262
            'offset',
8263
            'opacity',
8264
            'color'
8265
        ],
8266
8267
        MakeObservable: function (object) {
8268
8269
            _.each(Stop.Properties, function (property) {
8270
8271
                var object = this;
8272
                var secret = '_' + property;
8273
                var flag = '_flag' + property.charAt(0).toUpperCase() + property.slice(1);
8274
8275
                Object.defineProperty(object, property, {
8276
                    enumerable: true,
8277
                    get: function () {
8278
                        return this[secret];
8279
                    },
8280
                    set: function (v) {
8281
                        this[secret] = v;
8282
                        this[flag] = true;
8283
                        if (this.parent) {
8284
                            this.parent._flagStops = true;
8285
                        }
8286
                    }
8287
                });
8288
8289
            }, object);
8290
8291
        }
8292
8293
    });
8294
8295
    _.extend(Stop.prototype, Two.Utils.Events, {
8296
8297
        clone: function () {
8298
8299
            var clone = new Stop();
8300
8301
            _.each(Stop.Properties, function (property) {
8302
                clone[property] = this[property];
8303
            }, this);
8304
8305
            return clone;
8306
8307
        },
8308
8309
        toObject: function () {
8310
8311
            var result = {};
8312
8313
            _.each(Stop.Properties, function (k) {
8314
                result[k] = this[k];
8315
            }, this);
8316
8317
            return result;
8318
8319
        },
8320
8321
        flagReset: function () {
8322
8323
            this._flagOffset = this._flagColor = this._flagOpacity = false;
8324
8325
            return this;
8326
8327
        }
8328
8329
    });
8330
8331
    Stop.MakeObservable(Stop.prototype);
8332
8333
    var Gradient = Two.Gradient = function (stops) {
8334
8335
        this._renderer = {};
8336
        this._renderer.type = 'gradient';
8337
8338
        this.id = Two.Identifier + Two.uniqueId();
8339
        this.classList = [];
8340
8341
        this._renderer.flagStops = _.bind(Gradient.FlagStops, this);
8342
        this._renderer.bindStops = _.bind(Gradient.BindStops, this);
8343
        this._renderer.unbindStops = _.bind(Gradient.UnbindStops, this);
8344
8345
        this.spread = 'pad';
8346
8347
        this.stops = stops;
8348
8349
    };
8350
8351
    _.extend(Gradient, {
8352
8353
        Stop: Stop,
8354
8355
        Properties: [
8356
            'spread'
8357
        ],
8358
8359
        MakeObservable: function (object) {
8360
8361
            _.each(Gradient.Properties, Two.Utils.defineProperty, object);
8362
8363
            Object.defineProperty(object, 'stops', {
8364
8365
                enumerable: true,
8366
8367
                get: function () {
8368
                    return this._stops;
8369
                },
8370
8371
                set: function (stops) {
8372
8373
                    var updateStops = this._renderer.flagStops;
8374
                    var bindStops = this._renderer.bindStops;
8375
                    var unbindStops = this._renderer.unbindStops;
8376
8377
                    // Remove previous listeners
8378
                    if (this._stops) {
8379
                        this._stops
8380
                            .unbind(Two.Events.insert, bindStops)
8381
                            .unbind(Two.Events.remove, unbindStops);
8382
                    }
8383
8384
                    // Create new Collection with copy of Stops
8385
                    this._stops = new Two.Utils.Collection((stops || []).slice(0));
8386
8387
                    // Listen for Collection changes and bind / unbind
8388
                    this._stops
8389
                        .bind(Two.Events.insert, bindStops)
8390
                        .bind(Two.Events.remove, unbindStops);
8391
8392
                    // Bind Initial Stops
8393
                    bindStops(this._stops);
8394
8395
                }
8396
8397
            });
8398
8399
        },
8400
8401
        FlagStops: function () {
8402
            this._flagStops = true;
8403
        },
8404
8405
        BindStops: function (items) {
8406
8407
            // This function is called a lot
8408
            // when importing a large SVG
8409
            var i = items.length;
8410
            while (i--) {
8411
                items[i].bind(Two.Events.change, this._renderer.flagStops);
8412
                items[i].parent = this;
8413
            }
8414
8415
            this._renderer.flagStops();
8416
8417
        },
8418
8419
        UnbindStops: function (items) {
8420
8421
            var i = items.length;
8422
            while (i--) {
8423
                items[i].unbind(Two.Events.change, this._renderer.flagStops);
8424
                delete items[i].parent;
8425
            }
8426
8427
            this._renderer.flagStops();
8428
8429
        }
8430
8431
    });
8432
8433
    _.extend(Gradient.prototype, Two.Utils.Events, {
8434
8435
        _flagStops: false,
8436
        _flagSpread: false,
8437
8438
        clone: function (parent) {
8439
8440
            parent = parent || this.parent;
8441
8442
            var stops = _.map(this.stops, function (s) {
8443
                return s.clone();
8444
            });
8445
8446
            var clone = new Gradient(stops);
8447
8448
            _.each(Two.Gradient.Properties, function (k) {
8449
                clone[k] = this[k];
8450
            }, this);
8451
8452
            if (parent) {
8453
                parent.add(clone);
8454
            }
8455
8456
            return clone;
8457
8458
        },
8459
8460
        toObject: function () {
8461
8462
            var result = {
8463
                stops: _.map(this.stops, function (s) {
8464
                    return s.toObject();
8465
                })
8466
            };
8467
8468
            _.each(Gradient.Properties, function (k) {
8469
                result[k] = this[k];
8470
            }, this);
8471
8472
            return result;
8473
8474
        },
8475
8476
        _update: function () {
8477
8478
            if (this._flagSpread || this._flagStops) {
8479
                this.trigger(Two.Events.change);
8480
            }
8481
8482
            return this;
8483
8484
        },
8485
8486
        flagReset: function () {
8487
8488
            this._flagSpread = this._flagStops = false;
8489
8490
            return this;
8491
8492
        }
8493
8494
    });
8495
8496
    Gradient.MakeObservable(Gradient.prototype);
8497
8498
})((typeof global !== 'undefined' ? global : this).Two);
8499
8500
(function (Two) {
8501
8502
    var _ = Two.Utils;
8503
8504
    var LinearGradient = Two.LinearGradient = function (x1, y1, x2, y2, stops) {
8505
8506
        Two.Gradient.call(this, stops);
8507
8508
        this._renderer.type = 'linear-gradient';
8509
8510
        var flagEndPoints = _.bind(LinearGradient.FlagEndPoints, this);
8511
        this.left = new Two.Vector().bind(Two.Events.change, flagEndPoints);
8512
        this.right = new Two.Vector().bind(Two.Events.change, flagEndPoints);
8513
8514
        if (_.isNumber(x1)) {
8515
            this.left.x = x1;
8516
        }
8517
        if (_.isNumber(y1)) {
8518
            this.left.y = y1;
8519
        }
8520
        if (_.isNumber(x2)) {
8521
            this.right.x = x2;
8522
        }
8523
        if (_.isNumber(y2)) {
8524
            this.right.y = y2;
8525
        }
8526
8527
    };
8528
8529
    _.extend(LinearGradient, {
8530
8531
        Stop: Two.Gradient.Stop,
8532
8533
        MakeObservable: function (object) {
8534
            Two.Gradient.MakeObservable(object);
8535
        },
8536
8537
        FlagEndPoints: function () {
8538
            this._flagEndPoints = true;
8539
        }
8540
8541
    });
8542
8543
    _.extend(LinearGradient.prototype, Two.Gradient.prototype, {
8544
8545
        _flagEndPoints: false,
8546
8547
        clone: function (parent) {
8548
8549
            parent = parent || this.parent;
8550
8551
            var stops = _.map(this.stops, function (stop) {
8552
                return stop.clone();
8553
            });
8554
8555
            var clone = new LinearGradient(this.left._x, this.left._y,
8556
                this.right._x, this.right._y, stops);
8557
8558
            _.each(Two.Gradient.Properties, function (k) {
8559
                clone[k] = this[k];
8560
            }, this);
8561
8562
            if (parent) {
8563
                parent.add(clone);
8564
            }
8565
8566
            return clone;
8567
8568
        },
8569
8570
        toObject: function () {
8571
8572
            var result = Two.Gradient.prototype.toObject.call(this);
8573
8574
            result.left = this.left.toObject();
8575
            result.right = this.right.toObject();
8576
8577
            return result;
8578
8579
        },
8580
8581
        _update: function () {
8582
8583
            if (this._flagEndPoints || this._flagSpread || this._flagStops) {
8584
                this.trigger(Two.Events.change);
8585
            }
8586
8587
            return this;
8588
8589
        },
8590
8591
        flagReset: function () {
8592
8593
            this._flagEndPoints = false;
8594
8595
            Two.Gradient.prototype.flagReset.call(this);
8596
8597
            return this;
8598
8599
        }
8600
8601
    });
8602
8603
    LinearGradient.MakeObservable(LinearGradient.prototype);
8604
8605
})((typeof global !== 'undefined' ? global : this).Two);
8606
8607
(function (Two) {
8608
8609
    var _ = Two.Utils;
8610
8611
    var RadialGradient = Two.RadialGradient = function (cx, cy, r, stops, fx, fy) {
8612
8613
        Two.Gradient.call(this, stops);
8614
8615
        this._renderer.type = 'radial-gradient';
8616
8617
        this.center = new Two.Vector()
8618
            .bind(Two.Events.change, _.bind(function () {
8619
                this._flagCenter = true;
8620
            }, this));
8621
8622
        this.radius = _.isNumber(r) ? r : 20;
8623
8624
        this.focal = new Two.Vector()
8625
            .bind(Two.Events.change, _.bind(function () {
8626
                this._flagFocal = true;
8627
            }, this));
8628
8629
        if (_.isNumber(cx)) {
8630
            this.center.x = cx;
8631
        }
8632
        if (_.isNumber(cy)) {
8633
            this.center.y = cy;
8634
        }
8635
8636
        this.focal.copy(this.center);
8637
8638
        if (_.isNumber(fx)) {
8639
            this.focal.x = fx;
8640
        }
8641
        if (_.isNumber(fy)) {
8642
            this.focal.y = fy;
8643
        }
8644
8645
    };
8646
8647
    _.extend(RadialGradient, {
8648
8649
        Stop: Two.Gradient.Stop,
8650
8651
        Properties: [
8652
            'radius'
8653
        ],
8654
8655
        MakeObservable: function (object) {
8656
8657
            Two.Gradient.MakeObservable(object);
8658
8659
            _.each(RadialGradient.Properties, Two.Utils.defineProperty, object);
8660
8661
        }
8662
8663
    });
8664
8665
    _.extend(RadialGradient.prototype, Two.Gradient.prototype, {
8666
8667
        _flagRadius: false,
8668
        _flagCenter: false,
8669
        _flagFocal: false,
8670
8671
        clone: function (parent) {
8672
8673
            parent = parent || this.parent;
8674
8675
            var stops = _.map(this.stops, function (stop) {
8676
                return stop.clone();
8677
            });
8678
8679
            var clone = new RadialGradient(this.center._x, this.center._y,
8680
                this._radius, stops, this.focal._x, this.focal._y);
8681
8682
            _.each(Two.Gradient.Properties.concat(RadialGradient.Properties), function (k) {
8683
                clone[k] = this[k];
8684
            }, this);
8685
8686
            if (parent) {
8687
                parent.add(clone);
8688
            }
8689
8690
            return clone;
8691
8692
        },
8693
8694
        toObject: function () {
8695
8696
            var result = Two.Gradient.prototype.toObject.call(this);
8697
8698
            _.each(RadialGradient.Properties, function (k) {
8699
                result[k] = this[k];
8700
            }, this);
8701
8702
            result.center = this.center.toObject();
8703
            result.focal = this.focal.toObject();
8704
8705
            return result;
8706
8707
        },
8708
8709
        _update: function () {
8710
8711
            if (this._flagRadius || this._flatCenter || this._flagFocal
8712
                || this._flagSpread || this._flagStops) {
8713
                this.trigger(Two.Events.change);
8714
            }
8715
8716
            return this;
8717
8718
        },
8719
8720
        flagReset: function () {
8721
8722
            this._flagRadius = this._flagCenter = this._flagFocal = false;
8723
8724
            Two.Gradient.prototype.flagReset.call(this);
8725
8726
            return this;
8727
8728
        }
8729
8730
    });
8731
8732
    RadialGradient.MakeObservable(RadialGradient.prototype);
8733
8734
})((typeof global !== 'undefined' ? global : this).Two);
8735
8736
(function (Two) {
8737
8738
    var _ = Two.Utils;
8739
    var anchor;
8740
    var regex = {
8741
        video: /\.(mp4|webm)$/i,
8742
        image: /\.(jpe?g|png|gif|tiff)$/i
8743
    };
8744
8745
    if (this.document) {
8746
        anchor = document.createElement('a');
8747
    }
8748
8749
    var Texture = Two.Texture = function (src, callback) {
8750
8751
        this._renderer = {};
8752
        this._renderer.type = 'texture';
8753
        this._renderer.flagOffset = _.bind(Texture.FlagOffset, this);
8754
        this._renderer.flagScale = _.bind(Texture.FlagScale, this);
8755
8756
        this.id = Two.Identifier + Two.uniqueId();
8757
        this.classList = [];
8758
8759
        this.offset = new Two.Vector();
8760
8761
        if (_.isFunction(callback)) {
8762
            var loaded = _.bind(function () {
8763
                this.unbind(Two.Events.load, loaded);
8764
                if (_.isFunction(callback)) {
8765
                    callback();
8766
                }
8767
            }, this);
8768
            this.bind(Two.Events.load, loaded);
8769
        }
8770
8771
        if (_.isString(src)) {
8772
            this.src = src;
8773
        } else if (_.isElement(src)) {
8774
            this.image = src;
8775
        }
8776
8777
        this._update();
8778
8779
    };
8780
8781
    _.extend(Texture, {
8782
8783
        Properties: [
8784
            'src',
8785
            'loaded',
8786
            'repeat'
8787
        ],
8788
8789
        ImageRegistry: new Two.Registry(),
8790
8791
        getAbsoluteURL: function (path) {
8792
            if (!anchor) {
8793
                // TODO: Fix for headless environment
8794
                return path;
8795
            }
8796
            anchor.href = path;
8797
            return anchor.href;
8798
        },
8799
8800
        getImage: function (src) {
8801
8802
            var absoluteSrc = Texture.getAbsoluteURL(src);
8803
8804
            if (Texture.ImageRegistry.contains(absoluteSrc)) {
8805
                return Texture.ImageRegistry.get(absoluteSrc);
8806
            }
8807
8808
            var image;
8809
8810
            if (regex.video.test(absoluteSrc)) {
8811
                image = document.createElement('video');
8812
            } else {
8813
                image = document.createElement('img');
8814
            }
8815
8816
            image.crossOrigin = 'anonymous';
8817
8818
            return image;
8819
8820
        },
8821
8822
        Register: {
8823
            canvas: function (texture, callback) {
8824
                texture._src = '#' + texture.id;
8825
                Texture.ImageRegistry.add(texture.src, texture.image);
8826
                if (_.isFunction(callback)) {
8827
                    callback();
8828
                }
8829
            },
8830
            img: function (texture, callback) {
8831
8832
                var loaded = function (e) {
8833
                    texture.image.removeEventListener('load', loaded, false);
8834
                    texture.image.removeEventListener('error', error, false);
8835
                    if (_.isFunction(callback)) {
8836
                        callback();
8837
                    }
8838
                };
8839
                var error = function (e) {
8840
                    texture.image.removeEventListener('load', loaded, false);
8841
                    texture.image.removeEventListener('error', error, false);
8842
                    throw new Two.Utils.Error('unable to load ' + texture.src);
8843
                };
8844
8845
                if (_.isNumber(texture.image.width) && texture.image.width > 0
8846
                    && _.isNumber(texture.image.height) && texture.image.height > 0) {
8847
                    loaded();
8848
                } else {
8849
                    texture.image.addEventListener('load', loaded, false);
8850
                    texture.image.addEventListener('error', error, false);
8851
                }
8852
8853
                texture._src = Texture.getAbsoluteURL(texture._src);
8854
8855
                if (texture.image && texture.image.getAttribute('two-src')) {
8856
                    return;
8857
                }
8858
8859
                texture.image.setAttribute('two-src', texture.src);
8860
                Texture.ImageRegistry.add(texture.src, texture.image);
8861
                texture.image.src = texture.src;
8862
8863
            },
8864
            video: function (texture, callback) {
8865
8866
                var loaded = function (e) {
8867
                    texture.image.removeEventListener('load', loaded, false);
8868
                    texture.image.removeEventListener('error', error, false);
8869
                    texture.image.width = texture.image.videoWidth;
8870
                    texture.image.height = texture.image.videoHeight;
8871
                    texture.image.play();
8872
                    if (_.isFunction(callback)) {
8873
                        callback();
8874
                    }
8875
                };
8876
                var error = function (e) {
8877
                    texture.image.removeEventListener('load', loaded, false);
8878
                    texture.image.removeEventListener('error', error, false);
8879
                    throw new Two.Utils.Error('unable to load ' + texture.src);
8880
                };
8881
8882
                texture._src = Texture.getAbsoluteURL(texture._src);
8883
                texture.image.addEventListener('canplaythrough', loaded, false);
8884
                texture.image.addEventListener('error', error, false);
8885
8886
                if (texture.image && texture.image.getAttribute('two-src')) {
8887
                    return;
8888
                }
8889
8890
                texture.image.setAttribute('two-src', texture.src);
8891
                Texture.ImageRegistry.add(texture.src, texture.image);
8892
                texture.image.src = texture.src;
8893
                texture.image.loop = true;
8894
                texture.image.load();
8895
8896
            }
8897
        },
8898
8899
        load: function (texture, callback) {
8900
8901
            var src = texture.src;
8902
            var image = texture.image;
8903
            var tag = image && image.nodeName.toLowerCase();
8904
8905
            if (texture._flagImage) {
8906
                if (/canvas/i.test(tag)) {
8907
                    Texture.Register.canvas(texture, callback);
8908
                } else {
8909
                    texture._src = image.getAttribute('two-src') || image.src;
8910
                    Texture.Register[tag](texture, callback);
8911
                }
8912
            }
8913
8914
            if (texture._flagSrc) {
8915
                if (!image) {
8916
                    texture.image = Texture.getImage(texture.src);
8917
                }
8918
                tag = texture.image.nodeName.toLowerCase();
8919
                Texture.Register[tag](texture, callback);
8920
            }
8921
8922
        },
8923
8924
        FlagOffset: function () {
8925
            this._flagOffset = true;
8926
        },
8927
8928
        FlagScale: function () {
8929
            this._flagScale = true;
8930
        },
8931
8932
        MakeObservable: function (object) {
8933
8934
            _.each(Texture.Properties, Two.Utils.defineProperty, object);
8935
8936
            Object.defineProperty(object, 'image', {
8937
                enumerable: true,
8938
                get: function () {
8939
                    return this._image;
8940
                },
8941
                set: function (image) {
8942
8943
                    var tag = image && image.nodeName.toLowerCase();
8944
                    var index;
8945
8946
                    switch (tag) {
8947
                        case 'canvas':
8948
                            index = '#' + image.id;
8949
                            break;
8950
                        default:
8951
                            index = image.src;
8952
                    }
8953
8954
                    if (Texture.ImageRegistry.contains(index)) {
8955
                        this._image = Texture.ImageRegistry.get(image.src);
8956
                    } else {
8957
                        this._image = image;
8958
                    }
8959
8960
                    this._flagImage = true;
8961
8962
                }
8963
8964
            });
8965
8966
            Object.defineProperty(object, 'offset', {
8967
                enumerable: true,
8968
                get: function () {
8969
                    return this._offset;
8970
                },
8971
                set: function (v) {
8972
                    if (this._offset) {
8973
                        this._offset.unbind(Two.Events.change, this._renderer.flagOffset);
8974
                    }
8975
                    this._offset = v;
8976
                    this._offset.bind(Two.Events.change, this._renderer.flagOffset);
8977
                    this._flagOffset = true;
8978
                }
8979
            });
8980
8981
            Object.defineProperty(object, 'scale', {
8982
                enumerable: true,
8983
                get: function () {
8984
                    return this._scale;
8985
                },
8986
                set: function (v) {
8987
8988
                    if (this._scale instanceof Two.Vector) {
8989
                        this._scale.unbind(Two.Events.change, this._renderer.flagScale);
8990
                    }
8991
8992
                    this._scale = v;
8993
8994
                    if (this._scale instanceof Two.Vector) {
8995
                        this._scale.bind(Two.Events.change, this._renderer.flagScale);
8996
                    }
8997
8998
                    this._flagScale = true;
8999
9000
                }
9001
            });
9002
9003
        }
9004
9005
    });
9006
9007
    _.extend(Texture.prototype, Two.Utils.Events, Two.Shape.prototype, {
9008
9009
        _flagSrc: false,
9010
        _flagImage: false,
9011
        _flagVideo: false,
9012
        _flagLoaded: false,
9013
        _flagRepeat: false,
9014
9015
        _flagOffset: false,
9016
        _flagScale: false,
9017
9018
        _src: '',
9019
        _image: null,
9020
        _loaded: false,
9021
        _repeat: 'no-repeat',
9022
9023
        _scale: 1,
9024
        _offset: null,
9025
9026
        clone: function () {
9027
            return new Texture(this.src);
9028
        },
9029
9030
        toObject: function () {
9031
            return {
9032
                src: this.src,
9033
                image: this.image
9034
            }
9035
        },
9036
9037
        _update: function () {
9038
9039
            if (this._flagSrc || this._flagImage || this._flagVideo) {
9040
9041
                this.trigger(Two.Events.change);
9042
9043
                if (this._flagSrc || this._flagImage) {
9044
                    this.loaded = false;
9045
                    Texture.load(this, _.bind(function () {
9046
                        this.loaded = true;
9047
                        this
9048
                            .trigger(Two.Events.change)
9049
                            .trigger(Two.Events.load);
9050
                    }, this));
9051
                }
9052
9053
            }
9054
9055
            if (this._image && this._image.readyState >= 4) {
9056
                this._flagVideo = true;
9057
            }
9058
9059
            return this;
9060
9061
        },
9062
9063
        flagReset: function () {
9064
9065
            this._flagSrc = this._flagImage = this._flagLoaded
9066
                = this._flagVideo = this._flagScale = this._flagOffset = false;
9067
9068
            return this;
9069
9070
        }
9071
9072
    });
9073
9074
    Texture.MakeObservable(Texture.prototype);
9075
9076
})((typeof global !== 'undefined' ? global : this).Two);
9077
9078
(function (Two) {
9079
9080
    var _ = Two.Utils;
9081
    var Path = Two.Path;
9082
    var Rectangle = Two.Rectangle;
9083
9084
    var Sprite = Two.Sprite = function (path, ox, oy, cols, rows, frameRate) {
9085
9086
        Path.call(this, [
9087
            new Two.Anchor(),
9088
            new Two.Anchor(),
9089
            new Two.Anchor(),
9090
            new Two.Anchor()
9091
        ], true);
9092
9093
        this.noStroke();
9094
        this.noFill();
9095
9096
        if (path instanceof Two.Texture) {
9097
            this.texture = path;
9098
        } else if (_.isString(path)) {
9099
            this.texture = new Two.Texture(path);
9100
        }
9101
9102
        this._update();
9103
        this.translation.set(ox || 0, oy || 0);
9104
9105
        if (_.isNumber(cols)) {
9106
            this.columns = cols;
9107
        }
9108
        if (_.isNumber(rows)) {
9109
            this.rows = rows;
9110
        }
9111
        if (_.isNumber(frameRate)) {
9112
            this.frameRate = frameRate;
9113
        }
9114
9115
    };
9116
9117
    _.extend(Sprite, {
9118
9119
        Properties: [
9120
            'texture', 'columns', 'rows', 'frameRate', 'index'
9121
        ],
9122
9123
        MakeObservable: function (obj) {
9124
9125
            Rectangle.MakeObservable(obj);
9126
            _.each(Sprite.Properties, Two.Utils.defineProperty, obj);
9127
9128
        }
9129
9130
    })
9131
9132
    _.extend(Sprite.prototype, Rectangle.prototype, {
9133
9134
        _flagTexture: false,
9135
        _flagColumns: false,
9136
        _flagRows: false,
9137
        _flagFrameRate: false,
9138
        flagIndex: false,
9139
9140
        // Private variables
9141
        _amount: 1,
9142
        _duration: 0,
9143
        _startTime: 0,
9144
        _playing: false,
9145
        _firstFrame: 0,
9146
        _lastFrame: 0,
9147
        _loop: true,
9148
9149
        // Exposed through getter-setter
9150
        _texture: null,
9151
        _columns: 1,
9152
        _rows: 1,
9153
        _frameRate: 0,
9154
        _index: 0,
9155
9156
        play: function (firstFrame, lastFrame, onLastFrame) {
9157
9158
            this._playing = true;
9159
            this._firstFrame = 0;
9160
            this._lastFrame = this.amount - 1;
9161
            this._startTime = _.performance.now();
9162
9163
            if (_.isNumber(firstFrame)) {
9164
                this._firstFrame = firstFrame;
9165
            }
9166
            if (_.isNumber(lastFrame)) {
9167
                this._lastFrame = lastFrame;
9168
            }
9169
            if (_.isFunction(onLastFrame)) {
9170
                this._onLastFrame = onLastFrame;
9171
            } else {
9172
                delete this._onLastFrame;
9173
            }
9174
9175
            if (this._index !== this._firstFrame) {
9176
                this._startTime -= 1000 * Math.abs(this._index - this._firstFrame)
9177
                    / this._frameRate;
9178
            }
9179
9180
            return this;
9181
9182
        },
9183
9184
        pause: function () {
9185
9186
            this._playing = false;
9187
            return this;
9188
9189
        },
9190
9191
        stop: function () {
9192
9193
            this._playing = false;
9194
            this._index = 0;
9195
9196
            return this;
9197
9198
        },
9199
9200
        clone: function (parent) {
9201
9202
            parent = parent || this.parent;
9203
9204
            var clone = new Sprite(
9205
                this.texture, this.translation.x, this.translation.y,
9206
                this.columns, this.rows, this.frameRate
9207
            );
9208
9209
            if (this.playing) {
9210
                clone.play(this._firstFrame, this._lastFrame);
9211
                clone._loop = this._loop;
9212
            }
9213
9214
            if (parent) {
9215
                parent.add(clone);
9216
            }
9217
9218
            return clone;
9219
9220
        },
9221
9222
        _update: function () {
9223
9224
            var effect = this._texture;
9225
            var cols = this._columns;
9226
            var rows = this._rows;
9227
9228
            var width, height, elapsed, amount, duration;
9229
            var index, iw, ih, isRange, frames;
9230
9231
            if (this._flagColumns || this._flagRows) {
9232
                this._amount = this._columns * this._rows;
9233
            }
9234
9235
            if (this._flagFrameRate) {
9236
                this._duration = 1000 * this._amount / this._frameRate;
9237
            }
9238
9239
            if (this._flagTexture) {
9240
                this.fill = this._texture;
9241
            }
9242
9243
            if (this._texture.loaded) {
9244
9245
                iw = effect.image.width;
9246
                ih = effect.image.height;
9247
9248
                width = iw / cols;
9249
                height = ih / rows;
9250
                amount = this._amount;
9251
9252
                if (this.width !== width) {
9253
                    this.width = width;
9254
                }
9255
                if (this.height !== height) {
9256
                    this.height = height;
9257
                }
9258
9259
                if (this._playing && this._frameRate > 0) {
9260
9261
                    if (_.isNaN(this._lastFrame)) {
9262
                        this._lastFrame = amount - 1;
9263
                    }
9264
9265
                    // TODO: Offload perf logic to instance of `Two`.
9266
                    elapsed = _.performance.now() - this._startTime;
9267
                    frames = this._lastFrame + 1;
9268
                    duration = 1000 * (frames - this._firstFrame) / this._frameRate;
9269
9270
                    if (this._loop) {
9271
                        elapsed = elapsed % duration;
9272
                    } else {
9273
                        elapsed = Math.min(elapsed, duration);
9274
                    }
9275
9276
                    index = _.lerp(this._firstFrame, frames, elapsed / duration);
9277
                    index = Math.floor(index);
9278
9279
                    if (index !== this._index) {
9280
                        this._index = index;
9281
                        if (index >= this._lastFrame - 1 && this._onLastFrame) {
9282
                            this._onLastFrame();  // Shortcut for chainable sprite animations
9283
                        }
9284
                    }
9285
9286
                }
9287
9288
                var col = this._index % cols;
9289
                var row = Math.floor(this._index / cols);
9290
9291
                var ox = - width * col + (iw - width) / 2;
9292
                var oy = - height * row + (ih - height) / 2;
9293
9294
                // TODO: Improve performance
9295
                if (ox !== effect.offset.x) {
9296
                    effect.offset.x = ox;
9297
                }
9298
                if (oy !== effect.offset.y) {
9299
                    effect.offset.y = oy;
9300
                }
9301
9302
            }
9303
9304
            Rectangle.prototype._update.call(this);
9305
9306
            return this;
9307
9308
        },
9309
9310
        flagReset: function () {
9311
9312
            this._flagTexture = this._flagColumns = this._flagRows
9313
                = this._flagFrameRate = false;
9314
9315
            Rectangle.prototype.flagReset.call(this);
9316
9317
            return this;
9318
        }
9319
9320
9321
    });
9322
9323
    Sprite.MakeObservable(Sprite.prototype);
9324
9325
})((typeof global !== 'undefined' ? global : this).Two);
9326
9327
(function (Two) {
9328
9329
    var _ = Two.Utils;
9330
    var Path = Two.Path;
9331
    var Rectangle = Two.Rectangle;
9332
9333
    var ImageSequence = Two.ImageSequence = function (paths, ox, oy, frameRate) {
9334
9335
        Path.call(this, [
9336
            new Two.Anchor(),
9337
            new Two.Anchor(),
9338
            new Two.Anchor(),
9339
            new Two.Anchor()
9340
        ], true);
9341
9342
        this._renderer.flagTextures = _.bind(ImageSequence.FlagTextures, this);
9343
        this._renderer.bindTextures = _.bind(ImageSequence.BindTextures, this);
9344
        this._renderer.unbindTextures = _.bind(ImageSequence.UnbindTextures, this);
9345
9346
        this.noStroke();
9347
        this.noFill();
9348
9349
        this.textures = _.map(paths, ImageSequence.GenerateTexture, this);
9350
9351
        this._update();
9352
        this.translation.set(ox || 0, oy || 0);
9353
9354
        if (_.isNumber(frameRate)) {
9355
            this.frameRate = frameRate;
9356
        } else {
9357
            this.frameRate = ImageSequence.DefaultFrameRate;
9358
        }
9359
9360
    };
9361
9362
    _.extend(ImageSequence, {
9363
9364
        Properties: [
9365
            'frameRate',
9366
            'index'
9367
        ],
9368
9369
        DefaultFrameRate: 30,
9370
9371
        FlagTextures: function () {
9372
            this._flagTextures = true;
9373
        },
9374
9375
        BindTextures: function (items) {
9376
9377
            var i = items.length;
9378
            while (i--) {
9379
                items[i].bind(Two.Events.change, this._renderer.flagTextures);
9380
            }
9381
9382
            this._renderer.flagTextures();
9383
9384
        },
9385
9386
        UnbindTextures: function (items) {
9387
9388
            var i = items.length;
9389
            while (i--) {
9390
                items[i].unbind(Two.Events.change, this._renderer.flagTextures);
9391
            }
9392
9393
            this._renderer.flagTextures();
9394
9395
        },
9396
9397
        MakeObservable: function (obj) {
9398
9399
            Rectangle.MakeObservable(obj);
9400
            _.each(ImageSequence.Properties, Two.Utils.defineProperty, obj);
9401
9402
            Object.defineProperty(obj, 'textures', {
9403
9404
                enumerable: true,
9405
9406
                get: function () {
9407
                    return this._textures;
9408
                },
9409
9410
                set: function (textures) {
9411
9412
                    var updateTextures = this._renderer.flagTextures;
9413
                    var bindTextures = this._renderer.bindTextures;
9414
                    var unbindTextures = this._renderer.unbindTextures;
9415
9416
                    // Remove previous listeners
9417
                    if (this._textures) {
9418
                        this._textures
9419
                            .unbind(Two.Events.insert, bindTextures)
9420
                            .unbind(Two.Events.remove, unbindTextures);
9421
                    }
9422
9423
                    // Create new Collection with copy of vertices
9424
                    this._textures = new Two.Utils.Collection((textures || []).slice(0));
9425
9426
                    // Listen for Collection changes and bind / unbind
9427
                    this._textures
9428
                        .bind(Two.Events.insert, bindTextures)
9429
                        .bind(Two.Events.remove, unbindTextures);
9430
9431
                    // Bind Initial Textures
9432
                    bindTextures(this._textures);
9433
9434
                }
9435
9436
            });
9437
9438
        },
9439
9440
        GenerateTexture: function (obj) {
9441
            if (obj instanceof Two.Texture) {
9442
                return obj;
9443
            } else if (_.isString(obj)) {
9444
                return new Two.Texture(obj);
9445
            }
9446
        }
9447
9448
    });
9449
9450
    _.extend(ImageSequence.prototype, Rectangle.prototype, {
9451
9452
        _flagTextures: false,
9453
        _flagFrameRate: false,
9454
        _flagIndex: false,
9455
9456
        // Private variables
9457
        _amount: 1,
9458
        _duration: 0,
9459
        _index: 0,
9460
        _startTime: 0,
9461
        _playing: false,
9462
        _firstFrame: 0,
9463
        _lastFrame: 0,
9464
        _loop: true,
9465
9466
        // Exposed through getter-setter
9467
        _textures: null,
9468
        _frameRate: 0,
9469
9470
        play: function (firstFrame, lastFrame, onLastFrame) {
9471
9472
            this._playing = true;
9473
            this._firstFrame = 0;
9474
            this._lastFrame = this.amount - 1;
9475
            this._startTime = _.performance.now();
9476
9477
            if (_.isNumber(firstFrame)) {
9478
                this._firstFrame = firstFrame;
9479
            }
9480
            if (_.isNumber(lastFrame)) {
9481
                this._lastFrame = lastFrame;
9482
            }
9483
            if (_.isFunction(onLastFrame)) {
9484
                this._onLastFrame = onLastFrame;
9485
            } else {
9486
                delete this._onLastFrame;
9487
            }
9488
9489
            if (this._index !== this._firstFrame) {
9490
                this._startTime -= 1000 * Math.abs(this._index - this._firstFrame)
9491
                    / this._frameRate;
9492
            }
9493
9494
            return this;
9495
9496
        },
9497
9498
        pause: function () {
9499
9500
            this._playing = false;
9501
            return this;
9502
9503
        },
9504
9505
        stop: function () {
9506
9507
            this._playing = false;
9508
            this._index = 0;
9509
9510
            return this;
9511
9512
        },
9513
9514
        clone: function (parent) {
9515
9516
            parent = parent || this.parent;
9517
9518
            var clone = new ImageSequence(this.textures, this.translation.x,
9519
                this.translation.y, this.frameRate)
9520
9521
            clone._loop = this._loop;
9522
9523
            if (this._playing) {
9524
                clone.play();
9525
            }
9526
9527
            if (parent) {
9528
                parent.add(clone);
9529
            }
9530
9531
            return clone;
9532
9533
        },
9534
9535
        _update: function () {
9536
9537
            var effects = this._textures;
9538
            var width, height, elapsed, amount, duration, texture;
9539
            var index, frames;
9540
9541
            if (this._flagTextures) {
9542
                this._amount = effects.length;
9543
            }
9544
9545
            if (this._flagFrameRate) {
9546
                this._duration = 1000 * this._amount / this._frameRate;
9547
            }
9548
9549
            if (this._playing && this._frameRate > 0) {
9550
9551
                amount = this._amount;
9552
9553
                if (_.isNaN(this._lastFrame)) {
9554
                    this._lastFrame = amount - 1;
9555
                }
9556
9557
                // TODO: Offload perf logic to instance of `Two`.
9558
                elapsed = _.performance.now() - this._startTime;
9559
                frames = this._lastFrame + 1;
9560
                duration = 1000 * (frames - this._firstFrame) / this._frameRate;
9561
9562
                if (this._loop) {
9563
                    elapsed = elapsed % duration;
9564
                } else {
9565
                    elapsed = Math.min(elapsed, duration);
9566
                }
9567
9568
                index = _.lerp(this._firstFrame, frames, elapsed / duration);
9569
                index = Math.floor(index);
9570
9571
                if (index !== this._index) {
9572
9573
                    this._index = index;
9574
                    texture = effects[this._index];
9575
9576
                    if (texture.loaded) {
9577
9578
                        width = texture.image.width;
9579
                        height = texture.image.height;
9580
9581
                        if (this.width !== width) {
9582
                            this.width = width;
9583
                        }
9584
                        if (this.height !== height) {
9585
                            this.height = height;
9586
                        }
9587
9588
                        this.fill = texture;
9589
9590
                        if (index >= this._lastFrame - 1 && this._onLastFrame) {
9591
                            this._onLastFrame();  // Shortcut for chainable sprite animations
9592
                        }
9593
9594
                    }
9595
9596
                }
9597
9598
            } else if (this._flagIndex || !(this.fill instanceof Two.Texture)) {
9599
9600
                texture = effects[this._index];
9601
9602
                if (texture.loaded) {
9603
9604
                    width = texture.image.width;
9605
                    height = texture.image.height;
9606
9607
                    if (this.width !== width) {
9608
                        this.width = width;
9609
                    }
9610
                    if (this.height !== height) {
9611
                        this.height = height;
9612
                    }
9613
9614
                }
9615
9616
                this.fill = texture;
9617
9618
            }
9619
9620
            Rectangle.prototype._update.call(this);
9621
9622
            return this;
9623
9624
        },
9625
9626
        flagReset: function () {
9627
9628
            this._flagTextures = this._flagFrameRate = false;
9629
            Rectangle.prototype.flagReset.call(this);
9630
9631
            return this;
9632
9633
        }
9634
9635
    });
9636
9637
    ImageSequence.MakeObservable(ImageSequence.prototype);
9638
9639
})((typeof global !== 'undefined' ? global : this).Two);
9640
9641
(function (Two) {
9642
9643
    /**
9644
        * Constants
9645
        */
9646
    var min = Math.min, max = Math.max;
9647
    var _ = Two.Utils;
9648
9649
    /**
9650
        * A children collection which is accesible both by index and by object id
9651
        * @constructor
9652
        */
9653
    var Children = function () {
9654
9655
        Two.Utils.Collection.apply(this, arguments);
9656
9657
        Object.defineProperty(this, '_events', {
9658
            value: {},
9659
            enumerable: false
9660
        });
9661
9662
        this.ids = {};
9663
9664
        this.on(Two.Events.insert, this.attach);
9665
        this.on(Two.Events.remove, this.detach);
9666
        Children.prototype.attach.apply(this, arguments);
9667
9668
    };
9669
9670
    Children.prototype = new Two.Utils.Collection();
9671
    Children.prototype.constructor = Children;
9672
9673
    _.extend(Children.prototype, {
9674
9675
        attach: function (children) {
9676
            for (var i = 0; i < children.length; i++) {
9677
                this.ids[children[i].id] = children[i];
9678
            }
9679
            return this;
9680
        },
9681
9682
        detach: function (children) {
9683
            for (var i = 0; i < children.length; i++) {
9684
                delete this.ids[children[i].id];
9685
            }
9686
            return this;
9687
        }
9688
9689
    });
9690
9691
    var Group = Two.Group = function () {
9692
9693
        Two.Shape.call(this, true);
9694
9695
        this._renderer.type = 'group';
9696
9697
        this.additions = [];
9698
        this.subtractions = [];
9699
9700
        this.children = arguments;
9701
9702
    };
9703
9704
    _.extend(Group, {
9705
9706
        Children: Children,
9707
9708
        InsertChildren: function (children) {
9709
            for (var i = 0; i < children.length; i++) {
9710
                replaceParent.call(this, children[i], this);
9711
            }
9712
        },
9713
9714
        RemoveChildren: function (children) {
9715
            for (var i = 0; i < children.length; i++) {
9716
                replaceParent.call(this, children[i]);
9717
            }
9718
        },
9719
9720
        OrderChildren: function (children) {
9721
            this._flagOrder = true;
9722
        },
9723
9724
        MakeObservable: function (object) {
9725
9726
            var properties = Two.Path.Properties.slice(0);
9727
            var oi = _.indexOf(properties, 'opacity');
9728
9729
            if (oi >= 0) {
9730
9731
                properties.splice(oi, 1);
9732
9733
                Object.defineProperty(object, 'opacity', {
9734
9735
                    enumerable: true,
9736
9737
                    get: function () {
9738
                        return this._opacity;
9739
                    },
9740
9741
                    set: function (v) {
9742
                        // Only set flag if there is an actual difference
9743
                        this._flagOpacity = (this._opacity != v);
9744
                        this._opacity = v;
9745
                    }
9746
9747
                });
9748
9749
            }
9750
9751
            Two.Shape.MakeObservable(object);
9752
            Group.MakeGetterSetters(object, properties);
9753
9754
            Object.defineProperty(object, 'children', {
9755
9756
                enumerable: true,
9757
9758
                get: function () {
9759
                    return this._children;
9760
                },
9761
9762
                set: function (children) {
9763
9764
                    var insertChildren = _.bind(Group.InsertChildren, this);
9765
                    var removeChildren = _.bind(Group.RemoveChildren, this);
9766
                    var orderChildren = _.bind(Group.OrderChildren, this);
9767
9768
                    if (this._children) {
9769
                        this._children.unbind();
9770
                    }
9771
9772
                    this._children = new Children(children);
9773
                    this._children.bind(Two.Events.insert, insertChildren);
9774
                    this._children.bind(Two.Events.remove, removeChildren);
9775
                    this._children.bind(Two.Events.order, orderChildren);
9776
9777
                }
9778
9779
            });
9780
9781
            Object.defineProperty(object, 'mask', {
9782
9783
                enumerable: true,
9784
9785
                get: function () {
9786
                    return this._mask;
9787
                },
9788
9789
                set: function (v) {
9790
                    this._mask = v;
9791
                    this._flagMask = true;
9792
                    if (!v.clip) {
9793
                        v.clip = true;
9794
                    }
9795
                }
9796
9797
            });
9798
9799
        },
9800
9801
        MakeGetterSetters: function (group, properties) {
9802
9803
            if (!_.isArray(properties)) {
9804
                properties = [properties];
9805
            }
9806
9807
            _.each(properties, function (k) {
9808
                Group.MakeGetterSetter(group, k);
9809
            });
9810
9811
        },
9812
9813
        MakeGetterSetter: function (group, k) {
9814
9815
            var secret = '_' + k;
9816
9817
            Object.defineProperty(group, k, {
9818
9819
                enumerable: true,
9820
9821
                get: function () {
9822
                    return this[secret];
9823
                },
9824
9825
                set: function (v) {
9826
                    this[secret] = v;
9827
                    _.each(this.children, function (child) { // Trickle down styles
9828
                        child[k] = v;
9829
                    });
9830
                }
9831
9832
            });
9833
9834
        }
9835
9836
    });
9837
9838
    _.extend(Group.prototype, Two.Shape.prototype, {
9839
9840
        // Flags
9841
        // http://en.wikipedia.org/wiki/Flag
9842
9843
        _flagAdditions: false,
9844
        _flagSubtractions: false,
9845
        _flagOrder: false,
9846
        _flagOpacity: true,
9847
9848
        _flagMask: false,
9849
9850
        // Underlying Properties
9851
9852
        _fill: '#fff',
9853
        _stroke: '#000',
9854
        _linewidth: 1.0,
9855
        _opacity: 1.0,
9856
        _visible: true,
9857
9858
        _cap: 'round',
9859
        _join: 'round',
9860
        _miter: 4,
9861
9862
        _closed: true,
9863
        _curved: false,
9864
        _automatic: true,
9865
        _beginning: 0,
9866
        _ending: 1.0,
9867
9868
        _mask: null,
9869
9870
        /**
9871
            * TODO: Group has a gotcha in that it's at the moment required to be bound to
9872
            * an instance of two in order to add elements correctly. This needs to
9873
            * be rethought and fixed.
9874
            */
9875
        clone: function (parent) {
9876
9877
            parent = parent || this.parent;
9878
9879
            var group = new Group();
9880
            var children = _.map(this.children, function (child) {
9881
                return child.clone(group);
9882
            });
9883
9884
            group.add(children);
9885
9886
            group.opacity = this.opacity;
9887
9888
            if (this.mask) {
9889
                group.mask = this.mask;
9890
            }
9891
9892
            group.translation.copy(this.translation);
9893
            group.rotation = this.rotation;
9894
            group.scale = this.scale;
9895
9896
            if (parent) {
9897
                parent.add(group);
9898
            }
9899
9900
            return group;
9901
9902
        },
9903
9904
        /**
9905
            * Export the data from the instance of Two.Group into a plain JavaScript
9906
            * object. This also makes all children plain JavaScript objects. Great
9907
            * for turning into JSON and storing in a database.
9908
            */
9909
        toObject: function () {
9910
9911
            var result = {
9912
                children: [],
9913
                translation: this.translation.toObject(),
9914
                rotation: this.rotation,
9915
                scale: this.scale,
9916
                opacity: this.opacity,
9917
                mask: (this.mask ? this.mask.toObject() : null)
9918
            };
9919
9920
            _.each(this.children, function (child, i) {
9921
                result.children[i] = child.toObject();
9922
            }, this);
9923
9924
            return result;
9925
9926
        },
9927
9928
        /**
9929
            * Anchor all children to the upper left hand corner
9930
            * of the group.
9931
            */
9932
        corner: function () {
9933
9934
            var rect = this.getBoundingClientRect(true),
9935
                corner = { x: rect.left, y: rect.top };
9936
9937
            this.children.forEach(function (child) {
9938
                child.translation.subSelf(corner);
9939
            });
9940
9941
            return this;
9942
9943
        },
9944
9945
        /**
9946
            * Anchors all children around the center of the group,
9947
            * effectively placing the shape around the unit circle.
9948
            */
9949
        center: function () {
9950
9951
            var rect = this.getBoundingClientRect(true);
9952
9953
            rect.centroid = {
9954
                x: rect.left + rect.width / 2,
9955
                y: rect.top + rect.height / 2
9956
            };
9957
9958
            this.children.forEach(function (child) {
9959
                if (child.isShape) {
9960
                    child.translation.subSelf(rect.centroid);
9961
                }
9962
            });
9963
9964
            // this.translation.copy(rect.centroid);
9965
9966
            return this;
9967
9968
        },
9969
9970
        /**
9971
            * Recursively search for id. Returns the first element found.
9972
            * Returns null if none found.
9973
            */
9974
        getById: function (id) {
9975
            var search = function (node, id) {
9976
                if (node.id === id) {
9977
                    return node;
9978
                } else if (node.children) {
9979
                    var i = node.children.length;
9980
                    while (i--) {
9981
                        var found = search(node.children[i], id);
9982
                        if (found) return found;
9983
                    }
9984
                }
9985
9986
            };
9987
            return search(this, id) || null;
9988
        },
9989
9990
        /**
9991
            * Recursively search for classes. Returns an array of matching elements.
9992
            * Empty array if none found.
9993
            */
9994
        getByClassName: function (cl) {
9995
            var found = [];
9996
            var search = function (node, cl) {
9997
                if (node.classList.indexOf(cl) != -1) {
9998
                    found.push(node);
9999
                } else if (node.children) {
10000
                    node.children.forEach(function (child) {
10001
                        search(child, cl);
10002
                    });
10003
                }
10004
                return found;
10005
            };
10006
            return search(this, cl);
10007
        },
10008
10009
        /**
10010
            * Recursively search for children of a specific type,
10011
            * e.g. Two.Polygon. Pass a reference to this type as the param.
10012
            * Returns an empty array if none found.
10013
            */
10014
        getByType: function (type) {
10015
            var found = [];
10016
            var search = function (node, type) {
10017
                for (var id in node.children) {
10018
                    if (node.children[id] instanceof type) {
10019
                        found.push(node.children[id]);
10020
                    } else if (node.children[id] instanceof Two.Group) {
10021
                        search(node.children[id], type);
10022
                    }
10023
                }
10024
                return found;
10025
            };
10026
            return search(this, type);
10027
        },
10028
10029
        /**
10030
            * Add objects to the group.
10031
            */
10032
        add: function (objects) {
10033
10034
            // Allow to pass multiple objects either as array or as multiple arguments
10035
            // If it's an array also create copy of it in case we're getting passed
10036
            // a childrens array directly.
10037
            if (!(objects instanceof Array)) {
10038
                objects = _.toArray(arguments);
10039
            } else {
10040
                objects = objects.slice();
10041
            }
10042
10043
            // Add the objects
10044
            for (var i = 0; i < objects.length; i++) {
10045
                if (!(objects[i] && objects[i].id)) continue;
10046
                this.children.push(objects[i]);
10047
            }
10048
10049
            return this;
10050
10051
        },
10052
10053
        /**
10054
            * Remove objects from the group.
10055
            */
10056
        remove: function (objects) {
10057
10058
            var l = arguments.length,
10059
                grandparent = this.parent;
10060
10061
            // Allow to call remove without arguments
10062
            // This will detach the object from the scene.
10063
            if (l <= 0 && grandparent) {
10064
                grandparent.remove(this);
10065
                return this;
10066
            }
10067
10068
            // Allow to pass multiple objects either as array or as multiple arguments
10069
            // If it's an array also create copy of it in case we're getting passed
10070
            // a childrens array directly.
10071
            if (!(objects instanceof Array)) {
10072
                objects = _.toArray(arguments);
10073
            } else {
10074
                objects = objects.slice();
10075
            }
10076
10077
            // Remove the objects
10078
            for (var i = 0; i < objects.length; i++) {
10079
                if (!objects[i] || !(this.children.ids[objects[i].id])) continue;
10080
                this.children.splice(_.indexOf(this.children, objects[i]), 1);
10081
            }
10082
10083
            return this;
10084
10085
        },
10086
10087
        /**
10088
            * Return an object with top, left, right, bottom, width, and height
10089
            * parameters of the group.
10090
            */
10091
        getBoundingClientRect: function (shallow) {
10092
            var rect;
10093
10094
            // TODO: Update this to not __always__ update. Just when it needs to.
10095
            this._update(true);
10096
10097
            // Variables need to be defined here, because of nested nature of groups.
10098
            var left = Infinity, right = -Infinity,
10099
                top = Infinity, bottom = -Infinity;
10100
10101
            this.children.forEach(function (child) {
10102
10103
                if (/(linear-gradient|radial-gradient|gradient)/.test(child._renderer.type)) {
10104
                    return;
10105
                }
10106
10107
                rect = child.getBoundingClientRect(shallow);
10108
10109
                if (!_.isNumber(rect.top) || !_.isNumber(rect.left) ||
10110
                    !_.isNumber(rect.right) || !_.isNumber(rect.bottom)) {
10111
                    return;
10112
                }
10113
10114
                top = min(rect.top, top);
10115
                left = min(rect.left, left);
10116
                right = max(rect.right, right);
10117
                bottom = max(rect.bottom, bottom);
10118
10119
            }, this);
10120
10121
            return {
10122
                top: top,
10123
                left: left,
10124
                right: right,
10125
                bottom: bottom,
10126
                width: right - left,
10127
                height: bottom - top
10128
            };
10129
10130
        },
10131
10132
        /**
10133
            * Trickle down of noFill
10134
            */
10135
        noFill: function () {
10136
            this.children.forEach(function (child) {
10137
                child.noFill();
10138
            });
10139
            return this;
10140
        },
10141
10142
        /**
10143
            * Trickle down of noStroke
10144
            */
10145
        noStroke: function () {
10146
            this.children.forEach(function (child) {
10147
                child.noStroke();
10148
            });
10149
            return this;
10150
        },
10151
10152
        /**
10153
            * Trickle down subdivide
10154
            */
10155
        subdivide: function () {
10156
            var args = arguments;
10157
            this.children.forEach(function (child) {
10158
                child.subdivide.apply(child, args);
10159
            });
10160
            return this;
10161
        },
10162
10163
        flagReset: function () {
10164
10165
            if (this._flagAdditions) {
10166
                this.additions.length = 0;
10167
                this._flagAdditions = false;
10168
            }
10169
10170
            if (this._flagSubtractions) {
10171
                this.subtractions.length = 0;
10172
                this._flagSubtractions = false;
10173
            }
10174
10175
            this._flagOrder = this._flagMask = this._flagOpacity = false;
10176
10177
            Two.Shape.prototype.flagReset.call(this);
10178
10179
            return this;
10180
10181
        }
10182
10183
    });
10184
10185
    Group.MakeObservable(Group.prototype);
10186
10187
    /**
10188
        * Helper function used to sync parent-child relationship within the
10189
        * `Two.Group.children` object.
10190
        *
10191
        * Set the parent of the passed object to another object
10192
        * and updates parent-child relationships
10193
        * Calling with one arguments will simply remove the parenting
10194
        */
10195
    function replaceParent(child, newParent) {
10196
10197
        var parent = child.parent;
10198
        var index;
10199
10200
        if (parent === newParent) {
10201
            this.additions.push(child);
10202
            this._flagAdditions = true;
10203
            return;
10204
        }
10205
10206
        if (parent && parent.children.ids[child.id]) {
10207
10208
            index = _.indexOf(parent.children, child);
10209
            parent.children.splice(index, 1);
10210
10211
            // If we're passing from one parent to another...
10212
            index = _.indexOf(parent.additions, child);
10213
10214
            if (index >= 0) {
10215
                parent.additions.splice(index, 1);
10216
            } else {
10217
                parent.subtractions.push(child);
10218
                parent._flagSubtractions = true;
10219
            }
10220
10221
        }
10222
10223
        if (newParent) {
10224
            child.parent = newParent;
10225
            this.additions.push(child);
10226
            this._flagAdditions = true;
10227
            return;
10228
        }
10229
10230
        // If we're passing from one parent to another...
10231
        index = _.indexOf(this.additions, child);
10232
10233
        if (index >= 0) {
10234
            this.additions.splice(index, 1);
10235
        } else {
10236
            this.subtractions.push(child);
10237
            this._flagSubtractions = true;
10238
        }
10239
10240
        delete child.parent;
10241
10242
    }
10243
10244
})((typeof global !== 'undefined' ? global : this).Two);