/* MIT License Copyright (c) 2012 - 2025 @jonobr1 / http://jono.fyi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ var Two = (() => { var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/two.js var two_exports = {}; __export(two_exports, { default: () => Two }); // src/utils/canvas-polyfill.js var CanvasPolyfill = { Image: null, isHeadless: false, shim: function(elem, name) { elem.tagName = elem.nodeName = name || "canvas"; elem.nodeType = 1; elem.getAttribute = function(prop) { return this[prop]; }; elem.setAttribute = function(prop, val) { this[prop] = val; return this; }; return elem; }, polyfill: function(canvas3, Image) { CanvasPolyfill.shim(canvas3); if (typeof Image !== "undefined") { CanvasPolyfill.Image = Image; } CanvasPolyfill.isHeadless = true; return canvas3; } }; // src/utils/curves.js var curves_exports = {}; __export(curves_exports, { Curve: () => Curve, getAnchorsFromArcData: () => getAnchorsFromArcData, getComponentOnCubicBezier: () => getComponentOnCubicBezier, getControlPoints: () => getControlPoints, getCurveBoundingBox: () => getCurveBoundingBox, getCurveFromPoints: () => getCurveFromPoints, getCurveLength: () => getCurveLength, getReflection: () => getReflection, integrate: () => integrate, subdivide: () => subdivide }); // src/utils/math.js var math_exports = {}; __export(math_exports, { HALF_PI: () => HALF_PI, NumArray: () => NumArray, TWO_PI: () => TWO_PI, decomposeMatrix: () => decomposeMatrix, getComputedMatrix: () => getComputedMatrix, getPoT: () => getPoT, lerp: () => lerp, mod: () => mod, setMatrix: () => setMatrix, toFixed: () => toFixed }); // src/utils/root.js var root; if (typeof window !== "undefined") { root = window; } else if (typeof global !== "undefined") { root = global; } else if (typeof self !== "undefined") { root = self; } // src/utils/math.js var Matrix; var TWO_PI = Math.PI * 2; var HALF_PI = Math.PI * 0.5; function decomposeMatrix(matrix, b, c, d, e, f) { let a; if (arguments.length <= 1) { a = matrix.a; b = matrix.b; c = matrix.c; d = matrix.d; e = matrix.e; f = matrix.f; } else { a = matrix; } return { translateX: e, translateY: f, scaleX: Math.sqrt(a * a + b * b), scaleY: Math.sqrt(c * c + d * d), rotation: 180 * Math.atan2(b, a) / Math.PI }; } function setMatrix(matrix) { Matrix = matrix; } function getComputedMatrix(object, matrix) { matrix = matrix && matrix.identity() || new Matrix(); let parent = object; const matrices = []; while (parent && parent._matrix) { matrices.push(parent._matrix); parent = parent.parent; } matrices.reverse(); for (let i = 0; i < matrices.length; i++) { const m = matrices[i]; const e = m.elements; matrix.multiply( e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9] ); } return matrix; } function lerp(a, b, t) { return t * (b - a) + a; } var pots = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]; function getPoT(value) { let i = 0; while (pots[i] && pots[i] < value) { i++; } return pots[i]; } function mod(v, l) { while (v < 0) { v += l; } return v % l; } var NumArray = root.Float32Array || Array; var floor = Math.floor; function toFixed(v) { return floor(v * 1e6) / 1e6; } // src/utils/path-commands.js var Commands = { move: "M", line: "L", curve: "C", arc: "A", close: "Z" }; // src/events.js var Events = class { _events = {}; _bound = false; constructor() { } addEventListener(name, handler) { const list = this._events[name] || (this._events[name] = []); list.push(handler); this._bound = true; return this; } on() { return this.addEventListener.apply(this, arguments); } bind() { return this.addEventListener.apply(this, arguments); } removeEventListener(name, handler) { if (!this._events) { return this; } if (!name && !handler) { this._events = {}; this._bound = false; return this; } const names = name ? [name] : Object.keys(this._events); for (let i = 0, l = names.length; i < l; i++) { name = names[i]; let list = this._events[name]; if (list) { let events = []; if (handler) { for (let j = 0, k = list.length; j < k; j++) { let e = list[j]; e = e.handler ? e.handler : e; if (handler !== e) { events.push(e); } } } this._events[name] = events; } } return this; } off() { return this.removeEventListener.apply(this, arguments); } unbind() { return this.removeEventListener.apply(this, arguments); } dispatchEvent(name) { if (!this._events) { return this; } const args = Array.prototype.slice.call(arguments, 1); const events = this._events[name]; if (events) { for (let i = 0; i < events.length; i++) { events[i].call(this, ...args); } } return this; } trigger() { return this.dispatchEvent.apply(this, arguments); } listen(obj, name, handler) { const scope = this; if (obj) { e.obj = obj; e.name = name; e.handler = handler; obj.on(name, e); } function e() { handler.apply(scope, arguments); } return scope; } ignore(obj, name, handler) { obj.off(name, handler); return this; } }; __publicField(Events, "Types", { play: "play", pause: "pause", update: "update", render: "render", resize: "resize", change: "change", remove: "remove", insert: "insert", order: "order", load: "load" }); __publicField(Events, "Methods", [ "addEventListener", "on", "removeEventListener", "off", "unbind", "dispatchEvent", "trigger", "listen", "ignore" ]); // src/vector.js var proto = { x: { enumerable: true, get: function() { return this._x; }, set: function(v) { if (this._x !== v) { this._x = v; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } }, y: { enumerable: true, get: function() { return this._y; }, set: function(v) { if (this._y !== v) { this._y = v; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } } }; var _Vector = class extends Events { _x = 0; _y = 0; constructor(x = 0, y = 0) { super(); for (let prop in proto) { Object.defineProperty(this, prop, proto[prop]); } this.x = x; this.y = y; } static add(v1, v2) { return new _Vector(v1.x + v2.x, v1.y + v2.y); } static sub(v1, v2) { return new _Vector(v1.x - v2.x, v1.y - v2.y); } static subtract(v1, v2) { return _Vector.sub(v1, v2); } static ratioBetween(v1, v2) { return (v1.x * v2.x + v1.y * v2.y) / (v1.length() * v2.length()); } static angleBetween(v1, v2) { if (arguments.length >= 4) { const dx2 = arguments[0] - arguments[2]; const dy2 = arguments[1] - arguments[3]; return Math.atan2(dy2, dx2); } const dx = v1.x - v2.x; const dy = v1.y - v2.y; return Math.atan2(dy, dx); } static distanceBetween(v1, v2) { return Math.sqrt(_Vector.distanceBetweenSquared(v1, v2)); } static distanceBetweenSquared(v1, v2) { const dx = v1.x - v2.x; const dy = v1.y - v2.y; return dx * dx + dy * dy; } set(x, y) { this.x = x; this.y = y; return this; } copy(v) { this.x = v.x; this.y = v.y; return this; } clear() { this.x = 0; this.y = 0; return this; } clone() { return new _Vector(this.x, this.y); } add(x, y) { if (arguments.length <= 0) { return this; } else if (arguments.length <= 1) { if (typeof x === "number") { this.x += x; this.y += x; } else if (x && typeof x.x === "number" && typeof x.y === "number") { this.x += x.x; this.y += x.y; } } else { this.x += x; this.y += y; } return this; } addSelf(v) { return this.add.apply(this, arguments); } sub(x, y) { if (arguments.length <= 0) { return this; } else if (arguments.length <= 1) { if (typeof x === "number") { this.x -= x; this.y -= x; } else if (x && typeof x.x === "number" && typeof x.y === "number") { this.x -= x.x; this.y -= x.y; } } else { this.x -= x; this.y -= y; } return this; } subtract() { return this.sub.apply(this, arguments); } subSelf(v) { return this.sub.apply(this, arguments); } subtractSelf(v) { return this.sub.apply(this, arguments); } multiply(x, y) { if (arguments.length <= 0) { return this; } else if (arguments.length <= 1) { if (typeof x === "number") { this.x *= x; this.y *= x; } else if (x && typeof x.x === "number" && typeof x.y === "number") { this.x *= x.x; this.y *= x.y; } } else { this.x *= x; this.y *= y; } return this; } multiplySelf(v) { return this.multiply.apply(this, arguments); } multiplyScalar(s) { return this.multiply(s); } divide(x, y) { if (arguments.length <= 0) { return this; } else if (arguments.length <= 1) { if (typeof x === "number") { this.x /= x; this.y /= x; } else if (x && typeof x.x === "number" && typeof x.y === "number") { this.x /= x.x; this.y /= x.y; } } else { this.x /= x; this.y /= y; } if (isNaN(this.x)) { this.x = 0; } if (isNaN(this.y)) { this.y = 0; } return this; } divideSelf(v) { return this.divide.apply(this, arguments); } divideScalar(s) { return this.divide(s); } negate() { return this.multiply(-1); } dot(v) { return this.x * v.x + this.y * v.y; } length() { return Math.sqrt(this.lengthSquared()); } lengthSquared() { return this.x * this.x + this.y * this.y; } normalize() { return this.divideScalar(this.length()); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x; const dy = this.y - v.y; return dx * dx + dy * dy; } setLength(l) { return this.normalize().multiplyScalar(l); } equals(v, eps) { eps = typeof eps === "undefined" ? 1e-4 : eps; return this.distanceTo(v) < eps; } lerp(v, t) { const x = (v.x - this.x) * t + this.x; const y = (v.y - this.y) * t + this.y; return this.set(x, y); } isZero(eps) { eps = typeof eps === "undefined" ? 1e-4 : eps; return this.length() < eps; } toString() { return this.x + ", " + this.y; } toObject() { return { x: toFixed(this.x), y: toFixed(this.y) }; } rotate(radians) { const x = this.x; const y = this.y; const cos7 = Math.cos(radians); const sin7 = Math.sin(radians); this.x = x * cos7 - y * sin7; this.y = x * sin7 + y * cos7; return this; } }; var Vector = _Vector; __publicField(Vector, "zero", new _Vector()); __publicField(Vector, "left", new _Vector(-1, 0)); __publicField(Vector, "right", new _Vector(1, 0)); __publicField(Vector, "up", new _Vector(0, -1)); __publicField(Vector, "down", new _Vector(0, 1)); // src/anchor.js var Anchor = class extends Vector { controls = { left: new Vector(), right: new Vector() }; _command = Commands.move; _relative = true; _rx = 0; _ry = 0; _xAxisRotation = 0; _largeArcFlag = 0; _sweepFlag = 1; constructor(x = 0, y = 0, ax = 0, ay = 0, bx = 0, by = 0, command = Commands.move) { super(x, y); for (let prop in proto2) { Object.defineProperty(this, prop, proto2[prop]); } this.command = command; this.relative = true; const broadcast = Anchor.makeBroadcast(this); this.controls.left.set(ax, ay).addEventListener(Events.Types.change, broadcast); this.controls.right.set(bx, by).addEventListener(Events.Types.change, broadcast); } static makeBroadcast(scope) { return broadcast; function broadcast() { if (scope._bound) { scope.dispatchEvent(Events.Types.change); } } } static fromObject(obj) { return new Anchor().copy(obj); } copy(v) { this.x = v.x; this.y = v.y; if (typeof v.command === "string") { this.command = v.command; } if (v.controls) { if (v.controls.left) { this.controls.left.copy(v.controls.left); } if (v.controls.right) { this.controls.right.copy(v.controls.right); } } if (typeof v.relative === "boolean") { this.relative = v.relative; } if (typeof v.rx === "number") { this.rx = v.rx; } if (typeof v.ry === "number") { this.ry = v.ry; } if (typeof v.xAxisRotation === "number") { this.xAxisRotation = v.xAxisRotation; } if (typeof v.largeArcFlag === "number") { this.largeArcFlag = v.largeArcFlag; } if (typeof v.sweepFlag === "number") { this.sweepFlag = v.sweepFlag; } return this; } clone() { return new Anchor().copy(this); } toObject() { return { x: toFixed(this.x), y: toFixed(this.y), command: this.command, relative: this.relative, controls: { left: this.controls.left.toObject(), right: this.controls.right.toObject() }, rx: toFixed(this.rx), ry: toFixed(this.ry), xAxisRotation: toFixed(this.xAxisRotation), largeArcFlag: toFixed(this.largeArcFlag), sweepFlag: toFixed(this.sweepFlag) }; } toString() { return JSON.stringify(this.toObject()); } }; var proto2 = { command: { enumerable: true, get: function() { return this._command; }, set: function(command) { if (this._command !== command) { this._command = command; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } }, relative: { enumerable: true, get: function() { return this._relative; }, set: function(relative) { if (this._relative !== !!relative) { this._relative = !!relative; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } }, rx: { enumerable: true, get: function() { return this._rx; }, set: function(rx) { if (this._rx !== rx) { this._rx = rx; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } }, ry: { enumerable: true, get: function() { return this._ry; }, set: function(ry) { if (this._ry !== ry) { this._ry = ry; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } }, xAxisRotation: { enumerable: true, get: function() { return this._xAxisRotation; }, set: function(xAxisRotation) { if (this._xAxisRotation !== xAxisRotation) { this._xAxisRotation = xAxisRotation; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } }, largeArcFlag: { enumerable: true, get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { if (this._largeArcFlag !== largeArcFlag) { this._largeArcFlag = largeArcFlag; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } }, sweepFlag: { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { if (this._sweepFlag !== sweepFlag) { this._sweepFlag = sweepFlag; if (this._bound) { this.dispatchEvent(Events.Types.change); } } } } }; // src/constants.js var count = 0; var Constants = { NextFrameId: null, Types: { webgl: "WebGLRenderer", svg: "SVGRenderer", canvas: "CanvasRenderer" }, Version: "v0.8.19", PublishDate: "2025-06-27T18:00:39.187Z", Identifier: "two-", Resolution: 12, AutoCalculateImportedMatrices: true, Instances: [], uniqueId: function() { return count++; } }; // src/utils/curves.js var Curve = { CollinearityEpsilon: Math.pow(10, -30), RecursionLimit: 16, CuspLimit: 0, Tolerance: { distance: 0.25, angle: 0, epsilon: Number.EPSILON }, abscissas: [ [0.5773502691896257], [0, 0.7745966692414834], [0.33998104358485626, 0.8611363115940526], [0, 0.5384693101056831, 0.906179845938664], [ 0.2386191860831969, 0.6612093864662645, 0.932469514203152 ], [ 0, 0.4058451513773972, 0.7415311855993945, 0.9491079123427585 ], [ 0.1834346424956498, 0.525532409916329, 0.7966664774136267, 0.9602898564975363 ], [ 0, 0.3242534234038089, 0.6133714327005904, 0.8360311073266358, 0.9681602395076261 ], [ 0.14887433898163122, 0.4333953941292472, 0.6794095682990244, 0.8650633666889845, 0.9739065285171717 ], [ 0, 0.26954315595234496, 0.5190961292068118, 0.7301520055740494, 0.8870625997680953, 0.978228658146057 ], [ 0.1252334085114689, 0.3678314989981802, 0.5873179542866175, 0.7699026741943047, 0.9041172563704749, 0.9815606342467192 ], [ 0, 0.2304583159551348, 0.44849275103644687, 0.6423493394403402, 0.8015780907333099, 0.9175983992229779, 0.9841830547185881 ], [ 0.10805494870734367, 0.31911236892788974, 0.5152486363581541, 0.6872929048116855, 0.827201315069765, 0.9284348836635735, 0.9862838086968123 ], [ 0, 0.20119409399743451, 0.3941513470775634, 0.5709721726085388, 0.7244177313601701, 0.8482065834104272, 0.937273392400706, 0.9879925180204854 ], [ 0.09501250983763744, 0.2816035507792589, 0.45801677765722737, 0.6178762444026438, 0.755404408355003, 0.8656312023878318, 0.9445750230732326, 0.9894009349916499 ] ], weights: [ [1], [0.8888888888888888, 0.5555555555555556], [0.6521451548625461, 0.34785484513745385], [ 0.5688888888888889, 0.47862867049936647, 0.23692688505618908 ], [ 0.46791393457269104, 0.3607615730481386, 0.17132449237917036 ], [ 0.4179591836734694, 0.3818300505051189, 0.27970539148927664, 0.1294849661688697 ], [ 0.362683783378362, 0.31370664587788727, 0.22238103445337448, 0.10122853629037626 ], [ 0.3302393550012598, 0.31234707704000286, 0.26061069640293544, 0.1806481606948574, 0.08127438836157441 ], [ 0.29552422471475287, 0.26926671930999635, 0.21908636251598204, 0.1494513491505806, 0.06667134430868814 ], [ 0.2729250867779006, 0.26280454451024665, 0.23319376459199048, 0.18629021092773426, 0.1255803694649046, 0.05566856711617366 ], [ 0.24914704581340277, 0.2334925365383548, 0.20316742672306592, 0.16007832854334622, 0.10693932599531843, 0.04717533638651183 ], [ 0.2325515532308739, 0.22628318026289723, 0.2078160475368885, 0.17814598076194574, 0.13887351021978725, 0.09212149983772845, 0.04048400476531588 ], [ 0.2152638534631578, 0.2051984637212956, 0.18553839747793782, 0.15720316715819355, 0.12151857068790319, 0.08015808715976021, 0.03511946033175186 ], [ 0.2025782419255613, 0.19843148532711158, 0.1861610000155622, 0.16626920581699392, 0.13957067792615432, 0.10715922046717194, 0.07036604748810812, 0.03075324199611727 ], [ 0.1894506104550685, 0.18260341504492358, 0.16915651939500254, 0.14959598881657674, 0.12462897125553388, 0.09515851168249279, 0.062253523938647894, 0.027152459411754096 ] ] }; function getComponentOnCubicBezier(t, a, b, c, d) { const k = 1 - t; return k * k * k * a + 3 * k * k * t * b + 3 * k * t * t * c + t * t * t * d; } function subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit) { limit = limit || Curve.RecursionLimit; const amount = limit + 1; if (Math.abs(x1 - x4) < 1e-3 && Math.abs(y1 - y4) < 1e-3) { return [new Anchor(x4, y4)]; } const result = []; for (let i = 0; i < amount; i++) { const t = i / amount; const x = getComponentOnCubicBezier(t, x1, x2, x3, x4); const y = getComponentOnCubicBezier(t, y1, y2, y3, y4); result.push(new Anchor(x, y)); } return result; } function getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit) { if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) { const dx = x4 - x1; const dy = y4 - y1; return Math.sqrt(dx * dx + dy * dy); } const ax = 9 * (x2 - x3) + 3 * (x4 - x1), bx = 6 * (x1 + x3) - 12 * x2, cx = 3 * (x2 - x1), ay = 9 * (y2 - y3) + 3 * (y4 - y1), by = 6 * (y1 + y3) - 12 * y2, cy = 3 * (y2 - y1); function integrand(t) { const dx = (ax * t + bx) * t + cx, dy = (ay * t + by) * t + cy; return Math.sqrt(dx * dx + dy * dy); } return integrate(integrand, 0, 1, limit || Curve.RecursionLimit); } function getCurveBoundingBox(x1, y1, x2, y2, x3, y3, x4, y4) { const tvalues = []; const bounds = [[], []]; let a, b, c, t, t1, t2, b2ac, sqrtb2ac; for (let i = 0; i < 2; ++i) { if (i == 0) { b = 6 * x1 - 12 * x2 + 6 * x3; a = -3 * x1 + 9 * x2 - 9 * x3 + 3 * x4; c = 3 * x2 - 3 * x1; } else { b = 6 * y1 - 12 * y2 + 6 * y3; a = -3 * y1 + 9 * y2 - 9 * y3 + 3 * y4; c = 3 * y2 - 3 * y1; } if (Math.abs(a) < 1e-12) { if (Math.abs(b) < 1e-12) { continue; } t = -c / b; if (0 < t && t < 1) { tvalues.push(t); } continue; } b2ac = b * b - 4 * c * a; sqrtb2ac = Math.sqrt(b2ac); if (b2ac < 0) { continue; } t1 = (-b + sqrtb2ac) / (2 * a); if (0 < t1 && t1 < 1) { tvalues.push(t1); } t2 = (-b - sqrtb2ac) / (2 * a); if (0 < t2 && t2 < 1) { tvalues.push(t2); } } let j = tvalues.length; let jlen = j; let mt; while (j--) { t = tvalues[j]; mt = 1 - t; bounds[0][j] = mt * mt * mt * x1 + 3 * mt * mt * t * x2 + 3 * mt * t * t * x3 + t * t * t * x4; bounds[1][j] = mt * mt * mt * y1 + 3 * mt * mt * t * y2 + 3 * mt * t * t * y3 + t * t * t * y4; } bounds[0][jlen] = x1; bounds[1][jlen] = y1; bounds[0][jlen + 1] = x4; bounds[1][jlen + 1] = y4; bounds[0].length = bounds[1].length = jlen + 2; return { min: { x: Math.min.apply(0, bounds[0]), y: Math.min.apply(0, bounds[1]) }, max: { x: Math.max.apply(0, bounds[0]), y: Math.max.apply(0, bounds[1]) } }; } function integrate(f, a, b, n) { let x = Curve.abscissas[n - 2], w = Curve.weights[n - 2], A = 0.5 * (b - a), B = A + a, i = 0, m = n + 1 >> 1, sum = n & 1 ? w[i++] * f(B) : 0; while (i < m) { const Ax = A * x[i]; sum += w[i++] * (f(B + Ax) + f(B - Ax)); } return A * sum; } function getCurveFromPoints(points, closed2) { const l = points.length, last = l - 1; for (let i = 0; i < l; i++) { const point = points[i]; const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0); const next = closed2 ? mod(i + 1, l) : Math.min(i + 1, last); const a = points[prev]; const b = point; const c = points[next]; getControlPoints(a, b, c); b.command = i === 0 ? Commands.move : Commands.curve; } } function getControlPoints(a, b, c) { const a1 = Vector.angleBetween(a, b); const a2 = Vector.angleBetween(c, b); let d1 = Vector.distanceBetween(a, b); let d2 = Vector.distanceBetween(c, b); let mid = (a1 + a2) / 2; if (d1 < 1e-4 || d2 < 1e-4) { if (typeof b.relative === "boolean" && !b.relative) { b.controls.left.copy(b); b.controls.right.copy(b); } return b; } d1 *= 0.33; d2 *= 0.33; if (a2 < a1) { mid += HALF_PI; } else { mid -= HALF_PI; } b.controls.left.x = Math.cos(mid) * d1; b.controls.left.y = Math.sin(mid) * d1; mid -= Math.PI; b.controls.right.x = Math.cos(mid) * d2; b.controls.right.y = Math.sin(mid) * d2; if (typeof b.relative === "boolean" && !b.relative) { b.controls.left.x += b.x; b.controls.left.y += b.y; b.controls.right.x += b.x; b.controls.right.y += b.y; } return b; } function getReflection(a, b, relative) { return new Vector( 2 * a.x - (b.x + a.x) - (relative ? a.x : 0), 2 * a.y - (b.y + a.y) - (relative ? a.y : 0) ); } function getAnchorsFromArcData(center, xAxisRotation, rx, ry, ts, td, ccw) { const resolution = Constants.Resolution; const anchors = []; for (let i = 0; i < resolution; i++) { let pct = (i + 1) / resolution; if (ccw) { pct = 1 - pct; } const theta = pct * td + ts; const x = rx * Math.cos(theta); const y = ry * Math.sin(theta); const anchor2 = new Anchor(x, y); anchor2.command = Commands.line; anchors.push(anchor2); } } // src/utils/underscore.js var slice = Array.prototype.slice; function isArrayLike(collection) { if (collection === null || collection === void 0) return false; const length = collection.length; return typeof length == "number" && length >= 0 && length < 4294967296; } var _ = { isNaN: function(obj) { return typeof obj === "number" && obj !== +obj; }, isElement: function(obj) { return !!(obj && obj.nodeType === 1); }, isObject: function(obj) { const type = typeof obj; return type === "function" || type === "object" && !!obj; }, isFunction: function(obj) { return typeof obj === "function"; }, extend: function(base) { const sources = slice.call(arguments, 1); for (let i = 0; i < sources.length; i++) { const obj = sources[i]; for (let k in obj) { base[k] = obj[k]; } } return base; }, defaults: function(base) { const sources = slice.call(arguments, 1); for (let i = 0; i < sources.length; i++) { const obj = sources[i]; for (let k in obj) { if (base[k] === void 0) { base[k] = obj[k]; } } } return base; }, each: function(obj, iteratee, context) { const ctx = context || this; const keys = !isArrayLike(obj) && Object.keys(obj); const length = (keys || obj).length; for (let i = 0; i < length; i++) { const k = keys ? keys[i] : i; iteratee.call(ctx, obj[k], k, obj); } return obj; }, performance: root.performance && root.performance.now ? root.performance : Date }; // src/utils/dom.js var dom = { hasEventListeners: typeof root.addEventListener === "function", bind: function(elem, event, func, bool) { if (this.hasEventListeners) { elem.addEventListener(event, func, !!bool); } else { elem.attachEvent("on" + event, func); } return dom; }, unbind: function(elem, event, func, bool) { if (dom.hasEventListeners) { elem.removeEventListeners(event, func, !!bool); } else { elem.detachEvent("on" + event, func); } return dom; }, getRequestAnimationFrame: function() { const vendors = ["ms", "moz", "webkit", "o"]; let lastTime = 0; let request = root.requestAnimationFrame; if (!request) { for (let i = 0; i < vendors.length; i++) { request = root[vendors[i] + "RequestAnimationFrame"] || request; } request = request || fallbackRequest; } function fallbackRequest(callback, element) { const currTime = new Date().getTime(); const timeToCall = Math.max(0, 16 - (currTime - lastTime)); const id = root.setTimeout(nextRequest, timeToCall); lastTime = currTime + timeToCall; function nextRequest() { callback(currTime + timeToCall); } return id; } return request; } }; var temp = root.document ? root.document.createElement("div") : {}; temp.id = "help-two-load"; Object.defineProperty(dom, "temp", { enumerable: true, get: function() { if (_.isElement(temp) && !root.document.head.contains(temp)) { temp.style.display = "none"; root.document.head.appendChild(temp); } return temp; } }); // src/utils/error.js var TwoError = class extends Error { name = "Two.js"; message; constructor(message) { super(); this.message = message; } }; // src/utils/device-pixel-ratio.js var devicePixelRatio = root.devicePixelRatio || 1; function getBackingStoreRatio(ctx) { return ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; } function getRatio(ctx) { return devicePixelRatio / getBackingStoreRatio(ctx); } // src/registry.js var Registry = class { map = {}; constructor() { } add(id, obj) { this.map[id] = obj; return this; } remove(id) { delete this.map[id]; return this; } get(id) { return this.map[id]; } contains(id) { return id in this.map; } }; // src/collection.js var Collection = class extends Array { #events = new Events(); get _events() { return this.#events; } set _events(e) { this.#events = e; } get _bound() { return this.#events._bound; } set _bound(v) { this.#events._bound = v; } addEventListener() { return this.#events.addEventListener.apply(this, arguments); } on() { return this.#events.on.apply(this, arguments); } bind() { return this.#events.bind.apply(this, arguments); } removeEventListener() { return this.#events.removeEventListener.apply(this, arguments); } off() { return this.#events.off.apply(this, arguments); } unbind() { return this.#events.unbind.apply(this, arguments); } dispatchEvent() { return this.#events.dispatchEvent.apply(this, arguments); } trigger() { return this.#events.trigger.apply(this, arguments); } listen() { return this.#events.listen.apply(this, arguments); } ignore() { return this.#events.ignore.apply(this, arguments); } constructor() { super(); if (arguments[0] && Array.isArray(arguments[0])) { if (arguments[0].length > 0) { this.push.apply(this, arguments[0]); } } else if (arguments.length > 0) { this.push.apply(this, arguments); } } pop() { const popped = super.pop.apply(this, arguments); this.trigger(Events.Types.remove, [popped]); return popped; } shift() { const shifted = super.shift.apply(this, arguments); this.trigger(Events.Types.remove, [shifted]); return shifted; } push() { const pushed = super.push.apply(this, arguments); this.trigger(Events.Types.insert, arguments); return pushed; } unshift() { const unshifted = super.unshift.apply(this, arguments); this.trigger(Events.Types.insert, arguments); return unshifted; } splice() { const spliced = super.splice.apply(this, arguments); this.trigger(Events.Types.remove, spliced); if (arguments.length > 2) { const inserted = this.slice( arguments[0], arguments[0] + arguments.length - 2 ); this.trigger(Events.Types.insert, inserted); this.trigger(Events.Types.order); } return spliced; } sort() { super.sort.apply(this, arguments); this.trigger(Events.Types.order); return this; } reverse() { super.reverse.apply(this, arguments); this.trigger(Events.Types.order); return this; } indexOf() { return super.indexOf.apply(this, arguments); } map(func, scope) { const results = []; for (let key = 0; key < this.length; key++) { const value = this[key]; let result; if (scope) { result = func.call(scope, value, key); } else { result = func(value, key); } results.push(result); } return results; } }; // src/element.js var _Element = class extends Events { _flagId = false; _flagClassName = false; _renderer = {}; _id = Constants.Identifier + Constants.uniqueId(); _className = ""; classList = []; constructor() { super(); for (let prop in proto3) { Object.defineProperty(this, prop, proto3[prop]); } } static fromObject(obj) { const elem = new _Element().copy(obj); if ("id" in obj) { elem.id = obj.id; } return elem; } flagReset() { this._flagId = this._flagClassName = false; return this; } copy(element) { this.renderer.type = element.renderer.type; this.className = element.className; return this; } toObject() { return { renderer: { type: this.renderer.type }, id: this.id, className: this.className }; } }; var Element = _Element; __publicField(Element, "Properties", ["renderer", "id", "className"]); var proto3 = { renderer: { enumerable: false, get: function() { return this._renderer; } }, id: { enumerable: true, get: function() { return this._id; }, set: function(v) { const id = this._id; if (v === this._id) { return; } this._id = v; this._flagId = true; if (this.parent) { delete this.parent.children.ids[id]; this.parent.children.ids[this._id] = this; } } }, className: { enumerable: true, get: function() { return this._className; }, set: function(v) { if (this._className !== v) { this._flagClassName = true; this.classList = v.split(/\s+?/); this._className = v; } } } }; // src/effects/texture.js var anchor; var regex = { video: /\.(mp4|webm|ogg)$/i, image: /\.(jpe?g|png|gif|tiff|webp)$/i, effect: /texture|gradient/i }; if (root.document) { anchor = document.createElement("a"); } var _Texture = class extends Element { _flagSrc = false; _flagImage = false; _flagVideo = false; _flagLoaded = false; _flagRepeat = false; _flagOffset = false; _flagScale = false; _src = ""; _image = null; _loaded = false; _repeat = "no-repeat"; _scale = 1; _offset = null; constructor(src, callback) { super(); for (let prop in proto4) { Object.defineProperty(this, prop, proto4[prop]); } this._renderer.type = "texture"; this._renderer.flagOffset = FlagOffset.bind(this); this._renderer.flagScale = FlagScale.bind(this); this.loaded = false; this.repeat = "no-repeat"; this.offset = new Vector(); if (typeof callback === "function") { const loaded = function() { this.unbind(Events.Types.load, loaded); if (typeof callback === "function") { callback(); } }.bind(this); this.bind(Events.Types.load, loaded); } if (typeof src === "string") { this.src = src; } else if (typeof src === "object") { const elemString = Object.prototype.toString.call(src); if (elemString === "[object HTMLImageElement]" || elemString === "[object HTMLCanvasElement]" || elemString === "[object HTMLVideoElement]" || elemString === "[object Image]") { this.image = src; } } this._update(); } fromObject(obj) { const texture = new _Texture().copy(obj); if ("id" in obj) { texture.id = obj.id; } return texture; } static getAbsoluteURL(path) { if (!anchor) { return path; } anchor.href = path; return anchor.href; } static loadHeadlessBuffer(texture, onLoad) { texture.image.onload = onLoad; texture.image.src = texture.src; } static getTag(image) { return image && image.nodeName && image.nodeName.toLowerCase() || "img"; } static getImage(src) { const absoluteSrc = _Texture.getAbsoluteURL(src); if (_Texture.ImageRegistry.contains(absoluteSrc)) { return _Texture.ImageRegistry.get(absoluteSrc); } let image; if (CanvasPolyfill.Image) { image = new CanvasPolyfill.Image(); CanvasPolyfill.shim(image, "img"); } else if (root.document) { if (regex.video.test(absoluteSrc)) { image = document.createElement("video"); } else { image = document.createElement("img"); } } else { console.warn("Two.js: no prototypical image defined for Two.Texture"); } image.crossOrigin = "anonymous"; image.referrerPolicy = "no-referrer"; return image; } static load(texture, callback) { let image = texture.image; let tag = _Texture.getTag(image); if (texture._flagImage) { if (/canvas/i.test(tag)) { _Texture.Register.canvas(texture, callback); } else { texture._src = !CanvasPolyfill.isHeadless && image.getAttribute("two-src") || image.src; _Texture.Register[tag](texture, callback); } } if (texture._flagSrc) { if (!image) { image = _Texture.getImage(texture.src); texture.image = image; } tag = _Texture.getTag(image); _Texture.Register[tag](texture, callback); } } clone() { const clone = new _Texture(this.src); clone.repeat = this.repeat; clone.offset.copy(this.offset); clone.scale = this.scale; return clone; } copy(texture) { this.src = texture.src; this.repeat = texture.repeat; this.offset = typeof texture.offset === "number" || texture.offset instanceof Vector ? texture.offset : new Vector().copy(texture.offset); this.scale = typeof texture.scale === "number" || texture.scale instanceof Vector ? texture.scale : new Vector().copy(texture.scale); return this; } toObject() { const result = super.toObject.call(this); result.renderer.type = "texture"; result.src = this.src; result.repeat = this.repeat; result.offset = this.offset.toObject(); result.scale = typeof this.scale === "number" ? this.scale : this.scale.toObject(); return result; } _update() { if (this._flagSrc || this._flagImage) { this.trigger(Events.Types.change); if (this._flagSrc || this._flagImage) { this.loaded = false; _Texture.load( this, function() { this.loaded = true; this.trigger(Events.Types.change).trigger(Events.Types.load); }.bind(this) ); } } if (this._image && this._image.readyState >= 4) { this._flagVideo = true; } return this; } flagReset() { this._flagSrc = this._flagImage = this._flagLoaded = this._flagRepeat = this._flagVideo = this._flagScale = this._flagOffset = false; super.flagReset.call(this); return this; } dispose() { if ("elem" in this._renderer) { const elem = this._renderer.elem; elem.parentNode.removeChild(elem); } if ("effect" in this._renderer) { this._renderer.effect = null; } return this; } }; var Texture = _Texture; __publicField(Texture, "Properties", ["src", "loaded", "repeat", "scale", "offset", "image"]); __publicField(Texture, "RegularExpressions", regex); __publicField(Texture, "ImageRegistry", new Registry()); __publicField(Texture, "Register", { canvas: function(texture, callback) { texture._src = "#" + texture.id; _Texture.ImageRegistry.add(texture.src, texture.image); if (typeof callback === "function") { callback(); } }, img: function(texture, callback) { const image = texture.image; const loaded = function(e) { if (!CanvasPolyfill.isHeadless && image.removeEventListener && typeof image.removeEventListener === "function") { image.removeEventListener("load", loaded, false); image.removeEventListener("error", error, false); } if (typeof callback === "function") { callback(); } }; const error = function(e) { if (!CanvasPolyfill.isHeadless && typeof image.removeEventListener === "function") { image.removeEventListener("load", loaded, false); image.removeEventListener("error", error, false); } throw new TwoError("unable to load " + texture.src); }; if (typeof image.width === "number" && image.width > 0 && typeof image.height === "number" && image.height > 0) { loaded(); } else if (!CanvasPolyfill.isHeadless && typeof image.addEventListener === "function") { image.addEventListener("load", loaded, false); image.addEventListener("error", error, false); } texture._src = _Texture.getAbsoluteURL(texture._src); if (!CanvasPolyfill.isHeadless && image && image.getAttribute("two-src")) { return; } if (!CanvasPolyfill.isHeadless) { image.setAttribute("two-src", texture.src); } _Texture.ImageRegistry.add(texture.src, image); if (CanvasPolyfill.isHeadless) { _Texture.loadHeadlessBuffer(texture, loaded); } else { texture.image.src = texture.src; } }, video: function(texture, callback) { if (CanvasPolyfill.isHeadless) { throw new TwoError( "video textures are not implemented in headless environments." ); } const loaded = function(e) { texture.image.removeEventListener("canplaythrough", loaded, false); texture.image.removeEventListener("error", error, false); texture.image.width = texture.image.videoWidth; texture.image.height = texture.image.videoHeight; if (typeof callback === "function") { callback(); } }; const error = function(e) { texture.image.removeEventListener("canplaythrough", loaded, false); texture.image.removeEventListener("error", error, false); throw new TwoError("unable to load " + texture.src); }; texture._src = _Texture.getAbsoluteURL(texture._src); if (!texture.image.getAttribute("two-src")) { texture.image.setAttribute("two-src", texture.src); _Texture.ImageRegistry.add(texture.src, texture.image); } if (texture.image.readyState >= 4) { loaded(); } else { texture.image.addEventListener("canplaythrough", loaded, false); texture.image.addEventListener("error", error, false); texture.image.src = texture.src; texture.image.load(); } } }); var proto4 = { src: { enumerable: true, get: function() { return this._src; }, set: function(v) { this._src = v; this._flagSrc = true; } }, loaded: { enumerable: true, get: function() { return this._loaded; }, set: function(v) { this._loaded = v; this._flagLoaded = true; } }, repeat: { enumerable: true, get: function() { return this._repeat; }, set: function(v) { this._repeat = v; this._flagRepeat = true; } }, image: { enumerable: true, get: function() { return this._image; }, set: function(image) { const tag = Texture.getTag(image); let index; switch (tag) { case "canvas": index = "#" + image.id; break; default: index = image.src; } if (Texture.ImageRegistry.contains(index)) { this._image = Texture.ImageRegistry.get(image.src); } else { this._image = image; } this._flagImage = true; } }, offset: { enumerable: true, get: function() { return this._offset; }, set: function(v) { if (this._offset) { this._offset.unbind(Events.Types.change, this._renderer.flagOffset); } this._offset = v; this._offset.bind(Events.Types.change, this._renderer.flagOffset); this._flagOffset = true; } }, scale: { enumerable: true, get: function() { return this._scale; }, set: function(v) { if (this._scale instanceof Vector) { this._scale.unbind(Events.Types.change, this._renderer.flagScale); } this._scale = v; if (this._scale instanceof Vector) { this._scale.bind(Events.Types.change, this._renderer.flagScale); } this._flagScale = true; } } }; function FlagOffset() { this._flagOffset = true; } function FlagScale() { this._flagScale = true; } // src/effects/stop.js var _Stop = class extends Element { _flagOffset = true; _flagOpacity = true; _flagColor = true; _offset = 0; _opacity = 1; _color = "#fff"; constructor(offset, color, opacity) { super(); for (let prop in proto5) { Object.defineProperty(this, prop, proto5[prop]); } this._renderer.type = "stop"; this.offset = typeof offset === "number" ? offset : _Stop.Index <= 0 ? 0 : 1; this.opacity = typeof opacity === "number" ? opacity : 1; this.color = typeof color === "string" ? color : _Stop.Index <= 0 ? "#fff" : "#000"; _Stop.Index = (_Stop.Index + 1) % 2; } static fromObject(obj) { const stop = new _Stop().copy(obj); if ("id" in obj) { stop.id = obj.id; } return stop; } copy(stop) { super.copy.call(this, stop); for (let i = 0; i < _Stop.Properties.length; i++) { const k = _Stop.Properties[i]; if (k in stop) { this[k] = stop[k]; } } return this; } clone(parent) { const clone = new _Stop(); _.each( _Stop.Properties, function(property) { clone[property] = this[property]; }, this ); if (parent && parent.stops) { parent.stops.push(clone); } return clone; } toObject() { const result = super.toObject.call(this); result.renderer.type = "stop"; _.each( _Stop.Properties, (k) => { result[k] = this[k]; }, this ); return result; } flagReset() { this._flagOffset = this._flagColor = this._flagOpacity = false; super.flagReset.call(this); return this; } }; var Stop = _Stop; __publicField(Stop, "Index", 0); __publicField(Stop, "Properties", ["offset", "opacity", "color"]); var proto5 = { offset: { enumerable: true, get: function() { return this._offset; }, set: function(v) { this._offset = v; this._flagOffset = true; if (this.parent) { this.parent._flagStops = true; } } }, opacity: { enumerable: true, get: function() { return this._opacity; }, set: function(v) { this._opacity = v; this._flagOpacity = true; if (this.parent) { this.parent._flagStops = true; } } }, color: { enumerable: true, get: function() { return this._color; }, set: function(v) { this._color = v; this._flagColor = true; if (this.parent) { this.parent._flagStops = true; } } } }; // src/effects/gradient.js var _Gradient = class extends Element { _flagStops = false; _flagSpread = false; _flagUnits = false; _spread = ""; _units = ""; constructor(stops) { super(); for (let prop in proto6) { Object.defineProperty(this, prop, proto6[prop]); } this._renderer.type = "gradient"; this._renderer.flagStops = FlagStops.bind(this); this._renderer.bindStops = BindStops.bind(this); this._renderer.unbindStops = UnbindStops.bind(this); this.spread = "pad"; this.units = "objectBoundingBox"; if (stops) { this.stops = stops; } } static fromObject(obj) { let stops = obj.stops; if (stops && stops.length > 0) { stops = stops.map((o) => o instanceof Stop ? o : new Stop().copy(o)); } const gradient = new _Gradient().copy(obj); if ("id" in obj) { gradient.id = obj.id; } return gradient; } clone(parent) { const stops = this.stops.map((s) => { return s.clone(); }); const clone = new _Gradient(stops); _.each( _Gradient.Properties, (k) => { clone[k] = this[k]; }, this ); if (parent) { parent.add(clone); } return clone; } copy(gradient) { super.copy.call(this, gradient); for (let i = 0; i < _Gradient.Properties.length; i++) { const k = _Gradient.Properties[i]; if (k in gradient) { this[k] = gradient[k]; } } return this; } toObject() { const result = { stops: this.stops.map((s) => { return s.toObject(); }) }; _.each( _Gradient.Properties, (k) => { result[k] = this[k]; }, this ); return result; } _update() { if (this._flagSpread || this._flagStops) { this.trigger(Events.Types.change); } return this; } flagReset() { this._flagSpread = this._flagUnits = this._flagStops = false; super.flagReset.call(this); return this; } dispose() { if ("elem" in this._renderer) { const elem = this._renderer.elem; elem.parentNode.removeChild(elem); } if ("effect" in this._renderer) { this._renderer.effect = null; } return this; } }; var Gradient = _Gradient; __publicField(Gradient, "Stop", Stop); __publicField(Gradient, "Properties", ["spread", "stops", "units"]); var proto6 = { spread: { enumerable: true, get: function() { return this._spread; }, set: function(v) { this._spread = v; this._flagSpread = true; } }, units: { enumerable: true, get: function() { return this._units; }, set: function(v) { this._units = v; this._flagUnits = true; } }, stops: { enumerable: true, get: function() { return this._stops; }, set: function(stops) { const bindStops = this._renderer.bindStops; const unbindStops = this._renderer.unbindStops; if (this._stops) { this._stops.unbind(Events.Types.insert, bindStops).unbind(Events.Types.remove, unbindStops); } this._stops = new Collection((stops || []).slice(0)); this._stops.bind(Events.Types.insert, bindStops).bind(Events.Types.remove, unbindStops); bindStops(this._stops); } } }; function FlagStops() { this._flagStops = true; } function BindStops(items) { let i = items.length; while (i--) { items[i].bind(Events.Types.change, this._renderer.flagStops); items[i].parent = this; } this._renderer.flagStops(); } function UnbindStops(items) { let i = items.length; while (i--) { items[i].unbind(Events.Types.change, this._renderer.flagStops); delete items[i].parent; } this._renderer.flagStops(); } // src/effects/linear-gradient.js var _LinearGradient = class extends Gradient { _flagEndPoints = false; _left = null; _right = null; constructor(x1, y1, x2, y2, stops) { super(stops); for (let prop in proto7) { Object.defineProperty(this, prop, proto7[prop]); } this._renderer.type = "linear-gradient"; this._renderer.flagEndPoints = FlagEndPoints.bind(this); this.left = new Vector(); this.right = new Vector(); if (typeof x1 === "number") { this.left.x = x1; } if (typeof y1 === "number") { this.left.y = y1; } if (typeof x2 === "number") { this.right.x = x2; } if (typeof y2 === "number") { this.right.y = y2; } } static fromObject(obj) { const gradient = new _LinearGradient().copy(obj); if ("id" in obj) { gradient.id = obj.id; } return gradient; } copy(gradient) { super.copy.call(this, gradient); for (let i = 0; i < _LinearGradient.Properties.length; i++) { const k = _LinearGradient.Properties[i]; if (k in gradient) { this[k] = gradient[k] instanceof Vector ? gradient[k] : new Vector().copy(gradient[k]); } } return this; } clone(parent) { const stops = this.stops.map(function(stop) { return stop.clone(); }); const clone = new _LinearGradient( this.left._x, this.left._y, this.right._x, this.right._y, stops ); _.each( Gradient.Properties, function(k) { clone[k] = this[k]; }, this ); if (parent) { parent.add(clone); } return clone; } toObject() { const result = super.toObject.call(this); result.left = this.left.toObject(); result.right = this.right.toObject(); return result; } _update() { if (this._flagEndPoints || this._flagSpread || this._flagStops) { this.trigger(Events.Types.change); } return this; } flagReset() { this._flagEndPoints = false; super.flagReset.call(this); return this; } }; var LinearGradient = _LinearGradient; __publicField(LinearGradient, "Stop", Stop); __publicField(LinearGradient, "Properties", ["left", "right"]); var proto7 = { left: { enumerable: true, get: function() { return this._left; }, set: function(v) { if (this._left instanceof Vector) { this._left.unbind(Events.Types.change, this._renderer.flagEndPoints); } this._left = v; this._left.bind(Events.Types.change, this._renderer.flagEndPoints); this._flagEndPoints = true; } }, right: { enumerable: true, get: function() { return this._right; }, set: function(v) { if (this._right instanceof Vector) { this._right.unbind(Events.Types.change, this._renderer.flagEndPoints); } this._right = v; this._right.bind(Events.Types.change, this._renderer.flagEndPoints); this._flagEndPoints = true; } } }; function FlagEndPoints() { this._flagEndPoints = true; } // src/effects/radial-gradient.js var _RadialGradient = class extends Gradient { _flagRadius = false; _flagCenter = false; _flagFocal = false; _radius = 0; _center = null; _focal = null; constructor(cx, cy, r, stops, fx, fy) { super(stops); for (let prop in proto8) { Object.defineProperty(this, prop, proto8[prop]); } this._renderer.type = "radial-gradient"; this._renderer.flagCenter = FlagCenter.bind(this); this._renderer.flagFocal = FlagFocal.bind(this); this.center = new Vector(); this.radius = typeof r === "number" ? r : 1; this.focal = new Vector(); if (typeof cx === "number") { this.center.x = cx; } if (typeof cy === "number") { this.center.y = cy; } this.focal.copy(this.center); if (typeof fx === "number") { this.focal.x = fx; } if (typeof fy === "number") { this.focal.y = fy; } } static fromObject(obj) { const gradient = new _RadialGradient().copy(obj); if ("id" in obj) { gradient.id = obj.id; } return gradient; } copy(gradient) { super.copy.call(this, gradient); for (let i = 0; i < _RadialGradient.Properties.length; i++) { const k = _RadialGradient.Properties[i]; if (k in gradient) { if (/(center|focal)i/.test(k)) { this[k] = gradient[k] instanceof Vector ? gradient[k] : new Vector().copy(gradient[k]); } else if (typeof gradient[k] === "number") { this[k] = gradient[MediaKeySystemAccess]; } } } return this; } clone(parent) { const stops = this.stops.map(function(stop) { return stop.clone(); }); const clone = new _RadialGradient( this.center._x, this.center._y, this._radius, stops, this.focal._x, this.focal._y ); _.each( Gradient.Properties.concat(_RadialGradient.Properties), function(k) { clone[k] = this[k]; }, this ); if (parent) { parent.add(clone); } return clone; } toObject() { const result = super.toObject.call(this); _.each( _RadialGradient.Properties, function(k) { result[k] = this[k]; }, this ); result.center = this.center.toObject(); result.focal = this.focal.toObject(); return result; } _update() { if (this._flagRadius || this._flatCenter || this._flagFocal || this._flagSpread || this._flagStops) { this.trigger(Events.Types.change); } return this; } flagReset() { this._flagRadius = this._flagCenter = this._flagFocal = false; super.flagReset.call(this); return this; } }; var RadialGradient = _RadialGradient; __publicField(RadialGradient, "Stop", Stop); __publicField(RadialGradient, "Properties", ["center", "radius", "focal"]); var proto8 = { radius: { enumerable: true, get: function() { return this._radius; }, set: function(v) { this._radius = v; this._flagRadius = true; } }, center: { enumerable: true, get: function() { return this._center; }, set: function(v) { if (this._center) { this._center.unbind(Events.Types.change, this._renderer.flagCenter); } this._center = v; this._center.bind(Events.Types.change, this._renderer.flagCenter); this._flagCenter = true; } }, focal: { enumerable: true, get: function() { return this._focal; }, set: function(v) { if (this._focal) { this._focal.unbind(Events.Types.change, this._renderer.flagFocal); } this._focal = v; this._focal.bind(Events.Types.change, this._renderer.flagFocal); this._flagFocal = true; } } }; function FlagCenter() { this._flagCenter = true; } function FlagFocal() { this._flagFocal = true; } // src/utils/shape.js function contains(path, t) { if (t === 0 || t === 1) { return true; } const length = path._length; const target = length * t; let elapsed = 0; for (let i = 0; i < path._lengths.length; i++) { const dist = path._lengths[i]; if (elapsed >= target) { return target - elapsed >= 0; } elapsed += dist; } return false; } function getIdByLength(path, target) { const total = path._length; if (target <= 0) { return 0; } else if (target >= total) { return path._lengths.length - 1; } for (let i = 0, sum = 0; i < path._lengths.length; i++) { if (sum + path._lengths[i] >= target) { target -= sum; return Math.max(i - 1, 0) + target / path._lengths[i]; } sum += path._lengths[i]; } return -1; } function getCurveLength2(a, b, limit) { let x1, x2, x3, x4, y1, y2, y3, y4; const right = b.controls && b.controls.right; const left = a.controls && a.controls.left; x1 = b.x; y1 = b.y; x2 = (right || b).x; y2 = (right || b).y; x3 = (left || a).x; y3 = (left || a).y; x4 = a.x; y4 = a.y; if (right && b._relative) { x2 += b.x; y2 += b.y; } if (left && a._relative) { x3 += a.x; y3 += a.y; } return getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit); } function getSubdivisions(a, b, limit) { let x1, x2, x3, x4, y1, y2, y3, y4; const right = b.controls && b.controls.right; const left = a.controls && a.controls.left; x1 = b.x; y1 = b.y; x2 = (right || b).x; y2 = (right || b).y; x3 = (left || a).x; y3 = (left || a).y; x4 = a.x; y4 = a.y; if (right && b._relative) { x2 += b.x; y2 += b.y; } if (left && a._relative) { x3 += a.x; y3 += a.y; } return subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit); } function getEffectFromObject(obj) { switch (obj.renderer.type) { case "texture": return Texture.fromObject(obj); case "gradient": return Gradient.fromObject(obj); case "linear-gradient": return LinearGradient.fromObject(obj); case "radial-gradient": return RadialGradient.fromObject(obj); } return obj; } // src/matrix.js var cos = Math.cos; var sin = Math.sin; var tan = Math.tan; var array = []; var _Matrix = class extends Events { elements = new NumArray(9); manual = false; constructor(a, b, c, d, e, f) { super(); let elements = a; if (!Array.isArray(elements)) { elements = Array.prototype.slice.call(arguments); } this.identity(); if (elements.length > 0) { this.set(elements); } } static Multiply(A, B, C) { if (B.length <= 3) { const e = A; let x, y, z; const a = B[0] || 0, b = B[1] || 0, c = B[2] || 0; x = e[0] * a + e[1] * b + e[2] * c; y = e[3] * a + e[4] * b + e[5] * c; z = e[6] * a + e[7] * b + e[8] * c; return [x, y, z]; } const A0 = A[0], A1 = A[1], A2 = A[2]; const A3 = A[3], A4 = A[4], A5 = A[5]; const A6 = A[6], A7 = A[7], A8 = A[8]; const B0 = B[0], B1 = B[1], B2 = B[2]; const B3 = B[3], B4 = B[4], B5 = B[5]; const B6 = B[6], B7 = B[7], B8 = B[8]; C = C || new NumArray(9); C[0] = A0 * B0 + A1 * B3 + A2 * B6; C[1] = A0 * B1 + A1 * B4 + A2 * B7; C[2] = A0 * B2 + A1 * B5 + A2 * B8; C[3] = A3 * B0 + A4 * B3 + A5 * B6; C[4] = A3 * B1 + A4 * B4 + A5 * B7; C[5] = A3 * B2 + A4 * B5 + A5 * B8; C[6] = A6 * B0 + A7 * B3 + A8 * B6; C[7] = A6 * B1 + A7 * B4 + A8 * B7; C[8] = A6 * B2 + A7 * B5 + A8 * B8; return C; } static fromObject(obj) { return new _Matrix().copy(obj); } set(a, b, c, d, e, f, g, h, i) { if (typeof b === "undefined") { const elements = a; a = elements[0]; b = elements[1]; c = elements[2]; d = elements[3]; e = elements[4]; f = elements[5]; g = elements[6]; h = elements[7]; i = elements[8]; } this.elements[0] = a; this.elements[1] = b; this.elements[2] = c; this.elements[3] = d; this.elements[4] = e; this.elements[5] = f; this.elements[6] = g; this.elements[7] = h; this.elements[8] = i; return this.trigger(Events.Types.change); } copy(m) { this.elements[0] = m.elements[0]; this.elements[1] = m.elements[1]; this.elements[2] = m.elements[2]; this.elements[3] = m.elements[3]; this.elements[4] = m.elements[4]; this.elements[5] = m.elements[5]; this.elements[6] = m.elements[6]; this.elements[7] = m.elements[7]; this.elements[8] = m.elements[8]; this.manual = m.manual; return this.trigger(Events.Types.change); } identity() { this.elements[0] = _Matrix.Identity[0]; this.elements[1] = _Matrix.Identity[1]; this.elements[2] = _Matrix.Identity[2]; this.elements[3] = _Matrix.Identity[3]; this.elements[4] = _Matrix.Identity[4]; this.elements[5] = _Matrix.Identity[5]; this.elements[6] = _Matrix.Identity[6]; this.elements[7] = _Matrix.Identity[7]; this.elements[8] = _Matrix.Identity[8]; return this.trigger(Events.Types.change); } multiply(a, b, c, d, e, f, g, h, i) { if (typeof b === "undefined") { this.elements[0] *= a; this.elements[1] *= a; this.elements[2] *= a; this.elements[3] *= a; this.elements[4] *= a; this.elements[5] *= a; this.elements[6] *= a; this.elements[7] *= a; this.elements[8] *= a; return this.trigger(Events.Types.change); } if (typeof c === "undefined") { c = 1; } if (typeof d === "undefined") { a = a || 0; b = b || 0; c = c || 0; e = this.elements; const x = e[0] * a + e[1] * b + e[2] * c; const y = e[3] * a + e[4] * b + e[5] * c; const z = e[6] * a + e[7] * b + e[8] * c; return [x, y, z]; } const A = this.elements; const B = [a, b, c, d, e, f, g, h, i]; const A0 = A[0], A1 = A[1], A2 = A[2]; const A3 = A[3], A4 = A[4], A5 = A[5]; const A6 = A[6], A7 = A[7], A8 = A[8]; const B0 = B[0], B1 = B[1], B2 = B[2]; const B3 = B[3], B4 = B[4], B5 = B[5]; const B6 = B[6], B7 = B[7], B8 = B[8]; this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6; this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7; this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8; this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6; this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7; this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8; this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6; this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7; this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8; return this.trigger(Events.Types.change); } inverse(output) { const a = this.elements; output = output || new _Matrix(); const a00 = a[0], a01 = a[1], a02 = a[2]; const a10 = a[3], a11 = a[4], a12 = a[5]; const a20 = a[6], a21 = a[7], a22 = a[8]; const b01 = a22 * a11 - a12 * a21; const b11 = -a22 * a10 + a12 * a20; const b21 = a21 * a10 - a11 * a20; let det = a00 * b01 + a01 * b11 + a02 * b21; if (!det) { return null; } det = 1 / det; output.elements[0] = b01 * det; output.elements[1] = (-a22 * a01 + a02 * a21) * det; output.elements[2] = (a12 * a01 - a02 * a11) * det; output.elements[3] = b11 * det; output.elements[4] = (a22 * a00 - a02 * a20) * det; output.elements[5] = (-a12 * a00 + a02 * a10) * det; output.elements[6] = b21 * det; output.elements[7] = (-a21 * a00 + a01 * a20) * det; output.elements[8] = (a11 * a00 - a01 * a10) * det; return output; } scale(sx, sy) { const l = arguments.length; if (l <= 1) { sy = sx; } return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1); } rotate(n) { const c = cos(n); const s = sin(n); return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1); } translate(x, y) { return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1); } skewX(n) { const a = tan(n); return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1); } skewY(n) { const a = tan(n); return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1); } toString(fullMatrix) { array.length = 0; this.toTransformArray(fullMatrix, array); return array.map(toFixed).join(" "); } toTransformArray(fullMatrix, output) { const elements = this.elements; const hasOutput = !!output; const a = elements[0]; const b = elements[1]; const c = elements[2]; const d = elements[3]; const e = elements[4]; const f = elements[5]; if (fullMatrix) { const g = elements[6]; const h = elements[7]; const i = elements[8]; if (hasOutput) { output[0] = a; output[1] = d; output[2] = g; output[3] = b; output[4] = e; output[5] = h; output[6] = c; output[7] = f; output[8] = i; return; } return [a, d, g, b, e, h, c, f, i]; } if (hasOutput) { output[0] = a; output[1] = d; output[2] = b; output[3] = e; output[4] = c; output[5] = f; return; } return [ a, d, b, e, c, f ]; } toArray(fullMatrix, output) { const elements = this.elements; const hasOutput = !!output; const a = elements[0]; const b = elements[1]; const c = elements[2]; const d = elements[3]; const e = elements[4]; const f = elements[5]; if (fullMatrix) { const g = elements[6]; const h = elements[7]; const i = elements[8]; if (hasOutput) { output[0] = a; output[1] = b; output[2] = c; output[3] = d; output[4] = e; output[5] = f; output[6] = g; output[7] = h; output[8] = i; return; } return [a, b, c, d, e, f, g, h, i]; } if (hasOutput) { output[0] = a; output[1] = b; output[2] = c; output[3] = d; output[4] = e; output[5] = f; return; } return [a, b, c, d, e, f]; } toObject() { return { renderer: { type: "matrix" }, elements: this.toArray(true), manual: !!this.manual }; } clone() { return new _Matrix().copy(this); } }; var Matrix2 = _Matrix; __publicField(Matrix2, "Identity", [1, 0, 0, 0, 1, 0, 0, 0, 1]); setMatrix(Matrix2); // src/shape.js var _Shape = class extends Element { _flagMatrix = true; _flagScale = false; _matrix = null; _worldMatrix = null; _position = null; _rotation = 0; _scale = 1; _skewX = 0; _skewY = 0; constructor() { super(); for (let prop in proto9) { Object.defineProperty(this, prop, proto9[prop]); } this._renderer.flagMatrix = FlagMatrix.bind(this); this.isShape = true; this.matrix = new Matrix2(); this.worldMatrix = new Matrix2(); this.position = new Vector(); this.rotation = 0; this.scale = 1; this.skewX = 0; this.skewY = 0; } static fromObject(obj) { const shape = new _Shape().copy(obj); if ("id" in obj) { shape.id = obj.id; } return shape; } get renderer() { return this._renderer; } set renderer(v) { this._renderer = v; } get translation() { return proto9.position.get.apply(this, arguments); } set translation(v) { proto9.position.set.apply(this, arguments); } addTo(group) { group.add(this); return this; } remove() { if (!this.parent) { return this; } this.parent.remove(this); return this; } copy(shape) { super.copy.call(this, shape); if ("position" in shape) { if (shape.position instanceof Vector) { this.position = shape.position; } else { this.position.copy(shape.position); } } if ("rotation" in shape) { this.rotation = shape.rotation; } if ("scale" in shape) { this.scale = typeof shape.scale === "number" || shape.scale instanceof Vector ? shape.scale : new Vector(shape.scale.x, shape.scale.y); } if ("skewX" in shape) { this.skewX = shape.skewX; } if ("skewY" in shape) { this.skewY = shape.skewY; } if ("matrix" in shape && shape.matrix.manual) { this.matrix.copy(shape.matrix); this.matrix.manual = true; } return this; } clone(parent) { const clone = new _Shape(); clone.position.copy(this.position); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } if (parent) { parent.add(clone); } return clone._update(); } toObject() { const result = super.toObject.call(this); result.renderer = { type: "shape" }; result.isShape = true; result.translation = this.translation.toObject(); result.rotation = this.translation.rotation; result.scale = this.scale instanceof Vector ? this.scale.toObject() : this.scale; result.skewX = this.skewX; result.skewY = this.skewY; result.matrix = this.matrix.toObject(); return result; } _update(bubbles) { if (!this._matrix.manual && this._flagMatrix) { this._matrix.identity().translate(this.position.x, this.position.y); this._matrix.rotate(this.rotation); if (this._scale instanceof Vector) { this._matrix.scale(this._scale.x, this._scale.y); } else { this._matrix.scale(this._scale); } this._matrix.skewX(this.skewX); this._matrix.skewY(this.skewY); } if (bubbles) { if (this.parent && this.parent._update) { this.parent._update(); } } return this; } flagReset() { this._flagMatrix = this._flagScale = false; super.flagReset.call(this); return this; } }; var Shape = _Shape; __publicField(Shape, "Properties", [ "position", "rotation", "scale", "skewX", "skewY", "matrix", "worldMatrix" ]); var proto9 = { position: { enumerable: true, get: function() { return this._position; }, set: function(v) { if (this._position) { this._position.unbind(Events.Types.change, this._renderer.flagMatrix); } this._position = v; this._position.bind(Events.Types.change, this._renderer.flagMatrix); FlagMatrix.call(this); } }, rotation: { enumerable: true, get: function() { return this._rotation; }, set: function(v) { this._rotation = v; this._flagMatrix = true; } }, scale: { enumerable: true, get: function() { return this._scale; }, set: function(v) { if (this._scale instanceof Vector) { this._scale.unbind(Events.Types.change, this._renderer.flagMatrix); } this._scale = v; if (this._scale instanceof Vector) { this._scale.bind(Events.Types.change, this._renderer.flagMatrix); } this._flagMatrix = true; this._flagScale = true; } }, skewX: { enumerable: true, get: function() { return this._skewX; }, set: function(v) { this._skewX = v; this._flagMatrix = true; } }, skewY: { enumerable: true, get: function() { return this._skewY; }, set: function(v) { this._skewY = v; this._flagMatrix = true; } }, matrix: { enumerable: true, get: function() { return this._matrix; }, set: function(v) { this._matrix = v; this._flagMatrix = true; } }, worldMatrix: { enumerable: true, get: function() { getComputedMatrix(this, this._worldMatrix); return this._worldMatrix; }, set: function(v) { this._worldMatrix = v; } } }; function FlagMatrix() { this._flagMatrix = true; } // src/path.js var min = Math.min; var max = Math.max; var ceil = Math.ceil; var floor2 = Math.floor; var vector = new Vector(); var _Path = class extends Shape { _flagVertices = true; _flagLength = true; _flagFill = true; _flagStroke = true; _flagLinewidth = true; _flagOpacity = true; _flagVisible = true; _flagCap = true; _flagJoin = true; _flagMiter = true; _flagMask = false; _flagClip = false; _length = 0; _fill = "#fff"; _stroke = "#000"; _linewidth = 1; _opacity = 1; _visible = true; _cap = "round"; _join = "round"; _miter = 4; _closed = true; _curved = false; _automatic = true; _beginning = 0; _ending = 1; _mask = null; _clip = false; _dashes = null; constructor(vertices, closed2, curved, manual) { super(); for (let prop in proto10) { Object.defineProperty(this, prop, proto10[prop]); } this._renderer.type = "path"; this._renderer.flagVertices = FlagVertices.bind(this); this._renderer.bindVertices = BindVertices.bind(this); this._renderer.unbindVertices = UnbindVertices.bind(this); this._renderer.flagFill = FlagFill.bind(this); this._renderer.flagStroke = FlagStroke.bind(this); this._renderer.vertices = []; this._renderer.collection = []; this.closed = !!closed2; this.curved = !!curved; this.beginning = 0; this.ending = 1; this.fill = "#fff"; this.stroke = "#000"; this.linewidth = 1; this.opacity = 1; this.className = ""; this.visible = true; this.cap = "butt"; this.join = "miter"; this.miter = 4; this.vertices = vertices; this.automatic = !manual; this.dashes = []; this.dashes.offset = 0; } static fromObject(obj) { const fill = typeof obj.fill === "string" ? obj.fill : getEffectFromObject(obj.fill); const stroke = typeof obj.stroke === "string" ? obj.stroke : getEffectFromObject(obj.stroke); const path = new _Path().copy({ ...obj, fill, stroke }); if ("id" in obj) { path.id = obj.id; } return path; } copy(path) { super.copy.call(this, path); this.vertices = []; for (let j = 0; j < path.vertices.length; j++) { const v = path.vertices[j]; if (v instanceof Anchor) { this.vertices.push(path.vertices[j].clone()); } else { this.vertices.push(new Anchor().copy(v)); } } for (let i = 0; i < _Path.Properties.length; i++) { const k = _Path.Properties[i]; if (k in path) { this[k] = path[k]; } } return this; } clone(parent) { const clone = new _Path(); for (let j = 0; j < this.vertices.length; j++) { clone.vertices.push(this.vertices[j].clone()); } for (let i = 0; i < _Path.Properties.length; i++) { const k = _Path.Properties[i]; clone[k] = this[k]; } clone.className = this.className; clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } if (parent) { parent.add(clone); } return clone._update(); } toObject() { const result = super.toObject.call(this); result.renderer.type = "path"; result.vertices = this.vertices.map((v) => v.toObject()); _.each( _Path.Properties, (k) => { if (typeof this[k] !== "undefined") { if (this[k].toObject) { result[k] = this[k].toObject(); } else { result[k] = this[k]; } } }, this ); return result; } noFill() { this.fill = "none"; return this; } noStroke() { this.stroke = "none"; this.linewidth = 0; return this; } corner() { const rect = this.getBoundingClientRect(true); const hw = rect.width / 2; const hh = rect.height / 2; const cx = rect.left + rect.width / 2; const cy = rect.top + rect.height / 2; for (let i = 0; i < this.vertices.length; i++) { const v = this.vertices[i]; v.x -= cx; v.y -= cy; v.x += hw; v.y += hh; } if (this.mask) { this.mask.translation.x -= cx; this.mask.translation.x += hw; this.mask.translation.y -= cy; this.mask.translation.y += hh; } return this; } center() { const rect = this.getBoundingClientRect(true); const cx = rect.left + rect.width / 2 - this.translation.x; const cy = rect.top + rect.height / 2 - this.translation.y; for (let i = 0; i < this.vertices.length; i++) { const v = this.vertices[i]; v.x -= cx; v.y -= cy; } if (this.mask) { this.mask.translation.x -= cx; this.mask.translation.y -= cy; } return this; } getBoundingClientRect(shallow) { let matrix, border, l, i, v0, v1; let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity; this._update(true); matrix = shallow ? this.matrix : this.worldMatrix; border = (this.linewidth || 0) / 2; l = this._renderer.vertices.length; if (this.linewidth > 0 || this.stroke && !/(transparent|none)/i.test(this.stroke)) { if (this.matrix.manual) { const { scaleX, scaleY } = decomposeMatrix( matrix.elements[0], matrix.elements[3], matrix.elements[1], matrix.elements[4], matrix.elements[2], matrix.elements[5] ); if (typeof scaleX === "number" && typeof scaleY === "number") { border = Math.max(scaleX, scaleY) * (this.linewidth || 0) / 2; } } else { border *= typeof this.scale === "number" ? this.scale : Math.max(this.scale.x, this.scale.y); } } if (l <= 0) { return { width: 0, height: 0 }; } for (i = 0; i < l; i++) { v1 = this._renderer.vertices[i]; v0 = this._renderer.vertices[(i + l - 1) % l]; const [v0x, v0y] = matrix.multiply(v0.x, v0.y); const [v1x, v1y] = matrix.multiply(v1.x, v1.y); if (v0.controls && v1.controls) { let rx = v0.controls.right.x; let ry = v0.controls.right.y; if (v0.relative) { rx += v0.x; ry += v0.y; } let [c0x, c0y] = matrix.multiply(rx, ry); let lx = v1.controls.left.x; let ly = v1.controls.left.y; if (v1.relative) { lx += v1.x; ly += v1.y; } let [c1x, c1y] = matrix.multiply(lx, ly); const bb = getCurveBoundingBox(v0x, v0y, c0x, c0y, c1x, c1y, v1x, v1y); top = min(bb.min.y - border, top); left = min(bb.min.x - border, left); right = max(bb.max.x + border, right); bottom = max(bb.max.y + border, bottom); } else { if (i <= 1) { top = min(v0y - border, top); left = min(v0x - border, left); right = max(v0x + border, right); bottom = max(v0y + border, bottom); } top = min(v1y - border, top); left = min(v1x - border, left); right = max(v1x + border, right); bottom = max(v1y + border, bottom); } } return { top, left, right, bottom, width: right - left, height: bottom - top }; } getPointAt(t, obj) { let ia, ib, result; let x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right; let target = this.length * Math.min(Math.max(t, 0), 1); const length = this.vertices.length; const last = length - 1; let a = null; let b = null; for (let i = 0, l = this._lengths.length, sum = 0; i < l; i++) { if (sum + this._lengths[i] >= target) { if (this._closed) { ia = mod(i, length); ib = mod(i - 1, length); if (i === 0) { ia = ib; ib = i; } } else { ia = i; ib = Math.min(Math.max(i - 1, 0), last); } a = this.vertices[ia]; b = this.vertices[ib]; target -= sum; if (this._lengths[i] !== 0) { t = target / this._lengths[i]; } else { t = 0; } break; } sum += this._lengths[i]; } if (a === null || b === null) { return null; } if (!a) { return b; } else if (!b) { return a; } right = b.controls && b.controls.right; left = a.controls && a.controls.left; x1 = b.x; y1 = b.y; x2 = (right || b).x; y2 = (right || b).y; x3 = (left || a).x; y3 = (left || a).y; x4 = a.x; y4 = a.y; if (right && b.relative) { x2 += b.x; y2 += b.y; } if (left && a.relative) { x3 += a.x; y3 += a.y; } x = getComponentOnCubicBezier(t, x1, x2, x3, x4); y = getComponentOnCubicBezier(t, y1, y2, y3, y4); const t1x = lerp(x1, x2, t); const t1y = lerp(y1, y2, t); const t2x = lerp(x2, x3, t); const t2y = lerp(y2, y3, t); const t3x = lerp(x3, x4, t); const t3y = lerp(y3, y4, t); const brx = lerp(t1x, t2x, t); const bry = lerp(t1y, t2y, t); const alx = lerp(t2x, t3x, t); const aly = lerp(t2y, t3y, t); if (_.isObject(obj)) { obj.x = x; obj.y = y; if (obj instanceof Anchor) { obj.controls.left.x = brx; obj.controls.left.y = bry; obj.controls.right.x = alx; obj.controls.right.y = aly; if (!(typeof obj.relative === "boolean") || obj.relative) { obj.controls.left.x -= x; obj.controls.left.y -= y; obj.controls.right.x -= x; obj.controls.right.y -= y; } } obj.t = t; return obj; } result = new Anchor( x, y, brx - x, bry - y, alx - x, aly - y, this._curved ? Commands.curve : Commands.line ); result.t = t; return result; } plot() { if (this.curved) { getCurveFromPoints(this._collection, this.closed); return this; } for (let i = 0; i < this._collection.length; i++) { this._collection[i].command = i === 0 ? Commands.move : Commands.line; } return this; } subdivide(limit) { this._update(); const last = this.vertices.length - 1; const closed2 = this._closed || this.vertices[last]._command === Commands.close; let b = this.vertices[last]; let points = [], verts; _.each( this.vertices, function(a, i) { if (i <= 0 && !closed2) { b = a; return; } if (a.command === Commands.move) { points.push(new Anchor(b.x, b.y)); if (i > 0) { points[points.length - 1].command = Commands.line; } b = a; return; } verts = getSubdivisions(a, b, limit); points = points.concat(verts); _.each(verts, function(v, i2) { if (i2 <= 0 && b.command === Commands.move) { v.command = Commands.move; } else { v.command = Commands.line; } }); if (i >= last) { if (this._closed && this._automatic) { b = a; verts = getSubdivisions(a, b, limit); points = points.concat(verts); _.each(verts, function(v, i2) { if (i2 <= 0 && b.command === Commands.move) { v.command = Commands.move; } else { v.command = Commands.line; } }); } points.push(new Anchor(a.x, a.y)); points[points.length - 1].command = closed2 ? Commands.close : Commands.line; } b = a; }, this ); this._automatic = false; this._curved = false; this.vertices = points; return this; } _updateLength(limit, silent) { if (!silent) { this._update(); } const length = this.vertices.length; const last = length - 1; const closed2 = false; let b = this.vertices[last]; let sum = 0; if (typeof this._lengths === "undefined") { this._lengths = []; } _.each( this.vertices, function(a, i) { if (i <= 0 && !closed2 || a.command === Commands.move) { b = a; this._lengths[i] = 0; return; } this._lengths[i] = getCurveLength2(a, b, limit); sum += this._lengths[i]; if (i >= last && closed2) { b = this.vertices[(i + 1) % length]; this._lengths[i + 1] = getCurveLength2(a, b, limit); sum += this._lengths[i + 1]; } b = a; }, this ); this._length = sum; this._flagLength = false; return this; } _update() { if (this._flagVertices) { if (this._automatic) { this.plot(); } if (this._flagLength) { this._updateLength(void 0, true); } const l = this._collection.length; const closed2 = this._closed; const beginning = Math.min(this._beginning, this._ending); const ending = Math.max(this._beginning, this._ending); const bid = getIdByLength(this, beginning * this._length); const eid = getIdByLength(this, ending * this._length); const low = ceil(bid); const high = floor2(eid); let left, right, prev, next, v, i; this._renderer.vertices.length = 0; for (i = 0; i < l; i++) { if (this._renderer.collection.length <= i) { this._renderer.collection.push(new Anchor()); } if (i > high && !right) { v = this._renderer.collection[i].copy(this._collection[i]); this.getPointAt(ending, v); v.command = this._renderer.collection[i].command; this._renderer.vertices.push(v); right = v; prev = this._collection[i - 1]; if (prev && prev.controls) { if (v.relative) { v.controls.right.clear(); } else { v.controls.right.copy(v); } if (prev.relative) { this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(Vector.zero, 1 - v.t); } else { this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(prev, 1 - v.t); } } } else if (i >= low && i <= high) { v = this._renderer.collection[i].copy(this._collection[i]); this._renderer.vertices.push(v); if (i === high && contains(this, ending)) { right = v; if (!closed2 && right.controls) { if (right.relative) { right.controls.right.clear(); } else { right.controls.right.copy(right); } } } else if (i === low && contains(this, beginning)) { left = v; left.command = Commands.move; if (!closed2 && left.controls) { if (left.relative) { left.controls.left.clear(); } else { left.controls.left.copy(left); } } } } } if (low > 0 && !left) { i = low - 1; v = this._renderer.collection[i].copy(this._collection[i]); this.getPointAt(beginning, v); v.command = Commands.move; this._renderer.vertices.unshift(v); next = this._collection[i + 1]; if (next && next.controls) { v.controls.left.clear(); if (next.relative) { this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(Vector.zero, v.t); } else { vector.copy(next); this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(next, v.t); } } } } Shape.prototype._update.apply(this, arguments); return this; } flagReset() { this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagCap = this._flagJoin = this._flagMiter = this._flagClip = false; Shape.prototype.flagReset.call(this); return this; } }; var Path = _Path; __publicField(Path, "Properties", [ "fill", "stroke", "linewidth", "opacity", "visible", "cap", "join", "miter", "closed", "curved", "automatic", "beginning", "ending", "dashes" ]); __publicField(Path, "Utils", { getCurveLength: getCurveLength2 }); var proto10 = { linewidth: { enumerable: true, get: function() { return this._linewidth; }, set: function(v) { this._linewidth = v; this._flagLinewidth = true; } }, opacity: { enumerable: true, get: function() { return this._opacity; }, set: function(v) { this._opacity = v; this._flagOpacity = true; } }, visible: { enumerable: true, get: function() { return this._visible; }, set: function(v) { this._visible = v; this._flagVisible = true; } }, cap: { enumerable: true, get: function() { return this._cap; }, set: function(v) { this._cap = v; this._flagCap = true; } }, join: { enumerable: true, get: function() { return this._join; }, set: function(v) { this._join = v; this._flagJoin = true; } }, miter: { enumerable: true, get: function() { return this._miter; }, set: function(v) { this._miter = v; this._flagMiter = true; } }, fill: { enumerable: true, get: function() { return this._fill; }, set: function(f) { if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) { this._fill.unbind(Events.Types.change, this._renderer.flagFill); } this._fill = f; this._flagFill = true; if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) { this._fill.bind(Events.Types.change, this._renderer.flagFill); } } }, stroke: { enumerable: true, get: function() { return this._stroke; }, set: function(f) { if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) { this._stroke.unbind(Events.Types.change, this._renderer.flagStroke); } this._stroke = f; this._flagStroke = true; if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) { this._stroke.bind(Events.Types.change, this._renderer.flagStroke); } } }, length: { get: function() { if (this._flagLength) { this._updateLength(); } return this._length; } }, closed: { enumerable: true, get: function() { return this._closed; }, set: function(v) { this._closed = !!v; this._flagVertices = true; } }, curved: { enumerable: true, get: function() { return this._curved; }, set: function(v) { this._curved = !!v; this._flagVertices = true; } }, automatic: { enumerable: true, get: function() { return this._automatic; }, set: function(v) { if (v === this._automatic) { return; } this._automatic = !!v; const method = this._automatic ? "ignore" : "listen"; _.each(this.vertices, function(v2) { v2[method](); }); } }, beginning: { enumerable: true, get: function() { return this._beginning; }, set: function(v) { this._beginning = v; this._flagVertices = true; } }, ending: { enumerable: true, get: function() { return this._ending; }, set: function(v) { this._ending = v; this._flagVertices = true; } }, vertices: { enumerable: true, get: function() { return this._collection; }, set: function(vertices) { const bindVertices = this._renderer.bindVertices; const unbindVertices = this._renderer.unbindVertices; if (this._collection) { this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices); } if (vertices instanceof Collection) { this._collection = vertices; } else { this._collection = new Collection(vertices || []); } this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices); bindVertices(this._collection); } }, mask: { enumerable: true, get: function() { return this._mask; }, set: function(v) { this._mask = v; this._flagMask = true; if (_.isObject(v) && !v.clip) { v.clip = true; } } }, clip: { enumerable: true, get: function() { return this._clip; }, set: function(v) { this._clip = v; this._flagClip = true; } }, dashes: { enumerable: true, get: function() { return this._dashes; }, set: function(v) { if (typeof v.offset !== "number") { v.offset = this.dashes && this._dashes.offset || 0; } this._dashes = v; } } }; function FlagVertices() { this._flagVertices = true; this._flagLength = true; if (this.parent) { this.parent._flagLength = true; } } function BindVertices(items) { let i = items.length; while (i--) { items[i].bind(Events.Types.change, this._renderer.flagVertices); } this._renderer.flagVertices(); } function UnbindVertices(items) { let i = items.length; while (i--) { items[i].unbind(Events.Types.change, this._renderer.flagVertices); } this._renderer.flagVertices(); } function FlagFill() { this._flagFill = true; } function FlagStroke() { this._flagStroke = true; } // src/shapes/rectangle.js var _Rectangle = class extends Path { constructor(x, y, width, height) { const points = [ new Anchor(), new Anchor(), new Anchor(), new Anchor() ]; super(points, true, false, true); this._renderer.type = "rectangle"; for (let prop in proto11) { Object.defineProperty(this, prop, proto11[prop]); } this.width = typeof width === "number" ? width : 1; this.height = typeof height === "number" ? height : 1; this.origin = new Vector(); if (typeof x === "number") { this.translation.x = x; } if (typeof y === "number") { this.translation.y = y; } this._update(); } static fromObject(obj) { const rectangle = new _Rectangle().copy(obj); if ("id" in obj) { rectangle.id = obj.id; } return rectangle; } copy(rectangle) { super.copy.call(this, rectangle); for (let i = 0; i < _Rectangle.Properties.length; i++) { const k = _Rectangle.Properties[i]; if (k in rectangle && typeof rectangle[k] === "number") { this[k] = rectangle[k]; } } return this; } _flagWidth = 0; _flagHeight = 0; _width = 0; _height = 0; _origin = null; _update() { if (this._flagVertices || this._flagWidth || this._flagHeight) { const xr = this._width / 2; const yr = this._height / 2; if (!this._closed && this.vertices.length === 4) { this.vertices.push(new Anchor()); } this.vertices[0].set(-xr, -yr).sub(this._origin).command = Commands.move; this.vertices[1].set(xr, -yr).sub(this._origin).command = Commands.line; this.vertices[2].set(xr, yr).sub(this._origin).command = Commands.line; this.vertices[3].set(-xr, yr).sub(this._origin).command = Commands.line; if (this.vertices[4]) { this.vertices[4].set(-xr, -yr).sub(this._origin).command = Commands.line; } } super._update.call(this); return this; } flagReset() { this._flagWidth = this._flagHeight = false; super.flagReset.call(this); return this; } clone(parent) { const clone = new _Rectangle(0, 0, this.width, this.height); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } for (let i = 0; i < Path.Properties.length; i++) { const k = Path.Properties[i]; clone[k] = this[k]; } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.renderer.type = "rectangle"; object.width = this.width; object.height = this.height; object.origin = this.origin.toObject(); return object; } }; var Rectangle = _Rectangle; __publicField(Rectangle, "Properties", ["width", "height"]); var proto11 = { width: { enumerable: true, get: function() { return this._width; }, set: function(v) { this._width = v; this._flagWidth = true; } }, height: { enumerable: true, get: function() { return this._height; }, set: function(v) { this._height = v; this._flagHeight = true; } }, origin: { enumerable: true, get: function() { return this._origin; }, set: function(v) { if (this._origin) { this._origin.unbind(Events.Types.change, this._renderer.flagVertices); } this._origin = v; this._origin.bind(Events.Types.change, this._renderer.flagVertices); this._renderer.flagVertices(); } } }; // src/effects/sprite.js var _Sprite = class extends Rectangle { _flagTexture = false; _flagColumns = false; _flagRows = false; _flagFrameRate = false; _flagIndex = false; _amount = 1; _duration = 0; _startTime = 0; _playing = false; _firstFrame = 0; _lastFrame = 0; _loop = true; _texture = null; _columns = 1; _rows = 1; _frameRate = 0; _index = 0; _origin = null; constructor(path, ox, oy, cols, rows, frameRate) { super(ox, oy, 0, 0); for (let prop in proto12) { Object.defineProperty(this, prop, proto12[prop]); } this.noStroke(); this.noFill(); if (path instanceof Texture) { this.texture = path; } else if (typeof path === "string") { this.texture = new Texture(path); } this.origin = new Vector(); this._update(); if (typeof cols === "number") { this.columns = cols; } if (typeof rows === "number") { this.rows = rows; } if (typeof frameRate === "number") { this.frameRate = frameRate; } this.index = 0; } static fromObject(obj) { const sprite = new _Sprite().copy(obj); if ("id" in obj) { sprite.id = obj.id; } return sprite; } copy(sprite) { super.copy.call(this, sprite); for (let i = 0; i < _Sprite.Properties.length; i++) { const k = _Sprite.Properties[i]; if (k in sprite) { this[k] = sprite[k]; } } return this; } play(firstFrame, lastFrame, onLastFrame) { this._playing = true; this._firstFrame = 0; this._lastFrame = this.amount - 1; this._startTime = _.performance.now(); if (typeof firstFrame === "number") { this._firstFrame = firstFrame; } if (typeof lastFrame === "number") { this._lastFrame = lastFrame; } if (typeof onLastFrame === "function") { this._onLastFrame = onLastFrame; } else { delete this._onLastFrame; } if (this._index !== this._firstFrame) { this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate; } return this; } pause() { this._playing = false; return this; } stop() { this._playing = false; this._index = 0; return this; } clone(parent) { const clone = new _Sprite( this.texture, this.translation.x, this.translation.y, this.columns, this.rows, this.frameRate ); if (this.playing) { clone.play(this._firstFrame, this._lastFrame); } clone.loop = this.loop; clone.firstFrame = this.firstFrame; clone.lastFrame = this.lastFrame; if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.texture = this.texture.toObject(); object.columns = this.columns; object.rows = this.rows; object.frameRate = this.frameRate; object.index = this.index; object.firstFrame = this.firstFrame; object.lastFrame = this.lastFrame; object.loop = this.loop; return object; } _update() { const effect = this._texture; const cols = this._columns; const rows = this._rows; let width, height, elapsed, amount, duration; let index, iw, ih, frames; if (effect) { if (this._flagColumns || this._flagRows) { this._amount = this._columns * this._rows; } if (this._flagFrameRate) { this._duration = 1e3 * this._amount / this._frameRate; } if (this._flagTexture) { this.fill = effect; } if (effect.loaded) { iw = effect.image.width; ih = effect.image.height; width = iw / cols; height = ih / rows; amount = this._amount; if (this.width !== width) { this.width = width; } if (this.height !== height) { this.height = height; } if (this._playing && this._frameRate > 0) { if (_.isNaN(this._lastFrame)) { this._lastFrame = amount - 1; } elapsed = _.performance.now() - this._startTime; frames = this._lastFrame + 1; duration = 1e3 * (frames - this._firstFrame) / this._frameRate; if (this._loop) { elapsed = elapsed % duration; } else { elapsed = Math.min(elapsed, duration); } index = lerp(this._firstFrame, frames, elapsed / duration); index = Math.floor(index); if (index !== this._index) { this._index = index; if (index >= this._lastFrame - 1 && this._onLastFrame) { this._onLastFrame(); } } } const col = this._index % cols; const row = Math.floor(this._index / cols); const ox = -width * col + (iw - width) / 2; const oy = -height * row + (ih - height) / 2; if (ox !== effect.offset.x) { effect.offset.x = ox; } if (oy !== effect.offset.y) { effect.offset.y = oy; } } } super._update.call(this); return this; } flagReset() { this._flagTexture = this._flagColumns = this._flagRows = this._flagFrameRate = false; super.flagReset.call(this); return this; } }; var Sprite = _Sprite; __publicField(Sprite, "Properties", [ "texture", "columns", "rows", "frameRate", "index", "firstFrame", "lastFrame", "loop" ]); var proto12 = { texture: { enumerable: true, get: function() { return this._texture; }, set: function(v) { this._texture = v; this._flagTexture = true; } }, columns: { enumerable: true, get: function() { return this._columns; }, set: function(v) { this._columns = v; this._flagColumns = true; } }, rows: { enumerable: true, get: function() { return this._rows; }, set: function(v) { this._rows = v; this._flagRows = true; } }, frameRate: { enumerable: true, get: function() { return this._frameRate; }, set: function(v) { this._frameRate = v; this._flagFrameRate = true; } }, index: { enumerable: true, get: function() { return this._index; }, set: function(v) { this._index = v; this._flagIndex = true; } }, firstFrame: { enumerable: true, get: function() { return this._firstFrame; }, set: function(v) { this._firstFrame = v; } }, lastFrame: { enumerable: true, get: function() { return this._lastFrame; }, set: function(v) { this._lastFrame = v; } }, loop: { enumerable: true, get: function() { return this._loop; }, set: function(v) { this._loop = !!v; } } }; // src/children.js var Children = class extends Collection { #ids = {}; get ids() { return this.#ids; } constructor(children) { children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments); super(children); this.attach(children); this.on(Events.Types.insert, this.attach); this.on(Events.Types.remove, this.detach); } attach(children) { for (let i = 0; i < children.length; i++) { const child = children[i]; if (child && child.id) { this.ids[child.id] = child; } } return this; } detach(children) { for (let i = 0; i < children.length; i++) { delete this.ids[children[i].id]; } return this; } }; // src/shapes/arc-segment.js var _ArcSegment = class extends Path { _flagStartAngle = false; _flagEndAngle = false; _flagInnerRadius = false; _flagOuterRadius = false; _startAngle = 0; _endAngle = TWO_PI; _innerRadius = 0; _outerRadius = 0; constructor(x, y, ir, or, sa, ea, res) { const amount = res || Constants.Resolution * 3; const points = []; for (let i = 0; i < amount; i++) { points.push(new Anchor()); } super(points, true, false, true); this._renderer.type = "arc-segment"; for (let prop in proto13) { Object.defineProperty(this, prop, proto13[prop]); } if (typeof ir === "number") { this.innerRadius = ir; } if (typeof or === "number") { this.outerRadius = or; } if (typeof sa === "number") { this.startAngle = sa; } if (typeof ea === "number") { this.endAngle = ea; } this._update(); if (typeof x === "number") { this.translation.x = x; } if (typeof y === "number") { this.translation.y = y; } } static fromObject(obj) { const segment = new _ArcSegment().copy(obj); if ("id" in obj) { segment.id = obj.id; } return segment; } copy(arcSegment) { super.copy.call(this, arcSegment); for (let i = 0; i < _ArcSegment.Properties.length; i++) { const k = _ArcSegment.Properties[i]; if (k in arcSegment && typeof arcSegment[k] === "number") { this[k] = arcSegment[k]; } } return this; } _update() { if (this._flagVertices || this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius || this._flagOuterRadius) { const sa = this._startAngle; const ea = this._endAngle; const ir = this._innerRadius; const or = this._outerRadius; const connected = mod(sa, TWO_PI) === mod(ea, TWO_PI); const punctured = ir > 0; const vertices = this.vertices; let length = punctured ? vertices.length / 2 : vertices.length; let command, id = 0; let i, last, pct, v, theta, step, x, y, amp; if (connected) { length--; } else if (!punctured) { length -= 2; } for (i = 0, last = length - 1; i < length; i++) { pct = i / last; v = vertices[id]; theta = pct * (ea - sa) + sa; step = (ea - sa) / length; x = or * Math.cos(theta); y = or * Math.sin(theta); switch (i) { case 0: command = Commands.move; break; default: command = Commands.curve; } v.command = command; v.x = x; v.y = y; v.controls.left.clear(); v.controls.right.clear(); if (v.command === Commands.curve) { amp = or * step / Math.PI; v.controls.left.x = amp * Math.cos(theta - HALF_PI); v.controls.left.y = amp * Math.sin(theta - HALF_PI); v.controls.right.x = amp * Math.cos(theta + HALF_PI); v.controls.right.y = amp * Math.sin(theta + HALF_PI); if (i === 1) { v.controls.left.multiplyScalar(2); } if (i === last) { v.controls.right.multiplyScalar(2); } } id++; } if (punctured) { if (connected) { vertices[id].command = Commands.close; id++; } else { length--; last = length - 1; } for (i = 0; i < length; i++) { pct = i / last; v = vertices[id]; theta = (1 - pct) * (ea - sa) + sa; step = (ea - sa) / length; x = ir * Math.cos(theta); y = ir * Math.sin(theta); command = Commands.curve; if (i <= 0) { command = connected ? Commands.move : Commands.line; } v.command = command; v.x = x; v.y = y; v.controls.left.clear(); v.controls.right.clear(); if (v.command === Commands.curve) { amp = ir * step / Math.PI; v.controls.left.x = amp * Math.cos(theta + HALF_PI); v.controls.left.y = amp * Math.sin(theta + HALF_PI); v.controls.right.x = amp * Math.cos(theta - HALF_PI); v.controls.right.y = amp * Math.sin(theta - HALF_PI); if (i === 1) { v.controls.left.multiplyScalar(2); } if (i === last) { v.controls.right.multiplyScalar(2); } } id++; } vertices[id].copy(vertices[0]); vertices[id].command = Commands.line; } else if (!connected) { vertices[id].command = Commands.line; vertices[id].x = 0; vertices[id].y = 0; id++; vertices[id].copy(vertices[0]); vertices[id].command = Commands.line; } } super._update.call(this); return this; } flagReset() { super.flagReset.call(this); this._flagStartAngle = this._flagEndAngle = this._flagInnerRadius = this._flagOuterRadius = false; return this; } clone(parent) { const ir = this.innerRadius; const or = this.outerRadius; const sa = this.startAngle; const ea = this.endAngle; const resolution = this.vertices.length; const clone = new _ArcSegment(0, 0, ir, or, sa, ea, resolution); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } for (let i = 0; i < Path.Properties.length; i++) { const k = Path.Properties[i]; clone[k] = this[k]; } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.renderer.type = "arc-segment"; for (let i = 0; i < _ArcSegment.Properties.length; i++) { const k = _ArcSegment.Properties[i]; object[k] = this[k]; } return object; } }; var ArcSegment = _ArcSegment; __publicField(ArcSegment, "Properties", ["startAngle", "endAngle", "innerRadius", "outerRadius"]); var proto13 = { startAngle: { enumerable: true, get: function() { return this._startAngle; }, set: function(v) { this._startAngle = v; this._flagStartAngle = true; } }, endAngle: { enumerable: true, get: function() { return this._endAngle; }, set: function(v) { this._endAngle = v; this._flagEndAngle = true; } }, innerRadius: { enumerable: true, get: function() { return this._innerRadius; }, set: function(v) { this._innerRadius = v; this._flagInnerRadius = true; } }, outerRadius: { enumerable: true, get: function() { return this._outerRadius; }, set: function(v) { this._outerRadius = v; this._flagOuterRadius = true; } } }; // src/shapes/circle.js var cos2 = Math.cos; var sin2 = Math.sin; var _Circle = class extends Path { _flagRadius = false; _radius = 0; constructor(ox, oy, r, resolution) { const amount = resolution ? Math.max(resolution, 2) : 4; const points = []; for (let i = 0; i < amount; i++) { points.push(new Anchor(0, 0, 0, 0, 0, 0)); } super(points, true, true, true); this._renderer.type = "circle"; for (let prop in proto14) { Object.defineProperty(this, prop, proto14[prop]); } if (typeof r === "number") { this.radius = r; } this._update(); if (typeof ox === "number") { this.translation.x = ox; } if (typeof oy === "number") { this.translation.y = oy; } } static fromObject(obj) { const circle = new _Circle().copy(obj); if ("id" in obj) { circle.id = obj.id; } return circle; } copy(circle) { super.copy.call(this, circle); for (let i = 0; i < _Circle.Properties.length; i++) { const k = _Circle.Properties[i]; if (k in circle && typeof circle[k] === "number") { this[k] = circle[k]; } } return this; } _update() { if (this._flagVertices || this._flagRadius) { let length = this.vertices.length; if (!this._closed && length > 2) { length -= 1; } const c = 4 / 3 * Math.tan(Math.PI / (length * 2)); const radius = this._radius; const rc = radius * c; for (let i = 0; i < this.vertices.length; i++) { const pct = i / length; const theta = pct * TWO_PI; const x = radius * cos2(theta); const y = radius * sin2(theta); const lx = rc * cos2(theta - HALF_PI); const ly = rc * sin2(theta - HALF_PI); const rx = rc * cos2(theta + HALF_PI); const ry = rc * sin2(theta + HALF_PI); const v = this.vertices[i]; v.command = i === 0 ? Commands.move : Commands.curve; v.set(x, y); v.controls.left.set(lx, ly); v.controls.right.set(rx, ry); } } super._update.call(this); return this; } flagReset() { this._flagRadius = false; super.flagReset.call(this); return this; } clone(parent) { const clone = new _Circle(0, 0, this.radius, this.vertices.length); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } for (let i = 0; i < Path.Properties.length; i++) { const k = Path.Properties[i]; clone[k] = this[k]; } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.renderer.type = "circle"; for (let i = 0; i < _Circle.Properties.length; i++) { const k = _Circle.Properties[i]; object[k] = this[k]; } return object; } }; var Circle = _Circle; __publicField(Circle, "Properties", ["radius"]); var proto14 = { radius: { enumerable: true, get: function() { return this._radius; }, set: function(v) { this._radius = v; this._flagRadius = true; } } }; // src/shapes/ellipse.js var cos3 = Math.cos; var sin3 = Math.sin; var _Ellipse = class extends Path { _flagWidth = false; _flagHeight = false; _width = 0; _height = 0; constructor(x, y, rx, ry, resolution) { if (typeof ry !== "number" && typeof rx === "number") { ry = rx; } const amount = resolution ? Math.max(resolution, 2) : 4; const points = []; for (let i = 0; i < amount; i++) { points.push(new Anchor()); } super(points, true, true, true); this._renderer.type = "ellipse"; for (let prop in proto15) { Object.defineProperty(this, prop, proto15[prop]); } if (typeof rx === "number") { this.width = rx * 2; } if (typeof ry === "number") { this.height = ry * 2; } this._update(); if (typeof x === "number") { this.translation.x = x; } if (typeof y === "number") { this.translation.y = y; } } static fromObject(obj) { const ellipse = new _Ellipse().copy(obj); if ("id" in obj) { ellipse.id = obj.id; } return ellipse; } copy(ellipse) { super.copy.call(this, ellipse); for (let i = 0; i < _Ellipse.Properties.length; i++) { const k = _Ellipse.Properties[i]; if (k in ellipse && typeof ellipse[k] === "number") { this[k] = ellipse[k]; } } return this; } _update() { if (this._flagVertices || this._flagWidth || this._flagHeight) { let length = this.vertices.length; if (!this._closed && length > 2) { length -= 1; } const c = 4 / 3 * Math.tan(Math.PI / (this.vertices.length * 2)); const radiusX = this._width / 2; const radiusY = this._height / 2; for (let i = 0; i < this.vertices.length; i++) { const pct = i / length; const theta = pct * TWO_PI; const x = radiusX * cos3(theta); const y = radiusY * sin3(theta); const lx = radiusX * c * cos3(theta - HALF_PI); const ly = radiusY * c * sin3(theta - HALF_PI); const rx = radiusX * c * cos3(theta + HALF_PI); const ry = radiusY * c * sin3(theta + HALF_PI); const v = this.vertices[i]; v.command = i === 0 ? Commands.move : Commands.curve; v.set(x, y); v.controls.left.set(lx, ly); v.controls.right.set(rx, ry); } } super._update.call(this); return this; } flagReset() { this._flagWidth = this._flagHeight = false; super.flagReset.call(this); return this; } clone(parent) { const rx = this.width / 2; const ry = this.height / 2; const resolution = this.vertices.length; const clone = new _Ellipse(0, 0, rx, ry, resolution); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } for (let i = 0; i < Path.Properties.length; i++) { const k = Path.Properties[i]; clone[k] = this[k]; } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.renderer.type = "ellipse"; for (let i = 0; i < _Ellipse.Properties.length; i++) { const k = _Ellipse.Properties[i]; object[k] = this[k]; } return object; } }; var Ellipse = _Ellipse; __publicField(Ellipse, "Properties", ["width", "height"]); var proto15 = { width: { enumerable: true, get: function() { return this._width; }, set: function(v) { this._width = v; this._flagWidth = true; } }, height: { enumerable: true, get: function() { return this._height; }, set: function(v) { this._height = v; this._flagHeight = true; } } }; // src/shapes/points.js var ceil2 = Math.ceil; var floor3 = Math.floor; var _Points = class extends Shape { _flagVertices = true; _flagLength = true; _flagFill = true; _flagStroke = true; _flagLinewidth = true; _flagOpacity = true; _flagVisible = true; _flagSize = true; _flagSizeAttenuation = true; _length = 0; _fill = "#fff"; _stroke = "#000"; _linewidth = 1; _opacity = 1; _visible = true; _size = 1; _sizeAttenuation = false; _beginning = 0; _ending = 1; _dashes = null; constructor(vertices) { super(); for (let prop in proto16) { Object.defineProperty(this, prop, proto16[prop]); } this._renderer.type = "points"; this._renderer.flagVertices = FlagVertices.bind(this); this._renderer.bindVertices = BindVertices.bind(this); this._renderer.unbindVertices = UnbindVertices.bind(this); this._renderer.flagFill = FlagFill.bind(this); this._renderer.flagStroke = FlagStroke.bind(this); this._renderer.vertices = null; this._renderer.collection = null; this.size = 1; this.sizeAttenuation = false; this.beginning = 0; this.ending = 1; this.fill = "#fff"; this.stroke = "#000"; this.linewidth = 1; this.opacity = 1; this.className = ""; this.visible = true; this.vertices = vertices; this.dashes = []; this.dashes.offset = 0; } static fromObject(obj) { const fill = typeof obj.fill === "string" ? obj.fill : getEffectFromObject(obj.fill); const stroke = typeof obj.stroke === "string" ? obj.stroke : getEffectFromObject(obj.stroke); const points = new _Points().copy({ ...obj, fill, stroke }); if ("id" in obj) { points.id = obj.id; } return points; } copy(points) { super.copy.call(this, points); for (let j = 0; j < points.vertices.length; j++) { const v = points.vertices[j]; if (v instanceof Anchor) { this.vertices.push(points.vertices[j].clone()); } else { this.vertices.push(new Anchor().copy(v)); } } for (let i = 0; i < _Points.Properties.length; i++) { const k = _Points.Properties[i]; if (k in points) { this[k] = points[k]; } } return this; } clone(parent) { const clone = new _Points(); for (let j = 0; j < this.vertices.length; j++) { clone.vertices.push(this.vertices[j].clone()); } for (let i = 0; i < _Points.Properties.length; i++) { const k = _Points.Properties[i]; clone[k] = this[k]; } clone.className = this.className; clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } if (parent) { parent.add(clone); } return clone._update(); } toObject() { const result = super.toObject.call(this); result.renderer.type = "points"; result.vertices = this.vertices.map((v) => v.toObject()); _.each( _Points.Properties, function(k) { if (typeof this[k] !== "undefined") { if (this[k].toObject) { result[k] = this[k].toObject(); } else { result[k] = this[k]; } } }, this ); return result; } noFill = Path.prototype.noFill; noStroke = Path.prototype.noStroke; corner = Path.prototype.corner; center = Path.prototype.center; getBoundingClientRect = Path.prototype.getBoundingClientRect; subdivide(limit) { this._update(); let points = []; for (let i = 0; i < this.vertices.length; i++) { const a = this.vertices[i]; const b = this.vertices[i - 1]; if (!b) { continue; } const x1 = a.x; const y1 = a.y; const x2 = b.x; const y2 = b.y; const subdivisions = subdivide(x1, y1, x1, y1, x2, y2, x2, y2, limit); points = points.concat(subdivisions); } this.vertices = points; return this; } _updateLength = Path.prototype._updateLength; _update() { if (this._flagVertices) { if (this._flagLength) { this._updateLength(void 0, true); } const beginning = Math.min(this._beginning, this._ending); const ending = Math.max(this._beginning, this._ending); const bid = getIdByLength(this, beginning * this._length); const eid = getIdByLength(this, ending * this._length); const low = ceil2(bid); const high = floor3(eid); let j = 0, v; this._renderer.vertices = []; this._renderer.collection = []; for (let i = 0; i < this._collection.length; i++) { if (i >= low && i <= high) { v = this._collection[i]; this._renderer.collection.push(v); this._renderer.vertices[j * 2 + 0] = v.x; this._renderer.vertices[j * 2 + 1] = v.y; j++; } } } super._update.apply(this, arguments); return this; } flagReset() { this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagSize = this._flagSizeAttenuation = false; super.flagReset.call(this); return this; } }; var Points = _Points; __publicField(Points, "Properties", [ "fill", "stroke", "linewidth", "opacity", "visible", "size", "sizeAttenuation", "beginning", "ending", "dashes" ]); var proto16 = { linewidth: { enumerable: true, get: function() { return this._linewidth; }, set: function(v) { this._linewidth = v; this._flagLinewidth = true; } }, opacity: { enumerable: true, get: function() { return this._opacity; }, set: function(v) { this._opacity = v; this._flagOpacity = true; } }, visible: { enumerable: true, get: function() { return this._visible; }, set: function(v) { this._visible = v; this._flagVisible = true; } }, size: { enumerable: true, get: function() { return this._size; }, set: function(v) { this._size = v; this._flagSize = true; } }, sizeAttenuation: { enumerable: true, get: function() { return this._sizeAttenuation; }, set: function(v) { this._sizeAttenuation = v; this._flagSizeAttenuation = true; } }, fill: { enumerable: true, get: function() { return this._fill; }, set: function(f) { if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) { this._fill.unbind(Events.Types.change, this._renderer.flagFill); } this._fill = f; this._flagFill = true; if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) { this._fill.bind(Events.Types.change, this._renderer.flagFill); } } }, stroke: { enumerable: true, get: function() { return this._stroke; }, set: function(f) { if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) { this._stroke.unbind(Events.Types.change, this._renderer.flagStroke); } this._stroke = f; this._flagStroke = true; if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) { this._stroke.bind(Events.Types.change, this._renderer.flagStroke); } } }, length: { get: function() { if (this._flagLength) { this._updateLength(); } return this._length; } }, beginning: { enumerable: true, get: function() { return this._beginning; }, set: function(v) { this._beginning = v; this._flagVertices = true; } }, ending: { enumerable: true, get: function() { return this._ending; }, set: function(v) { this._ending = v; this._flagVertices = true; } }, vertices: { enumerable: true, get: function() { return this._collection; }, set: function(vertices) { const bindVertices = this._renderer.bindVertices; const unbindVertices = this._renderer.unbindVertices; if (this._collection) { this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices); } if (vertices instanceof Collection) { this._collection = vertices; } else { this._collection = new Collection(vertices || []); } this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices); bindVertices(this._collection); } }, dashes: { enumerable: true, get: function() { return this._dashes; }, set: function(v) { if (typeof v.offset !== "number") { v.offset = this.dashes && this._dashes.offset || 0; } this._dashes = v; } } }; // src/shapes/polygon.js var cos4 = Math.cos; var sin4 = Math.sin; var _Polygon = class extends Path { _flagWidth = false; _flagHeight = false; _flagSides = false; _radius = 0; _width = 0; _height = 0; _sides = 0; constructor(x, y, radius, sides) { sides = Math.max(sides || 0, 3); super(); this._renderer.type = "polygon"; for (let prop in proto17) { Object.defineProperty(this, prop, proto17[prop]); } this.closed = true; this.automatic = false; if (typeof radius === "number") { this.radius = radius; } if (typeof sides === "number") { this.sides = sides; } this._update(); if (typeof x === "number") { this.translation.x = x; } if (typeof y === "number") { this.translation.y = y; } } static fromObject(obj) { const polygon = new _Polygon().copy(obj); if ("id" in obj) { polygon.id = obj.id; } return polygon; } copy(polygon) { super.copy.call(this, polygon); for (let i = 0; i < _Polygon.Properties.length; i++) { const k = _Polygon.Properties[i]; if (k in polygon && typeof polygon[k] === "number") { this[k] = polygon[k]; } } return this; } _update() { if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagSides) { const sides = this._sides; const amount = sides + 1; let length = this.vertices.length; if (length > sides) { this.vertices.splice(sides - 1, length - sides); length = sides; } for (let i = 0; i < amount; i++) { const pct = (i + 0.5) / sides; const theta = TWO_PI * pct + Math.PI / 2; const x = this._width * cos4(theta) / 2; const y = this._height * sin4(theta) / 2; if (i >= length) { this.vertices.push(new Anchor(x, y)); } else { this.vertices[i].set(x, y); } this.vertices[i].command = i === 0 ? Commands.move : Commands.line; } } super._update.call(this); return this; } flagReset() { this._flagWidth = this._flagHeight = this._flagSides = false; super.flagReset.call(this); return this; } clone(parent) { const clone = new _Polygon(0, 0, 0, this.sides); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; clone.width = this.width; clone.height = this.height; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } for (let i = 0; i < Path.Properties.length; i++) { const k = Path.Properties[i]; clone[k] = this[k]; } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.renderer.type = "polygon"; for (let i = 0; i < _Polygon.Properties.length; i++) { const k = _Polygon.Properties[i]; object[k] = this[k]; } return object; } }; var Polygon = _Polygon; __publicField(Polygon, "Properties", ["width", "height", "sides"]); var proto17 = { radius: { enumerable: true, get: function() { return this._radius; }, set: function(v) { this._radius = v; this.width = v * 2; this.height = v * 2; } }, width: { enumerable: true, get: function() { return this._width; }, set: function(v) { this._width = v; this._flagWidth = true; this._radius = Math.max(this.width, this.height) / 2; } }, height: { enumerable: true, get: function() { return this._height; }, set: function(v) { this._height = v; this._flagHeight = true; this._radius = Math.max(this.width, this.height) / 2; } }, sides: { enumerable: true, get: function() { return this._sides; }, set: function(v) { this._sides = v; this._flagSides = true; } } }; // src/shapes/rounded-rectangle.js var _RoundedRectangle = class extends Path { _flagWidth = false; _flagHeight = false; _flagRadius = false; _width = 0; _height = 0; _radius = 12; constructor(x, y, width, height, radius) { if (typeof radius === "undefined" && typeof width === "number" && typeof height === "number") { radius = Math.floor(Math.min(width, height) / 12); } const points = []; for (let i = 0; i < 10; i++) { points.push( new Anchor(0, 0, 0, 0, 0, 0, i === 0 ? Commands.move : Commands.curve) ); } super(points); this._renderer.type = "rounded-rectangle"; for (let prop in proto18) { Object.defineProperty(this, prop, proto18[prop]); } this.closed = true; this.automatic = false; this._renderer.flagRadius = FlagRadius.bind(this); if (typeof width === "number") { this.width = width; } if (typeof height === "number") { this.height = height; } if (typeof radius === "number") { this.radius = radius; } this._update(); if (typeof x === "number") { this.translation.x = x; } if (typeof y === "number") { this.translation.y = y; } } static fromObject(obj) { const rectangle = new _RoundedRectangle().copy(obj); if ("id" in obj) { rectangle.id = obj.id; } return rectangle; } copy(roundedRectangle) { super.copy.call(this, roundedRectangle); for (let i = 0; i < _RoundedRectangle.Properties.length; i++) { const k = _RoundedRectangle.Properties[i]; if (k in roundedRectangle) { const value = roundedRectangle[k]; if (/radius/i.test(k)) { this[k] = typeof value === "number" || value instanceof Vector ? value : new Vector().copy(value); } else if (typeof value === "number") { this[k] = value; } } } return this; } _update() { if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagRadius) { const width = this._width; const height = this._height; let rx, ry; if (this._radius instanceof Vector) { rx = this._radius.x; ry = this._radius.y; } else { rx = this._radius; ry = this._radius; } let v; let w = width / 2; let h = height / 2; v = this.vertices[0]; v.x = -(w - rx); v.y = -h; v = this.vertices[1]; v.x = w - rx; v.y = -h; v.controls.left.clear(); v.controls.right.x = rx; v.controls.right.y = 0; v = this.vertices[2]; v.x = w; v.y = -(h - ry); v.controls.right.clear(); v.controls.left.clear(); v = this.vertices[3]; v.x = w; v.y = h - ry; v.controls.left.clear(); v.controls.right.x = 0; v.controls.right.y = ry; v = this.vertices[4]; v.x = w - rx; v.y = h; v.controls.right.clear(); v.controls.left.clear(); v = this.vertices[5]; v.x = -(w - rx); v.y = h; v.controls.left.clear(); v.controls.right.x = -rx; v.controls.right.y = 0; v = this.vertices[6]; v.x = -w; v.y = h - ry; v.controls.left.clear(); v.controls.right.clear(); v = this.vertices[7]; v.x = -w; v.y = -(h - ry); v.controls.left.clear(); v.controls.right.x = 0; v.controls.right.y = -ry; v = this.vertices[8]; v.x = -(w - rx); v.y = -h; v.controls.left.clear(); v.controls.right.clear(); v = this.vertices[9]; v.copy(this.vertices[8]); } super._update.call(this); return this; } flagReset() { this._flagWidth = this._flagHeight = this._flagRadius = false; super.flagReset.call(this); return this; } clone(parent) { const width = this.width; const height = this.height; const radius = this.radius; const clone = new _RoundedRectangle(0, 0, width, height, radius); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } for (let i = 0; i < Path.Properties.length; i++) { const k = Path.Properties[i]; clone[k] = this[k]; } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.renderer.type = "rounded-rectangle"; for (let i = 0; i < _RoundedRectangle.Properties.length; i++) { const k = _RoundedRectangle.Properties[i]; object[k] = this[k]; } object.radius = typeof this.radius === "number" ? this.radius : this.radius.toObject(); return object; } }; var RoundedRectangle = _RoundedRectangle; __publicField(RoundedRectangle, "Properties", ["width", "height", "radius"]); var proto18 = { width: { enumerable: true, get: function() { return this._width; }, set: function(v) { this._width = v; this._flagWidth = true; } }, height: { enumerable: true, get: function() { return this._height; }, set: function(v) { this._height = v; this._flagHeight = true; } }, radius: { enumerable: true, get: function() { return this._radius; }, set: function(v) { if (this._radius instanceof Vector) { this._radius.unbind(Events.Types.change, this._renderer.flagRadius); } this._radius = v; if (this._radius instanceof Vector) { this._radius.bind(Events.Types.change, this._renderer.flagRadius); } this._flagRadius = true; } } }; function FlagRadius() { this._flagRadius = true; } // src/shapes/star.js var cos5 = Math.cos; var sin5 = Math.sin; var _Star = class extends Path { _flagInnerRadius = false; _flagOuterRadius = false; _flagSides = false; _innerRadius = 0; _outerRadius = 0; _sides = 0; constructor(x, y, innerRadius, outerRadius, sides) { if (arguments.length <= 3) { outerRadius = innerRadius; innerRadius = outerRadius / 2; } if (typeof sides !== "number" || sides <= 0) { sides = 5; } super(); this._renderer.type = "star"; for (let prop in proto19) { Object.defineProperty(this, prop, proto19[prop]); } this.closed = true; this.automatic = false; if (typeof innerRadius === "number") { this.innerRadius = innerRadius; } if (typeof outerRadius === "number") { this.outerRadius = outerRadius; } if (typeof sides === "number") { this.sides = sides; } this._update(); if (typeof x === "number") { this.translation.x = x; } if (typeof y === "number") { this.translation.y = y; } } static fromObject(obj) { const star = new _Star().copy(obj); if ("id" in obj) { star.id = obj.id; } return star; } copy(star) { super.copy.call(this, star); for (let i = 0; i < _Star.Properties.length; i++) { const k = _Star.Properties[i]; if (k in star && typeof star[k] === "number") { this[k] = star[k]; } } return this; } _update() { if (this._flagVertices || this._flagInnerRadius || this._flagOuterRadius || this._flagSides) { const sides = this._sides * 2; const amount = sides + 1; let length = this.vertices.length; if (length > sides) { this.vertices.splice(sides - 1, length - sides); length = sides; } for (let i = 0; i < amount; i++) { const pct = (i + 0.5) / sides; const theta = TWO_PI * pct; const r = (!(i % 2) ? this._innerRadius : this._outerRadius) / 2; const x = r * cos5(theta); const y = r * sin5(theta); if (i >= length) { this.vertices.push(new Anchor(x, y)); } else { this.vertices[i].set(x, y); } this.vertices[i].command = i === 0 ? Commands.move : Commands.line; } } super._update.call(this); return this; } flagReset() { this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false; super.flagReset.call(this); return this; } clone(parent) { const ir = this.innerRadius; const or = this.outerRadius; const sides = this.sides; const clone = new _Star(0, 0, ir, or, sides); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.skewX = this.skewX; clone.skewY = this.skewY; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } for (let i = 0; i < Path.Properties.length; i++) { const k = Path.Properties[i]; clone[k] = this[k]; } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.renderer.type = "star"; for (let i = 0; i < _Star.Properties.length; i++) { const k = _Star.Properties[i]; object[k] = this[k]; } return object; } }; var Star = _Star; __publicField(Star, "Properties", ["innerRadius", "outerRadius", "sides"]); var proto19 = { innerRadius: { enumerable: true, get: function() { return this._innerRadius; }, set: function(v) { this._innerRadius = v; this._flagInnerRadius = true; } }, outerRadius: { enumerable: true, get: function() { return this._outerRadius; }, set: function(v) { this._outerRadius = v; this._flagOuterRadius = true; } }, sides: { enumerable: true, get: function() { return this._sides; }, set: function(v) { this._sides = v; this._flagSides = true; } } }; // src/text.js var canvas; var min2 = Math.min; var max2 = Math.max; if (root.document) { canvas = document.createElement("canvas"); } var _Text = class extends Shape { _flagValue = true; _flagFamily = true; _flagSize = true; _flagLeading = true; _flagAlignment = true; _flagBaseline = true; _flagStyle = true; _flagWeight = true; _flagDecoration = true; _flagFill = true; _flagStroke = true; _flagLinewidth = true; _flagOpacity = true; _flagVisible = true; _flagMask = false; _flagClip = false; _flagDirection = true; _value = ""; _family = "sans-serif"; _size = 13; _leading = 17; _alignment = "center"; _baseline = "middle"; _style = "normal"; _weight = 500; _decoration = "none"; _direction = "ltr"; _fill = "#000"; _stroke = "none"; _linewidth = 1; _opacity = 1; _visible = true; _mask = null; _clip = false; _dashes = null; constructor(message, x, y, styles) { super(); for (let prop in proto20) { Object.defineProperty(this, prop, proto20[prop]); } this._renderer.type = "text"; this._renderer.flagFill = FlagFill2.bind(this); this._renderer.flagStroke = FlagStroke2.bind(this); this.value = message; if (typeof x === "number") { this.translation.x = x; } if (typeof y === "number") { this.translation.y = y; } this.dashes = []; this.dashes.offset = 0; if (!_.isObject(styles)) { return this; } for (let i = 0; i < _Text.Properties.length; i++) { const property = _Text.Properties[i]; if (property in styles) { this[property] = styles[property]; } } } static Measure(text) { if (canvas) { const ctx = canvas.getContext("2d"); ctx.font = [ text._style, text._weight, `${text._size}px/${text._leading}px`, text._family ].join(" "); const metrics = ctx.measureText(text.value, 0, 0); const height = metrics.actualBoundingBoxDescent + metrics.actualBoundingBoxAscent; return { width: metrics.width, height }; } else { const width = this.value.length * this.size * _Text.Ratio; const height = this.leading; console.warn( "Two.Text: unable to accurately measure text, so using an approximation." ); return { width, height }; } } static fromObject(obj) { const fill = typeof obj.fill === "string" ? obj.fill : getEffectFromObject(obj.fill); const stroke = typeof obj.stroke === "string" ? obj.stroke : getEffectFromObject(obj.stroke); const text = new _Text().copy({ ...obj, fill, stroke }); if ("id" in obj) { text.id = obj.id; } return text; } copy(text) { super.copy.call(this, text); for (let i = 0; i < _Text.Properties.length; i++) { const k = _Text.Properties[i]; if (k in text) { this[k] = text[k]; } } return this; } clone(parent) { const clone = new _Text(this.value); clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; for (let i = 0; i < _Text.Properties.length; i++) { const prop = _Text.Properties[i]; clone[prop] = this[prop]; } if (this.matrix.manual) { clone.matrix.copy(this.matrix); } if (parent) { parent.add(clone); } return clone._update(); } toObject() { const result = super.toObject.call(this); result.renderer.type = "text"; for (let i = 0; i < _Text.Properties.length; i++) { const prop = _Text.Properties[i]; result[prop] = this[prop]; } return result; } noFill() { this.fill = "none"; return this; } noStroke() { this.stroke = "none"; this.linewidth = 0; return this; } getBoundingClientRect(shallow) { let matrix; let left, right, top, bottom; this._update(true); matrix = shallow ? this.matrix : this.worldMatrix; const { width, height } = _Text.Measure(this); const border = (this._linewidth || 0) / 2; switch (this.alignment) { case "left": left = -border; right = width + border; break; case "right": left = -(width + border); right = border; break; default: left = -(width / 2 + border); right = width / 2 + border; } switch (this.baseline) { case "middle": top = -(height / 2 + border); bottom = height / 2 + border; break; default: top = -(height + border); bottom = border; } const [ax, ay] = matrix.multiply(left, top); const [bx, by] = matrix.multiply(left, bottom); const [cx, cy] = matrix.multiply(right, top); const [dx, dy] = matrix.multiply(right, bottom); top = min2(ay, by, cy, dy); left = min2(ax, bx, cx, dx); right = max2(ax, bx, cx, dx); bottom = max2(ay, by, cy, dy); return { top, left, right, bottom, width: right - left, height: bottom - top }; } flagReset() { super.flagReset.call(this); this._flagValue = this._flagFamily = this._flagSize = this._flagLeading = this._flagAlignment = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagClip = this._flagDecoration = this._flagClassName = this._flagBaseline = this._flagWeight = this._flagStyle = this._flagDirection = false; return this; } }; var Text = _Text; __publicField(Text, "Ratio", 0.6); __publicField(Text, "Properties", [ "value", "family", "size", "leading", "alignment", "linewidth", "style", "weight", "decoration", "direction", "baseline", "opacity", "visible", "fill", "stroke", "dashes" ]); var proto20 = { value: { enumerable: true, get: function() { return this._value; }, set: function(v) { this._value = v; this._flagValue = true; } }, family: { enumerable: true, get: function() { return this._family; }, set: function(v) { this._family = v; this._flagFamily = true; } }, size: { enumerable: true, get: function() { return this._size; }, set: function(v) { this._size = v; this._flagSize = true; } }, leading: { enumerable: true, get: function() { return this._leading; }, set: function(v) { this._leading = v; this._flagLeading = true; } }, alignment: { enumerable: true, get: function() { return this._alignment; }, set: function(v) { this._alignment = v; this._flagAlignment = true; } }, linewidth: { enumerable: true, get: function() { return this._linewidth; }, set: function(v) { this._linewidth = v; this._flagLinewidth = true; } }, style: { enumerable: true, get: function() { return this._style; }, set: function(v) { this._style = v; this._flagStyle = true; } }, weight: { enumerable: true, get: function() { return this._weight; }, set: function(v) { this._weight = v; this._flagWeight = true; } }, decoration: { enumerable: true, get: function() { return this._decoration; }, set: function(v) { this._decoration = v; this._flagDecoration = true; } }, direction: { enumerable: true, get: function() { return this._direction; }, set: function(v) { this._direction = v; this._flagDirection = true; } }, baseline: { enumerable: true, get: function() { return this._baseline; }, set: function(v) { this._baseline = v; this._flagBaseline = true; } }, opacity: { enumerable: true, get: function() { return this._opacity; }, set: function(v) { this._opacity = v; this._flagOpacity = true; } }, visible: { enumerable: true, get: function() { return this._visible; }, set: function(v) { this._visible = v; this._flagVisible = true; } }, fill: { enumerable: true, get: function() { return this._fill; }, set: function(f) { if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) { this._fill.unbind(Events.Types.change, this._renderer.flagFill); } this._fill = f; this._flagFill = true; if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) { this._fill.bind(Events.Types.change, this._renderer.flagFill); } } }, stroke: { enumerable: true, get: function() { return this._stroke; }, set: function(f) { if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) { this._stroke.unbind(Events.Types.change, this._renderer.flagStroke); } this._stroke = f; this._flagStroke = true; if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) { this._stroke.bind(Events.Types.change, this._renderer.flagStroke); } } }, mask: { enumerable: true, get: function() { return this._mask; }, set: function(v) { this._mask = v; this._flagMask = true; if (_.isObject(v) && !v.clip) { v.clip = true; } } }, clip: { enumerable: true, get: function() { return this._clip; }, set: function(v) { this._clip = v; this._flagClip = true; } }, dashes: { enumerable: true, get: function() { return this._dashes; }, set: function(v) { if (typeof v.offset !== "number") { v.offset = this.dashes && this._dashes.offset || 0; } this._dashes = v; } } }; function FlagFill2() { this._flagFill = true; } function FlagStroke2() { this._flagStroke = true; } // src/group.js var min3 = Math.min; var max3 = Math.max; var _Group = class extends Shape { _flagAdditions = false; _flagSubtractions = false; _flagOrder = false; _flagOpacity = true; _flagBeginning = false; _flagEnding = false; _flagLength = false; _flagMask = false; _fill = "#fff"; _stroke = "#000"; _linewidth = 1; _opacity = 1; _visible = true; _cap = "round"; _join = "round"; _miter = 4; _closed = true; _curved = false; _automatic = true; _beginning = 0; _ending = 1; _length = 0; _mask = null; constructor(children) { super(); for (let prop in proto21) { Object.defineProperty(this, prop, proto21[prop]); } this._renderer.type = "group"; this.additions = []; this.subtractions = []; this.children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments); } static InsertChildren(children) { for (let i = 0; i < children.length; i++) { replaceParent.call(this, children[i], this); } } static RemoveChildren(children) { for (let i = 0; i < children.length; i++) { replaceParent.call(this, children[i]); } } static OrderChildren(children) { this._flagOrder = true; } static fromObject(obj) { const group = new _Group(); for (let i = 0; i < _Group.Properties.length; i++) { const k = _Group.Properties[i]; if (k in obj) { if (/(fill|stroke)/i.test(k)) { group[k] = typeof obj[k] === "string" ? obj[k] : getEffectFromObject(obj[k]); } else { group[k] = obj[k]; } } } if ("mask" in obj) { group.mask = getShapeFromObject(obj.mask); } if ("id" in obj) { group.id = obj.id; } group.children = obj.children.map(getShapeFromObject); return group; function getShapeFromObject(child) { if (child && child.renderer) { switch (child.renderer.type) { case "arc-segment": return ArcSegment.fromObject(child); case "circle": return Circle.fromObject(child); case "ellipse": return Ellipse.fromObject(child); case "points": return Points.fromObject(child); case "polygon": return Polygon.fromObject(child); case "rectangle": return Rectangle.fromObject(child); case "rounded-rectangle": return RoundedRectangle.fromObject(child); case "star": return Star.fromObject(child); case "path": return Path.fromObject(child); case "text": return Text.fromObject(child); case "group": return _Group.fromObject(child); case "shape": return Shape.fromObject(child); case "element": return Element.fromObject(child); } } return child; } } copy(group) { super.copy.call(this, group); console.warn("Two.Group.copy is not supported yet."); return this; } clone(parent) { const clone = new _Group(); const children = this.children.map(function(child) { return child.clone(); }); clone.add(children); clone.opacity = this.opacity; if (this.mask) { clone.mask = this.mask; } clone.translation.copy(this.translation); clone.rotation = this.rotation; clone.scale = this.scale; clone.className = this.className; if (this.matrix.manual) { clone.matrix.copy(this.matrix); } if (parent) { parent.add(clone); } return clone._update(); } toObject() { const result = super.toObject.call(this); result.renderer.type = "group"; result.children = []; result.opacity = this.opacity; result.className = this.className; result.mask = this.mask ? this.mask.toObject() : null; _.each( this.children, (child, i) => { result.children[i] = child.toObject(); }, this ); return result; } corner() { const rect = this.getBoundingClientRect(true); for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.translation.x -= rect.left; child.translation.y -= rect.top; } if (this.mask) { this.mask.translation.x -= rect.left; this.mask.translation.y -= rect.top; } return this; } center() { const rect = this.getBoundingClientRect(true); const cx = rect.left + rect.width / 2 - this.translation.x; const cy = rect.top + rect.height / 2 - this.translation.y; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; if (child.isShape) { child.translation.x -= cx; child.translation.y -= cy; } } if (this.mask) { this.mask.translation.x -= cx; this.mask.translation.y -= cy; } return this; } getById(id) { let found = null; function search(node) { if (node.id === id) { return node; } else if (node.children) { if (node.children.ids[id]) { return node.children.ids[id]; } for (let i = 0; i < node.children.length; i++) { found = search(node.children[i]); if (found) { return found; } } } return null; } return search(this); } getByClassName(className) { const found = []; function search(node) { if (Array.prototype.indexOf.call(node.classList, className) >= 0) { found.push(node); } if (node.children) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; search(child); } } return found; } return search(this); } getByType(type) { const found = []; function search(node) { if (node instanceof type) { found.push(node); } if (node.children) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; search(child); } } return found; } return search(this); } add(objects) { if (!(objects instanceof Array)) { objects = Array.prototype.slice.call(arguments); } else { objects = objects.slice(); } for (let i = 0; i < objects.length; i++) { const child = objects[i]; if (!(child && child.id)) { continue; } const index = Array.prototype.indexOf.call(this.children, child); if (index >= 0) { this.children.splice(index, 1); } this.children.push(child); } return this; } remove(objects) { const l = arguments.length, grandparent = this.parent; if (l <= 0 && grandparent) { grandparent.remove(this); return this; } if (!(objects instanceof Array)) { objects = Array.prototype.slice.call(arguments); } else { objects = objects.slice(); } for (let i = 0; i < objects.length; i++) { const object = objects[i]; if (!object || !this.children.ids[object.id]) { continue; } const index = this.children.indexOf(object); if (index >= 0) { this.children.splice(index, 1); } } return this; } getBoundingClientRect(shallow) { let rect, matrix, tc, lc, rc, bc; this._update(true); let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity; const regex3 = /texture|gradient/i; matrix = shallow ? this.matrix : this.worldMatrix; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; if (!child.visible || regex3.test(child._renderer.type)) { continue; } rect = child.getBoundingClientRect(shallow); tc = typeof rect.top !== "number" || _.isNaN(rect.top) || !isFinite(rect.top); lc = typeof rect.left !== "number" || _.isNaN(rect.left) || !isFinite(rect.left); rc = typeof rect.right !== "number" || _.isNaN(rect.right) || !isFinite(rect.right); bc = typeof rect.bottom !== "number" || _.isNaN(rect.bottom) || !isFinite(rect.bottom); if (tc || lc || rc || bc) { continue; } if (shallow) { const [ax, ay] = matrix.multiply(rect.left, rect.top); const [bx, by] = matrix.multiply(rect.right, rect.top); const [cx, cy] = matrix.multiply(rect.left, rect.bottom); const [dx, dy] = matrix.multiply(rect.right, rect.bottom); top = min3(ay, by, cy, dy, top); left = min3(ax, bx, cx, dx, left); right = max3(ax, bx, cx, dx, right); bottom = max3(ay, by, cy, dy, bottom); } else { top = min3(rect.top, top); left = min3(rect.left, left); right = max3(rect.right, right); bottom = max3(rect.bottom, bottom); } } return { top, left, right, bottom, width: right - left, height: bottom - top }; } noFill() { this.children.forEach(function(child) { child.noFill(); }); return this; } noStroke() { this.children.forEach(function(child) { child.noStroke(); }); return this; } subdivide() { const args = arguments; this.children.forEach(function(child) { child.subdivide.apply(child, args); }); return this; } _update() { let i, l, child; if (this._flagBeginning || this._flagEnding) { const beginning = Math.min(this._beginning, this._ending); const ending = Math.max(this._beginning, this._ending); const length = this.length; let sum = 0; const bd = beginning * length; const ed = ending * length; for (i = 0; i < this.children.length; i++) { child = this.children[i]; l = child.length; if (bd > sum + l) { child.beginning = 1; child.ending = 1; } else if (ed < sum) { child.beginning = 0; child.ending = 0; } else if (bd > sum && bd < sum + l) { child.beginning = (bd - sum) / l; child.ending = 1; } else if (ed > sum && ed < sum + l) { child.beginning = 0; child.ending = (ed - sum) / l; } else { child.beginning = 0; child.ending = 1; } sum += l; } } return super._update.apply(this, arguments); } flagReset() { if (this._flagAdditions) { this.additions.length = 0; this._flagAdditions = false; } if (this._flagSubtractions) { this.subtractions.length = 0; this._flagSubtractions = false; } this._flagOrder = this._flagMask = this._flagOpacity = this._flagBeginning = this._flagEnding = false; super.flagReset.call(this); return this; } }; var Group = _Group; __publicField(Group, "Children", Children); __publicField(Group, "Properties", [ "fill", "stroke", "linewidth", "cap", "join", "miter", "closed", "curved", "automatic" ]); var proto21 = { visible: { enumerable: true, get: function() { return this._visible; }, set: function(v) { this._flagVisible = this._visible !== v || this._flagVisible; this._visible = v; } }, opacity: { enumerable: true, get: function() { return this._opacity; }, set: function(v) { this._flagOpacity = this._opacity !== v || this._flagOpacity; this._opacity = v; } }, beginning: { enumerable: true, get: function() { return this._beginning; }, set: function(v) { this._flagBeginning = this._beginning !== v || this._flagBeginning; this._beginning = v; } }, ending: { enumerable: true, get: function() { return this._ending; }, set: function(v) { this._flagEnding = this._ending !== v || this._flagEnding; this._ending = v; } }, length: { enumerable: true, get: function() { if (this._flagLength || this._length <= 0) { this._length = 0; if (!this.children) { return this._length; } for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; this._length += child.length; } } return this._length; } }, fill: { enumerable: true, get: function() { return this._fill; }, set: function(v) { this._fill = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.fill = v; } } }, stroke: { enumerable: true, get: function() { return this._stroke; }, set: function(v) { this._stroke = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.stroke = v; } } }, linewidth: { enumerable: true, get: function() { return this._linewidth; }, set: function(v) { this._linewidth = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.linewidth = v; } } }, join: { enumerable: true, get: function() { return this._join; }, set: function(v) { this._join = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.join = v; } } }, miter: { enumerable: true, get: function() { return this._miter; }, set: function(v) { this._miter = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.miter = v; } } }, cap: { enumerable: true, get: function() { return this._cap; }, set: function(v) { this._cap = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.cap = v; } } }, closed: { enumerable: true, get: function() { return this._closed; }, set: function(v) { this._closed = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.closed = v; } } }, curved: { enumerable: true, get: function() { return this._curved; }, set: function(v) { this._curved = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.curved = v; } } }, automatic: { enumerable: true, get: function() { return this._automatic; }, set: function(v) { this._automatic = v; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; child.automatic = v; } } }, children: { enumerable: true, get: function() { return this._children; }, set: function(children) { const insertChildren = Group.InsertChildren.bind(this); const removeChildren = Group.RemoveChildren.bind(this); const orderChildren = Group.OrderChildren.bind(this); if (this._children) { this._children.unbind(); if (this._children.length > 0) { removeChildren(this._children); } } this._children = new Children(children); this._children.bind(Events.Types.insert, insertChildren); this._children.bind(Events.Types.remove, removeChildren); this._children.bind(Events.Types.order, orderChildren); if (children.length > 0) { insertChildren(children); } } }, mask: { enumerable: true, get: function() { return this._mask; }, set: function(v) { this._mask = v; this._flagMask = true; if (_.isObject(v) && !v.clip) { v.clip = true; } } } }; function replaceParent(child, newParent) { const parent = child.parent; let index; if (parent === newParent) { add(); return; } if (parent && parent.children.ids[child.id]) { index = Array.prototype.indexOf.call(parent.children, child); parent.children.splice(index, 1); splice(); } if (newParent) { add(); return; } splice(); if (parent._flagAdditions && parent.additions.length === 0) { parent._flagAdditions = false; } if (parent._flagSubtractions && parent.subtractions.length === 0) { parent._flagSubtractions = false; } delete child.parent; function add() { if (newParent.subtractions.length > 0) { index = Array.prototype.indexOf.call(newParent.subtractions, child); if (index >= 0) { newParent.subtractions.splice(index, 1); } } if (newParent.additions.length > 0) { index = Array.prototype.indexOf.call(newParent.additions, child); if (index >= 0) { newParent.additions.splice(index, 1); } } child.parent = newParent; newParent.additions.push(child); newParent._flagAdditions = true; } function splice() { index = Array.prototype.indexOf.call(parent.additions, child); if (index >= 0) { parent.additions.splice(index, 1); } index = Array.prototype.indexOf.call(parent.subtractions, child); if (index < 0) { parent.subtractions.push(child); parent._flagSubtractions = true; } } } // src/shapes/line.js var Line = class extends Path { constructor(x1, y1, x2, y2) { const points = [new Anchor(x1, y1), new Anchor(x2, y2)]; super(points); for (let prop in proto22) { Object.defineProperty(this, prop, proto22[prop]); } this.vertices[0].command = Commands.move; this.vertices[1].command = Commands.line; this.automatic = false; } }; __publicField(Line, "Properties", ["left", "right"]); var proto22 = { left: { enumerable: true, get: function() { return this.vertices[0]; }, set: function(v) { if (_.isObject(v)) { this.vertices.splice(0, 1, v); } else { const error = new TwoError("Two.Line.x argument is not an object."); console.warn(error.name, error.message); } } }, right: { enumerable: true, get: function() { return this.vertices[1]; }, set: function(v) { if (_.isObject(v)) { this.vertices.splice(1, 1, v); } else { const error = new TwoError("Two.Line.y argument is not an object."); console.warn(error.name, error.message); } } } }; // src/utils/interpret-svg.js var regex2 = { path: /[+-]?(?:\d*\.\d+|\d+)(?:[eE][+-]\d+)?/g, cssBackgroundImage: /url\(['"]?#([\w\d-_]*)['"]?\)/i, unitSuffix: /[a-zA-Z%]*/i }; var alignments = { start: "left", middle: "center", end: "right" }; var reservedAttributesToRemove = [ "id", "class", "transform", "xmlns", "viewBox" ]; var overwriteAttrs = ["x", "y", "width", "height", "href", "xlink:href"]; function getAlignment(anchor2) { return alignments[anchor2]; } function getBaseline(node) { const a = node.getAttribute("dominant-baseline"); const b = node.getAttribute("alignment-baseline"); return a || b; } function getTagName(tag) { return tag.replace(/svg:/gi, "").toLowerCase(); } function applyTransformsToVector(transforms, vector3) { vector3.x += transforms.translateX; vector3.y += transforms.translateY; vector3.x *= transforms.scaleX; vector3.y *= transforms.scaleY; if (transforms.rotation !== 0) { const l = vector3.length(); vector3.x = l * Math.cos(transforms.rotation); vector3.y = l * Math.sin(transforms.rotation); } } function extractCSSText(text, styles) { if (!styles) { styles = {}; } const commands = text.split(";"); for (let i = 0; i < commands.length; i++) { const command = commands[i].split(":"); const name = command[0]; const value = command[1]; if (typeof name === "undefined" || typeof value === "undefined") { continue; } const trimmedName = name.replace(/\s/g, ""); const trimmedValue = value.replace(/\s/g, ""); styles[trimmedName] = trimmedValue; } return styles; } function getSvgStyles(node) { const styles = {}; const attributes = getSvgAttributes(node); const length = Math.max(attributes.length, node.style.length); for (let i = 0; i < length; i++) { const command = node.style[i]; const attribute = attributes[i]; if (command) { styles[command] = node.style[command]; } if (attribute) { styles[attribute] = node.getAttribute(attribute); } } return styles; } function getSvgAttributes(node) { const attributes = node.getAttributeNames(); for (let i = 0; i < reservedAttributesToRemove.length; i++) { const keyword = reservedAttributesToRemove[i]; const index = Array.prototype.indexOf.call(attributes, keyword); if (index >= 0) { attributes.splice(index, 1); } } return attributes; } function applySvgViewBox(node, value) { const elements = value.split(/[\s,]/); const x = -parseFloat(elements[0]); const y = -parseFloat(elements[1]); const width = parseFloat(elements[2]); const height = parseFloat(elements[3]); if (x && y) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; if ("translation" in child) { child.translation.add(x, y); } else if ("x" in child) { child.x = x; } else if ("y" in child) { child.y = y; } } } const xExists = typeof node.x === "number"; const yExists = typeof node.y === "number"; const widthExists = typeof node.width === "number"; const heightExists = typeof node.height === "number"; if (xExists) { node.translation.x += node.x; } if (yExists) { node.translation.y += node.y; } if (widthExists || heightExists) { node.scale = new Vector(1, 1); } if (widthExists) { node.scale.x = node.width / width; } if (heightExists) { node.scale.y = node.height / height; } node.mask = new Rectangle(0, 0, width, height); node.mask.origin.set(-width / 2, -height / 2); return node; } function applySvgAttributes(node, elem, parentStyles) { const styles = {}, attributes = {}, extracted = {}; let i, m, key, value, prop, attr; let transforms, x, y; let id, scene, ref, tagName; let ca, cb, cc, error; if (node === null) { return styles; } if (root.getComputedStyle) { const computedStyles = root.getComputedStyle(node); i = computedStyles.length; while (i--) { key = computedStyles[i]; value = computedStyles[key]; if (typeof value !== "undefined") { styles[key] = value; } } } for (i = 0; i < node.attributes.length; i++) { attr = node.attributes[i]; if (/style/i.test(attr.nodeName)) { extractCSSText(attr.value, extracted); } else { attributes[attr.nodeName] = attr.value; } } if (typeof styles.opacity !== "undefined") { styles["stroke-opacity"] = styles.opacity; styles["fill-opacity"] = styles.opacity; delete styles.opacity; } if (parentStyles) { _.defaults(styles, parentStyles); } _.extend(styles, extracted, attributes); styles.visible = !(typeof styles.display === "undefined" && /none/i.test(styles.display)) || typeof styles.visibility === "undefined" && /hidden/i.test(styles.visibility); for (key in styles) { value = styles[key]; switch (key) { case "gradientTransform": if (/none/i.test(value)) break; m = node.gradientTransform && node.gradientTransform.baseVal && node.gradientTransform.baseVal.length > 0 ? node.gradientTransform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null; if (m === null) break; transforms = decomposeMatrix(m); switch (elem._renderer.type) { case "linear-gradient": applyTransformsToVector(transforms, elem.left); applyTransformsToVector(transforms, elem.right); break; case "radial-gradient": elem.center.x += transforms.translateX; elem.center.y += transforms.translateY; elem.focal.x += transforms.translateX; elem.focal.y += transforms.translateY; elem.radius *= Math.max(transforms.scaleX, transforms.scaleY); break; } break; case "transform": if (/none/i.test(value)) break; m = node.transform && node.transform.baseVal && node.transform.baseVal.length > 0 ? node.transform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null; if (m === null) break; if (Constants.AutoCalculateImportedMatrices) { transforms = decomposeMatrix(m); elem.translation.set(transforms.translateX, transforms.translateY); elem.rotation = Math.PI * (transforms.rotation / 180); elem.scale = new Vector(transforms.scaleX, transforms.scaleY); x = parseFloat((styles.x + "").replace("px")); y = parseFloat((styles.y + "").replace("px")); if (x) { elem.translation.x = x; } if (y) { elem.translation.y = y; } } else { m = node.getCTM(); elem._matrix.manual = true; elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f); } break; case "visible": if (elem instanceof Group) { elem._visible = value; break; } elem.visible = value; break; case "stroke-linecap": if (elem instanceof Group) { elem._cap = value; break; } elem.cap = value; break; case "stroke-linejoin": if (elem instanceof Group) { elem._join = value; break; } elem.join = value; break; case "stroke-miterlimit": if (elem instanceof Group) { elem._miter = value; break; } elem.miter = value; break; case "stroke-width": if (elem instanceof Group) { elem._linewidth = parseFloat(value); break; } elem.linewidth = parseFloat(value); break; case "opacity": case "stroke-opacity": case "fill-opacity": if (elem instanceof Group) { elem._opacity = parseFloat(value); break; } elem.opacity = parseFloat(value); break; case "clip-path": if (regex2.cssBackgroundImage.test(value)) { id = value.replace(regex2.cssBackgroundImage, "$1"); if (read.defs.current && read.defs.current.contains(id)) { ref = read.defs.current.get(id); if (ref && ref.childNodes.length > 0) { ref = ref.childNodes[0]; tagName = getTagName(ref.nodeName); elem.mask = read[tagName].call(this, ref, {}); switch (elem._renderer.type) { case "text": case "path": elem.position.add(elem.mask.position); elem.mask.position.clear(); break; } } } } break; case "fill": case "stroke": prop = (elem instanceof Group ? "_" : "") + key; if (regex2.cssBackgroundImage.test(value)) { id = value.replace(regex2.cssBackgroundImage, "$1"); if (read.defs.current && read.defs.current.contains(id)) { ref = read.defs.current.get(id); if (!ref.object) { tagName = getTagName(ref.nodeName); ref.object = read[tagName].call(this, ref, {}); } ref = ref.object; } else { scene = getScene(this); ref = scene.getById(id); } elem[prop] = ref; } else { elem[prop] = value; } break; case "id": elem.id = value; break; case "class": case "className": elem.classList = value.split(" "); elem._flagClassName = true; break; case "x": case "y": ca = elem instanceof Gradient; cb = elem instanceof LinearGradient; cc = elem instanceof RadialGradient; if (ca || cb || cc) { break; } if (value.match("[a-z%]$") && !value.endsWith("px")) { error = new TwoError( "only pixel values are supported with the " + key + " attribute." ); console.warn(error.name, error.message); } elem.translation[key] = parseFloat(value); break; case "font-family": if (elem instanceof Text) { elem.family = value; } break; case "font-size": if (elem instanceof Text) { if (value.match("[a-z%]$") && !value.endsWith("px")) { error = new TwoError( "only pixel values are supported with the " + key + " attribute." ); console.warn(error.name, error.message); } elem.size = parseFloat(value); } break; case "font-weight": if (elem instanceof Text) { elem.weight = value; } break; case "font-style": if (elem instanceof Text) { elem.style = value; } break; case "text-decoration": if (elem instanceof Text) { elem.decoration = value; } break; case "line-height": if (elem instanceof Text) { elem.leading = value; } break; } } if (Object.keys(node.dataset).length) elem.dataset = node.dataset; return styles; } function updateDefsCache(node, defsCache) { for (let i = 0, l = node.childNodes.length; i < l; i++) { const n = node.childNodes[i]; if (!n.id) continue; const tagName = getTagName(node.nodeName); if (tagName === "#text") continue; defsCache.add(n.id, n); } } function getScene(node) { while (node.parent) { node = node.parent; } return node.scene; } var read = { svg: function(node) { const defs = read.defs.current = new Registry(); const elements = node.getElementsByTagName("defs"); for (let i = 0; i < elements.length; i++) { updateDefsCache(elements[i], defs); } const svg2 = read.g.call(this, node); const viewBox = node.getAttribute("viewBox"); const x = node.getAttribute("x"); const y = node.getAttribute("y"); const width = node.getAttribute("width"); const height = node.getAttribute("height"); svg2.defs = defs; const viewBoxExists = viewBox !== null; const xExists = x !== null; const yExists = y !== null; const widthExists = width !== null; const heightExists = height !== null; if (xExists) { svg2.x = parseFloat(x.replace(regex2.unitSuffix, "")); } if (yExists) { svg2.y = parseFloat(y.replace(regex2.unitSuffix, "")); } if (widthExists) { svg2.width = parseFloat(width.replace(regex2.unitSuffix, "")); } if (heightExists) { svg2.height = parseFloat(height.replace(regex2.unitSuffix, "")); } if (viewBoxExists) { applySvgViewBox(svg2, viewBox); } delete read.defs.current; return svg2; }, defs: function(node) { return null; }, use: function(node, styles) { let error; const href = node.getAttribute("href") || node.getAttribute("xlink:href"); if (!href) { error = new TwoError("encountered <use /> with no href."); console.warn(error.name, error.message); return null; } const id = href.slice(1); if (!read.defs.current.contains(id)) { error = new TwoError( "unable to find element for reference " + href + "." ); console.warn(error.name, error.message); return null; } const template = read.defs.current.get(id); const fullNode = template.cloneNode(true); for (let i = 0; i < node.attributes.length; i++) { const attr = node.attributes[i]; const ca = overwriteAttrs.includes(attr.nodeName); const cb = !fullNode.hasAttribute(attr.nodeName); if (ca || cb) { fullNode.setAttribute(attr.nodeName, attr.value); } } const tagName = getTagName(fullNode.nodeName); return read[tagName].call(this, fullNode, styles); }, g: function(node, parentStyles) { const group = new Group(); applySvgAttributes.call(this, node, group, parentStyles); this.add(group); const styles = getSvgStyles.call(this, node); for (let i = 0, l = node.childNodes.length; i < l; i++) { const n = node.childNodes[i]; const tag = n.nodeName; if (!tag) return; const tagName = getTagName(tag); if (tagName in read) { const o = read[tagName].call(group, n, styles); if (!!o && !o.parent) { group.add(o); } } } return group; }, polygon: function(node, parentStyles) { let points; if (typeof node === "string") { points = node; } else { points = node.getAttribute("points"); } const verts = []; points.replace( /(-?[\d.eE-]+)[,|\s](-?[\d.eE-]+)/g, function(match, p1, p2) { verts.push(new Anchor(parseFloat(p1), parseFloat(p2))); } ); const poly = new Path(verts, true); poly.stroke = "none"; poly.fill = "black"; applySvgAttributes.call(this, node, poly, parentStyles); return poly; }, polyline: function(node, parentStyles) { const poly = read.polygon.call(this, node, parentStyles); poly.closed = false; return poly; }, path: function(node, parentStyles) { let path; if (typeof node === "string") { path = node; node = null; } else { path = node.getAttribute("d"); } let points = []; let closed2 = false, relative = false; if (path) { let coord = new Anchor(); let control, coords; let commands = path.match(/[a-df-z][^a-df-z]*/gi); const last = commands.length - 1; _.each(commands.slice(0), function(command, i) { const items = command.slice(1).trim().match(regex2.path); const type = command[0]; const lower = type.toLowerCase(); let bin, j, l, ct, times; const result = []; if (i === 0) { commands = []; } switch (lower) { case "h": case "v": if (items.length > 1) { bin = 1; } break; case "m": case "l": case "t": if (items.length > 2) { bin = 2; } break; case "s": case "q": if (items.length > 4) { bin = 4; } break; case "c": if (items.length > 6) { bin = 6; } break; case "a": if (items.length > 7) { bin = 7; } break; } if (bin) { for (j = 0, l = items.length, times = 0; j < l; j += bin) { ct = type; if (times > 0) { switch (type) { case "m": ct = "l"; break; case "M": ct = "L"; break; } } result.push(ct + items.slice(j, j + bin).join(" ")); times++; } commands = Array.prototype.concat.apply(commands, result); } else { commands.push(command); } }); _.each(commands, function(command, i) { let result, x, y; const type = command[0]; const lower = type.toLowerCase(); coords = command.slice(1).trim().match(regex2.path); relative = type === lower; let x1, y1, x2, y2, x3, y3, x4, y4, reflection; let a, b; let anchor2, rx, ry, xAxisRotation, largeArcFlag, sweepFlag; switch (lower) { case "z": if (i >= last) { closed2 = true; } else { x = coord.x; y = coord.y; result = new Anchor( x, y, void 0, void 0, void 0, void 0, Commands.close ); for (let j = points.length - 1; j >= 0; j--) { const point = points[j]; if (/m/i.test(point.command)) { coord = point; break; } } } break; case "m": case "l": control = void 0; x = parseFloat(coords[0]); y = parseFloat(coords[1]); result = new Anchor( x, y, void 0, void 0, void 0, void 0, /m/i.test(lower) ? Commands.move : Commands.line ); if (relative) { result.addSelf(coord); } coord = result; break; case "h": case "v": a = /h/i.test(lower) ? "x" : "y"; b = /x/i.test(a) ? "y" : "x"; result = new Anchor( void 0, void 0, void 0, void 0, void 0, void 0, Commands.line ); result[a] = parseFloat(coords[0]); result[b] = coord[b]; if (relative) { result[a] += coord[a]; } coord = result; break; case "c": case "s": x1 = coord.x; y1 = coord.y; if (!control) { control = new Vector(); } if (/c/i.test(lower)) { x2 = parseFloat(coords[0]); y2 = parseFloat(coords[1]); x3 = parseFloat(coords[2]); y3 = parseFloat(coords[3]); x4 = parseFloat(coords[4]); y4 = parseFloat(coords[5]); } else { reflection = getReflection(coord, control, relative); x2 = reflection.x; y2 = reflection.y; x3 = parseFloat(coords[0]); y3 = parseFloat(coords[1]); x4 = parseFloat(coords[2]); y4 = parseFloat(coords[3]); } if (relative) { x2 += x1; y2 += y1; x3 += x1; y3 += y1; x4 += x1; y4 += y1; } coord.controls.right.set(x2 - coord.x, y2 - coord.y); result = new Anchor( x4, y4, x3 - x4, y3 - y4, void 0, void 0, Commands.curve ); coord = result; control = result.controls.left; break; case "t": case "q": x1 = coord.x; y1 = coord.y; if (!control) { control = new Vector(); } if (/q/i.test(lower)) { x2 = parseFloat(coords[0]); y2 = parseFloat(coords[1]); x3 = parseFloat(coords[0]); y3 = parseFloat(coords[1]); x4 = parseFloat(coords[2]); y4 = parseFloat(coords[3]); } else { reflection = getReflection(coord, control, relative); x2 = reflection.x; y2 = reflection.y; x3 = reflection.x; y3 = reflection.y; x4 = parseFloat(coords[0]); y4 = parseFloat(coords[1]); } if (relative) { x2 += x1; y2 += y1; x3 += x1; y3 += y1; x4 += x1; y4 += y1; } coord.controls.right.set( (x2 - coord.x) * 0.33, (y2 - coord.y) * 0.33 ); result = new Anchor( x4, y4, x3 - x4, y3 - y4, void 0, void 0, Commands.curve ); coord = result; control = result.controls.left; break; case "a": x1 = coord.x; y1 = coord.y; rx = parseFloat(coords[0]); ry = parseFloat(coords[1]); xAxisRotation = parseFloat(coords[2]); largeArcFlag = parseFloat(coords[3]); sweepFlag = parseFloat(coords[4]); x4 = parseFloat(coords[5]); y4 = parseFloat(coords[6]); if (relative) { x4 += x1; y4 += y1; } anchor2 = new Anchor(x4, y4); anchor2.command = Commands.arc; anchor2.rx = rx; anchor2.ry = ry; anchor2.xAxisRotation = xAxisRotation; anchor2.largeArcFlag = largeArcFlag; anchor2.sweepFlag = sweepFlag; result = anchor2; coord = anchor2; control = void 0; break; } if (result) { if (Array.isArray(result)) { points = points.concat(result); } else { points.push(result); } } }); } path = new Path(points, closed2, void 0, true); path.stroke = "none"; path.fill = "black"; const rect = path.getBoundingClientRect(true); rect.centroid = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; _.each(path.vertices, function(v) { v.subSelf(rect.centroid); }); applySvgAttributes.call(this, node, path, parentStyles); path.translation.addSelf(rect.centroid); return path; }, circle: function(node, parentStyles) { const x = parseFloat(node.getAttribute("cx")); const y = parseFloat(node.getAttribute("cy")); const r = parseFloat(node.getAttribute("r")); const circle = new Circle(0, 0, r); circle.stroke = "none"; circle.fill = "black"; applySvgAttributes.call(this, node, circle, parentStyles); circle.translation.x = x; circle.translation.y = y; return circle; }, ellipse: function(node, parentStyles) { const x = parseFloat(node.getAttribute("cx")); const y = parseFloat(node.getAttribute("cy")); const width = parseFloat(node.getAttribute("rx")); const height = parseFloat(node.getAttribute("ry")); const ellipse = new Ellipse(0, 0, width, height); ellipse.stroke = "none"; ellipse.fill = "black"; applySvgAttributes.call(this, node, ellipse, parentStyles); ellipse.translation.x = x; ellipse.translation.y = y; return ellipse; }, rect: function(node, parentStyles) { const rx = parseFloat(node.getAttribute("rx")); const ry = parseFloat(node.getAttribute("ry")); if (!_.isNaN(rx) || !_.isNaN(ry)) { return read["rounded-rect"](node); } const width = parseFloat(node.getAttribute("width")); const height = parseFloat(node.getAttribute("height")); const w2 = width / 2; const h2 = height / 2; const rect = new Rectangle(0, 0, width, height); rect.stroke = "none"; rect.fill = "black"; applySvgAttributes.call(this, node, rect, parentStyles); rect.translation.x += w2; rect.translation.y += h2; return rect; }, "rounded-rect": function(node, parentStyles) { const rx = parseFloat(node.getAttribute("rx")) || 0; const ry = parseFloat(node.getAttribute("ry")) || 0; const width = parseFloat(node.getAttribute("width")); const height = parseFloat(node.getAttribute("height")); const w2 = width / 2; const h2 = height / 2; const radius = new Vector(rx, ry); const rect = new RoundedRectangle(0, 0, width, height, radius); rect.stroke = "none"; rect.fill = "black"; applySvgAttributes.call(this, node, rect, parentStyles); rect.translation.x += w2; rect.translation.y += h2; return rect; }, line: function(node, parentStyles) { const x1 = parseFloat(node.getAttribute("x1")); const y1 = parseFloat(node.getAttribute("y1")); const x2 = parseFloat(node.getAttribute("x2")); const y2 = parseFloat(node.getAttribute("y2")); const line = new Line(x1, y1, x2, y2).noFill(); applySvgAttributes.call(this, node, line, parentStyles); return line; }, lineargradient: function(node, parentStyles) { let units = node.getAttribute("gradientUnits"); let spread = node.getAttribute("spreadMethod"); if (!units) { units = "objectBoundingBox"; } if (!spread) { spread = "pad"; } let x1 = parseFloat(node.getAttribute("x1") || 0); let y1 = parseFloat(node.getAttribute("y1") || 0); let x2 = parseFloat(node.getAttribute("x2") || 0); let y2 = parseFloat(node.getAttribute("y2") || 0); const ox = (x2 + x1) / 2; const oy = (y2 + y1) / 2; if (/userSpaceOnUse/i.test(units)) { x1 -= ox; y1 -= oy; x2 -= ox; y2 -= oy; } const stops = []; for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; let offset = child.getAttribute("offset"); if (/%/gi.test(offset)) { offset = parseFloat(offset.replace(/%/gi, "")) / 100; } offset = parseFloat(offset); let color = child.getAttribute("stop-color"); let opacity = child.getAttribute("stop-opacity"); let style = child.getAttribute("style"); let matches; if (color === null) { matches = style ? style.match(/stop-color:\s?([#a-fA-F0-9]*)/) : false; color = matches && matches.length > 1 ? matches[1] : void 0; } if (opacity === null) { matches = style ? style.match(/stop-opacity:\s?([0-9.-]*)/) : false; opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1; } else { opacity = parseFloat(opacity); } stops.push(new Stop(offset, color, opacity)); } const gradient = new LinearGradient(x1, y1, x2, y2, stops); gradient.spread = spread; gradient.units = units; applySvgAttributes.call(this, node, gradient, parentStyles); return gradient; }, radialgradient: function(node, parentStyles) { let units = node.getAttribute("gradientUnits"); let spread = node.getAttribute("spreadMethod"); if (!units) { units = "objectBoundingBox"; } if (!spread) { spread = "pad"; } let cx = parseFloat(node.getAttribute("cx")) || 0; let cy = parseFloat(node.getAttribute("cy")) || 0; let r = parseFloat(node.getAttribute("r")); let fx = parseFloat(node.getAttribute("fx")); let fy = parseFloat(node.getAttribute("fy")); if (_.isNaN(fx)) { fx = cx; } if (_.isNaN(fy)) { fy = cy; } const ox = Math.abs(cx + fx) / 2; const oy = Math.abs(cy + fy) / 2; if (/userSpaceOnUse/i.test(units)) { cx -= ox; cy -= oy; fx -= ox; fy -= oy; } const stops = []; for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; let offset = child.getAttribute("offset"); if (/%/gi.test(offset)) { offset = parseFloat(offset.replace(/%/gi, "")) / 100; } offset = parseFloat(offset); let color = child.getAttribute("stop-color"); let opacity = child.getAttribute("stop-opacity"); let style = child.getAttribute("style"); let matches; if (color === null) { matches = style ? style.match(/stop-color:\s?([#a-fA-F0-9]*)/) : false; color = matches && matches.length > 1 ? matches[1] : void 0; } if (opacity === null) { matches = style ? style.match(/stop-opacity:\s?([0-9.-]*)/) : false; opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1; } else { opacity = parseFloat(opacity); } stops.push(new Stop(offset, color, opacity)); } const gradient = new RadialGradient(cx, cy, r, stops, fx, fy); gradient.spread = spread; gradient.units = units; applySvgAttributes.call(this, node, gradient, parentStyles); return gradient; }, text: function(node, parentStyles) { const alignment = getAlignment(node.getAttribute("text-anchor")) || "left"; const baseline = getBaseline(node) || "baseline"; let message = ""; if (node.childNodes.length > 0 && node.childNodes[0].tagName === "TSPAN") { message = node.childNodes[0].textContent; } else { message = node.textContent; } const text = new Text(message); applySvgAttributes.call(this, node, text, parentStyles); text.alignment = alignment; text.baseline = baseline; return text; }, clippath: function(node, parentStyles) { if (read.defs.current && !read.defs.current.contains(node.id)) { read.defs.current.add(node.id, node); } return null; }, image: function(node, parentStyles) { let error; const href = node.getAttribute("href") || node.getAttribute("xlink:href"); if (!href) { error = new TwoError("encountered <image /> with no href."); console.warn(error.name, error.message); return null; } const x = parseFloat(node.getAttribute("x")) || 0; const y = parseFloat(node.getAttribute("y")) || 0; const width = parseFloat(node.getAttribute("width")); const height = parseFloat(node.getAttribute("height")); const sprite = new Sprite(href, x, y); if (!_.isNaN(width)) { sprite.width = width; } if (!_.isNaN(height)) { sprite.height = height; } applySvgAttributes.call(this, node, sprite, parentStyles); return sprite; } }; // src/utils/xhr.js function xhr(path, callback) { const xhr2 = new XMLHttpRequest(); xhr2.open("GET", path); xhr2.onreadystatechange = function() { if (xhr2.readyState === 4 && xhr2.status === 200) { callback(xhr2.responseText); } }; xhr2.send(); return xhr2; } // src/effects/image-sequence.js var _ImageSequence = class extends Rectangle { _flagTextures = false; _flagFrameRate = false; _flagIndex = false; _amount = 1; _duration = 0; _index = 0; _startTime = 0; _playing = false; _firstFrame = 0; _lastFrame = 0; _loop = true; _textures = null; _frameRate = 0; _origin = null; constructor(paths, ox, oy, frameRate) { super(ox, oy, 0, 0); for (let prop in proto23) { Object.defineProperty(this, prop, proto23[prop]); } this._renderer.flagTextures = FlagTextures.bind(this); this._renderer.bindTextures = BindTextures.bind(this); this._renderer.unbindTextures = UnbindTextures.bind(this); this.noStroke(); this.noFill(); if (Array.isArray(paths)) { this.textures = paths.map(GenerateTexture.bind(this)); } else { this.textures = [GenerateTexture(paths)]; } this.origin = new Vector(); this._update(); if (typeof frameRate === "number") { this.frameRate = frameRate; } else { this.frameRate = _ImageSequence.DefaultFrameRate; } this.index = 0; } static fromObject(obj) { const sequence = new _ImageSequence().copy(obj); if ("id" in obj) { sequence.id = obj.id; } return sequence; } copy(imageSequence) { super.copy.call(this, imageSequence); for (let i = 0; i < _ImageSequence.Properties.length; i++) { const k = _ImageSequence.Properties[i]; if (k in imageSequence) { this[k] = imageSequence[k]; } } return this; } play(firstFrame, lastFrame, onLastFrame) { this._playing = true; this._firstFrame = 0; this._lastFrame = this.amount - 1; this._startTime = _.performance.now(); if (typeof firstFrame === "number") { this._firstFrame = firstFrame; } if (typeof lastFrame === "number") { this._lastFrame = lastFrame; } if (typeof onLastFrame === "function") { this._onLastFrame = onLastFrame; } else { delete this._onLastFrame; } if (this._index !== this._firstFrame) { this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate; } return this; } pause() { this._playing = false; return this; } stop() { this._playing = false; this._index = this._firstFrame; return this; } clone(parent) { const clone = new _ImageSequence( this.textures, this.translation.x, this.translation.y, this.frameRate ); clone._loop = this._loop; if (this._playing) { clone.play(); } if (parent) { parent.add(clone); } return clone; } toObject() { const object = super.toObject.call(this); object.textures = this.textures.map(function(texture) { return texture.toObject(); }); object.frameRate = this.frameRate; object.index = this.index; object.firstFrame = this.firstFrame; object.lastFrame = this.lastFrame; object.loop = this.loop; return object; } _update() { const effect = this._textures; let width, height, elapsed, amount, duration, texture; let index, frames; if (effect) { if (this._flagTextures) { this._amount = effect.length; } if (this._flagFrameRate) { this._duration = 1e3 * this._amount / this._frameRate; } if (this._playing && this._frameRate > 0) { amount = this._amount; if (_.isNaN(this._lastFrame)) { this._lastFrame = amount - 1; } elapsed = _.performance.now() - this._startTime; frames = this._lastFrame + 1; duration = 1e3 * (frames - this._firstFrame) / this._frameRate; if (this._loop) { elapsed = elapsed % duration; } else { elapsed = Math.min(elapsed, duration); } index = lerp(this._firstFrame, frames, elapsed / duration); index = Math.floor(index); if (index !== this._index) { this._index = index; texture = effect[this._index]; if (texture.loaded) { width = texture.image.width; height = texture.image.height; if (this.width !== width) { this.width = width; } if (this.height !== height) { this.height = height; } this.fill = texture; if (index >= this._lastFrame - 1 && this._onLastFrame) { this._onLastFrame(); } } } } else if (this._flagIndex || !(this.fill instanceof Texture)) { texture = effect[this._index]; if (texture.loaded) { width = texture.image.width; height = texture.image.height; if (this.width !== width) { this.width = width; } if (this.height !== height) { this.height = height; } } this.fill = texture; } } super._update.call(this); return this; } flagReset() { this._flagTextures = this._flagFrameRate = false; super.flagReset.call(this); return this; } }; var ImageSequence = _ImageSequence; __publicField(ImageSequence, "Properties", [ "textures", "frameRate", "index", "firstFrame", "lastFrame", "loop" ]); __publicField(ImageSequence, "DefaultFrameRate", 30); var proto23 = { frameRate: { enumerable: true, get: function() { return this._frameRate; }, set: function(v) { this._frameRate = v; this._flagFrameRate = true; } }, index: { enumerable: true, get: function() { return this._index; }, set: function(v) { this._index = v; this._flagIndex = true; } }, textures: { enumerable: true, get: function() { return this._textures; }, set: function(textures) { const bindTextures = this._renderer.bindTextures; const unbindTextures = this._renderer.unbindTextures; if (this._textures) { this._textures.unbind(Events.Types.insert, bindTextures).unbind(Events.Types.remove, unbindTextures); } this._textures = new Collection((textures || []).slice(0)); this._textures.bind(Events.Types.insert, bindTextures).bind(Events.Types.remove, unbindTextures); bindTextures(this._textures); } }, firstFrame: { enumerable: true, get: function() { return this._firstFrame; }, set: function(v) { this._firstFrame = v; } }, lastFrame: { enumerable: true, get: function() { return this._lastFrame; }, set: function(v) { this._lastFrame = v; } }, loop: { enumerable: true, get: function() { return this._loop; }, set: function(v) { this._loop = !!v; } } }; function FlagTextures() { this._flagTextures = true; } function BindTextures(items) { let i = items.length; while (i--) { items[i].bind(Events.Types.change, this._renderer.flagTextures); } this._renderer.flagTextures(); } function UnbindTextures(items) { let i = items.length; while (i--) { items[i].unbind(Events.Types.change, this._renderer.flagTextures); } this._renderer.flagTextures(); } function GenerateTexture(obj) { if (obj instanceof Texture) { return obj; } else if (typeof obj === "string") { return new Texture(obj); } } // src/renderers/canvas.js var emptyArray = []; var max4 = Math.max; var min4 = Math.min; var abs = Math.abs; var sin6 = Math.sin; var cos6 = Math.cos; var acos = Math.acos; var sqrt = Math.sqrt; var canvas2 = { isHidden: /(undefined|none|transparent)/i, alignments: { left: "start", middle: "center", right: "end" }, baselines: { top: "top", middle: "middle", bottom: "bottom", baseline: "alphabetic" }, getRendererType: function(type) { return type in canvas2 ? type : "path"; }, group: { renderChild: function(child) { const prop = canvas2.getRendererType(child._renderer.type); canvas2[prop].render.call(child, this.ctx, true, this.clip); }, render: function(ctx) { if (!this._visible) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const matrix = this._matrix.elements; const parent = this.parent; this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1); const mask = this._mask; const defaultMatrix = isDefaultMatrix(matrix); const shouldIsolate = !defaultMatrix || !!mask; if (!this._renderer.context) { this._renderer.context = {}; } this._renderer.context.ctx = ctx; if (shouldIsolate) { ctx.save(); if (!defaultMatrix) { ctx.transform( matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5] ); } } if (mask) { const prop = canvas2.getRendererType(mask._renderer.type); canvas2[prop].render.call(mask, ctx, true); } if (this._opacity > 0 && this._scale !== 0) { for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; const prop = canvas2.getRendererType(child._renderer.type); canvas2[prop].render.call(child, ctx); } } if (shouldIsolate) { ctx.restore(); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, path: { render: function(ctx, forced, parentClipped) { let matrix, stroke, linewidth, fill, opacity, visible, cap, join, miter, closed2, commands, length, last, prev, a, b, c, d, ux, uy, vx, vy, ar, bl, br, cl, x, y, mask, clip, defaultMatrix, isOffset, dashes, po; po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1; mask = this._mask; clip = this._clip; opacity = this._opacity * (po || 1); visible = this._visible; if (!forced && (!visible || clip || opacity === 0)) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); matrix = this._matrix.elements; stroke = this._stroke; linewidth = this._linewidth; fill = this._fill; cap = this._cap; join = this._join; miter = this._miter; closed2 = this._closed; commands = this._renderer.vertices; length = commands.length; last = length - 1; defaultMatrix = isDefaultMatrix(matrix); dashes = this.dashes; if (!defaultMatrix) { ctx.save(); ctx.transform( matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5] ); } if (mask) { const prop = canvas2.getRendererType(mask._renderer.type); canvas2[prop].render.call(mask, ctx, true); } if (fill) { if (typeof fill === "string") { ctx.fillStyle = fill; } else { const prop = canvas2.getRendererType(fill._renderer.type); canvas2[prop].render.call(fill, ctx, this); ctx.fillStyle = fill._renderer.effect; } } if (stroke) { if (typeof stroke === "string") { ctx.strokeStyle = stroke; } else { const prop = canvas2.getRendererType(stroke._renderer.type); canvas2[prop].render.call(stroke, ctx, this); ctx.strokeStyle = stroke._renderer.effect; } if (linewidth) { ctx.lineWidth = linewidth; } if (miter) { ctx.miterLimit = miter; } if (join) { ctx.lineJoin = join; } if (!closed2 && cap) { ctx.lineCap = cap; } } if (typeof opacity === "number") { ctx.globalAlpha = opacity; } if (dashes && dashes.length > 0) { ctx.lineDashOffset = dashes.offset || 0; ctx.setLineDash(dashes); } ctx.beginPath(); let rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay; for (let i = 0; i < length; i++) { b = commands[i]; x = b.x; y = b.y; switch (b.command) { case Commands.close: ctx.closePath(); break; case Commands.arc: rx = b.rx; ry = b.ry; xAxisRotation = b.xAxisRotation; largeArcFlag = b.largeArcFlag; sweepFlag = b.sweepFlag; prev = closed2 ? mod(i - 1, length) : max4(i - 1, 0); a = commands[prev]; ax = a.x; ay = a.y; canvas2.renderSvgArcCommand( ctx, ax, ay, rx, ry, largeArcFlag, sweepFlag, xAxisRotation, x, y ); break; case Commands.curve: prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0); a = commands[prev]; ar = a.controls && a.controls.right || Vector.zero; bl = b.controls && b.controls.left || Vector.zero; if (a._relative) { vx = ar.x + a.x; vy = ar.y + a.y; } else { vx = ar.x; vy = ar.y; } if (b._relative) { ux = bl.x + b.x; uy = bl.y + b.y; } else { ux = bl.x; uy = bl.y; } ctx.bezierCurveTo(vx, vy, ux, uy, x, y); if (i >= last && closed2) { c = d; br = b.controls && b.controls.right || Vector.zero; cl = c.controls && c.controls.left || Vector.zero; if (b._relative) { vx = br.x + b.x; vy = br.y + b.y; } else { vx = br.x; vy = br.y; } if (c._relative) { ux = cl.x + c.x; uy = cl.y + c.y; } else { ux = cl.x; uy = cl.y; } x = c.x; y = c.y; ctx.bezierCurveTo(vx, vy, ux, uy, x, y); } break; case Commands.line: ctx.lineTo(x, y); break; case Commands.move: d = b; ctx.moveTo(x, y); break; } } if (closed2) { ctx.closePath(); } if (!clip && !parentClipped) { if (!canvas2.isHidden.test(fill)) { isOffset = fill._renderer && fill._renderer.offset; if (isOffset) { ctx.save(); ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y); ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); } ctx.fill(); if (isOffset) { ctx.restore(); } } if (!canvas2.isHidden.test(stroke)) { isOffset = stroke._renderer && stroke._renderer.offset; if (isOffset) { ctx.save(); ctx.translate( -stroke._renderer.offset.x, -stroke._renderer.offset.y ); ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); ctx.lineWidth = linewidth / stroke._renderer.scale.x; } ctx.stroke(); if (isOffset) { ctx.restore(); } } } if (!defaultMatrix) { ctx.restore(); } if (clip && !parentClipped) { ctx.clip(); } if (dashes && dashes.length > 0) { ctx.setLineDash(emptyArray); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, points: { render: function(ctx, forced, parentClipped) { let me, stroke, linewidth, fill, opacity, visible, size, commands, length, b, x, y, defaultMatrix, isOffset, dashes, po; po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1; opacity = this._opacity * (po || 1); visible = this._visible; if (!forced && (!visible || opacity === 0)) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); me = this._matrix.elements; stroke = this._stroke; linewidth = this._linewidth; fill = this._fill; commands = this._renderer.collection; length = commands.length; defaultMatrix = isDefaultMatrix(me); dashes = this.dashes; size = this._size; if (!defaultMatrix) { ctx.save(); ctx.transform(me[0], me[3], me[1], me[4], me[2], me[5]); } if (fill) { if (typeof fill === "string") { ctx.fillStyle = fill; } else { const prop = canvas2.getRendererType(fill._renderer.type); canvas2[prop].render.call(fill, ctx, this); ctx.fillStyle = fill._renderer.effect; } } if (stroke) { if (typeof stroke === "string") { ctx.strokeStyle = stroke; } else { const prop = canvas2.getRendererType(stroke._renderer.type); canvas2[prop].render.call(stroke, ctx, this); ctx.strokeStyle = stroke._renderer.effect; } if (linewidth) { ctx.lineWidth = linewidth; } } if (typeof opacity === "number") { ctx.globalAlpha = opacity; } if (dashes && dashes.length > 0) { ctx.lineDashOffset = dashes.offset || 0; ctx.setLineDash(dashes); } ctx.beginPath(); let radius = size * 0.5, m; if (!this._sizeAttenuation) { m = this.worldMatrix.elements; m = decomposeMatrix(m[0], m[3], m[1], m[4], m[2], m[5]); radius /= Math.max(m.scaleX, m.scaleY); } for (let i = 0; i < length; i++) { b = commands[i]; x = b.x; y = b.y; ctx.moveTo(x + radius, y); ctx.arc(x, y, radius, 0, TWO_PI); } if (!parentClipped) { if (!canvas2.isHidden.test(fill)) { isOffset = fill._renderer && fill._renderer.offset; if (isOffset) { ctx.save(); ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y); ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); } ctx.fill(); if (isOffset) { ctx.restore(); } } if (!canvas2.isHidden.test(stroke)) { isOffset = stroke._renderer && stroke._renderer.offset; if (isOffset) { ctx.save(); ctx.translate( -stroke._renderer.offset.x, -stroke._renderer.offset.y ); ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); ctx.lineWidth = linewidth / stroke._renderer.scale.x; } ctx.stroke(); if (isOffset) { ctx.restore(); } } } if (!defaultMatrix) { ctx.restore(); } if (dashes && dashes.length > 0) { ctx.setLineDash(emptyArray); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, text: { render: function(ctx, forced, parentClipped) { const po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1; const opacity = this._opacity * po; const visible = this._visible; const mask = this._mask; const clip = this._clip; if (!forced && (!visible || clip || opacity === 0)) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const matrix = this._matrix.elements; const stroke = this._stroke; const linewidth = this._linewidth; const fill = this._fill; const decoration = this._decoration; const direction = this._direction; const defaultMatrix = isDefaultMatrix(matrix); const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset; const dashes = this.dashes; const alignment = canvas2.alignments[this._alignment] || this._alignment; const baseline = canvas2.baselines[this._baseline] || this._baseline; let a, b, c, d, e, sx, sy, x1, y1, x2, y2; if (!defaultMatrix) { ctx.save(); ctx.transform( matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5] ); } if (mask) { const prop = canvas2.getRendererType(mask._renderer.type); canvas2[prop].render.call(mask, ctx, true); } if (!isOffset) { ctx.font = [ this._style, this._weight, this._size + "px/" + this._leading + "px", this._family ].join(" "); } ctx.textAlign = alignment; ctx.textBaseline = baseline; ctx.direction = direction; if (fill) { if (typeof fill === "string") { ctx.fillStyle = fill; } else { const prop = canvas2.getRendererType(fill._renderer.type); canvas2[prop].render.call(fill, ctx, this); ctx.fillStyle = fill._renderer.effect; } } if (stroke) { if (typeof stroke === "string") { ctx.strokeStyle = stroke; } else { const prop = canvas2.getRendererType(stroke._renderer.type); canvas2[prop].render.call(stroke, ctx, this); ctx.strokeStyle = stroke._renderer.effect; } if (linewidth) { ctx.lineWidth = linewidth; } } if (typeof opacity === "number") { ctx.globalAlpha = opacity; } if (dashes && dashes.length > 0) { ctx.lineDashOffset = dashes.offset || 0; ctx.setLineDash(dashes); } if (!clip && !parentClipped) { if (!canvas2.isHidden.test(fill)) { if (fill._renderer && fill._renderer.offset) { sx = fill._renderer.scale.x; sy = fill._renderer.scale.y; ctx.save(); ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y); ctx.scale(sx, sy); a = this._size / fill._renderer.scale.y; b = this._leading / fill._renderer.scale.y; ctx.font = [ this._style, this._weight, a + "px/", b + "px", this._family ].join(" "); c = fill._renderer.offset.x / fill._renderer.scale.x; d = fill._renderer.offset.y / fill._renderer.scale.y; ctx.fillText(this.value, c, d); ctx.restore(); } else { ctx.fillText(this.value, 0, 0); } } if (!canvas2.isHidden.test(stroke)) { if (stroke._renderer && stroke._renderer.offset) { sx = stroke._renderer.scale.x; sy = stroke._renderer.scale.y; ctx.save(); ctx.translate( -stroke._renderer.offset.x, -stroke._renderer.offset.y ); ctx.scale(sx, sy); a = this._size / stroke._renderer.scale.y; b = this._leading / stroke._renderer.scale.y; ctx.font = [ this._style, this._weight, a + "px/", b + "px", this._family ].join(" "); c = stroke._renderer.offset.x / stroke._renderer.scale.x; d = stroke._renderer.offset.y / stroke._renderer.scale.y; e = linewidth / stroke._renderer.scale.x; ctx.lineWidth = e; ctx.strokeText(this.value, c, d); ctx.restore(); } else { ctx.strokeText(this.value, 0, 0); } } } if (/(underline|strikethrough)/i.test(decoration)) { const metrics = ctx.measureText(this.value); let scalar = 1; switch (decoration) { case "underline": y1 = metrics.actualBoundingBoxDescent; y2 = metrics.actualBoundingBoxDescent; break; case "strikethrough": y1 = 0; y2 = 0; scalar = 0.5; break; } switch (baseline) { case "top": y1 += this._size * scalar; y2 += this._size * scalar; break; case "baseline": case "bottom": y1 -= this._size * scalar; y2 -= this._size * scalar; break; } switch (alignment) { case "left": case "start": x1 = 0; x2 = metrics.width; break; case "right": case "end": x1 = -metrics.width; x2 = 0; break; default: x1 = -metrics.width / 2; x2 = metrics.width / 2; } ctx.lineWidth = Math.max(Math.floor(this._size / 15), 1); ctx.strokeStyle = ctx.fillStyle; ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); } if (!defaultMatrix) { ctx.restore(); } if (clip && !parentClipped) { ctx.clip(); } if (dashes && dashes.length > 0) { ctx.setLineDash(emptyArray); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, "linear-gradient": { render: function(ctx, parent) { if (!parent) { return; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) { let rect; let lx = this.left._x; let ly = this.left._y; let rx = this.right._x; let ry = this.right._y; if (/objectBoundingBox/i.test(this._units)) { rect = parent.getBoundingClientRect(true); lx = (lx - 0.5) * rect.width; ly = (ly - 0.5) * rect.height; rx = (rx - 0.5) * rect.width; ry = (ry - 0.5) * rect.height; } this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry); for (let i = 0; i < this.stops.length; i++) { const stop = this.stops[i]; this._renderer.effect.addColorStop(stop._offset, stop._color); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, "radial-gradient": { render: function(ctx, parent) { if (!parent) { return; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) { let rect; let cx = this.center._x; let cy = this.center._y; let fx = this.focal._x; let fy = this.focal._y; let radius = this._radius; if (/objectBoundingBox/i.test(this._units)) { rect = parent.getBoundingClientRect(true); cx = (cx - 0.5) * rect.width * 0.5; cy = (cy - 0.5) * rect.height * 0.5; fx = (fx - 0.5) * rect.width * 0.5; fy = (fy - 0.5) * rect.height * 0.5; radius *= Math.min(rect.width, rect.height); } this._renderer.effect = ctx.createRadialGradient( cx, cy, 0, fx, fy, radius ); for (let i = 0; i < this.stops.length; i++) { const stop = this.stops[i]; this._renderer.effect.addColorStop(stop._offset, stop._color); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, texture: { render: function(ctx) { if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const image = this.image; if (!this._renderer.effect || (this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) { this._renderer.effect = ctx.createPattern(this.image, this._repeat); } if (this._flagOffset || this._flagLoaded || this._flagScale) { if (!(this._renderer.offset instanceof Vector)) { this._renderer.offset = new Vector(); } this._renderer.offset.x = -this._offset.x; this._renderer.offset.y = -this._offset.y; if (image) { this._renderer.offset.x += image.width / 2; this._renderer.offset.y += image.height / 2; if (this._scale instanceof Vector) { this._renderer.offset.x *= this._scale.x; this._renderer.offset.y *= this._scale.y; } else { this._renderer.offset.x *= this._scale; this._renderer.offset.y *= this._scale; } } } if (this._flagScale || this._flagLoaded) { if (!(this._renderer.scale instanceof Vector)) { this._renderer.scale = new Vector(); } if (this._scale instanceof Vector) { this._renderer.scale.copy(this._scale); } else { this._renderer.scale.set(this._scale, this._scale); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, renderSvgArcCommand: function(ctx, ax, ay, rx, ry, largeArcFlag, sweepFlag, xAxisRotation, x, y) { xAxisRotation = xAxisRotation * Math.PI / 180; rx = abs(rx); ry = abs(ry); const dx2 = (ax - x) / 2; const dy2 = (ay - y) / 2; const x1p = cos6(xAxisRotation) * dx2 + sin6(xAxisRotation) * dy2; const y1p = -sin6(xAxisRotation) * dx2 + cos6(xAxisRotation) * dy2; const x1ps = x1p * x1p; const y1ps = y1p * y1p; let rxs = rx * rx; let rys = ry * ry; const cr = x1ps / rxs + y1ps / rys; if (cr > 1) { const s = sqrt(cr); rx = s * rx; ry = s * ry; rxs = rx * rx; rys = ry * ry; } const dq = rxs * y1ps + rys * x1ps; const pq = (rxs * rys - dq) / dq; let q = sqrt(max4(0, pq)); if (largeArcFlag === sweepFlag) q = -q; const cxp = q * rx * y1p / ry; const cyp = -q * ry * x1p / rx; const cx = cos6(xAxisRotation) * cxp - sin6(xAxisRotation) * cyp + (ax + x) / 2; const cy = sin6(xAxisRotation) * cxp + cos6(xAxisRotation) * cyp + (ay + y) / 2; const startAngle = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry); const delta = svgAngle( (x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry ) % TWO_PI; const endAngle = startAngle + delta; const clockwise = sweepFlag === 0; renderArcEstimate( ctx, cx, cy, rx, ry, startAngle, endAngle, clockwise, xAxisRotation ); } }; var Renderer = class extends Events { constructor(params) { super(); const smoothing = params.smoothing !== false; this.domElement = params.domElement || document.createElement("canvas"); this.ctx = this.domElement.getContext("2d"); this.overdraw = params.overdraw || false; if (typeof this.ctx.imageSmoothingEnabled !== "undefined") { this.ctx.imageSmoothingEnabled = smoothing; } this.scene = new Group(); this.scene.parent = this; } setSize(width, height, ratio) { this.width = width; this.height = height; this.ratio = typeof ratio === "undefined" ? getRatio(this.ctx) : ratio; this.domElement.width = width * this.ratio; this.domElement.height = height * this.ratio; if (this.domElement.style) { _.extend(this.domElement.style, { width: width + "px", height: height + "px" }); } return this.trigger(Events.Types.resize, width, height, ratio); } render() { const isOne = this.ratio === 1; if (!isOne) { this.ctx.save(); this.ctx.scale(this.ratio, this.ratio); } if (!this.overdraw) { this.ctx.clearRect(0, 0, this.width, this.height); } canvas2.group.render.call(this.scene, this.ctx); if (!isOne) { this.ctx.restore(); } return this; } }; __publicField(Renderer, "Utils", canvas2); function renderArcEstimate(ctx, ox, oy, rx, ry, startAngle, endAngle, clockwise, xAxisRotation) { const delta = endAngle - startAngle; const epsilon = Curve.Tolerance.epsilon; const samePoints = Math.abs(delta) < epsilon; let deltaAngle = mod(delta, TWO_PI); if (deltaAngle < epsilon) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = TWO_PI; } } if (clockwise === true && !samePoints) { if (deltaAngle === TWO_PI) { deltaAngle = -TWO_PI; } else { deltaAngle = deltaAngle - TWO_PI; } } for (let i = 0; i < Constants.Resolution; i++) { const t = i / (Constants.Resolution - 1); const angle = startAngle + t * deltaAngle; let x = ox + rx * Math.cos(angle); let y = oy + ry * Math.sin(angle); if (xAxisRotation !== 0) { const cos7 = Math.cos(xAxisRotation); const sin7 = Math.sin(xAxisRotation); const tx = x - ox; const ty = y - oy; x = tx * cos7 - ty * sin7 + ox; y = tx * sin7 + ty * cos7 + oy; } ctx.lineTo(x, y); } } function svgAngle(ux, uy, vx, vy) { const dot = ux * vx + uy * vy; const len = sqrt(ux * ux + uy * uy) * sqrt(vx * vx + vy * vy); let ang = acos(max4(-1, min4(1, dot / len))); if (ux * vy - uy * vx < 0) { ang = -ang; } return ang; } function isDefaultMatrix(m) { return m[0] == 1 && m[3] == 0 && m[1] == 0 && m[4] == 1 && m[2] == 0 && m[5] == 0; } // src/renderers/svg.js var svg = { version: 1.1, ns: "http://www.w3.org/2000/svg", xlink: "http://www.w3.org/1999/xlink", alignments: { left: "start", center: "middle", right: "end" }, baselines: { top: "hanging", middle: "middle", bottom: "ideographic", baseline: "alphabetic" }, createElement: function(name, attrs) { const tag = name; const elem = document.createElementNS(svg.ns, tag); if (tag === "svg") { attrs = _.defaults(attrs || {}, { version: svg.version }); } if (attrs && Object.keys(attrs).length > 0) { svg.setAttributes(elem, attrs); } return elem; }, setAttributes: function(elem, attrs) { const keys = Object.keys(attrs); for (let i = 0; i < keys.length; i++) { if (/href/.test(keys[i])) { elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]); } else { elem.setAttribute(keys[i], attrs[keys[i]]); } } return this; }, removeAttributes: function(elem, attrs) { for (let key in attrs) { elem.removeAttribute(key); } return this; }, toString: function(points, closed2) { let l = points.length, last = l - 1, d, string = ""; for (let i = 0; i < l; i++) { const b = points[i]; const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0); const a = points[prev]; let command, c; let vx, vy, ux, uy, ar, bl, br, cl; let rx, ry, xAxisRotation, largeArcFlag, sweepFlag; let x = toFixed(b.x); let y = toFixed(b.y); switch (b.command) { case Commands.close: command = Commands.close; break; case Commands.arc: rx = b.rx; ry = b.ry; xAxisRotation = b.xAxisRotation; largeArcFlag = b.largeArcFlag; sweepFlag = b.sweepFlag; command = Commands.arc + " " + rx + " " + ry + " " + xAxisRotation + " " + largeArcFlag + " " + sweepFlag + " " + x + " " + y; break; case Commands.curve: ar = a.controls && a.controls.right || Vector.zero; bl = b.controls && b.controls.left || Vector.zero; if (a.relative) { vx = toFixed(ar.x + a.x); vy = toFixed(ar.y + a.y); } else { vx = toFixed(ar.x); vy = toFixed(ar.y); } if (b.relative) { ux = toFixed(bl.x + b.x); uy = toFixed(bl.y + b.y); } else { ux = toFixed(bl.x); uy = toFixed(bl.y); } command = (i === 0 ? Commands.move : Commands.curve) + " " + vx + " " + vy + " " + ux + " " + uy + " " + x + " " + y; break; case Commands.move: d = b; command = Commands.move + " " + x + " " + y; break; default: command = b.command + " " + x + " " + y; } if (i >= last && closed2) { if (b.command === Commands.curve) { c = d; br = b.controls && b.controls.right || b; cl = c.controls && c.controls.left || c; if (b.relative) { vx = toFixed(br.x + b.x); vy = toFixed(br.y + b.y); } else { vx = toFixed(br.x); vy = toFixed(br.y); } if (c.relative) { ux = toFixed(cl.x + c.x); uy = toFixed(cl.y + c.y); } else { ux = toFixed(cl.x); uy = toFixed(cl.y); } x = toFixed(c.x); y = toFixed(c.y); command += " C " + vx + " " + vy + " " + ux + " " + uy + " " + x + " " + y; } if (b.command !== Commands.close) { command += " Z"; } } string += command + " "; } return string; }, pointsToString: function(points, size) { let string = ""; const r = size * 0.5; for (let i = 0; i < points.length; i++) { const x = points[i].x; const y = points[i].y - r; string += Commands.move + " " + x + " " + y + " "; string += "a " + r + " " + r + " 0 1 0 0.001 0 Z"; } return string; }, getClip: function(shape, domElement) { let clip = shape._renderer.clip; if (!clip) { clip = shape._renderer.clip = svg.createElement("clipPath", { "clip-rule": "nonzero" }); } if (clip.parentNode === null) { domElement.defs.appendChild(clip); } return clip; }, getRendererType: function(type) { return type in svg ? type : "path"; }, defs: { update: function(domElement) { const { defs } = domElement; if (defs._flagUpdate) { const children = Array.prototype.slice.call(defs.children, 0); for (let i = 0; i < children.length; i++) { const child = children[i]; const id = child.id; const selector = `[fill="url(#${id})"],[stroke="url(#${id})"],[clip-path="url(#${id})"]`; const exists = domElement.querySelector(selector); if (!exists) { defs.removeChild(child); } } defs._flagUpdate = false; } } }, group: { appendChild: function(object) { const elem = object._renderer.elem; if (!elem) { return; } const tag = elem.nodeName; if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) { return; } this.elem.appendChild(elem); }, removeChild: function(object) { const elem = object._renderer.elem; if (!elem || elem.parentNode != this.elem) { return; } const tag = elem.nodeName; if (!tag) { return; } if (object._clip) { return; } this.elem.removeChild(elem); }, orderChild: function(object) { this.elem.appendChild(object._renderer.elem); }, renderChild: function(child) { const prop = svg.getRendererType(child._renderer.type); svg[prop].render.call(child, this); }, render: function(domElement) { if (!this._visible && !this._flagVisible || this._opacity === 0 && !this._flagOpacity) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); if (!this._renderer.elem) { this._renderer.elem = svg.createElement("g", { id: this.id }); domElement.appendChild(this._renderer.elem); } const flagMatrix = this._matrix.manual || this._flagMatrix; const context = { domElement, elem: this._renderer.elem }; if (flagMatrix) { this._renderer.elem.setAttribute( "transform", "matrix(" + this._matrix.toString() + ")" ); } for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; const prop = svg.getRendererType(child._renderer.type); svg[prop].render.call(child, domElement); } if (this._flagId) { this._renderer.elem.setAttribute("id", this._id); } if (this._flagOpacity) { this._renderer.elem.setAttribute("opacity", this._opacity); } if (this._flagVisible) { this._renderer.elem.setAttribute( "display", this._visible ? "inline" : "none" ); } if (this._flagClassName) { this._renderer.elem.setAttribute("class", this.classList.join(" ")); } if (this._flagAdditions) { this.additions.forEach(svg.group.appendChild, context); } if (this._flagSubtractions) { this.subtractions.forEach(svg.group.removeChild, context); } if (this._flagOrder) { this.children.forEach(svg.group.orderChild, context); } if (this._flagMask) { if (this._mask) { const prop = svg.getRendererType(this._mask._renderer.type); svg[prop].render.call(this._mask, domElement); this._renderer.elem.setAttribute( "clip-path", "url(#" + this._mask.id + ")" ); } else { this._renderer.elem.removeAttribute("clip-path"); } } if (this.dataset) { Object.assign(this._renderer.elem.dataset, this.dataset); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, path: { render: function(domElement) { if (this._opacity === 0 && !this._flagOpacity) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const changed = {}; const flagMatrix = this._matrix.manual || this._flagMatrix; if (flagMatrix) { changed.transform = "matrix(" + this._matrix.toString() + ")"; } if (this._flagId) { changed.id = this._id; } if (this._flagVertices) { const vertices = svg.toString(this._renderer.vertices, this._closed); changed.d = vertices; } if (this._fill && this._fill._renderer) { this._renderer.hasFillEffect = true; this._fill._update(); const prop = svg.getRendererType(this._fill._renderer.type); svg[prop].render.call(this._fill, domElement, true); } if (this._flagFill) { changed.fill = this._fill && this._fill.id ? "url(#" + this._fill.id + ")" : this._fill; if (this._renderer.hasFillEffect && typeof this._fill.id === "undefined") { domElement.defs._flagUpdate = true; delete this._renderer.hasFillEffect; } } if (this._stroke && this._stroke._renderer) { this._renderer.hasStrokeEffect = true; this._stroke._update(); const prop = svg.getRendererType(this._stroke._renderer.type); svg[prop].render.call(this._stroke, domElement, true); } if (this._flagStroke) { changed.stroke = this._stroke && this._stroke.id ? "url(#" + this._stroke.id + ")" : this._stroke; if (this._renderer.hasStrokeEffect && typeof this._stroke.id === "undefined") { domElement.defs._flagUpdate = true; delete this._renderer.hasStrokeEffect; } } if (this._flagLinewidth) { changed["stroke-width"] = this._linewidth; } if (this._flagOpacity) { changed["stroke-opacity"] = this._opacity; changed["fill-opacity"] = this._opacity; } if (this._flagClassName) { changed["class"] = this.classList.join(" "); } if (this._flagVisible) { changed.visibility = this._visible ? "visible" : "hidden"; } if (this._flagCap) { changed["stroke-linecap"] = this._cap; } if (this._flagJoin) { changed["stroke-linejoin"] = this._join; } if (this._flagMiter) { changed["stroke-miterlimit"] = this._miter; } if (this.dashes && this.dashes.length > 0) { changed["stroke-dasharray"] = this.dashes.join(" "); changed["stroke-dashoffset"] = this.dashes.offset || 0; } if (!this._renderer.elem) { changed.id = this._id; this._renderer.elem = svg.createElement("path", changed); domElement.appendChild(this._renderer.elem); } else { svg.setAttributes(this._renderer.elem, changed); } if (this._flagClip) { const clip = svg.getClip(this, domElement); const elem = this._renderer.elem; if (this._clip) { elem.removeAttribute("id"); clip.setAttribute("id", this.id); clip.appendChild(elem); } else { clip.removeAttribute("id"); elem.setAttribute("id", this.id); this.parent._renderer.elem.appendChild(elem); } } if (this._flagMask) { if (this._mask) { const prop = svg.getRendererType(this._mask._renderer.type); svg[prop].render.call(this._mask, domElement); this._renderer.elem.setAttribute( "clip-path", "url(#" + this._mask.id + ")" ); } else { this._renderer.elem.removeAttribute("clip-path"); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, points: { render: function(domElement) { if (this._opacity === 0 && !this._flagOpacity) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const changed = {}; const flagMatrix = this._matrix.manual || this._flagMatrix; if (flagMatrix) { changed.transform = "matrix(" + this._matrix.toString() + ")"; } if (this._flagId) { changed.id = this._id; } if (this._flagVertices || this._flagSize || this._flagSizeAttenuation) { let size = this._size; if (!this._sizeAttenuation) { const me = this.worldMatrix.elements; const m = decomposeMatrix(me[0], me[3], me[1], me[4], me[2], me[5]); size /= Math.max(m.scaleX, m.scaleY); } const vertices = svg.pointsToString(this._renderer.collection, size); changed.d = vertices; } if (this._fill && this._fill._renderer) { this._renderer.hasFillEffect = true; this._fill._update(); const prop = svg.getRendererType(this._fill._renderer.type); svg[prop].render.call(this._fill, domElement, true); } if (this._flagFill) { changed.fill = this._fill && this._fill.id ? "url(#" + this._fill.id + ")" : this._fill; if (this._renderer.hasFillEffect && typeof this._fill.id === "undefined") { domElement.defs._flagUpdate = true; delete this._renderer.hasFillEffect; } } if (this._stroke && this._stroke._renderer) { this._renderer.hasStrokeEffect = true; this._stroke._update(); const prop = svg.getRendererType(this._stroke._renderer.type); svg[prop].render.call(this._stroke, domElement, true); } if (this._flagStroke) { changed.stroke = this._stroke && this._stroke.id ? "url(#" + this._stroke.id + ")" : this._stroke; if (this._renderer.hasStrokeEffect && typeof this._stroke.id === "undefined") { domElement.defs._flagUpdate = true; delete this._renderer.hasStrokeEffect; } } if (this._flagLinewidth) { changed["stroke-width"] = this._linewidth; } if (this._flagOpacity) { changed["stroke-opacity"] = this._opacity; changed["fill-opacity"] = this._opacity; } if (this._flagClassName) { changed["class"] = this.classList.join(" "); } if (this._flagVisible) { changed.visibility = this._visible ? "visible" : "hidden"; } if (this.dashes && this.dashes.length > 0) { changed["stroke-dasharray"] = this.dashes.join(" "); changed["stroke-dashoffset"] = this.dashes.offset || 0; } if (!this._renderer.elem) { changed.id = this._id; this._renderer.elem = svg.createElement("path", changed); domElement.appendChild(this._renderer.elem); } else { svg.setAttributes(this._renderer.elem, changed); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, text: { render: function(domElement) { this._update(); const changed = {}; const flagMatrix = this._matrix.manual || this._flagMatrix; if (flagMatrix) { changed.transform = "matrix(" + this._matrix.toString() + ")"; } if (this._flagId) { changed.id = this._id; } if (this._flagFamily) { changed["font-family"] = this._family; } if (this._flagSize) { changed["font-size"] = this._size; } if (this._flagLeading) { changed["line-height"] = this._leading; } if (this._flagAlignment) { changed["text-anchor"] = svg.alignments[this._alignment] || this._alignment; } if (this._flagBaseline) { changed["dominant-baseline"] = svg.baselines[this._baseline] || this._baseline; } if (this._flagStyle) { changed["font-style"] = this._style; } if (this._flagWeight) { changed["font-weight"] = this._weight; } if (this._flagDecoration) { changed["text-decoration"] = this._decoration; } if (this._flagDirection) { changed["direction"] = this._direction; } if (this._fill && this._fill._renderer) { this._renderer.hasFillEffect = true; this._fill._update(); const prop = svg.getRendererType(this._fill._renderer.type); svg[prop].render.call(this._fill, domElement, true); } if (this._flagFill) { changed.fill = this._fill && this._fill.id ? "url(#" + this._fill.id + ")" : this._fill; if (this._renderer.hasFillEffect && typeof this._fill.id === "undefined") { domElement.defs._flagUpdate = true; delete this._renderer.hasFillEffect; } } if (this._stroke && this._stroke._renderer) { this._renderer.hasStrokeEffect = true; this._stroke._update(); const prop = svg.getRendererType(this._stroke._renderer.type); svg[prop].render.call(this._stroke, domElement, true); } if (this._flagStroke) { changed.stroke = this._stroke && this._stroke.id ? "url(#" + this._stroke.id + ")" : this._stroke; if (this._renderer.hasStrokeEffect && typeof this._stroke.id === "undefined") { domElement.defs._flagUpdate = true; delete this._renderer.hasStrokeEffect; } } if (this._flagLinewidth) { changed["stroke-width"] = this._linewidth; } if (this._flagOpacity) { changed.opacity = this._opacity; } if (this._flagClassName) { changed["class"] = this.classList.join(" "); } if (this._flagVisible) { changed.visibility = this._visible ? "visible" : "hidden"; } if (this.dashes && this.dashes.length > 0) { changed["stroke-dasharray"] = this.dashes.join(" "); changed["stroke-dashoffset"] = this.dashes.offset || 0; } if (!this._renderer.elem) { changed.id = this._id; this._renderer.elem = svg.createElement("text", changed); domElement.appendChild(this._renderer.elem); } else { svg.setAttributes(this._renderer.elem, changed); } if (this._flagClip) { const clip = svg.getClip(this, domElement); const elem = this._renderer.elem; if (this._clip) { elem.removeAttribute("id"); clip.setAttribute("id", this.id); clip.appendChild(elem); } else { clip.removeAttribute("id"); elem.setAttribute("id", this.id); this.parent._renderer.elem.appendChild(elem); } } if (this._flagMask) { if (this._mask) { const prop = svg.getRendererType(this._mask._renderer.type); svg[prop].render.call(this._mask, domElement); this._renderer.elem.setAttribute( "clip-path", "url(#" + this._mask.id + ")" ); } else { this._renderer.elem.removeAttribute("clip-path"); } } if (this._flagValue) { this._renderer.elem.textContent = this._value; } return this.flagReset(); } }, "linear-gradient": { render: function(domElement, silent) { if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } if (!silent) { this._update(); } const changed = {}; if (this._flagId) { changed.id = this._id; } if (this._flagEndPoints) { changed.x1 = this.left._x; changed.y1 = this.left._y; changed.x2 = this.right._x; changed.y2 = this.right._y; } if (this._flagSpread) { changed.spreadMethod = this._spread; } if (this._flagUnits) { changed.gradientUnits = this._units; } if (!this._renderer.elem) { changed.id = this._id; this._renderer.elem = svg.createElement("linearGradient", changed); } else { svg.setAttributes(this._renderer.elem, changed); } if (this._renderer.elem.parentNode === null) { domElement.defs.appendChild(this._renderer.elem); } if (this._flagStops) { const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length; if (lengthChanged) { while (this._renderer.elem.lastChild) { this._renderer.elem.removeChild(this._renderer.elem.lastChild); } } for (let i = 0; i < this.stops.length; i++) { const stop = this.stops[i]; const attrs = {}; if (stop._flagOffset) { attrs.offset = 100 * stop._offset + "%"; } if (stop._flagColor) { attrs["stop-color"] = stop._color; } if (stop._flagOpacity) { attrs["stop-opacity"] = stop._opacity; } if (!stop._renderer.elem) { stop._renderer.elem = svg.createElement("stop", attrs); } else { svg.setAttributes(stop._renderer.elem, attrs); } if (lengthChanged) { this._renderer.elem.appendChild(stop._renderer.elem); } stop.flagReset(); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, "radial-gradient": { render: function(domElement, silent) { if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } if (!silent) { this._update(); } const changed = {}; if (this._flagId) { changed.id = this._id; } if (this._flagCenter) { changed.cx = this.center._x; changed.cy = this.center._y; } if (this._flagFocal) { changed.fx = this.focal._x; changed.fy = this.focal._y; } if (this._flagRadius) { changed.r = this._radius; } if (this._flagSpread) { changed.spreadMethod = this._spread; } if (this._flagUnits) { changed.gradientUnits = this._units; } if (!this._renderer.elem) { changed.id = this._id; this._renderer.elem = svg.createElement("radialGradient", changed); } else { svg.setAttributes(this._renderer.elem, changed); } if (this._renderer.elem.parentNode === null) { domElement.defs.appendChild(this._renderer.elem); } if (this._flagStops) { const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length; if (lengthChanged) { while (this._renderer.elem.lastChild) { this._renderer.elem.removeChild(this._renderer.elem.lastChild); } } for (let i = 0; i < this.stops.length; i++) { const stop = this.stops[i]; const attrs = {}; if (stop._flagOffset) { attrs.offset = 100 * stop._offset + "%"; } if (stop._flagColor) { attrs["stop-color"] = stop._color; } if (stop._flagOpacity) { attrs["stop-opacity"] = stop._opacity; } if (!stop._renderer.elem) { stop._renderer.elem = svg.createElement("stop", attrs); } else { svg.setAttributes(stop._renderer.elem, attrs); } if (lengthChanged) { this._renderer.elem.appendChild(stop._renderer.elem); } stop.flagReset(); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, texture: { render: function(domElement, silent) { if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } if (!silent) { this._update(); } const changed = {}; const styles = { x: 0, y: 0 }; const image = this.image; if (this._flagId) { changed.id = this._id; } if (this._flagLoaded && this.loaded) { switch (image.nodeName.toLowerCase()) { case "canvas": styles.href = styles["xlink:href"] = image.toDataURL("image/png"); break; case "img": case "image": styles.href = styles["xlink:href"] = this.src; break; } } if (this._flagOffset || this._flagLoaded || this._flagScale) { changed.x = this._offset.x; changed.y = this._offset.y; if (image) { changed.x -= image.width / 2; changed.y -= image.height / 2; if (this._scale instanceof Vector) { changed.x *= this._scale.x; changed.y *= this._scale.y; } else { changed.x *= this._scale; changed.y *= this._scale; } } if (changed.x > 0) { changed.x *= -1; } if (changed.y > 0) { changed.y *= -1; } } if (this._flagScale || this._flagLoaded || this._flagRepeat) { changed.width = 0; changed.height = 0; if (image) { changed.width = image.width; changed.height = image.height; switch (this._repeat) { case "no-repeat": changed.width += 1; changed.height += 1; break; } if (this._scale instanceof Vector) { changed.width *= this._scale.x; changed.height *= this._scale.y; } else { changed.width *= this._scale; changed.height *= this._scale; } styles.width = changed.width; styles.height = changed.height; } } if (this._flagScale || this._flagLoaded) { if (!this._renderer.image) { this._renderer.image = svg.createElement("image", styles); } else { svg.setAttributes(this._renderer.image, styles); } } if (!this._renderer.elem) { changed.id = this._id; changed.patternUnits = "userSpaceOnUse"; this._renderer.elem = svg.createElement("pattern", changed); } else if (Object.keys(changed).length !== 0) { svg.setAttributes(this._renderer.elem, changed); } if (this._renderer.elem.parentNode === null) { domElement.defs.appendChild(this._renderer.elem); } if (this._renderer.elem && this._renderer.image && !this._renderer.appended) { this._renderer.elem.appendChild(this._renderer.image); this._renderer.appended = true; } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } } }; var Renderer2 = class extends Events { constructor(params) { super(); this.domElement = params.domElement || svg.createElement("svg"); this.scene = new Group(); this.scene.parent = this; this.defs = svg.createElement("defs"); this.defs._flagUpdate = false; this.domElement.appendChild(this.defs); this.domElement.defs = this.defs; this.domElement.style.overflow = "hidden"; } setSize(width, height) { this.width = width; this.height = height; svg.setAttributes(this.domElement, { width, height }); return this.trigger(Events.Types.resize, width, height); } render() { svg.group.render.call(this.scene, this.domElement); svg.defs.update(this.domElement); return this; } }; __publicField(Renderer2, "Utils", svg); // src/utils/shaders.js var shaders = { create: function(gl, source, type) { const shader = gl.createShader(gl[type]); gl.shaderSource(shader, source); gl.compileShader(shader); const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!compiled) { const error = gl.getShaderInfoLog(shader); gl.deleteShader(shader); throw new TwoError("unable to compile shader " + shader + ": " + error); } return shader; }, types: { vertex: "VERTEX_SHADER", fragment: "FRAGMENT_SHADER" }, path: { vertex: ` precision mediump float; attribute vec2 a_position; uniform mat3 u_matrix; uniform vec2 u_resolution; uniform vec4 u_rect; varying vec2 v_textureCoords; void main() { vec2 rectCoords = (a_position * (u_rect.zw - u_rect.xy)) + u_rect.xy; vec2 projected = (u_matrix * vec3(rectCoords, 1.0)).xy; vec2 normal = projected / u_resolution; vec2 clipspace = (normal * 2.0) - 1.0; gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0); v_textureCoords = a_position; } `, fragment: ` precision mediump float; uniform sampler2D u_image; varying vec2 v_textureCoords; void main() { vec4 texel = texture2D(u_image, v_textureCoords); if (texel.a == 0.0) { discard; } gl_FragColor = texel; } ` }, points: { vertex: ` precision mediump float; attribute vec2 a_position; uniform float u_size; uniform mat3 u_matrix; uniform vec2 u_resolution; varying vec2 v_textureCoords; void main() { vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy; vec2 normal = projected / u_resolution; vec2 clipspace = (normal * 2.0) - 1.0; gl_PointSize = u_size; gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0); v_textureCoords = a_position; } `, fragment: ` precision mediump float; uniform sampler2D u_image; void main() { vec4 texel = texture2D(u_image, gl_PointCoord); if (texel.a == 0.0) { discard; } gl_FragColor = texel; } ` } }; // src/renderers/webgl.js var multiplyMatrix = Matrix2.Multiply; var identity = [1, 0, 0, 0, 1, 0, 0, 0, 1]; var transformation = new NumArray(9); var CanvasUtils = Renderer.Utils; var vector2 = new Vector(); var quad = new NumArray([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]); var webgl = { precision: 0.9, isHidden: /(undefined|none|transparent)/i, canvas: root.document ? root.document.createElement("canvas") : { getContext: function() { } }, alignments: { left: "start", middle: "center", right: "end" }, matrix: new Matrix2(), group: { removeChild: function(child, gl) { if (child.children) { for (let i = 0; i < child.children.length; i++) { webgl.group.removeChild(child.children[i], gl); } } if (child._renderer.texture) { gl.deleteTexture(child._renderer.texture); delete child._renderer.texture; } if (child._renderer.positionBuffer) { gl.deleteBuffer(child._renderer.positionBuffer); delete child._renderer.positionBuffer; } }, render: function(gl, programs) { if (!this._visible) { return; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const parent = this.parent; const flagParentMatrix = parent._matrix && parent._matrix.manual || parent._flagMatrix; const flagMatrix = this._matrix.manual || this._flagMatrix; if (flagParentMatrix || flagMatrix) { if (!this._renderer.matrix) { this._renderer.matrix = new NumArray(9); } this._matrix.toTransformArray(true, transformation); multiplyMatrix( transformation, parent._renderer.matrix, this._renderer.matrix ); if (!(this._renderer.scale instanceof Vector)) { this._renderer.scale = new Vector(); } if (this._scale instanceof Vector) { this._renderer.scale.x = this._scale.x; this._renderer.scale.y = this._scale.y; } else { this._renderer.scale.x = this._scale; this._renderer.scale.y = this._scale; } if (!/renderer/i.test(parent._renderer.type)) { this._renderer.scale.x *= parent._renderer.scale.x; this._renderer.scale.y *= parent._renderer.scale.y; } if (flagParentMatrix) { this._flagMatrix = true; } } if (this._mask) { gl.clear(gl.STENCIL_BUFFER_BIT); gl.enable(gl.STENCIL_TEST); gl.stencilFunc(gl.ALWAYS, 1, 0); gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); gl.colorMask(false, false, false, false); const prop = Renderer.Utils.getRendererType( this._mask._renderer.type ); webgl[prop].render.call(this._mask, gl, programs, this); gl.stencilFunc(gl.EQUAL, 1, 255); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.colorMask(true, true, true, true); } this._flagOpacity = parent._flagOpacity || this._flagOpacity; this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1); let i; if (this._flagSubtractions) { for (i = 0; i < this.subtractions.length; i++) { webgl.group.removeChild(this.subtractions[i], gl); } } for (i = 0; i < this.children.length; i++) { const child = this.children[i]; const prop = Renderer.Utils.getRendererType(child._renderer.type); webgl[prop].render.call(child, gl, programs); } if (this._mask) { gl.disable(gl.STENCIL_TEST); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, path: { updateCanvas: function(gl, elem) { let prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y; let isOffset; const commands = elem._renderer.vertices; const canvas3 = this.canvas; const ctx = this.ctx; const ratio = gl.renderer.ratio; const scale = vector2.copy(elem._renderer.scale).multiply(ratio); const stroke = elem._stroke; const linewidth = elem._linewidth; const fill = elem._fill; const opacity = elem._renderer.opacity || elem._opacity; const cap = elem._cap; const join = elem._join; const miter = elem._miter; const closed2 = elem._closed; const dashes = elem.dashes; const length = commands.length; const last = length - 1; canvas3.width = Math.max( Math.ceil(elem._renderer.rect.width * scale.x), 1 ); canvas3.height = Math.max( Math.ceil(elem._renderer.rect.height * scale.y), 1 ); const centroid = elem._renderer.rect.centroid; const cx = centroid.x; const cy = centroid.y; ctx.clearRect(0, 0, canvas3.width, canvas3.height); if (fill) { if (typeof fill === "string") { ctx.fillStyle = fill; } else { const prop = Renderer.Utils.getRendererType( fill._renderer.type ); webgl[prop].render.call(fill, ctx, elem); ctx.fillStyle = fill._renderer.effect; } } if (stroke) { if (typeof stroke === "string") { ctx.strokeStyle = stroke; } else { const prop = Renderer.Utils.getRendererType( stroke._renderer.type ); webgl[prop].render.call(stroke, ctx, elem); ctx.strokeStyle = stroke._renderer.effect; } if (linewidth) { ctx.lineWidth = linewidth; } if (miter) { ctx.miterLimit = miter; } if (join) { ctx.lineJoin = join; } if (!closed2 && cap) { ctx.lineCap = cap; } } if (typeof opacity === "number") { ctx.globalAlpha = opacity; } if (dashes && dashes.length > 0) { ctx.lineDashOffset = dashes.offset || 0; ctx.setLineDash(dashes); } let d, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay; ctx.save(); ctx.scale(scale.x, scale.y); ctx.translate(cx, cy); ctx.beginPath(); for (let i = 0; i < commands.length; i++) { const b = commands[i]; x = b.x; y = b.y; switch (b.command) { case Commands.close: ctx.closePath(); break; case Commands.arc: rx = b.rx; ry = b.ry; xAxisRotation = b.xAxisRotation; largeArcFlag = b.largeArcFlag; sweepFlag = b.sweepFlag; prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0); a = commands[prev]; ax = a.x; ay = a.y; CanvasUtils.renderSvgArcCommand( ctx, ax, ay, rx, ry, largeArcFlag, sweepFlag, xAxisRotation, x, y ); break; case Commands.curve: prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0); a = commands[prev]; ar = a.controls && a.controls.right || Vector.zero; bl = b.controls && b.controls.left || Vector.zero; if (a._relative) { vx = ar.x + a.x; vy = ar.y + a.y; } else { vx = ar.x; vy = ar.y; } if (b._relative) { ux = bl.x + b.x; uy = bl.y + b.y; } else { ux = bl.x; uy = bl.y; } ctx.bezierCurveTo(vx, vy, ux, uy, x, y); if (i >= last && closed2) { c = d; br = b.controls && b.controls.right || Vector.zero; cl = c.controls && c.controls.left || Vector.zero; if (b._relative) { vx = br.x + b.x; vy = br.y + b.y; } else { vx = br.x; vy = br.y; } if (c._relative) { ux = cl.x + c.x; uy = cl.y + c.y; } else { ux = cl.x; uy = cl.y; } x = c.x; y = c.y; ctx.bezierCurveTo(vx, vy, ux, uy, x, y); } break; case Commands.line: ctx.lineTo(x, y); break; case Commands.move: d = b; ctx.moveTo(x, y); break; } } if (closed2) { ctx.closePath(); } if (!webgl.isHidden.test(fill)) { isOffset = fill._renderer && fill._renderer.offset; if (isOffset) { ctx.save(); ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y); ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); } ctx.fill(); if (isOffset) { ctx.restore(); } } if (!webgl.isHidden.test(stroke)) { isOffset = stroke._renderer && stroke._renderer.offset; if (isOffset) { ctx.save(); ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y); ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); ctx.lineWidth = linewidth / stroke._renderer.scale.x; } ctx.stroke(); if (isOffset) { ctx.restore(); } } ctx.restore(); }, getBoundingClientRect: function(vertices, border, rect) { let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity, width, height; vertices.forEach(function(v) { const x = v.x, y = v.y, controls = v.controls; let a, b, c, d, cl, cr; top = Math.min(y, top); left = Math.min(x, left); right = Math.max(x, right); bottom = Math.max(y, bottom); if (!v.controls) { return; } cl = controls.left; cr = controls.right; if (!cl || !cr) { return; } a = v._relative ? cl.x + x : cl.x; b = v._relative ? cl.y + y : cl.y; c = v._relative ? cr.x + x : cr.x; d = v._relative ? cr.y + y : cr.y; if (!a || !b || !c || !d) { return; } top = Math.min(b, d, top); left = Math.min(a, c, left); right = Math.max(a, c, right); bottom = Math.max(b, d, bottom); }); if (typeof border === "number") { top -= border; left -= border; right += border; bottom += border; } width = right - left; height = bottom - top; rect.top = top; rect.left = left; rect.right = right; rect.bottom = bottom; rect.width = width; rect.height = height; if (!rect.centroid) { rect.centroid = {}; } rect.centroid.x = -left; rect.centroid.y = -top; }, render: function(gl, programs, forcedParent) { if (!this._visible || !this._opacity) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const parent = forcedParent || this.parent; const prop = Renderer.Utils.getRendererType(this._renderer.type); const program = programs[prop]; const flagParentMatrix = parent._matrix.manual || parent._flagMatrix; const flagMatrix = this._matrix.manual || this._flagMatrix; const parentChanged = this._renderer.parent !== parent; const flagTexture = this._flagVertices || this._flagFill || this._fill instanceof LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints) || this._fill instanceof RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal) || this._fill instanceof Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagImage || this._fill._flagVideo || this._fill._flagRepeat || this._fill._flagOffset || this._fill._flagScale) || this._stroke instanceof LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints) || this._stroke instanceof RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal) || this._stroke instanceof Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagImage || this._stroke._flagVideo || this._stroke._flagRepeat || this._stroke._flagOffset || this._fill._flagScale) || this._flagStroke || this._flagLinewidth || this._flagOpacity || parent._flagOpacity || this._flagVisible || this._flagCap || this._flagJoin || this._flagMiter || this._flagScale || this.dashes && this.dashes.length > 0 || !this._renderer.texture; if (flagParentMatrix || flagMatrix || parentChanged) { if (!this._renderer.matrix) { this._renderer.matrix = new NumArray(9); } this._matrix.toTransformArray(true, transformation); multiplyMatrix( transformation, parent._renderer.matrix, this._renderer.matrix ); if (!(this._renderer.scale instanceof Vector)) { this._renderer.scale = new Vector(); } let sx, sy; if (this._scale instanceof Vector) { sx = this._scale.x * parent._renderer.scale.x; sy = this._scale.y * parent._renderer.scale.y; } else { sx = this._scale * parent._renderer.scale.x; sy = this._scale * parent._renderer.scale.y; } this._renderer.scale.x = sx < 0 ? -sx : sx; this._renderer.scale.y = sy < 0 ? -sy : sy; if (parentChanged) { this._renderer.parent = parent; } } if (this._mask) { gl.clear(gl.STENCIL_BUFFER_BIT); gl.enable(gl.STENCIL_TEST); gl.stencilFunc(gl.ALWAYS, 1, 0); gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); gl.colorMask(false, false, false, false); const prop2 = Renderer.Utils.getRendererType( this._mask._renderer.type ); webgl[prop2].render.call(this._mask, gl, programs, this); gl.stencilFunc(gl.EQUAL, 1, 255); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.colorMask(true, true, true, true); } if (flagTexture) { if (!this._renderer.rect) { this._renderer.rect = {}; } this._renderer.opacity = this._opacity * parent._renderer.opacity; webgl.path.getBoundingClientRect( this._renderer.vertices, this._linewidth, this._renderer.rect ); webgl.updateTexture.call(webgl, gl, this); } else { if (this._fill && this._fill._update) { this._fill._update(); } if (this._stroke && this._stroke._update) { this._stroke._update(); } } if (this._clip && !forcedParent || !this._renderer.texture) { return this; } if (programs.current !== program) { gl.useProgram(program); gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position); gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(program.position); gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW); if (!programs.resolution.flagged) { gl.uniform2f( gl.getUniformLocation(program, "u_resolution"), programs.resolution.width, programs.resolution.height ); } programs.current = program; } if (programs.resolution.flagged) { gl.uniform2f( gl.getUniformLocation(program, "u_resolution"), programs.resolution.width, programs.resolution.height ); } gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture); const rect = this._renderer.rect; gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix); gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom); gl.drawArrays(gl.TRIANGLES, 0, 6); if (this._mask) { gl.disable(gl.STENCIL_TEST); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, points: { updateCanvas: function(gl, elem) { let isOffset; const canvas3 = this.canvas; const ctx = this.ctx; const ratio = gl.renderer.ratio; const stroke = elem._stroke; const linewidth = elem._linewidth; const fill = elem._fill; const opacity = elem._renderer.opacity || elem._opacity; const dashes = elem.dashes; const size = elem._size * ratio; let dimension = size; if (!webgl.isHidden.test(stroke)) { dimension += linewidth; } canvas3.width = getPoT(dimension); canvas3.height = canvas3.width; const aspect = dimension / canvas3.width; const cx = canvas3.width / 2; const cy = canvas3.height / 2; ctx.clearRect(0, 0, canvas3.width, canvas3.height); if (fill) { if (typeof fill === "string") { ctx.fillStyle = fill; } else { const prop = Renderer.Utils.getRendererType( fill._renderer.type ); webgl[prop].render.call(fill, ctx, elem); ctx.fillStyle = fill._renderer.effect; } } if (stroke) { if (typeof stroke === "string") { ctx.strokeStyle = stroke; } else { const prop = Renderer.Utils.getRendererType( stroke._renderer.type ); webgl[prop].render.call(stroke, ctx, elem); ctx.strokeStyle = stroke._renderer.effect; } if (linewidth) { ctx.lineWidth = linewidth / aspect; } } if (typeof opacity === "number") { ctx.globalAlpha = opacity; } if (dashes && dashes.length > 0) { ctx.lineDashOffset = dashes.offset || 0; ctx.setLineDash(dashes); } ctx.save(); ctx.translate(cx, cy); ctx.scale(webgl.precision, webgl.precision); ctx.beginPath(); ctx.arc(0, 0, size / aspect * 0.5, 0, TWO_PI); ctx.restore(); if (closed) { ctx.closePath(); } if (!webgl.isHidden.test(fill)) { isOffset = fill._renderer && fill._renderer.offset; if (isOffset) { ctx.save(); ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y); ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); } ctx.fill(); if (isOffset) { ctx.restore(); } } if (!webgl.isHidden.test(stroke)) { isOffset = stroke._renderer && stroke._renderer.offset; if (isOffset) { ctx.save(); ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y); ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); ctx.lineWidth = linewidth / stroke._renderer.scale.x; } ctx.stroke(); if (isOffset) { ctx.restore(); } } }, render: function(gl, programs, forcedParent) { if (!this._visible || !this._opacity) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); let size = this._size; const parent = forcedParent || this.parent; const program = programs[this._renderer.type]; const sizeAttenuation = this._sizeAttenuation; const stroke = this._stroke; const linewidth = this._linewidth; const flagParentMatrix = parent._matrix.manual || parent._flagMatrix; const flagMatrix = this._matrix.manual || this._flagMatrix; const parentChanged = this._renderer.parent !== parent; const commands = this._renderer.vertices; const length = this._renderer.collection.length; const flagVertices = this._flagVertices; const flagTexture = this._flagFill || this._fill instanceof LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints) || this._fill instanceof RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal) || this._fill instanceof Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagImage || this._fill._flagVideo || this._fill._flagRepeat || this._fill._flagOffset || this._fill._flagScale) || this._stroke instanceof LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints) || this._stroke instanceof RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal) || this._stroke instanceof Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagImage || this._stroke._flagVideo || this._stroke._flagRepeat || this._stroke._flagOffset || this._fill._flagScale) || this._flagStroke || this._flagLinewidth || this._flagOpacity || parent._flagOpacity || this._flagVisible || this._flagScale || this.dashes && this.dashes.length > 0 || !this._renderer.texture; if (flagParentMatrix || flagMatrix || parentChanged) { if (!this._renderer.matrix) { this._renderer.matrix = new NumArray(9); } this._matrix.toTransformArray(true, transformation); multiplyMatrix( transformation, parent._renderer.matrix, this._renderer.matrix ); if (!(this._renderer.scale instanceof Vector)) { this._renderer.scale = new Vector(); } let sx, sy; if (this._scale instanceof Vector) { sx = this._scale.x * parent._renderer.scale.x; sy = this._scale.y * parent._renderer.scale.y; } else { sx = this._scale * parent._renderer.scale.x; sy = this._scale * parent._renderer.scale.y; } this._renderer.scale.x = sx < 0 ? -sx : sx; this._renderer.scale.y = sy < 0 ? -sy : sy; if (parentChanged) { this._renderer.parent = parent; } } if (flagVertices) { const positionBuffer = this._renderer.positionBuffer; if (positionBuffer) { gl.deleteBuffer(positionBuffer); } this._renderer.positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.positionBuffer); gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(program.position); gl.bufferData(gl.ARRAY_BUFFER, commands, gl.STATIC_DRAW); } if (flagTexture) { this._renderer.opacity = this._opacity * parent._renderer.opacity; webgl.updateTexture.call(webgl, gl, this); } else { if (this._fill && this._fill._update) { this._fill._update(); } if (this._stroke && this._stroke._update) { this._stroke._update(); } } if (this._clip && !forcedParent || !this._renderer.texture) { return this; } if (!webgl.isHidden.test(stroke)) { size += linewidth; } size /= webgl.precision; if (sizeAttenuation) { size *= Math.max(this._renderer.scale.x, this._renderer.scale.y); } if (programs.current !== program) { gl.useProgram(program); if (!programs.resolution.flagged) { gl.uniform2f( gl.getUniformLocation(program, "u_resolution"), programs.resolution.width, programs.resolution.height ); } programs.current = program; } if (programs.resolution.flagged) { gl.uniform2f( gl.getUniformLocation(program, "u_resolution"), programs.resolution.width, programs.resolution.height ); } gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture); gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix); gl.uniform1f(program.size, size * programs.resolution.ratio); gl.drawArrays(gl.POINTS, 0, length); if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, text: { updateCanvas: function(gl, elem) { const canvas3 = this.canvas; const ctx = this.ctx; const ratio = gl.renderer.ratio; const scale = vector2.copy(elem._renderer.scale).multiply(ratio); const stroke = elem._stroke; const linewidth = elem._linewidth; const fill = elem._fill; const opacity = elem._renderer.opacity || elem._opacity; const dashes = elem.dashes; const decoration = elem._decoration; const direction = elem._direction; canvas3.width = Math.max( Math.ceil(elem._renderer.rect.width * scale.x), 1 ); canvas3.height = Math.max( Math.ceil(elem._renderer.rect.height * scale.y), 1 ); const centroid = elem._renderer.rect.centroid; const cx = centroid.x; const cy = centroid.y; let a, b, c, d, e, sx, sy, x1, y1, x2, y2; const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset; ctx.clearRect(0, 0, canvas3.width, canvas3.height); if (!isOffset) { ctx.font = [ elem._style, elem._weight, elem._size + "px/" + elem._leading + "px", elem._family ].join(" "); } ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.textDirection = direction; if (fill) { if (typeof fill === "string") { ctx.fillStyle = fill; } else { const prop = Renderer.Utils.getRendererType( fill._renderer.type ); webgl[prop].render.call(fill, ctx, elem); ctx.fillStyle = fill._renderer.effect; } } if (stroke) { if (typeof stroke === "string") { ctx.strokeStyle = stroke; } else { const prop = Renderer.Utils.getRendererType( stroke._renderer.type ); webgl[prop].render.call(stroke, ctx, elem); ctx.strokeStyle = stroke._renderer.effect; } if (linewidth) { ctx.lineWidth = linewidth; } } if (typeof opacity === "number") { ctx.globalAlpha = opacity; } if (dashes && dashes.length > 0) { ctx.lineDashOffset = dashes.offset || 0; ctx.setLineDash(dashes); } ctx.save(); ctx.scale(scale.x, scale.y); ctx.translate(cx, cy); if (!webgl.isHidden.test(fill)) { if (fill._renderer && fill._renderer.offset) { sx = fill._renderer.scale.x; sy = fill._renderer.scale.y; ctx.save(); ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y); ctx.scale(sx, sy); a = elem._size / fill._renderer.scale.y; b = elem._leading / fill._renderer.scale.y; ctx.font = [ elem._style, elem._weight, a + "px/", b + "px", elem._family ].join(" "); c = fill._renderer.offset.x / fill._renderer.scale.x; d = fill._renderer.offset.y / fill._renderer.scale.y; ctx.fillText(elem.value, c, d); ctx.restore(); } else { ctx.fillText(elem.value, 0, 0); } } if (!webgl.isHidden.test(stroke)) { if (stroke._renderer && stroke._renderer.offset) { sx = stroke._renderer.scale.x; sy = stroke._renderer.scale.y; ctx.save(); ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y); ctx.scale(sx, sy); a = elem._size / stroke._renderer.scale.y; b = elem._leading / stroke._renderer.scale.y; ctx.font = [ elem._style, elem._weight, a + "px/", b + "px", elem._family ].join(" "); c = stroke._renderer.offset.x / stroke._renderer.scale.x; d = stroke._renderer.offset.y / stroke._renderer.scale.y; e = linewidth / stroke._renderer.scale.x; ctx.lineWidth = e; ctx.strokeText(elem.value, c, d); ctx.restore(); } else { ctx.strokeText(elem.value, 0, 0); } } if (/(underline|strikethrough)/i.test(decoration)) { const metrics = ctx.measureText(elem.value); switch (decoration) { case "underline": y1 = metrics.actualBoundingBoxDescent; y2 = metrics.actualBoundingBoxDescent; break; case "strikethrough": y1 = 0; y2 = 0; break; } x1 = -metrics.width / 2; x2 = metrics.width / 2; ctx.lineWidth = Math.max(Math.floor(elem._size / 15), 1); ctx.strokeStyle = ctx.fillStyle; ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); } ctx.restore(); }, getBoundingClientRect: function(elem, rect) { const ctx = webgl.ctx; ctx.font = [ elem._style, elem._weight, elem._size + "px/" + elem._leading + "px", elem._family ].join(" "); ctx.textAlign = "center"; ctx.textBaseline = Renderer.Utils.baselines[elem._baseline] || elem._baseline; const metrics = ctx.measureText(elem._value); let width = metrics.width; let height = 1.15 * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent); if (this._linewidth && !webgl.isHidden.test(this._stroke)) { width += this._linewidth * 2; height += this._linewidth * 2; } const w = width / 2; const h = height / 2; switch (webgl.alignments[elem._alignment] || elem._alignment) { case webgl.alignments.left: if (elem.direction === "ltr") { rect.left = 0; rect.right = width; } else { rect.left = -width; rect.right = 0; } break; case webgl.alignments.right: if (elem.direction === "ltr") { rect.left = -width; rect.right = 0; } else { rect.left = 0; rect.right = width; } break; default: rect.left = -w; rect.right = w; } switch (elem._baseline) { case "bottom": rect.top = -height; rect.bottom = 0; break; case "top": rect.top = 0; rect.bottom = height; break; case "baseline": rect.top = -h * 1.5; rect.bottom = h * 0.5; break; default: rect.top = -h; rect.bottom = h; } rect.width = width; rect.height = height; if (!rect.centroid) { rect.centroid = {}; } rect.centroid.x = w; rect.centroid.y = h; }, render: function(gl, programs, forcedParent) { if (!this._visible || !this._opacity) { return this; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const parent = forcedParent || this.parent; const program = programs[this._renderer.type]; const flagParentMatrix = parent._matrix.manual || parent._flagMatrix; const flagMatrix = this._matrix.manual || this._flagMatrix; const parentChanged = this._renderer.parent !== parent; const flagTexture = this._flagVertices || this._flagFill || this._fill instanceof LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints) || this._fill instanceof RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal) || this._fill instanceof Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagImage || this._fill._flagVideo || this._fill._flagRepeat || this._fill._flagOffset || this._fill._flagScale) || this._stroke instanceof LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints) || this._stroke instanceof RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal) || this._stroke instanceof Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagImage || this._stroke._flagVideo || this._stroke._flagRepeat || this._stroke._flagOffset || this._fill._flagScale) || this._flagStroke || this._flagLinewidth || this._flagOpacity || parent._flagOpacity || this._flagVisible || this._flagScale || this._flagValue || this._flagFamily || this._flagSize || this._flagLeading || this._flagAlignment || this._flagBaseline || this._flagStyle || this._flagWeight || this._flagDecoration || this.dashes && this.dashes.length > 0 || !this._renderer.texture; if (flagParentMatrix || flagMatrix || parentChanged) { if (!this._renderer.matrix) { this._renderer.matrix = new NumArray(9); } this._matrix.toTransformArray(true, transformation); multiplyMatrix( transformation, parent._renderer.matrix, this._renderer.matrix ); if (!(this._renderer.scale instanceof Vector)) { this._renderer.scale = new Vector(); } let sx, sy; if (this._scale instanceof Vector) { sx = this._scale.x * parent._renderer.scale.x; sy = this._scale.y * parent._renderer.scale.y; } else { sx = this._scale * parent._renderer.scale.x; sy = this._scale * parent._renderer.scale.y; } this._renderer.scale.x = sx < 0 ? -sx : sx; this._renderer.scale.y = sy < 0 ? -sy : sy; if (parentChanged) { this._renderer.parent = parent; } } if (this._mask) { gl.clear(gl.STENCIL_BUFFER_BIT); gl.enable(gl.STENCIL_TEST); gl.stencilFunc(gl.ALWAYS, 1, 0); gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); gl.colorMask(false, false, false, false); const prop = Renderer.Utils.getRendererType( this._mask._renderer.type ); webgl[prop].render.call(this._mask, gl, programs, this); gl.stencilFunc(gl.EQUAL, 1, 255); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.colorMask(true, true, true, true); } if (flagTexture) { if (!this._renderer.rect) { this._renderer.rect = {}; } this._renderer.opacity = this._opacity * parent._renderer.opacity; webgl.text.getBoundingClientRect(this, this._renderer.rect); webgl.updateTexture.call(webgl, gl, this); } else { if (this._fill && this._fill._update) { this._fill._update(); } if (this._stroke && this._stroke._update) { this._stroke._update(); } } if (this._clip && !forcedParent || !this._renderer.texture) { return this; } if (programs.current !== program) { gl.useProgram(program); gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position); gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(program.position); gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW); if (!programs.resolution.flagged) { gl.uniform2f( gl.getUniformLocation(program, "u_resolution"), programs.resolution.width, programs.resolution.height ); } programs.current = program; } if (programs.resolution.flagged) { gl.uniform2f( gl.getUniformLocation(program, "u_resolution"), programs.resolution.width, programs.resolution.height ); } gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture); const rect = this._renderer.rect; gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix); gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom); gl.drawArrays(gl.TRIANGLES, 0, 6); if (this._mask) { gl.disable(gl.STENCIL_TEST); } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, "linear-gradient": { render: function(ctx, parent) { if (!ctx.canvas.getContext("2d") || !parent) { return; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) { let rect; let lx = this.left._x; let ly = this.left._y; let rx = this.right._x; let ry = this.right._y; if (/objectBoundingBox/i.test(this._units)) { rect = parent.getBoundingClientRect(true); lx = (lx - 0.5) * rect.width; ly = (ly - 0.5) * rect.height; rx = (rx - 0.5) * rect.width; ry = (ry - 0.5) * rect.height; } this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry); for (let i = 0; i < this.stops.length; i++) { const stop = this.stops[i]; this._renderer.effect.addColorStop(stop._offset, stop._color); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, "radial-gradient": { render: function(ctx, parent) { if (!ctx.canvas.getContext("2d") || !parent) { return; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) { let rect; let cx = this.center._x; let cy = this.center._y; let fx = this.focal._x; let fy = this.focal._y; let radius = this._radius; if (/objectBoundingBox/i.test(this._units)) { rect = parent.getBoundingClientRect(true); cx = (cx - 0.5) * rect.width * 0.5; cy = (cy - 0.5) * rect.height * 0.5; fx = (fx - 0.5) * rect.width * 0.5; fy = (fy - 0.5) * rect.height * 0.5; radius *= Math.min(rect.width, rect.height); } this._renderer.effect = ctx.createRadialGradient( cx, cy, 0, fx, fy, radius ); for (let i = 0; i < this.stops.length; i++) { const stop = this.stops[i]; this._renderer.effect.addColorStop(stop._offset, stop._color); } } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, texture: { render: function(ctx, elem) { if (!ctx.canvas.getContext("2d")) { return; } if (_.isFunction(this._renderer.onBeforeRender)) { this._renderer.onBeforeRender(); } this._update(); const image = this.image; if ((this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) { this._renderer.effect = ctx.createPattern(image, this._repeat); } else if (!this._renderer.effect) { return this.flagReset(); } if (this._flagOffset || this._flagLoaded || this._flagScale) { if (!(this._renderer.offset instanceof Vector)) { this._renderer.offset = new Vector(); } this._renderer.offset.x = -this._offset.x; this._renderer.offset.y = -this._offset.y; if (image) { this._renderer.offset.x += image.width / 2; this._renderer.offset.y += image.height / 2; if (this._scale instanceof Vector) { this._renderer.offset.x *= this._scale.x; this._renderer.offset.y *= this._scale.y; } else { this._renderer.offset.x *= this._scale; this._renderer.offset.y *= this._scale; } } } if (this._flagScale || this._flagLoaded) { if (!(this._renderer.scale instanceof Vector)) { this._renderer.scale = new Vector(); } let sx, sy; if (this._scale instanceof Vector) { sx = this._scale.x; sy = this._scale.y; } else { sx = this._scale; sy = this._scale; } this._renderer.scale.x = sx < 0 ? -sx : sx; this._renderer.scale.y = sy < 0 ? -sy : sy; } if (_.isFunction(this._renderer.onAfterRender)) { this._renderer.onAfterRender(); } return this.flagReset(); } }, updateTexture: function(gl, elem) { const prop = Renderer.Utils.getRendererType(elem._renderer.type); this[prop].updateCanvas.call(webgl, gl, elem); if (this.canvas.width <= 0 || this.canvas.height <= 0) { if (elem._renderer.texture) { gl.deleteTexture(elem._renderer.texture); } delete elem._renderer.texture; return; } if (!elem._renderer.texture) { elem._renderer.texture = gl.createTexture(); } gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvas ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); }, program: { create: function(gl, shaders2) { let program, linked, error; program = gl.createProgram(); _.each(shaders2, function(s) { gl.attachShader(program, s); }); gl.linkProgram(program); linked = gl.getProgramParameter(program, gl.LINK_STATUS); if (!linked) { error = gl.getProgramInfoLog(program); gl.deleteProgram(program); throw new TwoError("unable to link program: " + error); } return program; } }, extensions: { init: function(gl) { const extensions = {}; const names = [ "EXT_texture_filter_anisotropic", "WEBGL_compressed_texture_s3tc", "OES_texture_float_linear", "WEBGL_multisampled_render_to_texture" ]; for (let i = 0; i < names.length; i++) { const name = names[i]; extensions[name] = webgl.extensions.get(gl, name); } return extensions; }, get: function(gl, name) { return gl.getExtension(name) || gl.getExtension(`MOZ_${name}`) || gl.getExtension(`WEBKIT_${name}`); } }, TextureRegistry: new Registry() }; webgl.ctx = webgl.canvas.getContext("2d"); var Renderer3 = class extends Events { constructor(params) { super(); let gl, program, vs, fs; this.domElement = params.domElement || document.createElement("canvas"); if (typeof params.offscreenElement !== "undefined") { webgl.canvas = params.offscreenElement; webgl.ctx = webgl.canvas.getContext("2d"); } this.scene = new Group(); this.scene.parent = this; this._renderer = { type: "renderer", matrix: new NumArray(identity), scale: 1, opacity: 1 }; this._flagMatrix = true; params = _.defaults(params || {}, { antialias: false, alpha: true, premultipliedAlpha: true, stencil: true, preserveDrawingBuffer: true, overdraw: false }); this.overdraw = params.overdraw; gl = this.ctx = this.domElement.getContext("webgl", params) || this.domElement.getContext("experimental-webgl", params); if (!this.ctx) { throw new TwoError( "unable to create a webgl context. Try using another renderer." ); } vs = shaders.create(gl, shaders.path.vertex, shaders.types.vertex); fs = shaders.create(gl, shaders.path.fragment, shaders.types.fragment); this.programs = { current: null, buffers: { position: gl.createBuffer() }, resolution: { width: 0, height: 0, ratio: 1, flagged: false } }; program = this.programs.path = webgl.program.create(gl, [vs, fs]); this.programs.text = this.programs.path; gl.extensions = webgl.extensions.init(gl); gl.renderer = this; program.position = gl.getAttribLocation(program, "a_position"); program.matrix = gl.getUniformLocation(program, "u_matrix"); program.rect = gl.getUniformLocation(program, "u_rect"); const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(program.position); gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW); vs = shaders.create(gl, shaders.points.vertex, shaders.types.vertex); fs = shaders.create(gl, shaders.points.fragment, shaders.types.fragment); program = this.programs.points = webgl.program.create(gl, [vs, fs]); program.position = gl.getAttribLocation(program, "a_position"); program.matrix = gl.getUniformLocation(program, "u_matrix"); program.size = gl.getUniformLocation(program, "u_size"); gl.enable(gl.BLEND); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); } setSize(width, height, ratio) { let w, h; const ctx = this.ctx; this.width = width; this.height = height; this.ratio = typeof ratio === "undefined" ? getRatio(ctx) : ratio; this.domElement.width = width * this.ratio; this.domElement.height = height * this.ratio; if (_.isObject(this.domElement.style)) { _.extend(this.domElement.style, { width: width + "px", height: height + "px" }); } this._renderer.matrix[0] = this._renderer.matrix[4] = this._renderer.scale = this.ratio; this._flagMatrix = true; w = width * this.ratio; h = height * this.ratio; ctx.viewport(0, 0, w, h); this.programs.resolution.width = w; this.programs.resolution.height = h; this.programs.resolution.ratio = this.ratio; this.programs.resolution.flagged = true; return this.trigger(Events.Types.resize, width, height, ratio); } render() { const gl = this.ctx; if (!this.overdraw) { gl.clear(gl.COLOR_BUFFER_BIT); } webgl.group.render.call(this.scene, gl, this.programs); this._flagMatrix = false; this.programs.resolution.flagged = true; return this; } }; __publicField(Renderer3, "Utils", webgl); // src/two.js var Utils = _.extend( { Error: TwoError, getRatio, read, xhr }, _, CanvasPolyfill, curves_exports, math_exports ); var _Two = class { _events = new Events(); get _bound() { return this._events._bound; } set _bound(v) { this._events._bound = v; } addEventListener() { return this._events.addEventListener.apply(this, arguments); } on() { return this._events.addEventListener.apply(this, arguments); } bind() { return this._events.addEventListener.apply(this, arguments); } removeEventListener() { return this._events.removeEventListener.apply(this, arguments); } off() { return this._events.removeEventListener.apply(this, arguments); } unbind() { return this._events.removeEventListener.apply(this, arguments); } dispatchEvent() { return this._events.dispatchEvent.apply(this, arguments); } trigger() { return this._events.dispatchEvent.apply(this, arguments); } listen() { return this._events.listen.apply(this, arguments); } ignore() { return this._events.ignore.apply(this, arguments); } type = ""; renderer = null; scene = null; width = 0; height = 0; frameCount = 0; timeDelta = 0; playing = false; constructor(options) { const params = _.defaults(options || {}, { fullscreen: false, fitted: false, width: 640, height: 480, type: _Two.Types.svg, autostart: false }); _.each( params, function(v, k) { if (/fullscreen/i.test(k) || /autostart/i.test(k)) { return; } this[k] = v; }, this ); if (_.isElement(params.domElement)) { const tagName = params.domElement.tagName.toLowerCase(); if (!/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test( this.type + "-" + tagName )) { this.type = _Two.Types[tagName]; } } this.renderer = new _Two[this.type](this); this.setPlaying(params.autostart); this.frameCount = 0; if (params.fullscreen) { this.fit = fitToWindow.bind(this); this.fit.domElement = window; this.fit.attached = true; _.extend(document.body.style, { overflow: "hidden", margin: 0, padding: 0, top: 0, left: 0, right: 0, bottom: 0, position: "fixed" }); _.extend(this.renderer.domElement.style, { display: "block", top: 0, left: 0, right: 0, bottom: 0, position: "fixed" }); dom.bind(this.fit.domElement, "resize", this.fit); this.fit(); } else if (params.fitted) { this.fit = fitToParent.bind(this); _.extend(this.renderer.domElement.style, { display: "block" }); } else if (typeof params.width === "number" && typeof params.height === "number") { this.renderer.setSize(params.width, params.height, this.ratio); this.width = params.width; this.height = params.height; } this.renderer.bind(Events.Types.resize, updateDimensions.bind(this)); this.scene = this.renderer.scene; _Two.Instances.push(this); if (params.autostart) { raf.init(); } } appendTo(elem) { elem.appendChild(this.renderer.domElement); if (this.fit) { if (this.fit.domElement !== window) { this.fit.domElement = elem; this.fit.attached = false; } this.update(); } return this; } play() { this.playing = true; raf.init(); return this.trigger(Events.Types.play); } pause() { this.playing = false; return this.trigger(Events.Types.pause); } setPlaying(p) { this.playing = p; } release(obj) { let i, v, child; if (typeof obj === "undefined") { return this.release(this.scene); } if (typeof obj.unbind === "function") { obj.unbind(); } if (typeof obj.fill === "object" && "unbind" in obj.fill) { obj.fill.unbind(); } if (typeof obj.stroke === "object" && "unbind" in obj.stroke) { obj.stroke.unbind(); } if (obj.vertices) { if (typeof obj.vertices.unbind === "function") { obj.vertices.unbind(); } for (i = 0; i < obj.vertices.length; i++) { v = obj.vertices[i]; if (typeof v.unbind === "function") { v.unbind(); } if (v.controls) { if (v.controls.left && typeof v.controls.left.unbind === "function") { v.controls.left.unbind(); } if (v.controls.right && typeof v.controls.right.unbind === "function") { v.controls.right.unbind(); } } } } if (obj.children) { for (i = 0; i < obj.children.length; i++) { child = obj.children[i]; this.release(child); } if (typeof obj.children.unbind === "function") { obj.children.unbind(); } } return obj; } update() { const animated = !!this._lastFrame; const now = _.performance.now(); if (animated) { this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3)); } this._lastFrame = now; if (this.fit && this.fit.domElement && !this.fit.attached) { dom.bind(this.fit.domElement, "resize", this.fit); this.fit.attached = true; this.fit(); } const width = this.width; const height = this.height; const renderer = this.renderer; if (width !== renderer.width || height !== renderer.height) { renderer.setSize(width, height, this.ratio); } this.trigger(Events.Types.update, this.frameCount, this.timeDelta); return this.render(); } render() { this.renderer.render(); return this.trigger(Events.Types.render, this.frameCount++); } add(objects) { if (!(objects instanceof Array)) { objects = Array.prototype.slice.call(arguments); } this.scene.add(objects); return this; } remove(objects) { if (!(objects instanceof Array)) { objects = Array.prototype.slice.call(arguments); } this.scene.remove(objects); return this; } clear() { this.scene.remove(this.scene.children); return this; } makeLine(x1, y1, x2, y2) { const line = new Line(x1, y1, x2, y2); this.scene.add(line); return line; } makeArrow(x1, y1, x2, y2, size) { const headlen = typeof size === "number" ? size : 10; const angle = Math.atan2(y2 - y1, x2 - x1); const vertices = [ new Anchor( x1, y1, void 0, void 0, void 0, void 0, Commands.move ), new Anchor( x2, y2, void 0, void 0, void 0, void 0, Commands.line ), new Anchor( x2 - headlen * Math.cos(angle - Math.PI / 4), y2 - headlen * Math.sin(angle - Math.PI / 4), void 0, void 0, void 0, void 0, Commands.line ), new Anchor( x2, y2, void 0, void 0, void 0, void 0, Commands.move ), new Anchor( x2 - headlen * Math.cos(angle + Math.PI / 4), y2 - headlen * Math.sin(angle + Math.PI / 4), void 0, void 0, void 0, void 0, Commands.line ) ]; const path = new Path(vertices, false, false, true); path.noFill(); path.cap = "round"; path.join = "round"; this.scene.add(path); return path; } makeRectangle(x, y, width, height) { const rect = new Rectangle(x, y, width, height); this.scene.add(rect); return rect; } makeRoundedRectangle(x, y, width, height, sides) { const rect = new RoundedRectangle(x, y, width, height, sides); this.scene.add(rect); return rect; } makeCircle(x, y, radius, resolution) { const circle = new Circle(x, y, radius, resolution); this.scene.add(circle); return circle; } makeEllipse(x, y, rx, ry, resolution) { const ellipse = new Ellipse(x, y, rx, ry, resolution); this.scene.add(ellipse); return ellipse; } makeStar(x, y, outerRadius, innerRadius, sides) { const star = new Star(x, y, outerRadius, innerRadius, sides); this.scene.add(star); return star; } makeCurve(points) { const l = arguments.length; if (!Array.isArray(points)) { points = []; for (let i = 0; i < l; i += 2) { const x = arguments[i]; if (typeof x !== "number") { break; } const y = arguments[i + 1]; points.push(new Anchor(x, y)); } } const last = arguments[l - 1]; const curve = new Path( points, !(typeof last === "boolean" ? last : void 0), true ); const rect = curve.getBoundingClientRect(); curve.center().translation.set(rect.left + rect.width / 2, rect.top + rect.height / 2); this.scene.add(curve); return curve; } makePolygon(x, y, radius, sides) { const poly = new Polygon(x, y, radius, sides); this.scene.add(poly); return poly; } makeArcSegment(x, y, innerRadius, outerRadius, startAngle, endAngle, resolution) { const arcSegment = new ArcSegment( x, y, innerRadius, outerRadius, startAngle, endAngle, resolution ); this.scene.add(arcSegment); return arcSegment; } makePoints(p) { const l = arguments.length; let vertices = p; if (!Array.isArray(p)) { vertices = []; for (let i = 0; i < l; i += 2) { const x = arguments[i]; if (typeof x !== "number") { break; } const y = arguments[i + 1]; vertices.push(new Vector(x, y)); } } const points = new Points(vertices); this.scene.add(points); return points; } makePath(p) { const l = arguments.length; let points = p; if (!Array.isArray(p)) { points = []; for (let i = 0; i < l; i += 2) { const x = arguments[i]; if (typeof x !== "number") { break; } const y = arguments[i + 1]; points.push(new Anchor(x, y)); } } const last = arguments[l - 1]; const path = new Path( points, !(typeof last === "boolean" ? last : void 0) ); const rect = path.getBoundingClientRect(); if (typeof rect.top === "number" && typeof rect.left === "number" && typeof rect.right === "number" && typeof rect.bottom === "number") { path.center().translation.set( rect.left + rect.width / 2, rect.top + rect.height / 2 ); } this.scene.add(path); return path; } makeText(message, x, y, styles) { const text = new Text(message, x, y, styles); this.add(text); return text; } makeLinearGradient(x1, y1, x2, y2) { const stops = Array.prototype.slice.call(arguments, 4); const gradient = new LinearGradient(x1, y1, x2, y2, stops); this.add(gradient); return gradient; } makeRadialGradient(x1, y1, radius) { const stops = Array.prototype.slice.call(arguments, 3); const gradient = new RadialGradient(x1, y1, radius, stops); this.add(gradient); return gradient; } makeSprite(pathOrTexture, x, y, columns, rows, frameRate, autostart) { const sprite = new Sprite(pathOrTexture, x, y, columns, rows, frameRate); if (autostart) { sprite.play(); } this.add(sprite); return sprite; } makeImageSequence(pathsOrTextures, x, y, frameRate, autostart) { const imageSequence = new ImageSequence(pathsOrTextures, x, y, frameRate); if (autostart) { imageSequence.play(); } this.add(imageSequence); return imageSequence; } makeTexture(pathOrSource, callback) { const texture = new Texture(pathOrSource, callback); return texture; } makeGroup(objects) { if (!(objects instanceof Array)) { objects = Array.prototype.slice.call(arguments); } const group = new Group(); this.scene.add(group); group.add(objects); return group; } interpret(svg2, shallow, add) { const tag = svg2.tagName.toLowerCase(); add = typeof add !== "undefined" ? add : true; if (!(tag in read)) { return null; } const node = read[tag].call(this, svg2); if (add) { this.add(shallow && node instanceof Group ? node.children : node); } else if (node.parent) { node.remove(); } return node; } load(pathOrSVGContent, callback) { const group = new Group(); let elem, i, child; const attach = function(data) { dom.temp.innerHTML = data; for (i = 0; i < dom.temp.children.length; i++) { elem = dom.temp.children[i]; child = this.interpret(elem, false, false); if (child !== null) { group.add(child); } } if (typeof callback === "function") { const svg2 = dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children; callback(group, svg2); } }.bind(this); if (/\.svg$/i.test(pathOrSVGContent)) { xhr(pathOrSVGContent, attach); return group; } attach(pathOrSVGContent); return group; } }; var Two = _Two; __publicField(Two, "NextFrameId", Constants.NextFrameId); __publicField(Two, "Types", Constants.Types); __publicField(Two, "Version", Constants.Version); __publicField(Two, "PublishDate", Constants.PublishDate); __publicField(Two, "Identifier", Constants.Identifier); __publicField(Two, "Resolution", Constants.Resolution); __publicField(Two, "AutoCalculateImportedMatrices", Constants.AutoCalculateImportedMatrices); __publicField(Two, "Instances", Constants.Instances); __publicField(Two, "uniqueId", Constants.uniqueId); __publicField(Two, "Anchor", Anchor); __publicField(Two, "Collection", Collection); __publicField(Two, "Events", Events); __publicField(Two, "Group", Group); __publicField(Two, "Matrix", Matrix2); __publicField(Two, "Path", Path); __publicField(Two, "Registry", Registry); __publicField(Two, "Element", Element); __publicField(Two, "Shape", Shape); __publicField(Two, "Text", Text); __publicField(Two, "Vector", Vector); __publicField(Two, "Gradient", Gradient); __publicField(Two, "ImageSequence", ImageSequence); __publicField(Two, "LinearGradient", LinearGradient); __publicField(Two, "RadialGradient", RadialGradient); __publicField(Two, "Sprite", Sprite); __publicField(Two, "Stop", Stop); __publicField(Two, "Texture", Texture); __publicField(Two, "ArcSegment", ArcSegment); __publicField(Two, "Circle", Circle); __publicField(Two, "Ellipse", Ellipse); __publicField(Two, "Line", Line); __publicField(Two, "Points", Points); __publicField(Two, "Polygon", Polygon); __publicField(Two, "Rectangle", Rectangle); __publicField(Two, "RoundedRectangle", RoundedRectangle); __publicField(Two, "Star", Star); __publicField(Two, "CanvasRenderer", Renderer); __publicField(Two, "SVGRenderer", Renderer2); __publicField(Two, "WebGLRenderer", Renderer3); __publicField(Two, "Commands", Commands); __publicField(Two, "Utils", Utils); function fitToWindow() { const wr = document.body.getBoundingClientRect(); const width = this.width = wr.width; const height = this.height = wr.height; this.renderer.setSize(width, height, this.ratio); } function fitToParent() { const parent = this.renderer.domElement.parentElement; if (!parent) { console.warn("Two.js: Attempting to fit to parent, but no parent found."); return; } const wr = parent.getBoundingClientRect(); const width = this.width = wr.width; const height = this.height = wr.height; this.renderer.setSize(width, height, this.ratio); } function updateDimensions(width, height) { this.width = width; this.height = height; this.trigger(Events.Types.resize, width, height); } var raf = dom.getRequestAnimationFrame(); function loop() { for (let i = 0; i < Two.Instances.length; i++) { const t = Two.Instances[i]; if (t.playing) { t.update(); } } Two.NextFrameId = raf(loop); } raf.init = function() { loop(); raf.init = function() { }; }; return __toCommonJS(two_exports); })().default; (function(){if(typeof exports==='object'&&typeof module!=='undefined'){module.exports=Two}})()