[
  {
    "path": ".eslintignore",
    "content": "/**/*.d.ts"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n\t\"parser\": \"babel-eslint\",\n\t\"extends\": \"eslint:recommended\",\n\t\"env\": {\n\t\t\"browser\": true,\n\t\t\"commonjs\": true,\n\t\t\"amd\": true\n\t},\n\t\"parserOptions\": {\n\t\t\"sourceType\": \"module\",\n\t\t\"ecmaVersion\": 2015\n\t},\n\t\"rules\": {\n\t\t\"semi\": [\"error\", \"always\"],\n\t\t\"no-unused-vars\": [\"error\", {\"args\": \"none\"}]\n\t},\n\t\"overrides\": [\n\t\t{\n\t\t\t\"files\": [\"utils/**\"],\n\t\t\t\"env\": {\n\t\t\t\t\"node\": true,\n\t\t\t\t\"es6\": true\n\t\t\t},\n\t\t\t\"parserOptions\": {\n\t\t\t\t\"ecmaVersion\": 8\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"files\": [\"tests/**\", \"extras/js/**\"],\n\t\t\t\"globals\": {\n\t\t\t\t\"QUnit\": \"writable\",\n\t\t\t\t\"resemble\": \"writable\",\n\t\t\t\t\"Two\": \"writable\",\n\t\t\t\t\"_\": \"writable\"\n\t\t\t},\n\t\t\t\"rules\": {\n\t\t\t\t\"no-redeclare\": \"off\"\n\t\t\t}\n\t\t}\n\t],\n\t\"ignorePatterns\": [\"build/\", \"utils/start-comment.js\", \"utils/end-comment.js\", \"utils/exports.js\", \"junk/\"]\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Set the default behavior, in case people don't have core.autocrlf set.\n* text=auto\n\n# Explicitly declare text files you want to always be normalized and converted\n# to native line endings on checkout.\n*.c text\n*.h text\n\n# Declare files that will always have CRLF line endings on checkout.\n*.sln text eol=crlf\n\n# Denote all files that are truly binary and should not be modified.\n*.gif binary\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.mp4 binary\n*.webm binary\n*.eot binary\n*.woff binary\n*.woff2 binary\n*.ttf binary\n*.mp3 binary\n*.ogg binary\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [jonobr1] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\ncustom: # https://two.js.org/donate/\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help improve Two.js\ntitle: \"[Bug]\"\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Environment (please select one):**\n- [ ] Code executes in browser (e.g: using script tag to load library)\n- [ ] Packaged software (e.g: ES6 imports, react, angular, vue.js)\n- [ ] Running headless (usually Node.js)\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea or enhancement to Two.js\ntitle: \"[Enhancement]\"\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/output.md",
    "content": "---\nname: Output\nabout: Share what you have made with Two.js\ntitle: \"[Output]\"\nlabels: output\nassignees: ''\n\n---\n\n**What did you make with Two.js?**\nA clear and concise description of what you made and how Two.js was a part of it.\n\n**Can we showcase this on [the Two.js site](https://two.js.org/)?**\n- [ ] Yes\n- [ ] No\n\n**To be included for the Two.js site:**\n1. Project name\n2. We need an animated gif or still image at at least 512px wide. Also, this image needs to be hosted where we can input the URL.\n3. Tags: particular features of Two.js you used? Or any other defining aspects to your project that would make it easy for people to search for on the site?\n\n**Feedback**\nAnything you'd like to share with the Two.js team? Pros, cons, suggestions, praise?\n\n**Thank you for sharing!**\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: Question\nabout: Ask a question about Two.js\ntitle: \"[Question]\"\nlabels: question\nassignees: ''\n\n---\n\n**Describe your question**\n\n**Your code (either pasted here, or a link to a hosted example)**\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Environment (please select one):**\n- [ ] Code executes in browser (e.g: using script tag to load library)\n- [ ] Packaged software (e.g: ES6 imports, react, angular, vue.js)\n- [ ] Running headless (usually Node.js)\n\n---\n\nIf applicable:\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"dev\" ]\n  pull_request:\n    branches: [ \"dev\" ]\n  schedule:\n    - cron: \"22 9 * * 6\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: write\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ javascript ]\n\n    steps:\n      # checkout@v5\n      - name: Checkout repository\n        uses: jonobr1/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8\n\n      # setup-node@v5\n      - name: Set up Node.js\n        uses: jonobr1/setup-node@a0853c24544627f65ddf259abe73b1d18a591444\n        with:\n          node-version: '20' # Use latest LTS\n\n      # cache@v4\n      - name: Cache node modules\n        uses: jonobr1/cache@0400d5f644dc74513175e3cd8d07132dd4860809\n        with:\n          path: |\n            node_modules\n          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}\n\n      - name: Install dependencies\n        run: npm ci\n\n      # codeql-action/init@v3\n      - name: Initialize CodeQL\n        uses: jonobr1/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3\n        with:\n          languages: ${{ matrix.language }}\n          queries: security-and-quality\n\n      # codeql-action/autobuild@v3\n      - name: Autobuild\n        uses: jonobr1/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3\n\n      # codeql-action/analyze@v3\n      - name: Perform CodeQL Analysis\n        uses: jonobr1/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3\n        with:\n          category: \"/language:${{ matrix.language }}\"\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\non: [push, pull_request]\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      # checkout@v5\n    - uses: jonobr1/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8\n    - name: Install modules\n      run: npm install\n    - name: Run ESLint\n      run: npm run lint\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish Package\n\non:\n  push:\n    tags:\n      - 'v*'\n\npermissions:\n  id-token: write  # Required for OIDC\n  contents: read\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      # checkout@v6\n      - uses: jonobr1/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8\n      # setup-node@v6\n      - uses: jonobr1/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f\n        with:\n          node-version: '20'\n          registry-url: 'https://registry.npmjs.org'\n\n      # Ensure npm 11.5.1 or later is installed\n      - name: Update npm\n        run: npm install -g npm@latest\n      - run: npm ci\n      - run: npm run build --if-present\n      # - run: npm test\n      - run: npm publish"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n*.DS_Store\njunk/\n*.log\ndocs.json\ndist\n.idea\n.codacy\ntests/typescript/package-lock.json\n\n#Ignore vscode AI rules\n.github/instructions/codacy.instructions.md"
  },
  {
    "path": ".npmrc",
    "content": "package-lock = false\n"
  },
  {
    "path": ".nvmrc",
    "content": "v14.7.0"
  },
  {
    "path": "CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nTwo.js is a renderer-agnostic 2D drawing API for modern browsers. It provides a unified interface for creating graphics across multiple rendering contexts: WebGL, Canvas2D, and SVG.\n\n## Core Architecture\n\n### Main Entry Point\n- `src/two.js` - Main Two.js class and entry point that imports all modules\n- The Two class extends Events and provides factory methods for creating shapes\n\n### Rendering System\n- **Multi-renderer architecture**: Canvas, SVG, and WebGL renderers in `src/renderers/`\n- **Scene graph**: Hierarchical structure using Groups and Elements\n- **Automatic renderer selection**: Based on domElement type or explicit type specification\n\n### Core Classes\n- `Element` - Base class for all drawable objects\n- `Shape` - Extended Element with transformation and styling\n- `Group` - Container for organizing and transforming multiple objects\n- `Path` - Complex shapes defined by anchor points and curves\n- `Vector` - 2D vector mathematics\n- `Matrix` - 2D transformation matrices\n- `Anchor` - Control points for paths with B�zier curve handles\n\n### Shape Library\nLocated in `src/shapes/`:\n- Basic shapes: Rectangle, Circle, Ellipse, Line, Star, Polygon\n- Complex shapes: ArcSegment, RoundedRectangle, Points\n- All shapes inherit from Path or Shape classes\n\n### Effects System\nLocated in `src/effects/`:\n- Gradients: LinearGradient, RadialGradient with Stop objects\n- Images: Texture, Sprite, ImageSequence for bitmap rendering\n- All effects can be applied as fill or stroke to shapes\n\n## Build System\n\n### Commands\n- `npm run build` - Build all versions (UMD, ESM, minified) using esbuild\n- `npm run dev` - Development server with esbuild on port 8080\n- `npm run lint` - ESLint with auto-fix\n- `npm run docs:generate` - Generate documentation from JSDoc comments\n- `npm run docs:dev` - Local documentation server with Vuepress\n- `npm run docs:build` - Build static documentation site\n\n### Build Configuration\n- Build script: `utils/build.js`\n- Uses esbuild for fast bundling and minification\n- Outputs: `build/two.js` (UMD), `build/two.module.js` (ESM), `build/two.min.js` (minified)\n- Includes license header and module.exports compatibility\n\n## Development Patterns\n\n### Factory Methods\nThe Two class provides factory methods for creating and adding objects to the scene:\n- `makeRectangle()`, `makeCircle()`, `makeText()`, etc.\n- All factory methods automatically add objects to the scene\n- Return the created object for further manipulation\n\n### Event System\n- All objects inherit from Events class\n- Common events: update, render, resize, play, pause\n- Use `bind()`, `unbind()`, `trigger()` for event handling\n\n### Coordinate System\n- Origin (0,0) at top-left by default\n- Positive Y axis points down\n- Transformations applied via translation, rotation, scale properties\n\n### Memory Management\n- Use `release()` method to unbind events and free memory\n- Automatically handles nested objects, vertices, and effects\n- Important for preventing memory leaks in long-running applications\n\n## Testing\n\n### Test Structure\n- Tests located in `tests/` directory\n- Test suites in `tests/suite/` organized by functionality\n- HTML test runners: `tests/index.html`, `tests/noWebGL.html`\n- TypeScript compilation tests in `tests/typescript/` with `index.ts` that imports and uses Two.js API\n\n### Running Tests\n- Manual browser testing via HTML files: `tests/index.html` and `tests/noWebGL.html`\n- TypeScript compilation testing: `npx tsc --noEmit --skipLibCheck tests/typescript/index.ts` to verify types work correctly\n\n## Key Files to Understand\n\n- `src/two.js` - Main class with factory methods and core logic\n- `src/constants.js` - Global constants, types, and configuration\n- `src/utils/interpret-svg.js` - SVG parsing and import functionality\n- `utils/build.js` - Build system configuration\n- `src/**/*.d.ts` - TypeScript definitions collocated with source files (e.g., `src/vector.d.ts` alongside `src/vector.js`)\n- `src/two.d.ts` - Main TypeScript entry point that aggregates all type exports\n\n## Dependencies\n\nProduction: None (library designed to be dependency-free)\nDevelopment: esbuild, ESLint, TypeScript, Vuepress for documentation\n\n## Browser Compatibility\n\nDesigned for modern browsers with ES6+ support. Uses feature detection for renderer capabilities.\n\n## Development Workflow\n\n- Always run `npm run build && npm run lint` before committing\n- Test changes in `tests/index.html` for visual verification\n- Use `npm run dev` for development server on port 8080\n- Check TypeScript types with `npm run types`\n- Test across all three renderers (Canvas, SVG, WebGL) for compatibility\n\n## Code Style and Conventions\n\n- Use ES6+ features consistently\n- Prefer `const` over `let` where possible\n- Factory methods should always return the created object\n- All classes should extend appropriate base classes (Element, Shape, etc.)\n- Use JSDoc comments for public API methods\n- Use 2-space indentation for JavaScript files\n- Place new components in appropriate src/ subdirectories\n- Application runs lots of functions on requestAnimationFrame (or per animation frame) so:\n  - Reduce the amount of objects and functions created within methods\n  - Prefer caching variables to the module scope\n  - Do not use function based iterators (prefer native for loops, etc.)\n\n## Architecture Patterns\n\n- All shapes inherit from Path or Shape classes\n- Use factory methods (makeRectangle, makeCircle) instead of direct constructors\n- Effects (gradients, textures) are applied via fill/stroke properties\n- Memory management: always call release() for complex objects\n- Event binding: use bind/unbind pattern, avoid anonymous functions\n- Factory methods automatically add objects to the scene\n\n## Common Issues and Solutions\n\n- When adding new shapes, ensure they extend the correct base class\n- WebGL renderer has different capabilities than Canvas/SVG\n- Always test across all three renderers for compatibility\n- SVG imports may need manual matrix calculations\n- Memory leaks: unbind events in cleanup using release() method\n- Coordinate system: origin (0,0) at top-left, positive Y axis points down\n\n## Testing Guidelines\n\n- Open `tests/index.html` in browser for manual testing\n- Test new features across Canvas, SVG, and WebGL renderers\n- Check `tests/noWebGL.html` for fallback scenarios\n- TypeScript compilation tests: Run `npx tsc --noEmit --skipLibCheck tests/typescript/index.ts` to verify TypeScript definitions work correctly\n- Manual browser testing required - no automated test runner\n\n## File Organization Rules\n\n- New shapes go in `src/shapes/` and follow existing naming pattern\n- Effects belong in `src/effects/`\n- Utilities in `src/utils/` should be pure functions\n- Export new classes in `src/two.js` main file\n- **TypeScript definitions**: Create a `.d.ts` file alongside each source file (e.g., `src/vector.d.ts` next to `src/vector.js`)\n  - Each `.d.ts` file contains a `declare module 'two.js/src/...'` block matching the module path\n  - Import statements go at the END of the module declaration\n  - Main type exports are aggregated in `src/two.d.ts`\n- Renderers are in `src/renderers/` - modify with caution\n\n## Performance Guidelines\n\n- Minimize object creation in animation loops\n- Use object pooling for frequently created/destroyed objects\n- Batch DOM updates when possible\n- Prefer transform operations over position updates\n- Use release() method to prevent memory leaks in long-running applications\n\n## Integration Patterns\n\n- Node.js: Requires canvas polyfill for headless rendering\n- TypeScript: Import specific modules for tree-shaking\n- Bundlers: ESM build recommended for modern bundlers\n- Browser: UMD build for direct script inclusion\n\n### Nota Bene\n- All visual tests run in the browser via HTML files\n- TypeScript tests verify that the type definitions work correctly by compiling sample code – this is work in progress\n- Manual testing approach - no automated test runners or CI integration"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at inquiries+two.js@jono.fyi. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## How to contribute to Two.js\n\n#### **Do you have a question?**\n\n* All questions answered by Two.js maintainers are in the [Issues](https://github.com/jonobr1/two.js/issues?q=label%3Aquestion) section of the project with the label question. Ensure the question was not already asked by checking there first.\n\n* If you're unable to find a question that has been answered, [create one](https://github.com/jonobr1/two.js/issues/new?assignees=&labels=question&template=question.md&title=%5BQuestion%5D). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** in situ or on a third party site like [CodePen](http://codepen.io), [jsfiddle](http://jsfiddle.com), or [glitch](http://glitch.com). This helps us to better help you.\n\n#### **Did you find a bug?**\n\n* **Ensure the bug was not already reported** by searching on the project under [Issues](https://github.com/jonobr1/two.js/issues).\n\n* If you're unable to find an open issue, [open a new one](https://github.com/jonobr1/two.js/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5BBug%5D). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** in situ or on a third party site like [CodePen](http://codepen.io), [jsfiddle](http://jsfiddle.com), or [glitch](http://glitch.com) demonstrating the issue.\n\n#### **Did you write a patch that fixes a bug, add a new feature, or change an existing one?**\n\n* Open a new GitHub pull request with the patch.\n\n* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.\n\n#### **Do you want to contribute to the Two.js documentation?**\n\n* This is still in its infancy and if you're interested to help out, please send an email to [inquires@jono.fyi](mailto:inquiries@jono.fyi) with the subject \"Two.js Documentation\". In the body of your email please describe why or how you'd like to help.\n\nTwo.js is a volunteer effort, so we apologize in advance for any delays.\n\nThanks! For both taking the time to read this and contributing.\n\nMuch :heart: from the Two.js Team\n\n---\nThis document is adapted from the [Ruby on Rails](https://github.com/rails/rails/blob/main/CONTRIBUTING.md#how-to-contribute-to-ruby-on-rails) project.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2012 - 2025 @jonobr1 / http://jono.fyi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Two.js\n\n[![NPM Package][npm]][npm-url]\n[![Build Size][build-size]][build-size-url]\n[![NPM Downloads][npm-downloads]][npmtrends-url]\n\nA two-dimensional drawing api meant for modern browsers. It is renderer agnostic enabling the same api to render in multiple contexts: webgl, canvas2d, and svg.\n\n[Home](http://two.js.org/) • [Releases](https://github.com/jonobr1/two.js/releases) • [Examples](http://two.js.org/examples/) • [Documentation](https://two.js.org/docs/two/) • [Changelog](https://github.com/jonobr1/two.js/tree/dev/wiki/changelog) • [Help](https://github.com/jonobr1/two.js/issues/new/choose)\n\n## Usage\nDownload the latest [minified library](https://raw.github.com/jonobr1/two.js/dev/build/two.min.js) and include it in your html.\n\n```html\n<script src=\"js/two.min.js\"></script>\n```\n\nIt can also be installed via [npm](https://www.npmjs.com/package/two.js), Node Package Manager:\n\n```js\nnpm install --save two.js\n```\nAlternatively see [how to build the library yourself](https://github.com/jonobr1/two.js#custom-build).\n\n\nHere is boilerplate html in order to draw a spinning rectangle in two.js:\n\n```html\n<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <script src=\"js/two.min.js\"></script>\n  </head>\n  <body>\n    <script>\n      var two = new Two({\n        fullscreen: true,\n        autostart: true\n      }).appendTo(document.body);\n      var rect = two.makeRectangle(two.width / 2, two.height / 2, 50 ,50);\n      two.bind('update', function() {\n        rect.rotation += 0.001;\n      });\n    </script>\n  </body>\n</html>\n```\n\n## Custom Build\nTwo.js uses [nodejs](http://nodejs.org/) in order to build source files. You'll first want to install that. Once installed open up a terminal and head to the repository folder:\n\n```\ncd ~/path-to-repo/two.js\nnpm install\n```\n\nThis will give you a number of libraries that the development of Two.js relies on. If for instance you only use the `SVGRenderer` then you can really cut down on the file size by excluding the other renderers. To do this, modify `/utils/build.js` to only add the files you'd like. Then run:\n\n```\nnode ./utils/build\n```\n\nAnd the resulting `/build/two.js` and `/build/two.min.js` will be updated to your specification.\n\n---\n\n### Using ES6 Imports\n\nAs of version `v0.7.5+` Two.js is compatible with EcmaScript 6 imports. This is typically employed in contemporary frameworks like [React](https://reactjs.org/) and [Angular](https://angularjs.org/) as well as bundling libraries like [webpack](https://webpack.js.org/), [esbuild](https://esbuild.github.io/), and [gulp](https://gulpjs.com/). This adaptation of the boilerplate can be found on [CodeSandbox](https://codesandbox.io/s/beautiful-wilbur-ygxbc?file=/src/App.js:0-664):\n\n```jsx\nimport React, { useEffect, useRef } from \"react\";\nimport Two from \"two.js\";\n\nexport default function App() {\n  var domElement = useRef();\n\n  useEffect(setup, []);\n\n  function setup() {\n    var two = new Two({\n      fullscreen: true,\n      autostart: true\n    }).appendTo(domElement.current);\n\n    var rect = two.makeRectangle(two.width / 2, two.height / 2, 50, 50);\n    two.bind(\"update\", update);\n\n    return unmount;\n\n    function unmount() {\n      two.unbind(\"update\");\n      two.pause();\n      domElement.current.removeChild(two.renderer.domElement);\n    }\n\n    function update() {\n      rect.rotation += 0.001;\n    }\n  }\n\n  return <div ref={domElement} />;\n}\n```\n\nIn addition to importing, the published packages of Two.js include the specific modules. So, if necessary you can import specific modules from the source code and bundle / minify for yourself like so:\n\n```javascript\nimport { Vector } from 'two.js/src/vector.js';\n\n// In TypeScript environments leave out the \".js\"\n// when importing modules directly. e.g:\nimport { Vector } from 'two.js/src/vector';\n```\n\n_While useful, the main import of the `Two` namespace imports all modules. So, there isn't yet proper tree shaking implemented for the library, though it's on the roadmap._\n\n### Running in Headless Environments\n\nAs of version `v0.7.x` Two.js can also run in a headless environment, namely running on the server with the help of a library called [Node Canvas](https://github.com/Automattic/node-canvas). We don't add Node Canvas to dependencies of Two.js because it's _not necessary_ to run it in the browser. However, it has all the hooks set up to run in a cloud environment. To get started follow the installation instructions on Automattic's [readme](https://github.com/Automattic/node-canvas#installation). After you've done that run:\n\n```\nnpm install canvas\nnpm install two.js\n```\n\nNow in a JavaScript file set up your Two.js scenegraph and save out frames whenever you need to:\n\n```javascript\nvar { createCanvas, Image } = require('canvas');\nvar Two = require('two.js')\n\nvar fs = require('fs');\nvar path = require('path');\n\nvar width = 800;\nvar height = 600;\n\nvar canvas = createCanvas(width, height);\nTwo.Utils.polyfill(canvas, Image);\n\nvar time = Date.now();\n\nvar two = new Two({\n  width: width,\n  height: height,\n  domElement: canvas\n});\n\nvar rect = two.makeRectangle(width / 2, height / 2, 50, 50);\nrect.fill = 'rgb(255, 100, 100)';\nrect.noStroke();\n\ntwo.render();\n\nvar settings = { compressionLevel: 3, filters: canvas.PNG_FILTER_NONE };\nfs.writeFileSync(path.resolve(__dirname, './images/rectangle.png'), canvas.toBuffer('image/png', settings));\nconsole.log('Finished rendering. Time took: ', Date.now() - time);\n\nprocess.exit();\n\n```\n\n## Build Documentation\nThe [Two.js](http://two.js.org/) website is bundled with this repository. Relying on [Vuepress](https://vuepress.vuejs.org/) the repository generates a website based on numerous `README.md` files housed in the `wiki` directory. Use the following the node commands as follows:\n\n```bash\nnpm run docs:generate   // Generate README.md files for documentation from source code comments\nnpm run docs:dev        // Creates a local server to generate all documentation\nnpm run docs:build      // Builds out static site and associated files to wiki/.vuepress/dist\n```\n\nN.B: Vuepress is a legacy library and as such these commands rely on an older version of Node. Run `nvm use` if you get errors. If you don't use [Node Version Manager](https://github.com/nvm-sh/nvm) then see `.nvmrc` to install the correct version of node on your local machine.\n\n## Change Log\nTwo.js has been in operation since 2012. For a full list of changes from its first alpha version built with [Three.js](http://threejs.org/) to the most up-to-date tweaks. Check out the wiki [here](./wiki/changelog).\n\n---\n\n#### And a big thank you to our sponsors who include:\n[Epilogue Press](https://github.com/epiloguepress)\n\n[npm]: https://img.shields.io/npm/v/two.js\n[npm-url]: https://www.npmjs.com/package/two.js\n[build-size]: https://badgen.net/bundlephobia/minzip/two.js\n[build-size-url]: https://bundlephobia.com/result?p=two.js\n[npm-downloads]: https://img.shields.io/npm/dt/two.js\n[npmtrends-url]: https://www.npmtrends.com/two.js\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\nIf you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.\n\n**You may submit the report in the following ways:**\n\n- Github users can privately report security advisories directly [here](https://github.com/jonobr1/two.js/security/advisories/new)\n\n- Send an email to [inquiries+two.js@jono.fyi](mailto:inquiries+two.js@jono.fyi).\n\n**Please provide the following information in your report:**\n\n- The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting)\n- Full paths of source file(s) related to the manifestation of the issue\n- The location of the affected source code (tag/branch/commit or direct URL)\n- Any special configuration required to reproduce the issue\n- Step-by-step instructions to reproduce the issue\n- Proof-of-concept or exploit code (if possible)\n- Impact of the issue, including how an attacker might exploit the issue\n\nThis project is maintained by volunteers on a reasonable-effort basis. As such, we ask that you give us 90 days to work on a fix before public exposure.\n\n---\n\n_Two.js conforms to this [Incident Response Plan](https://two.js.org/incident-response-plan) in moments of security risks._"
  },
  {
    "path": "build/two.js",
    "content": "/*\nMIT License\n\nCopyright (c) 2012 - 2025 @jonobr1 / http://jono.fyi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\nvar Two = (() => {\n  var __defProp = Object.defineProperty;\n  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n  var __getOwnPropNames = Object.getOwnPropertyNames;\n  var __hasOwnProp = Object.prototype.hasOwnProperty;\n  var __export = (target, all) => {\n    for (var name in all)\n      __defProp(target, name, { get: all[name], enumerable: true });\n  };\n  var __copyProps = (to, from, except, desc) => {\n    if (from && typeof from === \"object\" || typeof from === \"function\") {\n      for (let key of __getOwnPropNames(from))\n        if (!__hasOwnProp.call(to, key) && key !== except)\n          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n    }\n    return to;\n  };\n  var __toCommonJS = (mod2) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod2);\n\n  // src/two.js\n  var two_exports = {};\n  __export(two_exports, {\n    default: () => Two\n  });\n\n  // src/utils/canvas-polyfill.js\n  var CanvasPolyfill = {\n    /**\n     * @param {Image}\n     */\n    Image: null,\n    /**\n     * @param {Boolean}\n     */\n    isHeadless: false,\n    /**\n     *\n     * @param {canvas} elem - An element to spoof as a `<canvas />`.\n     * @param {String} [name] - An optional tag and node name to spoof. Defaults to `'canvas'`.\n     * @returns {canvas} - The same `elem` passed in the first argument with updated attributes needed to be used by Two.js.\n     * @description Adds attributes invoked by Two.js in order to execute and run correctly. This is used by headless environments.\n     */\n    shim: function(elem, name) {\n      elem.tagName = elem.nodeName = name || \"canvas\";\n      elem.nodeType = 1;\n      elem.getAttribute = function(prop) {\n        return this[prop];\n      };\n      elem.setAttribute = function(prop, val) {\n        this[prop] = val;\n        return this;\n      };\n      return elem;\n    },\n    /**\n     * @name Two.Utils.polyfill\n     * @function\n     * @param {canvas} canvas - The instanced `Canvas` object provided by `node-canvas`.\n     * @param {Image} [Image] - The prototypical `Image` object provided by `node-canvas`. This is only necessary to pass if you're going to load bitmap imagery.\n     * @returns {canvas} Returns the instanced canvas object you passed from with additional attributes needed for Two.js.\n     * @description Convenience method for defining all the dependencies from the npm package `node-canvas`. See [node-canvas](https://github.com/Automattic/node-canvas) for additional information on setting up HTML5 `<canvas />` drawing in a node.js environment.\n     */\n    polyfill: function(canvas3, Image3) {\n      CanvasPolyfill.shim(canvas3);\n      if (typeof Image3 !== \"undefined\") {\n        CanvasPolyfill.Image = Image3;\n      }\n      CanvasPolyfill.isHeadless = true;\n      return canvas3;\n    }\n  };\n\n  // src/utils/curves.js\n  var curves_exports = {};\n  __export(curves_exports, {\n    Curve: () => Curve,\n    getAnchorsFromArcData: () => getAnchorsFromArcData,\n    getComponentOnCubicBezier: () => getComponentOnCubicBezier,\n    getControlPoints: () => getControlPoints,\n    getCurveBoundingBox: () => getCurveBoundingBox,\n    getCurveFromPoints: () => getCurveFromPoints,\n    getCurveLength: () => getCurveLength,\n    getReflection: () => getReflection,\n    integrate: () => integrate,\n    subdivide: () => subdivide\n  });\n\n  // src/utils/math.js\n  var math_exports = {};\n  __export(math_exports, {\n    HALF_PI: () => HALF_PI,\n    NumArray: () => NumArray,\n    TWO_PI: () => TWO_PI,\n    decomposeMatrix: () => decomposeMatrix,\n    getComputedMatrix: () => getComputedMatrix,\n    getEffectiveStrokeWidth: () => getEffectiveStrokeWidth,\n    getPoT: () => getPoT,\n    lerp: () => lerp,\n    mod: () => mod,\n    setMatrix: () => setMatrix,\n    toFixed: () => toFixed\n  });\n\n  // src/utils/root.js\n  var root;\n  if (typeof window !== \"undefined\") {\n    root = window;\n  } else if (typeof global !== \"undefined\") {\n    root = global;\n  } else if (typeof self !== \"undefined\") {\n    root = self;\n  }\n\n  // src/utils/math.js\n  var Matrix;\n  var TWO_PI = Math.PI * 2;\n  var HALF_PI = Math.PI * 0.5;\n  function decomposeMatrix(matrix, b, c, d, e, f) {\n    let a;\n    if (arguments.length <= 1) {\n      a = matrix.a;\n      b = matrix.b;\n      c = matrix.c;\n      d = matrix.d;\n      e = matrix.e;\n      f = matrix.f;\n    } else {\n      a = matrix;\n    }\n    return {\n      translateX: e,\n      translateY: f,\n      scaleX: Math.sqrt(a * a + b * b),\n      scaleY: Math.sqrt(c * c + d * d),\n      rotation: 180 * Math.atan2(b, a) / Math.PI\n    };\n  }\n  function setMatrix(matrix) {\n    Matrix = matrix;\n  }\n  function getComputedMatrix(object, matrix) {\n    matrix = matrix && matrix.identity() || new Matrix();\n    let parent = object;\n    const matrices = [];\n    while (parent && parent._matrix) {\n      matrices.push(parent._matrix);\n      parent = parent.parent;\n    }\n    matrices.reverse();\n    for (let i = 0; i < matrices.length; i++) {\n      const m = matrices[i];\n      const e = m.elements;\n      matrix.multiply(\n        e[0],\n        e[1],\n        e[2],\n        e[3],\n        e[4],\n        e[5],\n        e[6],\n        e[7],\n        e[8]\n      );\n    }\n    return matrix;\n  }\n  function lerp(a, b, t) {\n    return t * (b - a) + a;\n  }\n  var pots = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096];\n  function getPoT(value) {\n    let i = 0;\n    while (pots[i] && pots[i] < value) {\n      i++;\n    }\n    return pots[i];\n  }\n  function mod(v, l) {\n    while (v < 0) {\n      v += l;\n    }\n    return v % l;\n  }\n  var NumArray = root.Float32Array || Array;\n  var floor = Math.floor;\n  function toFixed(v) {\n    return floor(v * 1e6) / 1e6;\n  }\n  function getEffectiveStrokeWidth(object, worldMatrix) {\n    const linewidth = object._linewidth;\n    if (object.strokeAttenuation) {\n      return linewidth;\n    }\n    if (!worldMatrix) {\n      worldMatrix = object.worldMatrix || getComputedMatrix(object);\n    }\n    const decomposed = decomposeMatrix(\n      worldMatrix.elements[0],\n      worldMatrix.elements[3],\n      worldMatrix.elements[1],\n      worldMatrix.elements[4],\n      worldMatrix.elements[2],\n      worldMatrix.elements[5]\n    );\n    const scale = Math.max(Math.abs(decomposed.scaleX), Math.abs(decomposed.scaleY));\n    return scale > 0 ? linewidth / scale : linewidth;\n  }\n\n  // src/utils/path-commands.js\n  var Commands = {\n    move: \"M\",\n    line: \"L\",\n    curve: \"C\",\n    arc: \"A\",\n    close: \"Z\"\n  };\n\n  // src/events.js\n  var Events = class {\n    _events = {};\n    _bound = false;\n    constructor() {\n    }\n    /**\n     * @name Two.Events#addEventListener\n     * @function\n     * @param {String} [name] - The name of the event to bind a function to.\n     * @param {Function} [handler] - The function to be invoked when the event is dispatched.\n     * @description Call to add a listener to a specific event name.\n     */\n    addEventListener(name, handler) {\n      const list = this._events[name] || (this._events[name] = []);\n      list.push(handler);\n      this._bound = true;\n      return this;\n    }\n    /**\n     * @name Two.Events#on\n     * @function\n     * @description Alias for {@link Two.Events#addEventListener}.\n     */\n    on() {\n      return this.addEventListener.apply(this, arguments);\n    }\n    /**\n     * @name Two.Events#bind\n     * @function\n     * @description Alias for {@link Two.Events#addEventListener}.\n     */\n    bind() {\n      return this.addEventListener.apply(this, arguments);\n    }\n    /**\n     * @name Two.Events#removeEventListener\n     * @function\n     * @param {String} [name] - The name of the event intended to be removed.\n     * @param {Function} [handler] - The handler intended to be removed.\n     * @description Call to remove listeners from a specific event. If only `name` is passed then all the handlers attached to that `name` will be removed. If no arguments are passed then all handlers for every event on the obejct are removed.\n     */\n    removeEventListener(name, handler) {\n      if (!this._events) {\n        return this;\n      }\n      if (!name && !handler) {\n        this._events = {};\n        this._bound = false;\n        return this;\n      }\n      const names = name ? [name] : Object.keys(this._events);\n      for (let i = 0, l = names.length; i < l; i++) {\n        name = names[i];\n        let list = this._events[name];\n        if (list) {\n          let events = [];\n          if (handler) {\n            for (let j = 0, k = list.length; j < k; j++) {\n              let e = list[j];\n              e = e.handler ? e.handler : e;\n              if (handler !== e) {\n                events.push(e);\n              }\n            }\n          }\n          this._events[name] = events;\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Events#off\n     * @function\n     * @description Alias for {@link Two.Events#removeEventListener}.\n     */\n    off() {\n      return this.removeEventListener.apply(this, arguments);\n    }\n    /**\n     * @name Two.Events#unbind\n     * @function\n     * @description Alias for {@link Two.Events#removeEventListener}.\n     */\n    unbind() {\n      return this.removeEventListener.apply(this, arguments);\n    }\n    /**\n     * @name Two.Events#dispatchEvent\n     * @function\n     * @param {String} name - The name of the event to dispatch.\n     * @param args - Anything can be passed after the name and those will be passed on to handlers attached to the event in the order they are passed.\n     * @description Call to trigger a custom event. Any additional arguments passed after the name will be passed along to the attached handlers.\n     */\n    dispatchEvent(name) {\n      if (!this._events) {\n        return this;\n      }\n      const args = Array.prototype.slice.call(arguments, 1);\n      const events = this._events[name];\n      if (events) {\n        for (let i = 0; i < events.length; i++) {\n          events[i].call(this, ...args);\n        }\n      }\n      return this;\n    }\n    trigger() {\n      return this.dispatchEvent.apply(this, arguments);\n    }\n    listen(obj, name, handler) {\n      const scope = this;\n      if (obj) {\n        e.obj = obj;\n        e.name = name;\n        e.handler = handler;\n        obj.on(name, e);\n      }\n      function e() {\n        handler.apply(scope, arguments);\n      }\n      return scope;\n    }\n    ignore(obj, name, handler) {\n      obj.off(name, handler);\n      return this;\n    }\n    /**\n     * @name Two.Events.Types\n     * @property {Object} - Object of different types of Two.js specific events.\n     */\n    static Types = {\n      play: \"play\",\n      pause: \"pause\",\n      update: \"update\",\n      render: \"render\",\n      resize: \"resize\",\n      change: \"change\",\n      remove: \"remove\",\n      insert: \"insert\",\n      order: \"order\",\n      load: \"load\"\n    };\n    static Methods = [\n      \"addEventListener\",\n      \"on\",\n      \"removeEventListener\",\n      \"off\",\n      \"unbind\",\n      \"dispatchEvent\",\n      \"trigger\",\n      \"listen\",\n      \"ignore\"\n    ];\n  };\n\n  // src/vector.js\n  var proto = {\n    x: {\n      enumerable: true,\n      get: function() {\n        return this._x;\n      },\n      set: function(v) {\n        if (this._x !== v) {\n          this._x = v;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    y: {\n      enumerable: true,\n      get: function() {\n        return this._y;\n      },\n      set: function(v) {\n        if (this._y !== v) {\n          this._y = v;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    }\n  };\n  var Vector = class _Vector extends Events {\n    /**\n     * @name Two.Vector#_x\n     * @private\n     */\n    _x = 0;\n    /**\n     * @name Two.Vector#_y\n     * @private\n     */\n    _y = 0;\n    constructor(x = 0, y = 0) {\n      super();\n      for (let prop in proto) {\n        Object.defineProperty(this, prop, proto[prop]);\n      }\n      this.x = x;\n      this.y = y;\n    }\n    /**\n     * @name Two.Vector.zero\n     * @readonly\n     * @property {Two.Vector} - Handy reference to a vector with component values 0, 0 at all times.\n     */\n    static zero = new _Vector();\n    /**\n     * @name Two.Vector.left\n     * @readonly\n     * @property {Two.Vector} - Handy reference to a vector with component values -1, 0 at all times.\n     */\n    static left = new _Vector(-1, 0);\n    /**\n     * @name Two.Vector.right\n     * @readonly\n     * @property {Two.Vector} - Handy reference to a vector with component values 1, 0 at all times.\n     */\n    static right = new _Vector(1, 0);\n    /**\n     * @name Two.Vector.up\n     * @readonly\n     * @property {Two.Vector} - Handy reference to a vector with component values 0, -1 at all times.\n     */\n    static up = new _Vector(0, -1);\n    /**\n     * @name Two.Vector.down\n     * @readonly\n     * @property {Two.Vector} - Handy reference to a vector with component values 0, 1 at all times.\n     */\n    static down = new _Vector(0, 1);\n    /**\n     * @name Two.Vector.add\n     * @function\n     * @param {Two.Vector} v1 - First {@link Two.Vector}\n     * @param {Two.Vector} v2 - Second {@link Two.Vector}\n     * @returns {Two.Vector}\n     * @description Add two vectors together.\n     */\n    static add(v1, v2) {\n      return new _Vector(v1.x + v2.x, v1.y + v2.y);\n    }\n    /**\n     * @name Two.Vector.sub\n     * @function\n     * @param {Two.Vector} v1 - First {@link Two.Vector}\n     * @param {Two.Vector} v2 - Second {@link Two.Vector}\n     * @returns {Two.Vector}\n     * @description Subtract two vectors: `v2` from `v1`.\n     */\n    static sub(v1, v2) {\n      return new _Vector(v1.x - v2.x, v1.y - v2.y);\n    }\n    /**\n     * @name Two.Vector.subtract\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    static subtract(v1, v2) {\n      return _Vector.sub(v1, v2);\n    }\n    /**\n     * @name Two.Vector.ratioBetween\n     * @function\n     * @param {Two.Vector} v1 - First {@link Two.Vector}\n     * @param {Two.Vector} v2 - Second {@link Two.Vector}\n     * @returns {Number} The ratio betwen two points `v1` and `v2`.\n     */\n    static ratioBetween(v1, v2) {\n      return (v1.x * v2.x + v1.y * v2.y) / (v1.length() * v2.length());\n    }\n    /**\n     * @name Two.Vector.angleBetween\n     * @function\n     * @param {Two.Vector} v1 - First {@link Two.Vector}\n     * @param {Two.Vector} v2 - Second {@link Two.Vector}\n     * @returns {Number} The angle between points `v1` and `v2`.\n     */\n    static angleBetween(v1, v2) {\n      if (arguments.length >= 4) {\n        const dx2 = arguments[0] - arguments[2];\n        const dy2 = arguments[1] - arguments[3];\n        return Math.atan2(dy2, dx2);\n      }\n      const dx = v1.x - v2.x;\n      const dy = v1.y - v2.y;\n      return Math.atan2(dy, dx);\n    }\n    /**\n     * @name Two.Vector.distanceBetween\n     * @function\n     * @param {Two.Vector} v1 - First {@link Two.Vector}\n     * @param {Two.Vector} v2 - Second {@link Two.Vector}\n     * @returns {Number} The distance between points `v1` and `v2`. Distance is always positive.\n     */\n    static distanceBetween(v1, v2) {\n      return Math.sqrt(_Vector.distanceBetweenSquared(v1, v2));\n    }\n    /**\n     * @name Two.Vector.distanceBetweenSquared\n     * @function\n     * @param {Two.Vector} v1 - First {@link Two.Vector}\n     * @param {Two.Vector} v2 - Second {@link Two.Vector}\n     * @returns {Number} The squared distance between points `v1` and `v2`.\n     */\n    static distanceBetweenSquared(v1, v2) {\n      const dx = v1.x - v2.x;\n      const dy = v1.y - v2.y;\n      return dx * dx + dy * dy;\n    }\n    //\n    /**\n     * @name Two.Vector#set\n     * @function\n     * @param {number} x - Value of `x` component\n     * @param {number} y - Value of `y` component\n     */\n    set(x, y) {\n      this.x = x;\n      this.y = y;\n      return this;\n    }\n    /**\n     * @name Two.Vector#copy\n     * @function\n     * @param {Two.Vector} v - The {@link Two.Vector} to copy\n     * @description Copy the `x` / `y` components of another object {@link Two.Vector}.\n     */\n    copy(v) {\n      this.x = v.x;\n      this.y = v.y;\n      return this;\n    }\n    /**\n     * @name Two.Vector#clear\n     * @function\n     * @description Set the `x` / `y` component values of the vector to zero.\n     */\n    clear() {\n      this.x = 0;\n      this.y = 0;\n      return this;\n    }\n    /**\n     * @name Two.Vector#clone\n     * @function\n     * @description Create a new vector and copy the existing values onto the newly created instance.\n     * @return {Two.Vector}\n     */\n    clone() {\n      return new _Vector(this.x, this.y);\n    }\n    /**\n     * @name Two.Vector#add\n     * @function\n     * @param {Two.Vector} v - The {@link Two.Vector} to add\n     * @description Add an object with `x` / `y` component values to the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#add\n     * @function\n     * @param {Number} n - Number to add\n     * @description Add the **same** number to both `x` / `y` component values of the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#add\n     * @function\n     * @param {Number} x - Number to add to `x` component\n     * @param {Number} y - Number to add to `y` component\n     * @description Add `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    add(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x += x;\n          this.y += x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x += x.x;\n          this.y += x.y;\n        }\n      } else {\n        this.x += x;\n        this.y += y;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Vector#addSelf\n     * @function\n     * @description Alias for {@link Two.Vector.add}.\n     */\n    addSelf(v) {\n      return this.add.apply(this, arguments);\n    }\n    /**\n     * @name Two.Vector#sub\n     * @function\n     * @param {Two.Vector} v - The amount as a {@link Two.Vector} to subtract\n     * @description Subtract an object with `x` / `y` component values to the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#sub\n     * @function\n     * @param {Number} n - Number to subtract\n     * @description Subtract the **same** number to both `x` / `y` component values of the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#sub\n     * @function\n     * @param {Number} x - Number to subtract from `x` component\n     * @param {Number} y - Number to subtract from `y` component\n     * @description Subtract `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    sub(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x -= x;\n          this.y -= x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x -= x.x;\n          this.y -= x.y;\n        }\n      } else {\n        this.x -= x;\n        this.y -= y;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Vector#subtract\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    subtract() {\n      return this.sub.apply(this, arguments);\n    }\n    /**\n     * @name Two.Vector#subSelf\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    subSelf(v) {\n      return this.sub.apply(this, arguments);\n    }\n    /**\n     * @name Two.Vector#subtractSelf\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    subtractSelf(v) {\n      return this.sub.apply(this, arguments);\n    }\n    /**\n     * @name Two.Vector#multiply\n     * @function\n     * @param {Two.Vector} v - The {@link Two.Vector} to multiply\n     * @description Multiply an object with `x` / `y` component values to the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#multiply\n     * @function\n     * @param {Number} n - The number to multiply\n     * @description Multiply the **same** number to both x / y component values of the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#multiply\n     * @function\n     * @param {Number} x - The number to multiply to `x` component\n     * @param {Number} y - The number to multiply to `y` component\n     * @description Multiply `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    multiply(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x *= x;\n          this.y *= x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x *= x.x;\n          this.y *= x.y;\n        }\n      } else {\n        this.x *= x;\n        this.y *= y;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Vector#multiplySelf\n     * @function\n     * @description Alias for {@link Two.Vector.multiply}.\n     */\n    multiplySelf(v) {\n      return this.multiply.apply(this, arguments);\n    }\n    /**\n     * @name Two.Vector#multiplyScalar\n     * @function\n     * @param {Number} s - The scalar to multiply by.\n     * @description Mulitiply the vector by a single number. Shorthand to call {@link Two.Vector#multiply} directly.\n     */\n    multiplyScalar(s) {\n      return this.multiply(s);\n    }\n    /**\n     * @name Two.Vector#divide\n     * @function\n     * @param {Two.Vector} v - The {@link Two.Vector} to divide\n     * @description Divide an object with `x` / `y` component values to the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#divide\n     * @function\n     * @param {Number} n - The number to divide\n     * @description Divide the **same** number to both x / y component values of the instance.\n     * @overloaded\n     */\n    /**\n     * @name Two.Vector#divide\n     * @function\n     * @param {Number} x - The number to divide on the `x` component\n     * @param {Number} y - The number to divide on the `y` component\n     * @description Divide `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    divide(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x /= x;\n          this.y /= x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x /= x.x;\n          this.y /= x.y;\n        }\n      } else {\n        this.x /= x;\n        this.y /= y;\n      }\n      if (isNaN(this.x)) {\n        this.x = 0;\n      }\n      if (isNaN(this.y)) {\n        this.y = 0;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Vector#divideSelf\n     * @function\n     * @description Alias for {@link Two.Vector.divide}.\n     */\n    divideSelf(v) {\n      return this.divide.apply(this, arguments);\n    }\n    /**\n     * @name Two.Vector#divideScalar\n     * @function\n     * @param {Number} s - The scalar to divide by.\n     * @description Divide the vector by a single number. Shorthand to call {@link Two.Vector#divide} directly.\n     */\n    divideScalar(s) {\n      return this.divide(s);\n    }\n    /**\n     * @name Two.Vector#negate\n     * @function\n     * @description Invert each component's sign value.\n     */\n    negate() {\n      return this.multiply(-1);\n    }\n    /**\n     * @name Two.Vector#dot\n     * @function\n     * @returns {Number}\n     * @description Get the [dot product](https://en.wikipedia.org/wiki/Dot_product) of the vector.\n     */\n    dot(v) {\n      return this.x * v.x + this.y * v.y;\n    }\n    /**\n     * @name Two.Vector#length\n     * @function\n     * @returns {Number}\n     * @description Get the length of a vector.\n     */\n    length() {\n      return Math.sqrt(this.lengthSquared());\n    }\n    /**\n     * @name Two.Vector#lengthSquared\n     * @function\n     * @returns {Number}\n     * @description Get the length of the vector to the power of two. Widely used as less expensive than {@link Two.Vector#length} because it isn't square-rooting any numbers.\n     */\n    lengthSquared() {\n      return this.x * this.x + this.y * this.y;\n    }\n    /**\n     * @name Two.Vector#normalize\n     * @function\n     * @description Normalize the vector from negative one to one.\n     */\n    normalize() {\n      return this.divideScalar(this.length());\n    }\n    /**\n     * @name Two.Vector#distanceTo\n     * @function\n     * @returns {Number}\n     * @description Get the distance between two vectors.\n     */\n    distanceTo(v) {\n      return Math.sqrt(this.distanceToSquared(v));\n    }\n    /**\n     * @name Two.Vector#distanceToSquared\n     * @function\n     * @returns {Number}\n     * @description Get the distance between two vectors to the power of two. Widely used as less expensive than {@link Two.Vector#distanceTo} because it isn't square-rooting any numbers.\n     */\n    distanceToSquared(v) {\n      const dx = this.x - v.x;\n      const dy = this.y - v.y;\n      return dx * dx + dy * dy;\n    }\n    /**\n     * @name Two.Vector#setLength\n     * @function\n     * @param {Number} l - length to set vector to.\n     * @description Set the length of a vector.\n     */\n    setLength(l) {\n      return this.normalize().multiplyScalar(l);\n    }\n    /**\n     * @name Two.Vector#equals\n     * @function\n     * @param {Two.Vector} v - The vector to compare against.\n     * @param {Number} [eps=0.0001] - An options epsilon for precision.\n     * @returns {Boolean}\n     * @description Qualify if one vector roughly equal another. With a margin of error defined by epsilon.\n     */\n    equals(v, eps) {\n      eps = typeof eps === \"undefined\" ? 1e-4 : eps;\n      return this.distanceTo(v) < eps;\n    }\n    /**\n     * @name Two.Vector#lerp\n     * @function\n     * @param {Two.Vector} v - The destination vector to step towards.\n     * @param {Number} t - The zero to one value of how close the current vector gets to the destination vector.\n     * @description Linear interpolate one vector to another by an amount `t` defined as a zero to one number.\n     * @see [Matt DesLauriers](https://twitter.com/mattdesl/status/1031305279227478016) has a good thread about this.\n     */\n    lerp(v, t) {\n      const x = (v.x - this.x) * t + this.x;\n      const y = (v.y - this.y) * t + this.y;\n      return this.set(x, y);\n    }\n    /**\n     * @name Two.Vector#isZero\n     * @function\n     * @param {Number} [eps=0.0001] - Optional precision amount to check against.\n     * @returns {Boolean}\n     * @description Check to see if vector is roughly zero, based on the `epsilon` precision value.\n     */\n    isZero(eps) {\n      eps = typeof eps === \"undefined\" ? 1e-4 : eps;\n      return this.length() < eps;\n    }\n    /**\n     * @name Two.Vector#toString\n     * @function\n     * @returns {String}\n     * @description Return a comma-separated string of x, y value. Great for storing in a database.\n     */\n    toString() {\n      return this.x + \", \" + this.y;\n    }\n    /**\n     * @name Two.Vector#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the vector.\n     */\n    toObject() {\n      return { x: toFixed(this.x), y: toFixed(this.y) };\n    }\n    /**\n     * @name Two.Vector#rotate\n     * @function\n     * @param {Number} radians - The amount to rotate the vector by in radians.\n     * @description Rotate a vector.\n     */\n    rotate(radians) {\n      const x = this.x;\n      const y = this.y;\n      const cos7 = Math.cos(radians);\n      const sin7 = Math.sin(radians);\n      this.x = x * cos7 - y * sin7;\n      this.y = x * sin7 + y * cos7;\n      return this;\n    }\n  };\n\n  // src/anchor.js\n  var Anchor = class _Anchor extends Vector {\n    controls = {\n      left: new Vector(),\n      right: new Vector()\n    };\n    _command = Commands.move;\n    _relative = true;\n    _rx = 0;\n    _ry = 0;\n    _xAxisRotation = 0;\n    _largeArcFlag = 0;\n    _sweepFlag = 1;\n    constructor(x = 0, y = 0, ax = 0, ay = 0, bx = 0, by = 0, command = Commands.move) {\n      super(x, y);\n      for (let prop in proto2) {\n        Object.defineProperty(this, prop, proto2[prop]);\n      }\n      this.command = command;\n      this.relative = true;\n      const broadcast = _Anchor.makeBroadcast(this);\n      this.controls.left.set(ax, ay).addEventListener(Events.Types.change, broadcast);\n      this.controls.right.set(bx, by).addEventListener(Events.Types.change, broadcast);\n    }\n    static makeBroadcast(scope) {\n      return broadcast;\n      function broadcast() {\n        if (scope._bound) {\n          scope.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n    /**\n     * @name Two.Anchor.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Anchor} to create a new instance\n     * @returns {Two.Anchor}\n     * @description Create a new {@link Two.Anchor} from an object notation of a {@link Two.Anchor}.\n     * @nota-bene Works in conjunction with {@link Two.Anchor#toObject}\n     */\n    static fromObject(obj) {\n      return new _Anchor().copy(obj);\n    }\n    /**\n     * @name Two.Anchor#copy\n     * @function\n     * @param {Two.Anchor} v - The anchor to apply values to.\n     * @description Copy the properties of one {@link Two.Anchor} onto another.\n     */\n    copy(v) {\n      this.x = v.x;\n      this.y = v.y;\n      if (typeof v.command === \"string\") {\n        this.command = v.command;\n      }\n      if (v.controls) {\n        if (v.controls.left) {\n          this.controls.left.copy(v.controls.left);\n        }\n        if (v.controls.right) {\n          this.controls.right.copy(v.controls.right);\n        }\n      }\n      if (typeof v.relative === \"boolean\") {\n        this.relative = v.relative;\n      }\n      if (typeof v.rx === \"number\") {\n        this.rx = v.rx;\n      }\n      if (typeof v.ry === \"number\") {\n        this.ry = v.ry;\n      }\n      if (typeof v.xAxisRotation === \"number\") {\n        this.xAxisRotation = v.xAxisRotation;\n      }\n      if (typeof v.largeArcFlag === \"number\") {\n        this.largeArcFlag = v.largeArcFlag;\n      }\n      if (typeof v.sweepFlag === \"number\") {\n        this.sweepFlag = v.sweepFlag;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Anchor#clone\n     * @function\n     * @returns {Two.Anchor}\n     * @description Create a new {@link Two.Anchor}, set all its values to the current instance and return it for use.\n     */\n    clone() {\n      return new _Anchor().copy(this);\n    }\n    /**\n     * @name Two.Anchor#toObject\n     * @function\n     * @returns {Object} - An object with properties filled out to mirror {@link Two.Anchor}.\n     * @description Create a JSON compatible plain object of the current instance. Intended for use with storing values in a database.\n     * @nota-bene Works in conjunction with {@link Two.Anchor.fromObject}\n     */\n    toObject() {\n      return {\n        x: toFixed(this.x),\n        y: toFixed(this.y),\n        command: this.command,\n        relative: this.relative,\n        controls: {\n          left: this.controls.left.toObject(),\n          right: this.controls.right.toObject()\n        },\n        rx: toFixed(this.rx),\n        ry: toFixed(this.ry),\n        xAxisRotation: toFixed(this.xAxisRotation),\n        largeArcFlag: toFixed(this.largeArcFlag),\n        sweepFlag: toFixed(this.sweepFlag)\n      };\n    }\n    /**\n     * @name Two.Anchor#toString\n     * @function\n     * @returns {String} - A String with comma-separated values reflecting the various values on the current instance.\n     * @description Create a string form of the current instance. Intended for use with storing values in a database. This is lighter to store than the JSON compatible {@link Two.Anchor#toObject}.\n     */\n    toString() {\n      return JSON.stringify(this.toObject());\n    }\n  };\n  var proto2 = {\n    command: {\n      enumerable: true,\n      get: function() {\n        return this._command;\n      },\n      set: function(command) {\n        if (this._command !== command) {\n          this._command = command;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    relative: {\n      enumerable: true,\n      get: function() {\n        return this._relative;\n      },\n      set: function(relative) {\n        if (this._relative !== !!relative) {\n          this._relative = !!relative;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    rx: {\n      enumerable: true,\n      get: function() {\n        return this._rx;\n      },\n      set: function(rx) {\n        if (this._rx !== rx) {\n          this._rx = rx;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    ry: {\n      enumerable: true,\n      get: function() {\n        return this._ry;\n      },\n      set: function(ry) {\n        if (this._ry !== ry) {\n          this._ry = ry;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    xAxisRotation: {\n      enumerable: true,\n      get: function() {\n        return this._xAxisRotation;\n      },\n      set: function(xAxisRotation) {\n        if (this._xAxisRotation !== xAxisRotation) {\n          this._xAxisRotation = xAxisRotation;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    largeArcFlag: {\n      enumerable: true,\n      get: function() {\n        return this._largeArcFlag;\n      },\n      set: function(largeArcFlag) {\n        if (this._largeArcFlag !== largeArcFlag) {\n          this._largeArcFlag = largeArcFlag;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    sweepFlag: {\n      get: function() {\n        return this._sweepFlag;\n      },\n      set: function(sweepFlag) {\n        if (this._sweepFlag !== sweepFlag) {\n          this._sweepFlag = sweepFlag;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    }\n  };\n\n  // src/constants.js\n  var count = 0;\n  var Constants = {\n    /**\n     * @name Two.NextFrameId\n     * @property {Number}\n     * @description The id of the next `requestAnimationFrame` function. Used to control the (or cancel) the default behavior of Two.js animation loops.\n     */\n    NextFrameId: null,\n    // Primitive\n    /**\n     * @name Two.Types\n     * @property {Object} - The different rendering types available in the library.\n     */\n    Types: {\n      webgl: \"WebGLRenderer\",\n      svg: \"SVGRenderer\",\n      canvas: \"CanvasRenderer\"\n    },\n    /**\n     * @name Two.Version\n     * @property {String} - The current working version of the library.\n     */\n    Version: \"v0.8.23\",\n    /**\n     * @name Two.PublishDate\n     * @property {String} - The automatically generated publish date in the build process to verify version release candidates.\n     */\n    PublishDate: \"2026-01-05T18:28:31.207Z\",\n    /**\n     * @name Two.Identifier\n     * @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.\n     */\n    Identifier: \"two-\",\n    /**\n     * @name Two.Resolution\n     * @property {Number} - Default amount of vertices to be used for interpreting Arcs and ArcSegments.\n     */\n    Resolution: 12,\n    /**\n     * @name Two.AutoCalculateImportedMatrices\n     * @property {Boolean} - When importing SVGs through the {@link Two#interpret} and {@link Two#load}, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n     * @nota-bene `false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n     */\n    AutoCalculateImportedMatrices: true,\n    /**\n     * @name Two.Instances\n     * @property {Two[]} - Registered list of all Two.js instances in the current session.\n     */\n    Instances: [],\n    /**\n     * @function Two.uniqueId\n     * @description Simple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n     * @returns {Number} Ever increasing Number.\n     */\n    uniqueId: function() {\n      return count++;\n    }\n  };\n\n  // src/utils/curves.js\n  var Curve = {\n    CollinearityEpsilon: Math.pow(10, -30),\n    RecursionLimit: 16,\n    CuspLimit: 0,\n    Tolerance: {\n      distance: 0.25,\n      angle: 0,\n      epsilon: Number.EPSILON\n    },\n    // Lookup tables for abscissas and weights with values for n = 2 .. 16.\n    // As values are symmetric, only store half of them and adapt algorithm\n    // to factor in symmetry.\n    abscissas: [\n      [0.5773502691896],\n      [0, 0.7745966692415],\n      [0.3399810435849, 0.8611363115941],\n      [0, 0.5384693101057, 0.9061798459387],\n      [0.2386191860832, 0.6612093864663, 0.9324695142032],\n      [0, 0.4058451513774, 0.7415311855994, 0.9491079123428],\n      [0.1834346424956, 0.5255324099163, 0.7966664774136, 0.9602898564975],\n      [0, 0.3242534234038, 0.6133714327006, 0.8360311073266, 0.9681602395076],\n      [\n        0.1488743389816,\n        0.4333953941292,\n        0.679409568299,\n        0.865063366689,\n        0.9739065285172\n      ],\n      [\n        0,\n        0.2695431559523,\n        0.5190961292068,\n        0.730152005574,\n        0.8870625997681,\n        0.9782286581461\n      ],\n      [\n        0.1252334085115,\n        0.3678314989982,\n        0.5873179542866,\n        0.7699026741943,\n        0.9041172563705,\n        0.9815606342467\n      ],\n      [\n        0,\n        0.2304583159551,\n        0.4484927510364,\n        0.6423493394403,\n        0.8015780907333,\n        0.917598399223,\n        0.9841830547186\n      ],\n      [\n        0.1080549487073,\n        0.3191123689279,\n        0.5152486363582,\n        0.6872929048117,\n        0.8272013150698,\n        0.9284348836636,\n        0.9862838086968\n      ],\n      [\n        0,\n        0.2011940939974,\n        0.3941513470776,\n        0.5709721726085,\n        0.7244177313602,\n        0.8482065834104,\n        0.9372733924007,\n        0.9879925180205\n      ],\n      [\n        0.0950125098376,\n        0.2816035507793,\n        0.4580167776572,\n        0.6178762444026,\n        0.755404408355,\n        0.8656312023878,\n        0.9445750230732,\n        0.9894009349916\n      ]\n    ],\n    weights: [\n      [1],\n      [0.8888888888889, 0.5555555555556],\n      [0.6521451548625, 0.3478548451375],\n      [0.5688888888889, 0.4786286704994, 0.2369268850562],\n      [0.4679139345727, 0.3607615730481, 0.1713244923792],\n      [0.4179591836735, 0.3818300505051, 0.2797053914893, 0.1294849661689],\n      [0.3626837833784, 0.3137066458779, 0.2223810344534, 0.1012285362904],\n      [\n        0.3302393550013,\n        0.31234707704,\n        0.2606106964029,\n        0.1806481606949,\n        0.0812743883616\n      ],\n      [\n        0.2955242247148,\n        0.26926671931,\n        0.219086362516,\n        0.1494513491506,\n        0.0666713443087\n      ],\n      [\n        0.2729250867779,\n        0.2628045445102,\n        0.233193764592,\n        0.1862902109277,\n        0.1255803694649,\n        0.0556685671162\n      ],\n      [\n        0.2491470458134,\n        0.2334925365384,\n        0.2031674267231,\n        0.1600783285433,\n        0.1069393259953,\n        0.0471753363865\n      ],\n      [\n        0.2325515532309,\n        0.2262831802629,\n        0.2078160475369,\n        0.1781459807619,\n        0.1388735102198,\n        0.0921214998377,\n        0.0404840047653\n      ],\n      [\n        0.2152638534632,\n        0.2051984637213,\n        0.1855383974779,\n        0.1572031671582,\n        0.1215185706879,\n        0.0801580871598,\n        0.0351194603318\n      ],\n      [\n        0.2025782419256,\n        0.1984314853271,\n        0.1861610000156,\n        0.166269205817,\n        0.1395706779262,\n        0.1071592204672,\n        0.0703660474881,\n        0.0307532419961\n      ],\n      [\n        0.1894506104551,\n        0.1826034150449,\n        0.169156519395,\n        0.1495959888166,\n        0.1246289712555,\n        0.0951585116825,\n        0.0622535239386,\n        0.0271524594118\n      ]\n    ]\n  };\n  function getComponentOnCubicBezier(t, a, b, c, d) {\n    const k = 1 - t;\n    return k * k * k * a + 3 * k * k * t * b + 3 * k * t * t * c + t * t * t * d;\n  }\n  function subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n    limit = limit || Curve.RecursionLimit;\n    const amount = limit + 1;\n    if (Math.abs(x1 - x4) < 1e-3 && Math.abs(y1 - y4) < 1e-3) {\n      return [new Anchor(x4, y4)];\n    }\n    const result = [];\n    for (let i = 0; i < amount; i++) {\n      const t = i / amount;\n      const x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n      const y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n      result.push(new Anchor(x, y));\n    }\n    return result;\n  }\n  function getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n    if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) {\n      const dx = x4 - x1;\n      const dy = y4 - y1;\n      return Math.sqrt(dx * dx + dy * dy);\n    }\n    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);\n    function integrand(t) {\n      const dx = (ax * t + bx) * t + cx, dy = (ay * t + by) * t + cy;\n      return Math.sqrt(dx * dx + dy * dy);\n    }\n    return integrate(integrand, 0, 1, limit || Curve.RecursionLimit);\n  }\n  function getCurveBoundingBox(x1, y1, x2, y2, x3, y3, x4, y4) {\n    const tvalues = [];\n    const bounds = [[], []];\n    let a, b, c, t, t1, t2, b2ac, sqrtb2ac;\n    for (let i = 0; i < 2; ++i) {\n      if (i === 0) {\n        b = 6 * x1 - 12 * x2 + 6 * x3;\n        a = -3 * x1 + 9 * x2 - 9 * x3 + 3 * x4;\n        c = 3 * x2 - 3 * x1;\n      } else {\n        b = 6 * y1 - 12 * y2 + 6 * y3;\n        a = -3 * y1 + 9 * y2 - 9 * y3 + 3 * y4;\n        c = 3 * y2 - 3 * y1;\n      }\n      if (Math.abs(a) < 1e-3) {\n        if (Math.abs(b) < 1e-3) {\n          continue;\n        }\n        t = -c / b;\n        if (0 < t && t < 1) {\n          tvalues.push(t);\n        }\n        continue;\n      }\n      b2ac = b * b - 4 * c * a;\n      sqrtb2ac = Math.sqrt(b2ac);\n      if (b2ac < 0) {\n        continue;\n      }\n      t1 = (-b + sqrtb2ac) / (2 * a);\n      if (0 < t1 && t1 < 1) {\n        tvalues.push(t1);\n      }\n      t2 = (-b - sqrtb2ac) / (2 * a);\n      if (0 < t2 && t2 < 1) {\n        tvalues.push(t2);\n      }\n    }\n    let j = tvalues.length;\n    let jlen = j;\n    let mt;\n    while (j--) {\n      t = tvalues[j];\n      mt = 1 - t;\n      bounds[0][j] = mt * mt * mt * x1 + 3 * mt * mt * t * x2 + 3 * mt * t * t * x3 + t * t * t * x4;\n      bounds[1][j] = mt * mt * mt * y1 + 3 * mt * mt * t * y2 + 3 * mt * t * t * y3 + t * t * t * y4;\n    }\n    bounds[0][jlen] = x1;\n    bounds[1][jlen] = y1;\n    bounds[0][jlen + 1] = x4;\n    bounds[1][jlen + 1] = y4;\n    bounds[0].length = bounds[1].length = jlen + 2;\n    return {\n      min: { x: Math.min.apply(0, bounds[0]), y: Math.min.apply(0, bounds[1]) },\n      max: { x: Math.max.apply(0, bounds[0]), y: Math.max.apply(0, bounds[1]) }\n    };\n  }\n  function integrate(f, a, b, n) {\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;\n    while (i < m) {\n      const Ax = A * x[i];\n      sum += w[i++] * (f(B + Ax) + f(B - Ax));\n    }\n    return A * sum;\n  }\n  function getCurveFromPoints(points, closed2) {\n    const l = points.length, last = l - 1;\n    for (let i = 0; i < l; i++) {\n      const point = points[i];\n      const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0);\n      const next = closed2 ? mod(i + 1, l) : Math.min(i + 1, last);\n      const a = points[prev];\n      const b = point;\n      const c = points[next];\n      getControlPoints(a, b, c);\n      b.command = i === 0 ? Commands.move : Commands.curve;\n    }\n  }\n  function getControlPoints(a, b, c) {\n    const a1 = Vector.angleBetween(a, b);\n    const a2 = Vector.angleBetween(c, b);\n    let d1 = Vector.distanceBetween(a, b);\n    let d2 = Vector.distanceBetween(c, b);\n    let mid = (a1 + a2) / 2;\n    if (d1 < 1e-3 || d2 < 1e-3) {\n      if (typeof b.relative === \"boolean\" && !b.relative) {\n        b.controls.left.copy(b);\n        b.controls.right.copy(b);\n      }\n      return b;\n    }\n    d1 *= 0.33;\n    d2 *= 0.33;\n    if (a2 < a1) {\n      mid += HALF_PI;\n    } else {\n      mid -= HALF_PI;\n    }\n    b.controls.left.x = Math.cos(mid) * d1;\n    b.controls.left.y = Math.sin(mid) * d1;\n    mid -= Math.PI;\n    b.controls.right.x = Math.cos(mid) * d2;\n    b.controls.right.y = Math.sin(mid) * d2;\n    if (typeof b.relative === \"boolean\" && !b.relative) {\n      b.controls.left.x += b.x;\n      b.controls.left.y += b.y;\n      b.controls.right.x += b.x;\n      b.controls.right.y += b.y;\n    }\n    return b;\n  }\n  function getReflection(a, b, relative) {\n    return new Vector(\n      2 * a.x - (b.x + a.x) - (relative ? a.x : 0),\n      2 * a.y - (b.y + a.y) - (relative ? a.y : 0)\n    );\n  }\n  function getAnchorsFromArcData(center, xAxisRotation, rx, ry, ts, td, ccw) {\n    const resolution = Constants.Resolution;\n    const anchors = [];\n    for (let i = 0; i < resolution; i++) {\n      let pct = (i + 1) / resolution;\n      if (ccw) {\n        pct = 1 - pct;\n      }\n      const theta = pct * td + ts;\n      const x = rx * Math.cos(theta);\n      const y = ry * Math.sin(theta);\n      const anchor2 = new Anchor(x, y);\n      anchor2.command = Commands.line;\n      anchors.push(anchor2);\n    }\n  }\n\n  // src/utils/underscore.js\n  var slice = Array.prototype.slice;\n  function isArrayLike(collection) {\n    if (collection === null || collection === void 0) return false;\n    const length = collection.length;\n    return typeof length == \"number\" && length >= 0 && length < 4294967296;\n  }\n  var _ = {\n    isNaN: function(obj) {\n      return typeof obj === \"number\" && obj !== +obj;\n    },\n    isElement: function(obj) {\n      return !!(obj && obj.nodeType === 1);\n    },\n    isObject: function(obj) {\n      const type = typeof obj;\n      return type === \"function\" || type === \"object\" && !!obj;\n    },\n    isFunction: function(obj) {\n      return typeof obj === \"function\";\n    },\n    extend: function(base) {\n      const sources = slice.call(arguments, 1);\n      for (let i = 0; i < sources.length; i++) {\n        const obj = sources[i];\n        for (let k in obj) {\n          base[k] = obj[k];\n        }\n      }\n      return base;\n    },\n    defaults: function(base) {\n      const sources = slice.call(arguments, 1);\n      for (let i = 0; i < sources.length; i++) {\n        const obj = sources[i];\n        for (let k in obj) {\n          if (base[k] === void 0) {\n            base[k] = obj[k];\n          }\n        }\n      }\n      return base;\n    },\n    each: function(obj, iteratee, context) {\n      const ctx = context || this;\n      const keys = !isArrayLike(obj) && Object.keys(obj);\n      const length = (keys || obj).length;\n      for (let i = 0; i < length; i++) {\n        const k = keys ? keys[i] : i;\n        iteratee.call(ctx, obj[k], k, obj);\n      }\n      return obj;\n    },\n    /**\n     * @name Two.Utils.performance\n     * @property {Date} - A special `Date` like object to get the current millis of the session. Used internally to calculate time between frames.\n     * e.g: `Utils.performance.now() // milliseconds since epoch`\n     */\n    performance: root.performance && root.performance.now ? root.performance : Date\n  };\n\n  // src/utils/dom.js\n  var dom = {\n    hasEventListeners: typeof root.addEventListener === \"function\",\n    bind: function(elem, event, func, bool) {\n      if (this.hasEventListeners) {\n        elem.addEventListener(event, func, !!bool);\n      } else {\n        elem.attachEvent(\"on\" + event, func);\n      }\n      return dom;\n    },\n    unbind: function(elem, event, func, bool) {\n      if (dom.hasEventListeners) {\n        elem.removeEventListeners(event, func, !!bool);\n      } else {\n        elem.detachEvent(\"on\" + event, func);\n      }\n      return dom;\n    },\n    getRequestAnimationFrame: function() {\n      const vendors = [\"ms\", \"moz\", \"webkit\", \"o\"];\n      let lastTime = 0;\n      let request = root.requestAnimationFrame;\n      if (!request) {\n        for (let i = 0; i < vendors.length; i++) {\n          request = root[vendors[i] + \"RequestAnimationFrame\"] || request;\n        }\n        request = request || fallbackRequest;\n      }\n      function fallbackRequest(callback, element) {\n        const currTime = (/* @__PURE__ */ new Date()).getTime();\n        const timeToCall = Math.max(0, 16 - (currTime - lastTime));\n        const id = root.setTimeout(nextRequest, timeToCall);\n        lastTime = currTime + timeToCall;\n        function nextRequest() {\n          callback(currTime + timeToCall);\n        }\n        return id;\n      }\n      return request;\n    }\n  };\n  var temp = root.document ? root.document.createElement(\"div\") : {};\n  temp.id = \"help-two-load\";\n  Object.defineProperty(dom, \"temp\", {\n    enumerable: true,\n    get: function() {\n      if (_.isElement(temp) && !root.document.head.contains(temp)) {\n        temp.style.display = \"none\";\n        root.document.head.appendChild(temp);\n      }\n      return temp;\n    }\n  });\n\n  // src/utils/error.js\n  var TwoError = class extends Error {\n    name = \"Two.js\";\n    message;\n    constructor(message) {\n      super();\n      this.message = message;\n    }\n  };\n\n  // src/utils/device-pixel-ratio.js\n  var devicePixelRatio = root.devicePixelRatio || 1;\n  function getBackingStoreRatio(ctx) {\n    return ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;\n  }\n  function getRatio(ctx) {\n    return devicePixelRatio / getBackingStoreRatio(ctx);\n  }\n\n  // src/registry.js\n  var Registry = class {\n    map = {};\n    constructor() {\n    }\n    /**\n     * @name Two.Registry#add\n     * @function\n     * @param {String} id - A unique identifier.\n     * @param obj - Any type of variable to be registered to the directory.\n     * @description Adds any value to the directory. Assigned by the `id`.\n     */\n    add(id, obj) {\n      this.map[id] = obj;\n      return this;\n    }\n    /**\n     * @name Two.Registry#remove\n     * @function\n     * @param {String} id - A unique identifier.\n     * @description Remove any value from the directory by its `id`.\n     */\n    remove(id) {\n      delete this.map[id];\n      return this;\n    }\n    /**\n     * @name Two.Registry#get\n     * @function\n     * @param {String} id - A unique identifier.\n     * @returns {?Object} The associated value. If unavailable then `undefined` is returned.\n     * @description Get a registered value by its `id`.\n     */\n    get(id) {\n      return this.map[id];\n    }\n    /**\n     * @name Two.Registry#contains\n     * @function\n     * @param {String} id - A unique identifier.\n     * @returns {Boolean}\n     * @description Convenience method to see if a value is registered to an `id` already.\n     */\n    contains(id) {\n      return id in this.map;\n    }\n  };\n\n  // src/collection.js\n  var Collection = class extends Array {\n    // Warning: Multiple inheritance hack\n    /**\n     * @private\n     */\n    #events = new Events();\n    // N.B: Technique to disable enumeration on object\n    get _events() {\n      return this.#events;\n    }\n    set _events(e) {\n      this.#events = e;\n    }\n    // Getters and setters aren't enumerable\n    get _bound() {\n      return this.#events._bound;\n    }\n    set _bound(v) {\n      this.#events._bound = v;\n    }\n    addEventListener() {\n      return this.#events.addEventListener?.apply(this, arguments);\n    }\n    on() {\n      return this.#events.on?.apply(this, arguments);\n    }\n    bind() {\n      return this.#events.bind?.apply(this, arguments);\n    }\n    removeEventListener() {\n      return this.#events.removeEventListener?.apply(this, arguments);\n    }\n    off() {\n      return this.#events.off?.apply(this, arguments);\n    }\n    unbind() {\n      return this.#events.unbind?.apply(this, arguments);\n    }\n    dispatchEvent() {\n      return this.#events.dispatchEvent?.apply(this, arguments);\n    }\n    trigger() {\n      return this.#events.trigger?.apply(this, arguments);\n    }\n    listen() {\n      return this.#events.listen?.apply(this, arguments);\n    }\n    ignore() {\n      return this.#events.ignore?.apply(this, arguments);\n    }\n    constructor() {\n      super();\n      if (arguments[0] && Array.isArray(arguments[0])) {\n        if (arguments[0].length > 0) {\n          this.push.apply(this, arguments[0]);\n        }\n      } else if (arguments.length > 0) {\n        this.push.apply(this, arguments);\n      }\n    }\n    pop() {\n      const popped = super.pop.apply(this, arguments);\n      this.trigger(Events.Types.remove, [popped]);\n      return popped;\n    }\n    shift() {\n      const shifted = super.shift.apply(this, arguments);\n      this.trigger(Events.Types.remove, [shifted]);\n      return shifted;\n    }\n    push() {\n      const pushed = super.push.apply(this, arguments);\n      this.trigger(Events.Types.insert, arguments);\n      return pushed;\n    }\n    unshift() {\n      const unshifted = super.unshift.apply(this, arguments);\n      this.trigger(Events.Types.insert, arguments);\n      return unshifted;\n    }\n    splice() {\n      const spliced = super.splice.apply(this, arguments);\n      this.trigger(Events.Types.remove, spliced);\n      if (arguments.length > 2) {\n        const inserted = this.slice(\n          arguments[0],\n          arguments[0] + arguments.length - 2\n        );\n        this.trigger(Events.Types.insert, inserted);\n        this.trigger(Events.Types.order);\n      }\n      return spliced;\n    }\n    sort() {\n      super.sort.apply(this, arguments);\n      this.trigger(Events.Types.order);\n      return this;\n    }\n    reverse() {\n      super.reverse.apply(this, arguments);\n      this.trigger(Events.Types.order);\n      return this;\n    }\n    indexOf() {\n      return super.indexOf.apply(this, arguments);\n    }\n    map(func, scope) {\n      const results = [];\n      for (let key = 0; key < this.length; key++) {\n        const value = this[key];\n        let result;\n        if (scope) {\n          result = func.call(scope, value, key);\n        } else {\n          result = func(value, key);\n        }\n        results.push(result);\n      }\n      return results;\n    }\n  };\n\n  // src/element.js\n  var Element = class _Element extends Events {\n    /**\n     * @name Two.Element#_flagId\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Element#id} needs updating.\n     */\n    _flagId = false;\n    /**\n     * @name Two.Element#_flagClassName\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#className} need updating.\n     */\n    _flagClassName = false;\n    /**\n     * @name Two.Element#renderer\n     * @property {Object} - Object access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n     * @nota-bene With the {@link Two.SVGRenderer} you can access the underlying SVG element created via `shape.renderer.elem`.\n     */\n    _renderer = {};\n    /**\n     * @name Two.Element#id\n     * @property {String} - Session specific unique identifier.\n     * @nota-bene In the {@link Two.SVGRenderer} change this to change the underlying SVG element's id too.\n     */\n    _id = Constants.Identifier + Constants.uniqueId();\n    /**\n     * @name Two.Element#className\n     * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n     * @nota-bene Only rendered to DOM in the SVG renderer.\n     */\n    _className = \"\";\n    /**\n     * @name Two.Element#classList\n     * @property {String[]}\n     * @description A list of class strings stored if imported / interpreted  from an SVG element.\n     */\n    classList = [];\n    constructor() {\n      super();\n      for (let prop in proto3) {\n        Object.defineProperty(this, prop, proto3[prop]);\n      }\n    }\n    static Properties = [\"renderer\", \"id\", \"className\"];\n    /**\n     * @name Two.Element.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Element} to create a new instance\n     * @returns {Two.Element}\n     * @description Create a new {@link Two.Element} from an object notation of a {@link Two.Element}.\n     * @nota-bene Works in conjunction with {@link Two.Element#toObject}\n     */\n    static fromObject(obj) {\n      const elem = new _Element().copy(obj);\n      if (\"id\" in obj) {\n        elem.id = obj.id;\n      }\n      return elem;\n    }\n    /**\n     * @name Two.Element#flagReset\n     * @function\n     * @description Called internally by Two.js's renderer to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagId = this._flagClassName = false;\n      return this;\n    }\n    copy(element) {\n      if (element.renderer && typeof element.renderer.type === \"string\") {\n        this.renderer.type = element.renderer.type;\n      }\n      if (typeof element.className === \"string\") {\n        this.className = element.className;\n      }\n      return this;\n    }\n    toObject() {\n      return {\n        renderer: { type: this.renderer.type },\n        id: this.id,\n        className: this.className\n      };\n    }\n    /**\n     * @name Two.Element#dispose\n     * @function\n     * @description Release the element's renderer object and detach any events.\n     * This cleans up renderer-specific resources and unbinds all event listeners.\n     */\n    dispose() {\n      if (typeof this.unbind === \"function\") {\n        this.unbind();\n      }\n      if (this._renderer) {\n        if (this._renderer.elem && this._renderer.elem.parentNode) {\n          this._renderer.elem.parentNode.removeChild(this._renderer.elem);\n          delete this._renderer.elem;\n        }\n        if (this.type === \"WebGLRenderer\" && this.renderer.ctx) {\n          const gl = this.renderer.ctx;\n          if (this._renderer.texture) {\n            gl.deleteTexture(this._renderer.texture);\n            delete this._renderer.texture;\n          }\n          if (this._renderer.positionBuffer) {\n            gl.deleteBuffer(this._renderer.positionBuffer);\n            delete this._renderer.positionBuffer;\n          }\n          if (this._renderer.effect) {\n            this._renderer.effect = null;\n          }\n        }\n        if (this.type === \"CanvasRenderer\" && this._renderer.context) {\n          delete this._renderer.context;\n        }\n      }\n      const rendererType = this._renderer.type;\n      this._renderer = { type: rendererType };\n      return this;\n    }\n  };\n  var proto3 = {\n    renderer: {\n      enumerable: false,\n      get: function() {\n        return this._renderer;\n      }\n    },\n    id: {\n      enumerable: true,\n      get: function() {\n        return this._id;\n      },\n      set: function(v) {\n        const id = this._id;\n        if (v === this._id) {\n          return;\n        }\n        this._id = v;\n        this._flagId = true;\n        if (this.parent) {\n          delete this.parent.children.ids[id];\n          this.parent.children.ids[this._id] = this;\n        }\n      }\n    },\n    className: {\n      enumerable: true,\n      get: function() {\n        return this._className;\n      },\n      set: function(v) {\n        if (this._className !== v) {\n          this._flagClassName = true;\n          this.classList = v.split(/\\s+?/);\n          this._className = v;\n        }\n      }\n    }\n  };\n\n  // src/effects/texture.js\n  var anchor;\n  var regex = {\n    video: /\\.(mp4|webm|ogg)$/i,\n    image: /\\.(jpe?g|png|gif|tiff|webp)$/i,\n    effect: /texture|gradient/i\n  };\n  if (root.document) {\n    anchor = document.createElement(\"a\");\n  }\n  var Texture = class _Texture extends Element {\n    /**\n     * @name Two.Texture#_flagSrc\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#src} needs updating.\n     */\n    _flagSrc = false;\n    /**\n     * @name Two.Texture#_flagImage\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#image} needs updating.\n     */\n    _flagImage = false;\n    /**\n     * @name Two.Texture#_flagVideo\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#video} needs updating.\n     */\n    _flagVideo = false;\n    /**\n     * @name Two.Texture#_flagLoaded\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#loaded} needs updating.\n     */\n    _flagLoaded = false;\n    /**\n     * @name Two.Texture#_flagRepeat\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#repeat} needs updating.\n     */\n    _flagRepeat = false;\n    /**\n     * @name Two.Texture#_flagOffset\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#offset} needs updating.\n     */\n    _flagOffset = false;\n    /**\n     * @name Two.Texture#_flagScale\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#scale} needs updating.\n     */\n    _flagScale = false;\n    /**\n     * @name Two.Texture#_src\n     * @private\n     * @see {@link Two.Texture#src}\n     */\n    _src = \"\";\n    /**\n     * @name Two.Texture#_image\n     * @private\n     * @see {@link Two.Texture#image}\n     */\n    _image = null;\n    /**\n     * @name Two.Texture#_loaded\n     * @private\n     * @see {@link Two.Texture#loaded}\n     */\n    _loaded = false;\n    /**\n     * @name Two.Texture#_repeat\n     * @private\n     * @see {@link Two.Texture#repeat}\n     */\n    _repeat = \"no-repeat\";\n    /**\n     * @name Two.Texture#_scale\n     * @private\n     * @see {@link Two.Texture#scale}\n     */\n    _scale = 1;\n    /**\n     * @name Two.Texture#_offset\n     * @private\n     * @see {@link Two.Texture#offset}\n     */\n    _offset = null;\n    constructor(src, callback) {\n      super();\n      for (let prop in proto4) {\n        Object.defineProperty(this, prop, proto4[prop]);\n      }\n      this._renderer.type = \"texture\";\n      this._renderer.flagOffset = FlagOffset.bind(this);\n      this._renderer.flagScale = FlagScale.bind(this);\n      this.loaded = false;\n      this.repeat = \"no-repeat\";\n      this.offset = new Vector();\n      if (typeof callback === \"function\") {\n        const loaded = function() {\n          this.unbind(Events.Types.load, loaded);\n          if (typeof callback === \"function\") {\n            callback();\n          }\n        }.bind(this);\n        this.bind(Events.Types.load, loaded);\n      }\n      if (typeof src === \"string\") {\n        this.src = src;\n      } else if (typeof src === \"object\") {\n        const elemString = Object.prototype.toString.call(src);\n        if (elemString === \"[object HTMLImageElement]\" || elemString === \"[object HTMLCanvasElement]\" || elemString === \"[object HTMLVideoElement]\" || elemString === \"[object Image]\") {\n          this.image = src;\n        }\n      }\n      this._update();\n    }\n    /**\n     * @name Two.Texture.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Texture}.\n     */\n    static Properties = [\"src\", \"loaded\", \"repeat\", \"scale\", \"offset\", \"image\"];\n    /**\n     * @name Two.Texture.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Texture} to create a new instance\n     * @returns {Two.Texture}\n     * @description Create a new {@link Two.Texture} from an object notation of a {@link Two.Texture}.\n     * @nota-bene Works in conjunction with {@link Two.Texture#toObject}\n     */\n    fromObject(obj) {\n      const texture = new _Texture().copy(obj);\n      if (\"id\" in obj) {\n        texture.id = obj.id;\n      }\n      return texture;\n    }\n    /**\n     * @name Two.Texture.RegularExpressions\n     * @property {Object} - A map of compatible DOM Elements categorized by media format.\n     */\n    static RegularExpressions = regex;\n    /**\n     * @name Two.Texture.ImageRegistry\n     * @property {Two.Registry} - A canonical listing of image data used in a single session of Two.js.\n     * @nota-bene This object is used to cache image data between different textures.\n     */\n    static ImageRegistry = new Registry();\n    /**\n     * @name Two.Texture.getAbsoluteURL\n     * @property {Function} - Serializes a URL as an absolute path for canonical attribution in {@link Two.Texture.ImageRegistry}.\n     * @param {String} path\n     * @returns {String} - The serialized absolute path.\n     */\n    static getAbsoluteURL(path) {\n      if (!anchor) {\n        return path;\n      }\n      anchor.href = path;\n      return anchor.href;\n    }\n    /**\n     * @name Two.Texture.loadHeadlessBuffer\n     * @property {Function} - Loads an image as a buffer in headless environments.\n     * @param {Two.Texture} texture - The {@link Two.Texture} to be loaded.\n     * @param {Function} onLoad - The callback function to be triggered once the image is loaded.\n     * @nota-bene - This function uses node's `fs.readFileSync` to spoof the `<img />` loading process in the browser.\n     */\n    static loadHeadlessBuffer(texture, onLoad) {\n      texture.image.onload = onLoad;\n      texture.image.src = texture.src;\n    }\n    /**\n     * @name Two.Texture.getTag\n     * @property {Function} - Retrieves the tag name of an image, video, or canvas node.\n     * @param {HTMLImageElement} image - The image to infer the tag name from.\n     * @returns {String} - Returns the tag name of an image, video, or canvas node.\n     */\n    static getTag(image) {\n      return image && image.nodeName && image.nodeName.toLowerCase() || // Headless environments\n      \"img\";\n    }\n    /**\n     * @name Two.Texture.getImage\n     * @property {Function} - Convenience function to set {@link Two.Texture#image} properties with canonical versions set in {@link Two.Texture.ImageRegistry}.\n     * @param {String} src - The URL path of the image.\n     * @returns {HTMLImageElement} - Returns either a cached version of the image or a new one that is registered in {@link Two.Texture.ImageRegistry}.\n     */\n    static getImage(src) {\n      const absoluteSrc = _Texture.getAbsoluteURL(src);\n      if (_Texture.ImageRegistry.contains(absoluteSrc)) {\n        return _Texture.ImageRegistry.get(absoluteSrc);\n      }\n      let image;\n      if (CanvasPolyfill.Image) {\n        image = new CanvasPolyfill.Image();\n        CanvasPolyfill.shim(image, \"img\");\n      } else if (root.document) {\n        if (regex.video.test(absoluteSrc)) {\n          image = document.createElement(\"video\");\n        } else {\n          image = document.createElement(\"img\");\n        }\n      } else {\n        console.warn(\"Two.js: no prototypical image defined for Two.Texture\");\n      }\n      image.crossOrigin = \"anonymous\";\n      image.referrerPolicy = \"no-referrer\";\n      return image;\n    }\n    /**\n     * @name Two.Texture.Register\n     * @interface\n     * @description A collection of functions to register different types of textures. Used internally by a {@link Two.Texture}.\n     */\n    static Register = {\n      canvas: function(texture, callback) {\n        texture._src = \"#\" + texture.id;\n        _Texture.ImageRegistry.add(texture.src, texture.image);\n        if (typeof callback === \"function\") {\n          callback();\n        }\n      },\n      img: function(texture, callback) {\n        const image = texture.image;\n        const loaded = function(e) {\n          if (!CanvasPolyfill.isHeadless && image.removeEventListener && typeof image.removeEventListener === \"function\") {\n            image.removeEventListener(\"load\", loaded, false);\n            image.removeEventListener(\"error\", error, false);\n          }\n          if (typeof callback === \"function\") {\n            callback();\n          }\n        };\n        const error = function(e) {\n          if (!CanvasPolyfill.isHeadless && typeof image.removeEventListener === \"function\") {\n            image.removeEventListener(\"load\", loaded, false);\n            image.removeEventListener(\"error\", error, false);\n          }\n          throw new TwoError(\"unable to load \" + texture.src);\n        };\n        if (typeof image.width === \"number\" && image.width > 0 && typeof image.height === \"number\" && image.height > 0) {\n          loaded();\n        } else if (!CanvasPolyfill.isHeadless && typeof image.addEventListener === \"function\") {\n          image.addEventListener(\"load\", loaded, false);\n          image.addEventListener(\"error\", error, false);\n        }\n        texture._src = _Texture.getAbsoluteURL(texture._src);\n        if (!CanvasPolyfill.isHeadless && image && image.getAttribute(\"two-src\")) {\n          return;\n        }\n        if (!CanvasPolyfill.isHeadless) {\n          image.setAttribute(\"two-src\", texture.src);\n        }\n        _Texture.ImageRegistry.add(texture.src, image);\n        if (CanvasPolyfill.isHeadless) {\n          _Texture.loadHeadlessBuffer(texture, loaded);\n        } else {\n          texture.image.src = texture.src;\n        }\n      },\n      video: function(texture, callback) {\n        if (CanvasPolyfill.isHeadless) {\n          throw new TwoError(\n            \"video textures are not implemented in headless environments.\"\n          );\n        }\n        const loaded = function(e) {\n          texture.image.removeEventListener(\"canplaythrough\", loaded, false);\n          texture.image.removeEventListener(\"error\", error, false);\n          texture.image.width = texture.image.videoWidth;\n          texture.image.height = texture.image.videoHeight;\n          if (typeof callback === \"function\") {\n            callback();\n          }\n        };\n        const error = function(e) {\n          texture.image.removeEventListener(\"canplaythrough\", loaded, false);\n          texture.image.removeEventListener(\"error\", error, false);\n          throw new TwoError(\"unable to load \" + texture.src);\n        };\n        texture._src = _Texture.getAbsoluteURL(texture._src);\n        if (!texture.image.getAttribute(\"two-src\")) {\n          texture.image.setAttribute(\"two-src\", texture.src);\n          _Texture.ImageRegistry.add(texture.src, texture.image);\n        }\n        if (texture.image.readyState >= 4) {\n          loaded();\n        } else {\n          texture.image.addEventListener(\"canplaythrough\", loaded, false);\n          texture.image.addEventListener(\"error\", error, false);\n          texture.image.src = texture.src;\n          texture.image.load();\n        }\n      }\n    };\n    /**\n     * @name Two.Texture.load\n     * @function\n     * @param {Two.Texture} texture - The texture to load.\n     * @param {Function} callback - The function to be called once the texture is loaded.\n     */\n    static load(texture, callback) {\n      let image = texture.image;\n      let tag = _Texture.getTag(image);\n      if (texture._flagImage) {\n        if (/canvas/i.test(tag)) {\n          _Texture.Register.canvas(texture, callback);\n        } else {\n          texture._src = !CanvasPolyfill.isHeadless && image.getAttribute(\"two-src\") || image.src;\n          _Texture.Register[tag](texture, callback);\n        }\n      }\n      if (texture._flagSrc) {\n        if (!image) {\n          image = _Texture.getImage(texture.src);\n          texture.image = image;\n        }\n        tag = _Texture.getTag(image);\n        _Texture.Register[tag](texture, callback);\n      }\n    }\n    /**\n     * @name Two.Texture#clone\n     * @function\n     * @returns {Two.Texture}\n     * @description Create a new instance of {@link Two.Texture} with the same properties of the current texture.\n     */\n    clone() {\n      const clone = new _Texture(this.src);\n      clone.repeat = this.repeat;\n      clone.offset.copy(this.offset);\n      clone.scale = this.scale;\n      return clone;\n    }\n    /**\n     * @name Two.Texture#copy\n     * @function\n     * @param {Two.Texture} texture - The reference {@link Two.Texture}\n     * @description Copy the properties of one {@link Two.Texture} onto another.\n     */\n    copy(texture) {\n      this.src = texture.src;\n      this.repeat = texture.repeat;\n      this.offset = typeof texture.offset === \"number\" || texture.offset instanceof Vector ? texture.offset : new Vector().copy(texture.offset);\n      this.scale = typeof texture.scale === \"number\" || texture.scale instanceof Vector ? texture.scale : new Vector().copy(texture.scale);\n      return this;\n    }\n    /**\n     * @name Two.Texture#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the texture.\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.renderer.type = \"texture\";\n      result.src = this.src;\n      result.repeat = this.repeat;\n      result.offset = this.offset.toObject();\n      result.scale = typeof this.scale === \"number\" ? this.scale : this.scale.toObject();\n      return result;\n    }\n    /**\n     * @name Two.Texture#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagSrc || this._flagImage) {\n        this.trigger(Events.Types.change);\n        if (this._flagSrc || this._flagImage) {\n          this.loaded = false;\n          _Texture.load(\n            this,\n            function() {\n              this.loaded = true;\n              this.trigger(Events.Types.change).trigger(Events.Types.load);\n            }.bind(this)\n          );\n        }\n      }\n      if (this._image && this._image.readyState >= 4) {\n        this._flagVideo = true;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Texture#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagSrc = this._flagImage = this._flagLoaded = this._flagRepeat = this._flagVideo = this._flagScale = this._flagOffset = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Texture#dispose\n     * @function\n     * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n     */\n    dispose() {\n      super.dispose();\n      if (\"elem\" in this._renderer) {\n        const elem = this._renderer.elem;\n        elem.parentNode.removeChild(elem);\n      }\n      if (\"effect\" in this._renderer) {\n        this._renderer.effect = null;\n      }\n      return this;\n    }\n  };\n  var proto4 = {\n    src: {\n      enumerable: true,\n      get: function() {\n        return this._src;\n      },\n      set: function(v) {\n        this._src = v;\n        this._flagSrc = true;\n      }\n    },\n    loaded: {\n      enumerable: true,\n      get: function() {\n        return this._loaded;\n      },\n      set: function(v) {\n        this._loaded = v;\n        this._flagLoaded = true;\n      }\n    },\n    repeat: {\n      enumerable: true,\n      get: function() {\n        return this._repeat;\n      },\n      set: function(v) {\n        this._repeat = v;\n        this._flagRepeat = true;\n      }\n    },\n    image: {\n      enumerable: true,\n      get: function() {\n        return this._image;\n      },\n      set: function(image) {\n        const tag = Texture.getTag(image);\n        let index;\n        switch (tag) {\n          case \"canvas\":\n            index = \"#\" + image.id;\n            break;\n          default:\n            index = image.src;\n        }\n        if (Texture.ImageRegistry.contains(index)) {\n          this._image = Texture.ImageRegistry.get(image.src);\n        } else {\n          this._image = image;\n        }\n        this._flagImage = true;\n      }\n    },\n    offset: {\n      enumerable: true,\n      get: function() {\n        return this._offset;\n      },\n      set: function(v) {\n        if (this._offset) {\n          this._offset.unbind(Events.Types.change, this._renderer.flagOffset);\n        }\n        this._offset = v;\n        this._offset.bind(Events.Types.change, this._renderer.flagOffset);\n        this._flagOffset = true;\n      }\n    },\n    scale: {\n      enumerable: true,\n      get: function() {\n        return this._scale;\n      },\n      set: function(v) {\n        if (this._scale instanceof Vector) {\n          this._scale.unbind(Events.Types.change, this._renderer.flagScale);\n        }\n        this._scale = v;\n        if (this._scale instanceof Vector) {\n          this._scale.bind(Events.Types.change, this._renderer.flagScale);\n        }\n        this._flagScale = true;\n      }\n    }\n  };\n  function FlagOffset() {\n    this._flagOffset = true;\n  }\n  function FlagScale() {\n    this._flagScale = true;\n  }\n\n  // src/effects/stop.js\n  var Stop = class _Stop extends Element {\n    /**\n     * @name Two.Stop#_flagOffset\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#offset} needs updating.\n     */\n    _flagOffset = true;\n    /**\n     * @name Two.Stop#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#opacity} needs updating.\n     */\n    _flagOpacity = true;\n    /**\n     * @name Two.Stop#_flagColor\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#color} needs updating.\n     */\n    _flagColor = true;\n    /**\n     * @name Two.Stop#_offset\n     * @private\n     * @see {@link Two.Stop#offset}\n     */\n    _offset = 0;\n    /**\n     * @name Two.Stop#_opacity\n     * @private\n     * @see {@link Two.Stop#opacity}\n     */\n    _opacity = 1;\n    /**\n     * @name Two.Stop#_color\n     * @private\n     * @see {@link Two.Stop#color}\n     */\n    _color = \"#fff\";\n    constructor(offset, color, opacity) {\n      super();\n      for (let prop in proto5) {\n        Object.defineProperty(this, prop, proto5[prop]);\n      }\n      this._renderer.type = \"stop\";\n      this.offset = typeof offset === \"number\" ? offset : _Stop.Index <= 0 ? 0 : 1;\n      this.opacity = typeof opacity === \"number\" ? opacity : 1;\n      this.color = typeof color === \"string\" ? color : _Stop.Index <= 0 ? \"#fff\" : \"#000\";\n      _Stop.Index = (_Stop.Index + 1) % 2;\n    }\n    /**\n     * @name Two.Stop.Index\n     * @property {Number} - The current index being referenced for calculating a stop's default offset value.\n     */\n    static Index = 0;\n    /**\n     * @name Two.Stop.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Stop}.\n     */\n    static Properties = [\"offset\", \"opacity\", \"color\"];\n    /**\n     * @name Two.Stop.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Stop} to create a new instance\n     * @returns {Two.Stop}\n     * @description Create a new {@link Two.Stop} from an object notation of a {@link Two.Stop}.\n     * @nota-bene Works in conjunction with {@link Two.Stop#toObject}\n     */\n    static fromObject(obj) {\n      const stop = new _Stop().copy(obj);\n      if (\"id\" in obj) {\n        stop.id = obj.id;\n      }\n      return stop;\n    }\n    /**\n     * @name Two.Stop#copy\n     * @function\n     * @param {Two.Stop} stop - The reference {@link Two.Stop}\n     * @description Copy the properties of one {@link Two.Stop} onto another.\n     */\n    copy(stop) {\n      super.copy.call(this, stop);\n      for (let i = 0; i < _Stop.Properties.length; i++) {\n        const k = _Stop.Properties[i];\n        if (k in stop) {\n          this[k] = stop[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Stop#clone\n     * @function\n     * @param {Two.Gradient} [parent] - The parent gradient to add the clone to.\n     * @returns {Two.Stop}\n     * @description Create a new instance of {@link Two.Stop} with the same properties of the current path.\n     */\n    clone(parent) {\n      const clone = new _Stop();\n      _.each(\n        _Stop.Properties,\n        function(property) {\n          clone[property] = this[property];\n        },\n        this\n      );\n      if (parent && parent.stops) {\n        parent.stops.push(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Stop#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.renderer.type = \"stop\";\n      _.each(\n        _Stop.Properties,\n        (k) => {\n          result[k] = this[k];\n        },\n        this\n      );\n      return result;\n    }\n    /**\n     * @name Two.Stop#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagOffset = this._flagColor = this._flagOpacity = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto5 = {\n    offset: {\n      enumerable: true,\n      get: function() {\n        return this._offset;\n      },\n      set: function(v) {\n        this._offset = v;\n        this._flagOffset = true;\n        if (this.parent) {\n          this.parent._flagStops = true;\n        }\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n        if (this.parent) {\n          this.parent._flagStops = true;\n        }\n      }\n    },\n    color: {\n      enumerable: true,\n      get: function() {\n        return this._color;\n      },\n      set: function(v) {\n        this._color = v;\n        this._flagColor = true;\n        if (this.parent) {\n          this.parent._flagStops = true;\n        }\n      }\n    }\n  };\n\n  // src/effects/gradient.js\n  var Gradient = class _Gradient extends Element {\n    _flagStops = false;\n    _flagSpread = false;\n    _flagUnits = false;\n    _spread = \"\";\n    _units = \"\";\n    constructor(stops) {\n      super();\n      for (let prop in proto6) {\n        Object.defineProperty(this, prop, proto6[prop]);\n      }\n      this._renderer.type = \"gradient\";\n      this._renderer.flagStops = FlagStops.bind(this);\n      this._renderer.bindStops = BindStops.bind(this);\n      this._renderer.unbindStops = UnbindStops.bind(this);\n      this.spread = \"pad\";\n      this.units = \"objectBoundingBox\";\n      if (stops) {\n        this.stops = stops;\n      }\n    }\n    /**\n     * @name Two.Gradient.Stop\n     * @see {@link Two.Stop}\n     */\n    static Stop = Stop;\n    /**\n     * @name Two.Gradient.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Gradient}.\n     */\n    static Properties = [\"spread\", \"stops\", \"units\"];\n    /**\n     * @name Two.Gradient.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Gradient} to create a new instance\n     * @returns {Two.Gradient}\n     * @description Create a new {@link Two.Gradient} from an object notation of a {@link Two.Gradient}.\n     * @nota-bene Works in conjunction with {@link Two.Gradient#toObject}\n     */\n    static fromObject(obj) {\n      let stops = obj.stops;\n      if (stops && stops.length > 0) {\n        stops = stops.map((o) => o instanceof Stop ? o : new Stop().copy(o));\n      }\n      const gradient = new _Gradient(stops).copy(obj);\n      if (\"id\" in obj) {\n        gradient.id = obj.id;\n      }\n      return gradient;\n    }\n    /**\n     * @name Two.Gradient#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Gradient}\n     * @description Create a new instance of {@link Two.Gradient} with the same properties of the current path.\n     */\n    clone(parent) {\n      const stops = this.stops.map((s) => {\n        return s.clone();\n      });\n      const clone = new _Gradient(stops);\n      _.each(\n        _Gradient.Properties,\n        (k) => {\n          clone[k] = this[k];\n        },\n        this\n      );\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Gradient#copy\n     * @function\n     * @param {Two.Gradient} gradient - The reference {@link Two.Gradient}\n     * @description Copy the properties of one {@link Two.Gradient} onto another.\n     */\n    copy(gradient) {\n      super.copy.call(this, gradient);\n      for (let i = 0; i < _Gradient.Properties.length; i++) {\n        const k = _Gradient.Properties[i];\n        if (k in gradient) {\n          this[k] = gradient[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Gradient#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const result = {\n        stops: this.stops.map((s) => {\n          return s.toObject();\n        })\n      };\n      _.each(\n        _Gradient.Properties,\n        (k) => {\n          result[k] = this[k];\n        },\n        this\n      );\n      return result;\n    }\n    /**\n     * @name Two.Gradient#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagSpread || this._flagStops) {\n        this.trigger(Events.Types.change);\n      }\n      return this;\n    }\n    /**\n     * @name Two.Gradient#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagSpread = this._flagUnits = this._flagStops = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Gradient#dispose\n     * @function\n     * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n     */\n    dispose() {\n      if (\"elem\" in this._renderer) {\n        const elem = this._renderer.elem;\n        elem.parentNode.removeChild(elem);\n      }\n      if (\"effect\" in this._renderer) {\n        this._renderer.effect = null;\n      }\n      return this;\n    }\n  };\n  var proto6 = {\n    spread: {\n      enumerable: true,\n      get: function() {\n        return this._spread;\n      },\n      set: function(v) {\n        this._spread = v;\n        this._flagSpread = true;\n      }\n    },\n    units: {\n      enumerable: true,\n      get: function() {\n        return this._units;\n      },\n      set: function(v) {\n        this._units = v;\n        this._flagUnits = true;\n      }\n    },\n    stops: {\n      enumerable: true,\n      get: function() {\n        return this._stops;\n      },\n      set: function(stops) {\n        const bindStops = this._renderer.bindStops;\n        const unbindStops = this._renderer.unbindStops;\n        if (this._stops) {\n          this._stops.unbind(Events.Types.insert, bindStops).unbind(Events.Types.remove, unbindStops);\n        }\n        this._stops = new Collection((stops || []).slice(0));\n        this._stops.bind(Events.Types.insert, bindStops).bind(Events.Types.remove, unbindStops);\n        bindStops(this._stops);\n      }\n    }\n  };\n  function FlagStops() {\n    this._flagStops = true;\n  }\n  function BindStops(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].bind(Events.Types.change, this._renderer.flagStops);\n      items[i].parent = this;\n    }\n    this._renderer.flagStops();\n  }\n  function UnbindStops(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].unbind(Events.Types.change, this._renderer.flagStops);\n      delete items[i].parent;\n    }\n    this._renderer.flagStops();\n  }\n\n  // src/effects/linear-gradient.js\n  var LinearGradient = class _LinearGradient extends Gradient {\n    /**\n     * @name Two.LinearGradient#_flagEndPoints\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.LinearGradient#left} or {@link Two.LinearGradient#right} changed and needs to update.\n     */\n    _flagEndPoints = false;\n    _left = null;\n    _right = null;\n    constructor(x1, y1, x2, y2, stops) {\n      super(stops);\n      for (let prop in proto7) {\n        Object.defineProperty(this, prop, proto7[prop]);\n      }\n      this._renderer.type = \"linear-gradient\";\n      this._renderer.flagEndPoints = FlagEndPoints.bind(this);\n      this.left = new Vector();\n      this.right = new Vector();\n      if (typeof x1 === \"number\") {\n        this.left.x = x1;\n      }\n      if (typeof y1 === \"number\") {\n        this.left.y = y1;\n      }\n      if (typeof x2 === \"number\") {\n        this.right.x = x2;\n      }\n      if (typeof y2 === \"number\") {\n        this.right.y = y2;\n      }\n    }\n    /**\n     * @name Two.LinearGradient.Stop\n     * @see {@link Two.Stop}\n     */\n    static Stop = Stop;\n    static Properties = [\"left\", \"right\"];\n    /**\n     * @name Two.LinearGradient.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.LinearGradient} to create a new instance\n     * @returns {Two.LinearGradient}\n     * @description Create a new {@link Two.LinearGradient} from an object notation of a {@link Two.LinearGradient}.\n     * @nota-bene Works in conjunction with {@link Two.LinearGradient#toObject}\n     */\n    static fromObject(obj) {\n      const gradient = new _LinearGradient().copy(obj);\n      if (\"id\" in obj) {\n        gradient.id = obj.id;\n      }\n      return gradient;\n    }\n    /**\n     * @name Two.LinearGradient#copy\n     * @function\n     * @param {Two.LinearGradient} gradient - The reference {@link Two.LinearGradient}\n     * @description Copy the properties of one {@link Two.LinearGradient} onto another.\n     */\n    copy(gradient) {\n      super.copy.call(this, gradient);\n      for (let i = 0; i < _LinearGradient.Properties.length; i++) {\n        const k = _LinearGradient.Properties[i];\n        if (k in gradient) {\n          this[k] = gradient[k] instanceof Vector ? gradient[k] : new Vector().copy(gradient[k]);\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.LinearGradient#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Gradient}\n     * @description Create a new instance of {@link Two.LinearGradient} with the same properties of the current path.\n     */\n    clone(parent) {\n      const stops = this.stops.map(function(stop) {\n        return stop.clone();\n      });\n      const clone = new _LinearGradient(\n        this.left._x,\n        this.left._y,\n        this.right._x,\n        this.right._y,\n        stops\n      );\n      _.each(\n        Gradient.Properties,\n        function(k) {\n          clone[k] = this[k];\n        },\n        this\n      );\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.LinearGradient#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.left = this.left.toObject();\n      result.right = this.right.toObject();\n      return result;\n    }\n    /**\n     * @name Two.LinearGradient#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagEndPoints || this._flagSpread || this._flagStops) {\n        this.trigger(Events.Types.change);\n      }\n      return this;\n    }\n    /**\n     * @name Two.LinearGradient#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagEndPoints = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto7 = {\n    left: {\n      enumerable: true,\n      get: function() {\n        return this._left;\n      },\n      set: function(v) {\n        if (this._left instanceof Vector) {\n          this._left.unbind(Events.Types.change, this._renderer.flagEndPoints);\n        }\n        this._left = v;\n        this._left.bind(Events.Types.change, this._renderer.flagEndPoints);\n        this._flagEndPoints = true;\n      }\n    },\n    right: {\n      enumerable: true,\n      get: function() {\n        return this._right;\n      },\n      set: function(v) {\n        if (this._right instanceof Vector) {\n          this._right.unbind(Events.Types.change, this._renderer.flagEndPoints);\n        }\n        this._right = v;\n        this._right.bind(Events.Types.change, this._renderer.flagEndPoints);\n        this._flagEndPoints = true;\n      }\n    }\n  };\n  function FlagEndPoints() {\n    this._flagEndPoints = true;\n  }\n\n  // src/effects/radial-gradient.js\n  var RadialGradient = class _RadialGradient extends Gradient {\n    /**\n     * @name Two.RadialGradient#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#radius} changed and needs to update.\n     */\n    _flagRadius = false;\n    /**\n     * @name Two.RadialGradient#_flagCenter\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#center} changed and needs to update.\n     */\n    _flagCenter = false;\n    /**\n     * @name Two.RadialGradient#_flagFocal\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#focal} changed and needs to update.\n     */\n    _flagFocal = false;\n    _radius = 0;\n    _center = null;\n    _focal = null;\n    constructor(cx, cy, r, stops, fx, fy) {\n      super(stops);\n      for (let prop in proto8) {\n        Object.defineProperty(this, prop, proto8[prop]);\n      }\n      this._renderer.type = \"radial-gradient\";\n      this._renderer.flagCenter = FlagCenter.bind(this);\n      this._renderer.flagFocal = FlagFocal.bind(this);\n      this.center = new Vector();\n      this.radius = typeof r === \"number\" ? r : 1;\n      this.focal = new Vector();\n      if (typeof cx === \"number\") {\n        this.center.x = cx;\n      }\n      if (typeof cy === \"number\") {\n        this.center.y = cy;\n      }\n      this.focal.copy(this.center);\n      if (typeof fx === \"number\") {\n        this.focal.x = fx;\n      }\n      if (typeof fy === \"number\") {\n        this.focal.y = fy;\n      }\n    }\n    /**\n     * @name Two.RadialGradient.Stop\n     * @see {@link Two.Stop}\n     */\n    static Stop = Stop;\n    /**\n     * @name Two.RadialGradient.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.RadialGradient}.\n     */\n    static Properties = [\"center\", \"radius\", \"focal\"];\n    /**\n     * @name Two.RadialGradient.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.RadialGradient} to create a new instance\n     * @returns {Two.RadialGradient}\n     * @description Create a new {@link Two.RadialGradient} from an object notation of a {@link Two.RadialGradient}.\n     * @nota-bene Works in conjunction with {@link Two.RadialGradient#toObject}\n     */\n    static fromObject(obj) {\n      const gradient = new _RadialGradient().copy(obj);\n      if (\"id\" in obj) {\n        gradient.id = obj.id;\n      }\n      return gradient;\n    }\n    /**\n     * @name Two.RadialGradient#copy\n     * @function\n     * @param {Two.RadialGradient} gradient - The reference {@link Two.RadialGradient}\n     * @description Copy the properties of one {@link Two.RadialGradient} onto another.\n     */\n    copy(gradient) {\n      super.copy.call(this, gradient);\n      for (let i = 0; i < _RadialGradient.Properties.length; i++) {\n        const k = _RadialGradient.Properties[i];\n        if (k in gradient) {\n          if (/(center|focal)i/.test(k)) {\n            this[k] = gradient[k] instanceof Vector ? gradient[k] : new Vector().copy(gradient[k]);\n          } else if (typeof gradient[k] === \"number\") {\n            this[k] = gradient[MediaKeySystemAccess];\n          }\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.RadialGradient#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.RadialGradient}\n     * @description Create a new instance of {@link Two.RadialGradient} with the same properties of the current path.\n     */\n    clone(parent) {\n      const stops = this.stops.map(function(stop) {\n        return stop.clone();\n      });\n      const clone = new _RadialGradient(\n        this.center._x,\n        this.center._y,\n        this._radius,\n        stops,\n        this.focal._x,\n        this.focal._y\n      );\n      _.each(\n        Gradient.Properties.concat(_RadialGradient.Properties),\n        function(k) {\n          clone[k] = this[k];\n        },\n        this\n      );\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.RadialGradient#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      _.each(\n        _RadialGradient.Properties,\n        function(k) {\n          result[k] = this[k];\n        },\n        this\n      );\n      result.center = this.center.toObject();\n      result.focal = this.focal.toObject();\n      return result;\n    }\n    /**\n     * @name Two.RadialGradient#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagRadius || this._flatCenter || this._flagFocal || this._flagSpread || this._flagStops) {\n        this.trigger(Events.Types.change);\n      }\n      return this;\n    }\n    /**\n     * @name Two.RadialGradient#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagRadius = this._flagCenter = this._flagFocal = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto8 = {\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        this._radius = v;\n        this._flagRadius = true;\n      }\n    },\n    center: {\n      enumerable: true,\n      get: function() {\n        return this._center;\n      },\n      set: function(v) {\n        if (this._center) {\n          this._center.unbind(Events.Types.change, this._renderer.flagCenter);\n        }\n        this._center = v;\n        this._center.bind(Events.Types.change, this._renderer.flagCenter);\n        this._flagCenter = true;\n      }\n    },\n    focal: {\n      enumerable: true,\n      get: function() {\n        return this._focal;\n      },\n      set: function(v) {\n        if (this._focal) {\n          this._focal.unbind(Events.Types.change, this._renderer.flagFocal);\n        }\n        this._focal = v;\n        this._focal.bind(Events.Types.change, this._renderer.flagFocal);\n        this._flagFocal = true;\n      }\n    }\n  };\n  function FlagCenter() {\n    this._flagCenter = true;\n  }\n  function FlagFocal() {\n    this._flagFocal = true;\n  }\n\n  // src/utils/shape.js\n  function contains(path, t) {\n    if (t === 0 || t === 1) {\n      return true;\n    }\n    const length = path._length;\n    const target = length * t;\n    let elapsed = 0;\n    for (let i = 0; i < path._lengths.length; i++) {\n      const dist = path._lengths[i];\n      if (elapsed >= target) {\n        return target - elapsed >= 0;\n      }\n      elapsed += dist;\n    }\n    return false;\n  }\n  function getIdByLength(path, target) {\n    const total = path._length;\n    if (target <= 0) {\n      return 0;\n    } else if (target >= total) {\n      return path._lengths.length - 1;\n    }\n    for (let i = 0, sum = 0; i < path._lengths.length; i++) {\n      if (sum + path._lengths[i] >= target) {\n        target -= sum;\n        return Math.max(i - 1, 0) + target / path._lengths[i];\n      }\n      sum += path._lengths[i];\n    }\n    return -1;\n  }\n  function getCurveLength2(a, b, limit) {\n    let x1, x2, x3, x4, y1, y2, y3, y4;\n    const right = b.controls && b.controls.right;\n    const left = a.controls && a.controls.left;\n    x1 = b.x;\n    y1 = b.y;\n    x2 = (right || b).x;\n    y2 = (right || b).y;\n    x3 = (left || a).x;\n    y3 = (left || a).y;\n    x4 = a.x;\n    y4 = a.y;\n    if (right && b._relative) {\n      x2 += b.x;\n      y2 += b.y;\n    }\n    if (left && a._relative) {\n      x3 += a.x;\n      y3 += a.y;\n    }\n    return getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n  }\n  function getSubdivisions(a, b, limit) {\n    let x1, x2, x3, x4, y1, y2, y3, y4;\n    const right = b.controls && b.controls.right;\n    const left = a.controls && a.controls.left;\n    x1 = b.x;\n    y1 = b.y;\n    x2 = (right || b).x;\n    y2 = (right || b).y;\n    x3 = (left || a).x;\n    y3 = (left || a).y;\n    x4 = a.x;\n    y4 = a.y;\n    if (right && b._relative) {\n      x2 += b.x;\n      y2 += b.y;\n    }\n    if (left && a._relative) {\n      x3 += a.x;\n      y3 += a.y;\n    }\n    return subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n  }\n  function getEffectFromObject(obj) {\n    switch (obj.renderer.type) {\n      case \"texture\":\n        return Texture.fromObject(obj);\n      case \"gradient\":\n        return Gradient.fromObject(obj);\n      case \"linear-gradient\":\n        return LinearGradient.fromObject(obj);\n      case \"radial-gradient\":\n        return RadialGradient.fromObject(obj);\n    }\n    return obj;\n  }\n\n  // src/matrix.js\n  var cos = Math.cos;\n  var sin = Math.sin;\n  var tan = Math.tan;\n  var array = [];\n  var Matrix2 = class _Matrix extends Events {\n    /**\n     * @name Two.Matrix#elements\n     * @property {Number[]} - The underlying data stored as an array.\n     */\n    elements = new NumArray(9);\n    /**\n     * @name Two.Matrix#manual\n     * @property {Boolean} - Determines whether Two.js automatically calculates the values for the matrix or if the developer intends to manage the matrix.\n     * @nota-bene - Setting to `true` nullifies {@link Two.Shape#translation}, {@link Two.Shape#rotation}, and {@link Two.Shape#scale}.\n     */\n    manual = false;\n    constructor(a, b, c, d, e, f) {\n      super();\n      let elements = a;\n      if (!Array.isArray(elements)) {\n        elements = Array.prototype.slice.call(arguments);\n      }\n      this.identity();\n      if (elements.length > 0) {\n        this.set(elements);\n      }\n    }\n    //\n    /**\n     * @name Two.Matrix.Identity\n     * @property {Number[]} - A stored reference to the default value of a 3 x 3 matrix.\n     */\n    static Identity = [1, 0, 0, 0, 1, 0, 0, 0, 1];\n    /**\n     * @name Two.Matrix.Multiply\n     * @function\n     * @param {Number[]} A - The first {@link Two.Matrix} to multiply\n     * @param {Number[]} B - The second {@link Two.Matrix} to multiply\n     * @param {Number[]} [C] - An optional {@link Two.Matrix} to apply the result to\n     * @returns {Number[]} - If an optional `C` matrix isn't passed then a new one is created and returned.\n     * @description Multiply two matrices together and return the result.\n     */\n    static Multiply(A, B, C) {\n      if (B.length <= 3) {\n        const e = A;\n        let x, y, z;\n        const a = B[0] || 0, b = B[1] || 0, c = B[2] || 0;\n        x = e[0] * a + e[1] * b + e[2] * c;\n        y = e[3] * a + e[4] * b + e[5] * c;\n        z = e[6] * a + e[7] * b + e[8] * c;\n        return [x, y, z];\n      }\n      const A0 = A[0], A1 = A[1], A2 = A[2];\n      const A3 = A[3], A4 = A[4], A5 = A[5];\n      const A6 = A[6], A7 = A[7], A8 = A[8];\n      const B0 = B[0], B1 = B[1], B2 = B[2];\n      const B3 = B[3], B4 = B[4], B5 = B[5];\n      const B6 = B[6], B7 = B[7], B8 = B[8];\n      C = C || new NumArray(9);\n      C[0] = A0 * B0 + A1 * B3 + A2 * B6;\n      C[1] = A0 * B1 + A1 * B4 + A2 * B7;\n      C[2] = A0 * B2 + A1 * B5 + A2 * B8;\n      C[3] = A3 * B0 + A4 * B3 + A5 * B6;\n      C[4] = A3 * B1 + A4 * B4 + A5 * B7;\n      C[5] = A3 * B2 + A4 * B5 + A5 * B8;\n      C[6] = A6 * B0 + A7 * B3 + A8 * B6;\n      C[7] = A6 * B1 + A7 * B4 + A8 * B7;\n      C[8] = A6 * B2 + A7 * B5 + A8 * B8;\n      return C;\n    }\n    /**\n     * @name Two.Matrix.fromObject\n     * @function\n     * @param {Object} obj - The object notation of a Two.Matrix to create a new instance\n     * @returns {Two.Matrix}\n     * @description Create a new {@link Two.Matrix} from an object notation of a {@link Two.Matrix}.\n     * @nota-bene Works in conjunction with {@link Two.Matrix#toObject}\n     */\n    static fromObject(obj) {\n      return new _Matrix().copy(obj);\n    }\n    /**\n     * @name Two.Matrix#set\n     * @function\n     * @param {Number} a - The value for element at the first column and first row\n     * @param {Number} b - The value for element at the second column and first row\n     * @param {Number} c - The value for element at the third column and first row\n     * @param {Number} d - The value for element at the first column and second row\n     * @param {Number} e - The value for element at the second column and second row\n     * @param {Number} f - The value for element at the third column and second row\n     * @param {Number} g - The value for element at the first column and third row\n     * @param {Number} h - The value for element at the second column and third row\n     * @param {Number} i - The value for element at the third column and third row\n     * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n     */\n    /**\n     * @name Two.Matrix#set\n     * @function\n     * @param {Number[]} a - The array of elements to apply\n     * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n     */\n    set(a, b, c, d, e, f, g, h, i) {\n      if (typeof b === \"undefined\") {\n        const elements = a;\n        a = elements[0];\n        b = elements[1];\n        c = elements[2];\n        d = elements[3];\n        e = elements[4];\n        f = elements[5];\n        g = elements[6];\n        h = elements[7];\n        i = elements[8];\n      }\n      this.elements[0] = a;\n      this.elements[1] = b;\n      this.elements[2] = c;\n      this.elements[3] = d;\n      this.elements[4] = e;\n      this.elements[5] = f;\n      this.elements[6] = g;\n      this.elements[7] = h;\n      this.elements[8] = i;\n      return this.trigger(Events.Types.change);\n    }\n    /**\n     * @name Two.Matrix#copy\n     * @function\n     * @param {Two.Matrix} m - The matrix to copy\n     * @description Copy the matrix of one to the current instance.\n     */\n    copy(m) {\n      this.elements[0] = m.elements[0];\n      this.elements[1] = m.elements[1];\n      this.elements[2] = m.elements[2];\n      this.elements[3] = m.elements[3];\n      this.elements[4] = m.elements[4];\n      this.elements[5] = m.elements[5];\n      this.elements[6] = m.elements[6];\n      this.elements[7] = m.elements[7];\n      this.elements[8] = m.elements[8];\n      this.manual = m.manual;\n      return this.trigger(Events.Types.change);\n    }\n    /**\n     * @name Two.Matrix#identity\n     * @function\n     * @description Turn matrix to the identity, like resetting.\n     */\n    identity() {\n      this.elements[0] = _Matrix.Identity[0];\n      this.elements[1] = _Matrix.Identity[1];\n      this.elements[2] = _Matrix.Identity[2];\n      this.elements[3] = _Matrix.Identity[3];\n      this.elements[4] = _Matrix.Identity[4];\n      this.elements[5] = _Matrix.Identity[5];\n      this.elements[6] = _Matrix.Identity[6];\n      this.elements[7] = _Matrix.Identity[7];\n      this.elements[8] = _Matrix.Identity[8];\n      return this.trigger(Events.Types.change);\n    }\n    /**\n     * @name Two.Matrix#multiply\n     * @function\n     * @param {Number} s - The scalar to be multiplied.\n     * @description Multiply all components of the matrix against a single scalar value.\n     * @overloaded\n     */\n    /**\n     * @name Two.Matrix#multiply\n     * @function\n     * @param {Number} x - The `x` component to be multiplied.\n     * @param {Number} y - The `y` component to be multiplied.\n     * @param {Number} z - The `z` component to be multiplied.\n     * @description Multiply all components of a matrix against a 3 component vector.\n     * @overloaded\n     */\n    /**\n     * @name Two.Matrix#multiply\n     * @function\n     * @param {Number} a - The value at the first column and first row of the matrix to be multiplied.\n     * @param {Number} b - The value at the second column and first row of the matrix to be multiplied.\n     * @param {Number} c - The value at the third column and first row of the matrix to be multiplied.\n     * @param {Number} d - The value at the first column and second row of the matrix to be multiplied.\n     * @param {Number} e - The value at the second column and second row of the matrix to be multiplied.\n     * @param {Number} f - The value at the third column and second row of the matrix to be multiplied.\n     * @param {Number} g - The value at the first column and third row of the matrix to be multiplied.\n     * @param {Number} h - The value at the second column and third row of the matrix to be multiplied.\n     * @param {Number} i - The value at the third column and third row of the matrix to be multiplied.\n     * @description Multiply all components of a matrix against another matrix.\n     * @overloaded\n     */\n    multiply(a, b, c, d, e, f, g, h, i) {\n      if (typeof b === \"undefined\") {\n        this.elements[0] *= a;\n        this.elements[1] *= a;\n        this.elements[2] *= a;\n        this.elements[3] *= a;\n        this.elements[4] *= a;\n        this.elements[5] *= a;\n        this.elements[6] *= a;\n        this.elements[7] *= a;\n        this.elements[8] *= a;\n        return this.trigger(Events.Types.change);\n      }\n      if (typeof c === \"undefined\") {\n        c = 1;\n      }\n      if (typeof d === \"undefined\") {\n        a = a || 0;\n        b = b || 0;\n        c = c || 0;\n        e = this.elements;\n        const x = e[0] * a + e[1] * b + e[2] * c;\n        const y = e[3] * a + e[4] * b + e[5] * c;\n        const z = e[6] * a + e[7] * b + e[8] * c;\n        return [x, y, z];\n      }\n      const A = this.elements;\n      const B = [a, b, c, d, e, f, g, h, i];\n      const A0 = A[0], A1 = A[1], A2 = A[2];\n      const A3 = A[3], A4 = A[4], A5 = A[5];\n      const A6 = A[6], A7 = A[7], A8 = A[8];\n      const B0 = B[0], B1 = B[1], B2 = B[2];\n      const B3 = B[3], B4 = B[4], B5 = B[5];\n      const B6 = B[6], B7 = B[7], B8 = B[8];\n      this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6;\n      this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7;\n      this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8;\n      this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6;\n      this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7;\n      this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8;\n      this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6;\n      this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7;\n      this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8;\n      return this.trigger(Events.Types.change);\n    }\n    /**\n     * @name Two.Matrix#inverse\n     * @function\n     * @param {Two.Matrix} [output] - The optional matrix to apply the inversion to.\n     * @description Return an inverted version of the matrix. If no optional one is passed a new matrix is created and returned.\n     */\n    inverse(output) {\n      const a = this.elements;\n      output = output || new _Matrix();\n      const a00 = a[0], a01 = a[1], a02 = a[2];\n      const a10 = a[3], a11 = a[4], a12 = a[5];\n      const a20 = a[6], a21 = a[7], a22 = a[8];\n      const b01 = a22 * a11 - a12 * a21;\n      const b11 = -a22 * a10 + a12 * a20;\n      const b21 = a21 * a10 - a11 * a20;\n      let det = a00 * b01 + a01 * b11 + a02 * b21;\n      if (!det) {\n        return null;\n      }\n      det = 1 / det;\n      output.elements[0] = b01 * det;\n      output.elements[1] = (-a22 * a01 + a02 * a21) * det;\n      output.elements[2] = (a12 * a01 - a02 * a11) * det;\n      output.elements[3] = b11 * det;\n      output.elements[4] = (a22 * a00 - a02 * a20) * det;\n      output.elements[5] = (-a12 * a00 + a02 * a10) * det;\n      output.elements[6] = b21 * det;\n      output.elements[7] = (-a21 * a00 + a01 * a20) * det;\n      output.elements[8] = (a11 * a00 - a01 * a10) * det;\n      return output;\n    }\n    /**\n     * @name Two.Matrix#scale\n     * @function\n     * @param {Number} s - The one dimensional scale to apply to the matrix.\n     * @description Uniformly scale the transformation matrix.\n     */\n    /**\n     * @name Two.Matrix#scale\n     * @function\n     * @param {Number} sx - The horizontal scale factor.\n     * @param {Number} sy - The vertical scale factor\n     * @description Scale the transformation matrix in two dimensions.\n     */\n    scale(sx, sy) {\n      const l = arguments.length;\n      if (l <= 1) {\n        sy = sx;\n      }\n      return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1);\n    }\n    /**\n     * @name Two.Matrix#rotate\n     * @function\n     * @param {Number} n - The amount to rotate in Number.\n     * @description Rotate the matrix.\n     */\n    rotate(n) {\n      const c = cos(n);\n      const s = sin(n);\n      return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1);\n    }\n    /**\n     * @name Two.Matrix#translate\n     * @function\n     * @param {Number} x - The horizontal translation value to apply\n     * @param {Number} y - The vertical translation value to apply\n     * @description Translate the matrix to specific `x` / `y` values.\n     */\n    translate(x, y) {\n      return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1);\n    }\n    /**\n     * @name Two.Matrix#skewX\n     * @function\n     * @param {Number} n - The amount to skew\n     * @description Skew the matrix by an angle in the x axis direction.\n     */\n    skewX(n) {\n      const a = tan(n);\n      return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1);\n    }\n    /**\n     * @name Two.Matrix#skewY\n     * @function\n     * @param {Number} n - The amount to skew\n     * @description Skew the matrix by an angle in the y axis direction.\n     */\n    skewY(n) {\n      const a = tan(n);\n      return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1);\n    }\n    /**\n     * @name Two.Matrix#toString\n     * @function\n     * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n     * @returns {String} - The transformation matrix as a 6 component string separated by spaces.\n     * @description Create a transform string. Used for the Two.js rendering APIs.\n     */\n    toString(fullMatrix) {\n      array.length = 0;\n      this.toTransformArray(fullMatrix, array);\n      return array.map(toFixed).join(\" \");\n    }\n    /**\n     * @name Two.Matrix#toTransformArray\n     * @function\n     * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 in the format for 2D transformations.\n     * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n     * @description Create a transform array. Used for the Two.js rendering APIs.\n     */\n    toTransformArray(fullMatrix, output) {\n      const elements = this.elements;\n      const hasOutput = !!output;\n      const a = elements[0];\n      const b = elements[1];\n      const c = elements[2];\n      const d = elements[3];\n      const e = elements[4];\n      const f = elements[5];\n      if (fullMatrix) {\n        const g = elements[6];\n        const h = elements[7];\n        const i = elements[8];\n        if (hasOutput) {\n          output[0] = a;\n          output[1] = d;\n          output[2] = g;\n          output[3] = b;\n          output[4] = e;\n          output[5] = h;\n          output[6] = c;\n          output[7] = f;\n          output[8] = i;\n          return;\n        }\n        return [a, d, g, b, e, h, c, f, i];\n      }\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = d;\n        output[2] = b;\n        output[3] = e;\n        output[4] = c;\n        output[5] = f;\n        return;\n      }\n      return [\n        a,\n        d,\n        b,\n        e,\n        c,\n        f\n        // Specific format see LN:19\n      ];\n    }\n    /**\n     * @name Two.Matrix#toArray\n     * @function\n     * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n     * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n     * @description Create a transform array. Used for the Two.js rendering APIs.\n     */\n    toArray(fullMatrix, output) {\n      const elements = this.elements;\n      const hasOutput = !!output;\n      const a = elements[0];\n      const b = elements[1];\n      const c = elements[2];\n      const d = elements[3];\n      const e = elements[4];\n      const f = elements[5];\n      if (fullMatrix) {\n        const g = elements[6];\n        const h = elements[7];\n        const i = elements[8];\n        if (hasOutput) {\n          output[0] = a;\n          output[1] = b;\n          output[2] = c;\n          output[3] = d;\n          output[4] = e;\n          output[5] = f;\n          output[6] = g;\n          output[7] = h;\n          output[8] = i;\n          return;\n        }\n        return [a, b, c, d, e, f, g, h, i];\n      }\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = b;\n        output[2] = c;\n        output[3] = d;\n        output[4] = e;\n        output[5] = f;\n        return;\n      }\n      return [a, b, c, d, e, f];\n    }\n    /**\n     * @name Two.Matrix#toObject\n     * @function\n     * @description Create a JSON compatible object that represents information of the matrix.\n     * @nota-bene Works in conjunction with {@link Two.Matrix.fromObject}\n     */\n    toObject() {\n      return {\n        renderer: { type: \"matrix\" },\n        elements: this.toArray(true),\n        manual: !!this.manual\n      };\n    }\n    /**\n     * @name Two.Matrix#clone\n     * @function\n     * @description Clone the current matrix.\n     */\n    clone() {\n      return new _Matrix().copy(this);\n    }\n  };\n  setMatrix(Matrix2);\n\n  // src/shape.js\n  var Shape = class _Shape extends Element {\n    /**\n     * @name Two.Shape#_flagMatrix\n     * @private\n     * @property {Boolean} - Determines whether the matrix needs updating.\n     */\n    _flagMatrix = true;\n    /**\n     * @name Two.Shape#_flagScale\n     * @private\n     * @property {Boolean} - Determines whether the scale needs updating.\n     */\n    _flagScale = false;\n    // Underlying Properties\n    /**\n     * @name Two.Shape#_matrix\n     * @private\n     * @property {Two.Matrix} - The matrix value of the shape's position, rotation, and scale.\n     */\n    _matrix = null;\n    /**\n     * @name Two.Shape#_worldMatrix\n     * @private\n     * @property {Two.Matrix} - The matrix value of the shape's position, rotation, and scale in the scene.\n     */\n    _worldMatrix = null;\n    /**\n     * @name Two.Shape#_position\n     * @private\n     * @property {Two.Vector} - The translation values as a {@link Two.Vector}.\n     */\n    _position = null;\n    /**\n     * @name Two.Shape#_rotation\n     * @private\n     * @property {Number} - The rotation value in radians.\n     */\n    _rotation = 0;\n    /**\n     * @name Two.Shape#_scale\n     * @private\n     * @property {Number|Two.Vector} - The scale value in Number. Can be a vector for non-uniform scaling.\n     */\n    _scale = 1;\n    /**\n     * @name Two.Shape#_skewX\n     * @private\n     * @property {Number} - The rotation value in Number.\n     */\n    _skewX = 0;\n    /**\n     * @name Two.Shape#_skewY\n     * @private\n     * @property {Number} - The rotation value in Number.\n     */\n    _skewY = 0;\n    constructor() {\n      super();\n      for (let prop in proto9) {\n        Object.defineProperty(this, prop, proto9[prop]);\n      }\n      this._renderer.flagMatrix = FlagMatrix.bind(this);\n      this.isShape = true;\n      this.matrix = new Matrix2();\n      this.worldMatrix = new Matrix2();\n      this.position = new Vector();\n      this.rotation = 0;\n      this.scale = 1;\n      this.skewX = 0;\n      this.skewY = 0;\n    }\n    static Properties = [\n      \"position\",\n      \"rotation\",\n      \"scale\",\n      \"skewX\",\n      \"skewY\",\n      \"matrix\",\n      \"worldMatrix\"\n    ];\n    /**\n     * @name Two.Shape.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Shape} to create a new instance\n     * @returns {Two.Shape}\n     * @description Create a new {@link Two.Shape} from an object notation of a {@link Two.Shape}.\n     * @nota-bene Works in conjunction with {@link Two.Shape#toObject}\n     */\n    static fromObject(obj) {\n      const shape = new _Shape().copy(obj);\n      if (\"id\" in obj) {\n        shape.id = obj.id;\n      }\n      return shape;\n    }\n    get renderer() {\n      return this._renderer;\n    }\n    set renderer(v) {\n      this._renderer = v;\n    }\n    /**\n     * @name Two.Shape#translation\n     * @description Alias for {@link Two.Shape#position}.\n     */\n    get translation() {\n      return proto9.position.get.apply(this, arguments);\n    }\n    set translation(v) {\n      proto9.position.set.apply(this, arguments);\n    }\n    /**\n     * @name Two.Shape#addTo\n     * @function\n     * @param {Two.Group} group - The parent the shape adds itself to.\n     * @description Convenience method to add itself to the scenegraph.\n     */\n    addTo(group) {\n      group.add(this);\n      return this;\n    }\n    /**\n     * @name Two.Shape#remove\n     * @function\n     * @description Remove self from the scene / parent.\n     */\n    remove() {\n      if (!this.parent) {\n        return this;\n      }\n      this.parent.remove(this);\n      return this;\n    }\n    /**\n     * @name Two.Shape#contains\n     * @function\n     * @param {Number} x - x coordinate to hit test against\n     * @param {Number} y - y coordinate to hit test against\n     * @param {Object} [options] - Optional options object\n     * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `shape.visible = false` shapes\n     * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n     * @returns {Boolean}\n     * @description Check to see if coordinates are within a {@link Two.Shape}'s bounding rectangle\n     * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n     */\n    contains(x, y, options) {\n      const opts = options || {};\n      const ignoreVisibility = opts.ignoreVisibility === true;\n      if (!ignoreVisibility && \"visible\" in this && this.visible === false) {\n        return false;\n      }\n      if (!ignoreVisibility && \"opacity\" in this && typeof this.opacity === \"number\" && this.opacity <= 0) {\n        return false;\n      }\n      if (typeof this.getBoundingClientRect !== \"function\") {\n        return false;\n      }\n      const tolerance = typeof opts.tolerance === \"number\" ? opts.tolerance : 0;\n      this._update(true);\n      const rect = this.getBoundingClientRect();\n      if (!rect) {\n        return false;\n      }\n      return x >= rect.left - tolerance && x <= rect.right + tolerance && y >= rect.top - tolerance && y <= rect.bottom + tolerance;\n    }\n    /**\n     * @name Two.Shape#copy\n     * @function\n     * @param {Two.Shape} shape\n     * @description Copy the properties of one {@link Two.Shape} onto another.\n     */\n    copy(shape) {\n      super.copy.call(this, shape);\n      if (\"position\" in shape) {\n        if (shape.position instanceof Vector) {\n          this.position = shape.position;\n        } else {\n          this.position.copy(shape.position);\n        }\n      }\n      if (\"rotation\" in shape) {\n        this.rotation = shape.rotation;\n      }\n      if (\"scale\" in shape) {\n        this.scale = typeof shape.scale === \"number\" || shape.scale instanceof Vector ? shape.scale : new Vector(shape.scale.x, shape.scale.y);\n      }\n      if (\"skewX\" in shape) {\n        this.skewX = shape.skewX;\n      }\n      if (\"skewY\" in shape) {\n        this.skewY = shape.skewY;\n      }\n      if (\"matrix\" in shape && shape.matrix.manual) {\n        this.matrix.copy(shape.matrix);\n        this.matrix.manual = true;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Shape#clone\n     * @function\n     * @param {Two.Group} [parent] - Optional argument to automatically add the shape to a scenegraph.\n     * @returns {Two.Shape}\n     * @description Create a new {@link Two.Shape} with the same values as the current shape.\n     */\n    clone(parent) {\n      const clone = new _Shape();\n      clone.position.copy(this.position);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    /**\n     * @name Two.Shape#toObject\n     * @function\n     * @description Create a JSON compatible object that represents information of the shape.\n     * @nota-bene Works in conjunction with {@link Two.Shape.fromObject}\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.renderer = { type: \"shape\" };\n      result.isShape = true;\n      result.translation = this.translation.toObject();\n      result.rotation = this.translation.rotation;\n      result.scale = this.scale instanceof Vector ? this.scale.toObject() : this.scale;\n      result.skewX = this.skewX;\n      result.skewY = this.skewY;\n      result.matrix = this.matrix.toObject();\n      return result;\n    }\n    /**\n     * @name Two.Shape#dispose\n     * @function\n     * @description Release the shape's bound objects by unbinding relevant events.\n     */\n    dispose() {\n      super.dispose();\n      if (typeof this.translation === \"object\" && typeof this.translation.unbind === \"function\") {\n        this.translation.unbind();\n      }\n      if (typeof this.scale === \"object\" && typeof this.scale.unbind === \"function\") {\n        this.scale.unbind();\n      }\n    }\n    /**\n     * @name Two.Shape#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update(bubbles) {\n      if (!this._matrix.manual && this._flagMatrix) {\n        this._matrix.identity().translate(this.position.x, this.position.y);\n        this._matrix.rotate(this.rotation);\n        if (this._scale instanceof Vector) {\n          this._matrix.scale(this._scale.x, this._scale.y);\n        } else {\n          this._matrix.scale(this._scale);\n        }\n        this._matrix.skewX(this.skewX);\n        this._matrix.skewY(this.skewY);\n      }\n      if (bubbles) {\n        if (this.parent && this.parent._update) {\n          this.parent._update();\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Shape#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagMatrix = this._flagScale = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto9 = {\n    position: {\n      enumerable: true,\n      get: function() {\n        return this._position;\n      },\n      set: function(v) {\n        if (this._position) {\n          this._position.unbind(Events.Types.change, this._renderer.flagMatrix);\n        }\n        this._position = v;\n        this._position.bind(Events.Types.change, this._renderer.flagMatrix);\n        FlagMatrix.call(this);\n      }\n    },\n    rotation: {\n      enumerable: true,\n      get: function() {\n        return this._rotation;\n      },\n      set: function(v) {\n        this._rotation = v;\n        this._flagMatrix = true;\n      }\n    },\n    scale: {\n      enumerable: true,\n      get: function() {\n        return this._scale;\n      },\n      set: function(v) {\n        if (this._scale instanceof Vector) {\n          this._scale.unbind(Events.Types.change, this._renderer.flagMatrix);\n        }\n        this._scale = v;\n        if (this._scale instanceof Vector) {\n          this._scale.bind(Events.Types.change, this._renderer.flagMatrix);\n        }\n        this._flagMatrix = true;\n        this._flagScale = true;\n      }\n    },\n    skewX: {\n      enumerable: true,\n      get: function() {\n        return this._skewX;\n      },\n      set: function(v) {\n        this._skewX = v;\n        this._flagMatrix = true;\n      }\n    },\n    skewY: {\n      enumerable: true,\n      get: function() {\n        return this._skewY;\n      },\n      set: function(v) {\n        this._skewY = v;\n        this._flagMatrix = true;\n      }\n    },\n    matrix: {\n      enumerable: true,\n      get: function() {\n        return this._matrix;\n      },\n      set: function(v) {\n        this._matrix = v;\n        this._flagMatrix = true;\n      }\n    },\n    worldMatrix: {\n      enumerable: true,\n      get: function() {\n        getComputedMatrix(this, this._worldMatrix);\n        return this._worldMatrix;\n      },\n      set: function(v) {\n        this._worldMatrix = v;\n      }\n    }\n  };\n  function FlagMatrix() {\n    this._flagMatrix = true;\n  }\n\n  // src/utils/hit-test.js\n  var TRANSPARENT_REGEX = /^(?:none|transparent)$/i;\n  var DEFAULT_PRECISION = 8;\n  var EPSILON = Number.EPSILON;\n  function createPoint(x, y) {\n    return { x, y };\n  }\n  function pointsEqual(a, b, epsilon = EPSILON) {\n    return Math.abs(a.x - b.x) <= epsilon && Math.abs(a.y - b.y) <= epsilon;\n  }\n  function svgAngle(ux, uy, vx, vy) {\n    const dot = ux * vx + uy * vy;\n    const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy) || 1e-12;\n    let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));\n    if (ux * vy - uy * vx < 0) {\n      ang = -ang;\n    }\n    return ang;\n  }\n  function sampleArcPoints(prev, anchor2, precision) {\n    if (!prev) {\n      return [createPoint(anchor2.x, anchor2.y)];\n    }\n    let rx = anchor2.rx;\n    let ry = anchor2.ry;\n    if (!(rx && ry)) {\n      return [createPoint(anchor2.x, anchor2.y)];\n    }\n    const xAxisRotation = (anchor2.xAxisRotation || 0) * Math.PI / 180;\n    const largeArcFlag = anchor2.largeArcFlag ? 1 : 0;\n    const sweepFlag = anchor2.sweepFlag ? 1 : 0;\n    rx = Math.abs(rx);\n    ry = Math.abs(ry);\n    const ax = prev.x;\n    const ay = prev.y;\n    const x = anchor2.x;\n    const y = anchor2.y;\n    const dx2 = (ax - x) / 2;\n    const dy2 = (ay - y) / 2;\n    const cosRot = Math.cos(xAxisRotation);\n    const sinRot = Math.sin(xAxisRotation);\n    let x1p = cosRot * dx2 + sinRot * dy2;\n    let y1p = -sinRot * dx2 + cosRot * dy2;\n    let rxs = rx * rx;\n    let rys = ry * ry;\n    const cr = x1p * x1p / rxs + y1p * y1p / rys;\n    if (cr > 1) {\n      const s = Math.sqrt(cr);\n      rx *= s;\n      ry *= s;\n      rxs = rx * rx;\n      rys = ry * ry;\n    }\n    const dq = rxs * y1p * y1p + rys * x1p * x1p;\n    const pq = dq === 0 ? 0 : (rxs * rys - dq) / dq;\n    let q = Math.sqrt(Math.max(0, pq));\n    if (largeArcFlag === sweepFlag) {\n      q = -q;\n    }\n    const cxp = q * rx * y1p / ry;\n    const cyp = -q * ry * x1p / rx;\n    const cx = cosRot * cxp - sinRot * cyp + (ax + x) / 2;\n    const cy = sinRot * cxp + cosRot * cyp + (ay + y) / 2;\n    const startAngle = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n    const delta = svgAngle(\n      (x1p - cxp) / rx,\n      (y1p - cyp) / ry,\n      (-x1p - cxp) / rx,\n      (-y1p - cyp) / ry\n    ) % TWO_PI;\n    const endAngle = startAngle + delta;\n    const clockwise = sweepFlag === 0;\n    const angleDelta = (() => {\n      const raw = endAngle - startAngle;\n      const samePoints = Math.abs(raw) < Number.EPSILON;\n      let deltaAngle = mod(raw, TWO_PI);\n      if (deltaAngle < Number.EPSILON) {\n        deltaAngle = samePoints ? 0 : TWO_PI;\n      }\n      if (clockwise && !samePoints) {\n        deltaAngle = deltaAngle === TWO_PI ? -TWO_PI : deltaAngle - TWO_PI;\n      }\n      return deltaAngle;\n    })();\n    const steps = Math.max(Constants.Resolution, Math.max(precision * 2, 1));\n    const points = [];\n    for (let i = 1; i <= steps; i++) {\n      const t = i / steps;\n      const angle = startAngle + t * angleDelta;\n      let px = cx + rx * Math.cos(angle);\n      let py = cy + ry * Math.sin(angle);\n      if (xAxisRotation !== 0) {\n        const tx = px - cx;\n        const ty = py - cy;\n        const cosR = Math.cos(xAxisRotation);\n        const sinR = Math.sin(xAxisRotation);\n        px = tx * cosR - ty * sinR + cx;\n        py = tx * sinR + ty * cosR + cy;\n      }\n      points.push(createPoint(px, py));\n    }\n    return points;\n  }\n  function buildPathHitParts(path, precision = DEFAULT_PRECISION) {\n    const polygons = [];\n    const segments = [];\n    const vertices = path._renderer && path._renderer.vertices && path._renderer.vertices.length > 0 ? path._renderer.vertices : path.vertices;\n    if (!vertices || vertices.length === 0) {\n      return { polygons, segments };\n    }\n    const limit = Math.max(1, Math.floor(precision));\n    let currentPolygon = null;\n    let firstPoint = null;\n    let lastPoint = null;\n    let prevVertex = null;\n    const closePolygon = (forceClose = false) => {\n      if (!currentPolygon) {\n        return;\n      }\n      if (forceClose && firstPoint && lastPoint && !pointsEqual(firstPoint, lastPoint)) {\n        const closingPoint = createPoint(firstPoint.x, firstPoint.y);\n        segments.push({ a: lastPoint, b: closingPoint });\n        currentPolygon.push(closingPoint);\n        lastPoint = closingPoint;\n      }\n      if (currentPolygon.length >= 3 && firstPoint && lastPoint && pointsEqual(firstPoint, lastPoint)) {\n        polygons.push(currentPolygon);\n      }\n      currentPolygon = null;\n      firstPoint = null;\n      lastPoint = null;\n    };\n    const appendPoint = (pt) => {\n      if (!lastPoint) {\n        lastPoint = pt;\n        if (currentPolygon) {\n          currentPolygon.push(pt);\n        }\n        return;\n      }\n      if (pointsEqual(lastPoint, pt)) {\n        return;\n      }\n      segments.push({ a: lastPoint, b: pt });\n      if (currentPolygon) {\n        currentPolygon.push(pt);\n      }\n      lastPoint = pt;\n    };\n    for (let i = 0; i < vertices.length; i++) {\n      const vertex = vertices[i];\n      const command = vertex.command || (i === 0 ? Commands.move : Commands.line);\n      if (command === Commands.move) {\n        closePolygon(false);\n        const pt = createPoint(vertex.x, vertex.y);\n        currentPolygon = [pt];\n        firstPoint = pt;\n        lastPoint = pt;\n        prevVertex = vertex;\n        continue;\n      }\n      if (!prevVertex) {\n        prevVertex = vertices[Math.max(i - 1, 0)];\n      }\n      if (command === Commands.line) {\n        appendPoint(createPoint(vertex.x, vertex.y));\n      } else if (command === Commands.curve) {\n        const subdivisions = getSubdivisions(vertex, prevVertex, limit);\n        for (let j = 1; j < subdivisions.length; j++) {\n          const sv = subdivisions[j];\n          appendPoint(createPoint(sv.x, sv.y));\n        }\n        appendPoint(createPoint(vertex.x, vertex.y));\n      } else if (command === Commands.arc) {\n        const arcPoints = sampleArcPoints(prevVertex, vertex, limit);\n        for (let j = 0; j < arcPoints.length; j++) {\n          appendPoint(arcPoints[j]);\n        }\n      } else if (command === Commands.close) {\n        closePolygon(true);\n        prevVertex = vertex;\n        continue;\n      } else {\n        appendPoint(createPoint(vertex.x, vertex.y));\n      }\n      prevVertex = vertex;\n    }\n    if (currentPolygon) {\n      const shouldForceClose = !!path._closed || !!path.closed || firstPoint && lastPoint && !pointsEqual(firstPoint, lastPoint);\n      closePolygon(shouldForceClose);\n    }\n    return { polygons, segments };\n  }\n  function pointInPolygons(polygons, x, y) {\n    let inside = false;\n    for (let i = 0; i < polygons.length; i++) {\n      const polygon = polygons[i];\n      if (!polygon || polygon.length < 3) {\n        continue;\n      }\n      let lastIndex = polygon.length - 1;\n      for (let j = 0; j < polygon.length; j++) {\n        const v0 = polygon[lastIndex];\n        const v1 = polygon[j];\n        const intersects = v1.y > y !== v0.y > y && x < (v0.x - v1.x) * (y - v1.y) / (v0.y - v1.y || 1e-12) + v1.x;\n        if (intersects) {\n          inside = !inside;\n        }\n        lastIndex = j;\n      }\n    }\n    return inside;\n  }\n  function distanceToSegmentSquared(x, y, a, b) {\n    const dx = b.x - a.x;\n    const dy = b.y - a.y;\n    if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {\n      const ddx2 = x - a.x;\n      const ddy2 = y - a.y;\n      return ddx2 * ddx2 + ddy2 * ddy2;\n    }\n    const t = ((x - a.x) * dx + (y - a.y) * dy) / (dx * dx + dy * dy);\n    const clamped = Math.max(0, Math.min(1, t));\n    const cx = a.x + clamped * dx;\n    const cy = a.y + clamped * dy;\n    const ddx = x - cx;\n    const ddy = y - cy;\n    return ddx * ddx + ddy * ddy;\n  }\n  function distanceToSegments(segments, x, y) {\n    if (!segments || segments.length === 0) {\n      return Infinity;\n    }\n    let minDistance = Infinity;\n    for (let i = 0; i < segments.length; i++) {\n      const segment = segments[i];\n      const distance = distanceToSegmentSquared(x, y, segment.a, segment.b);\n      if (distance < minDistance) {\n        minDistance = distance;\n      }\n    }\n    return Math.sqrt(minDistance);\n  }\n  function hasVisibleFill(shape, override) {\n    if (typeof override === \"boolean\") {\n      return override;\n    }\n    const fill = shape.fill;\n    if (!fill && fill !== 0) {\n      return false;\n    }\n    if (typeof fill === \"string\") {\n      return !TRANSPARENT_REGEX.test(fill);\n    }\n    return true;\n  }\n  function hasVisibleStroke(shape, override) {\n    const linewidth = typeof shape.linewidth === \"number\" ? shape.linewidth : shape._linewidth || 0;\n    if (typeof override === \"boolean\") {\n      return override && linewidth > 0;\n    }\n    if (!(linewidth > 0)) {\n      return false;\n    }\n    const stroke = shape.stroke;\n    if (!stroke && stroke !== 0) {\n      return false;\n    }\n    if (typeof stroke === \"string\") {\n      return !TRANSPARENT_REGEX.test(stroke);\n    }\n    return true;\n  }\n  function boundsContains(rect, x, y, tolerance = 0) {\n    if (!rect) {\n      return false;\n    }\n    const left = rect.left - tolerance;\n    const right = rect.right + tolerance;\n    const top = rect.top - tolerance;\n    const bottom = rect.bottom + tolerance;\n    return x >= left && x <= right && y >= top && y <= bottom;\n  }\n\n  // src/utils/path.js\n  var EPSILON2 = Number.EPSILON;\n  function isRelativeAnchor(anchor2) {\n    return !(typeof anchor2.relative === \"boolean\") || !!anchor2.relative;\n  }\n  function setHandleComponent(anchor2, side, dx, dy) {\n    const controls = anchor2.controls;\n    if (!controls || !controls[side]) {\n      return;\n    }\n    if (Math.abs(dx) < EPSILON2 && Math.abs(dy) < EPSILON2) {\n      if (isRelativeAnchor(anchor2)) {\n        controls[side].clear();\n      } else {\n        controls[side].set(anchor2.x, anchor2.y);\n      }\n      return;\n    }\n    if (isRelativeAnchor(anchor2)) {\n      controls[side].set(dx, dy);\n    } else {\n      controls[side].set(anchor2.x + dx, anchor2.y + dy);\n    }\n  }\n  function clearHandleComponent(anchor2, side) {\n    setHandleComponent(anchor2, side, 0, 0);\n  }\n  function getHandleOffset(anchor2, side) {\n    const controls = anchor2.controls;\n    if (!controls || !controls[side]) {\n      return { x: 0, y: 0 };\n    }\n    if (isRelativeAnchor(anchor2)) {\n      return { x: controls[side].x, y: controls[side].y };\n    }\n    return {\n      x: controls[side].x - anchor2.x,\n      y: controls[side].y - anchor2.y\n    };\n  }\n  function hasNonZeroHandle(anchor2, side) {\n    const offset = getHandleOffset(anchor2, side);\n    return Math.abs(offset.x) > EPSILON2 || Math.abs(offset.y) > EPSILON2;\n  }\n  function updateAnchorCommand(anchor2) {\n    if (anchor2.command === Commands.move || anchor2.command === Commands.close) {\n      return;\n    }\n    anchor2.command = hasNonZeroHandle(anchor2, \"left\") || hasNonZeroHandle(anchor2, \"right\") ? Commands.curve : Commands.line;\n  }\n  function inheritRelative(anchor2, reference) {\n    if (typeof reference.relative === \"boolean\") {\n      anchor2.relative = reference.relative;\n    }\n  }\n  function isSegmentCurved(a, b) {\n    return hasNonZeroHandle(b, \"right\") || hasNonZeroHandle(a, \"left\") || hasNonZeroHandle(a, \"right\") || hasNonZeroHandle(b, \"left\") || a.command === Commands.curve || b.command === Commands.curve;\n  }\n  function lerpPoint(a, b, t) {\n    return {\n      x: lerp(a.x, b.x, t),\n      y: lerp(a.y, b.y, t)\n    };\n  }\n  function getAbsoluteHandle(anchor2, side) {\n    const controls = anchor2.controls && anchor2.controls[side];\n    if (!controls) {\n      return { x: anchor2.x, y: anchor2.y };\n    }\n    if (isRelativeAnchor(anchor2)) {\n      return { x: anchor2.x + controls.x, y: anchor2.y + controls.y };\n    }\n    return { x: controls.x, y: controls.y };\n  }\n  function splitSubdivisionSegment(start, end, t) {\n    const right = start.controls && start.controls.right;\n    const left = end.controls && end.controls.left;\n    const p0 = { x: start.x, y: start.y };\n    const p1 = right ? getAbsoluteHandle(start, \"right\") : { ...p0 };\n    const p3 = { x: end.x, y: end.y };\n    const p2 = left ? getAbsoluteHandle(end, \"left\") : { ...p3 };\n    const q0 = lerpPoint(p0, p1, t);\n    const q1 = lerpPoint(p1, p2, t);\n    const q2 = lerpPoint(p2, p3, t);\n    const r0 = lerpPoint(q0, q1, t);\n    const r1 = lerpPoint(q1, q2, t);\n    const point = lerpPoint(r0, r1, t);\n    const anchor2 = new Anchor(point.x, point.y);\n    inheritRelative(anchor2, start);\n    setHandleComponent(anchor2, \"left\", r0.x - point.x, r0.y - point.y);\n    setHandleComponent(anchor2, \"right\", r1.x - point.x, r1.y - point.y);\n    anchor2.command = Commands.curve;\n    return {\n      anchor: anchor2,\n      startOut: q0,\n      endIn: q2\n    };\n  }\n  function applyGlobalSmooth(vertices, from, to, closed2, loop2, asymmetric) {\n    const length = vertices.length;\n    const amount = to - from + 1;\n    let n = amount - 1;\n    let padding = loop2 ? Math.min(amount, 4) : 1;\n    let paddingLeft = padding;\n    let paddingRight = padding;\n    if (!closed2) {\n      paddingLeft = Math.min(1, from);\n      paddingRight = Math.min(1, length - to - 1);\n    }\n    n += paddingLeft + paddingRight;\n    if (n <= 1) {\n      return;\n    }\n    const knots = new Array(n + 1);\n    for (let i = 0, j = from - paddingLeft; i <= n; i += 1, j += 1) {\n      const index = mod(j, length);\n      knots[i] = vertices[index];\n    }\n    let x = knots[0].x + 2 * knots[1].x;\n    let y = knots[0].y + 2 * knots[1].y;\n    let f = 2;\n    const n1 = n - 1;\n    const rx = [x];\n    const ry = [y];\n    const rf = [f];\n    const px = new Array(n + 1);\n    const py = new Array(n + 1);\n    for (let i = 1; i < n; i += 1) {\n      const internal = i < n1;\n      const a = internal ? 1 : asymmetric ? 1 : 2;\n      const b = internal ? 4 : asymmetric ? 2 : 7;\n      const u = internal ? 4 : asymmetric ? 3 : 8;\n      const v = internal ? 2 : asymmetric ? 0 : 1;\n      const m = a / f;\n      f = rf[i] = b - m;\n      x = rx[i] = u * knots[i].x + v * knots[i + 1].x - m * x;\n      y = ry[i] = u * knots[i].y + v * knots[i + 1].y - m * y;\n    }\n    px[n1] = rx[n1] / rf[n1];\n    py[n1] = ry[n1] / rf[n1];\n    for (let i = n - 2; i >= 0; i -= 1) {\n      px[i] = (rx[i] - px[i + 1]) / rf[i];\n      py[i] = (ry[i] - py[i + 1]) / rf[i];\n    }\n    px[n] = (3 * knots[n].x - px[n1]) / 2;\n    py[n] = (3 * knots[n].y - py[n1]) / 2;\n    const max5 = n - paddingRight;\n    for (let i = paddingLeft, j = from; i <= max5; i += 1, j += 1) {\n      const index = mod(j, length);\n      const anchor2 = vertices[index];\n      const hx = px[i] - anchor2.x;\n      const hy = py[i] - anchor2.y;\n      if (loop2 || i < max5) {\n        setHandleComponent(anchor2, \"right\", hx, hy);\n      } else {\n        clearHandleComponent(anchor2, \"right\");\n      }\n      if (loop2 || i > paddingLeft) {\n        setHandleComponent(anchor2, \"left\", -hx, -hy);\n      } else {\n        clearHandleComponent(anchor2, \"left\");\n      }\n      updateAnchorCommand(anchor2);\n    }\n  }\n  function applyCatmullRom(anchor2, prev, next, factor, clampIn, clampOut) {\n    const p0 = prev || anchor2;\n    const p1 = anchor2;\n    const p2 = next || anchor2;\n    const d1 = Vector.distanceBetween(p0, p1);\n    const d2 = Vector.distanceBetween(p1, p2);\n    const a = factor === void 0 ? 0.5 : factor;\n    const d1a = Math.pow(d1, a);\n    const d2a = Math.pow(d2, a);\n    const d1_2a = d1a * d1a;\n    const d2_2a = d2a * d2a;\n    if (!clampIn && prev) {\n      const A = 2 * d2_2a + 3 * d2a * d1a + d1_2a;\n      const N = 3 * d2a * (d2a + d1a);\n      if (N !== 0) {\n        const hx = (d2_2a * p0.x + A * p1.x - d1_2a * p2.x) / N - p1.x;\n        const hy = (d2_2a * p0.y + A * p1.y - d1_2a * p2.y) / N - p1.y;\n        setHandleComponent(anchor2, \"left\", hx, hy);\n      } else {\n        clearHandleComponent(anchor2, \"left\");\n      }\n    } else {\n      clearHandleComponent(anchor2, \"left\");\n    }\n    if (!clampOut && next) {\n      const A = 2 * d1_2a + 3 * d1a * d2a + d2_2a;\n      const N = 3 * d1a * (d1a + d2a);\n      if (N !== 0) {\n        const hx = (d1_2a * p2.x + A * p1.x - d2_2a * p0.x) / N - p1.x;\n        const hy = (d1_2a * p2.y + A * p1.y - d2_2a * p0.y) / N - p1.y;\n        setHandleComponent(anchor2, \"right\", hx, hy);\n      } else {\n        clearHandleComponent(anchor2, \"right\");\n      }\n    } else {\n      clearHandleComponent(anchor2, \"right\");\n    }\n    updateAnchorCommand(anchor2);\n  }\n  function applyGeometric(anchor2, prev, next, factor, clampIn, clampOut) {\n    if (!(prev && next)) {\n      if (!prev) {\n        clearHandleComponent(anchor2, \"left\");\n      }\n      if (!next) {\n        clearHandleComponent(anchor2, \"right\");\n      }\n      updateAnchorCommand(anchor2);\n      return;\n    }\n    const p0 = prev;\n    const p1 = anchor2;\n    const p2 = next;\n    const d1 = Vector.distanceBetween(p0, p1);\n    const d2 = Vector.distanceBetween(p1, p2);\n    const total = d1 + d2;\n    const tension = factor === void 0 ? 0.4 : factor;\n    const vector3 = { x: p0.x - p2.x, y: p0.y - p2.y };\n    if (!clampIn && total !== 0) {\n      const k = tension * d1 / total;\n      setHandleComponent(anchor2, \"left\", vector3.x * k, vector3.y * k);\n    } else {\n      clearHandleComponent(anchor2, \"left\");\n    }\n    if (!clampOut && total !== 0) {\n      const k = tension * d1 / total - tension;\n      setHandleComponent(anchor2, \"right\", vector3.x * k, vector3.y * k);\n    } else {\n      clearHandleComponent(anchor2, \"right\");\n    }\n    updateAnchorCommand(anchor2);\n  }\n  function applyLocalSmooth(vertices, from, to, closed2, loop2, options) {\n    const type = options.type || \"catmull-rom\";\n    const factor = options.factor;\n    const length = vertices.length;\n    for (let i = from; i <= to; i += 1) {\n      const index = mod(i, length);\n      const anchor2 = vertices[index];\n      if (anchor2.command === Commands.move) {\n        clearHandleComponent(anchor2, \"left\");\n        clearHandleComponent(anchor2, \"right\");\n        continue;\n      }\n      const prevIndex = i === from && !loop2 ? null : i - 1;\n      const nextIndex = i === to && !loop2 ? null : i + 1;\n      const prev = prevIndex === null ? null : vertices[mod(prevIndex, length)];\n      const next = nextIndex === null ? null : vertices[mod(nextIndex, length)];\n      const clampIn = prevIndex === null;\n      const clampOut = nextIndex === null;\n      if (type === \"geometric\") {\n        applyGeometric(anchor2, prev, next, factor, clampIn, clampOut);\n      } else {\n        applyCatmullRom(anchor2, prev, next, factor, clampIn, clampOut);\n      }\n    }\n  }\n\n  // src/path.js\n  var min = Math.min;\n  var max = Math.max;\n  var ceil = Math.ceil;\n  var floor2 = Math.floor;\n  var vector = new Vector();\n  var hitTestMatrix = new Matrix2();\n  var Path = class _Path extends Shape {\n    /**\n     * @name Two.Path#_flagVertices\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#vertices} need updating.\n     */\n    _flagVertices = true;\n    /**\n     * @name Two.Path#_flagLength\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#length} needs updating.\n     */\n    _flagLength = true;\n    /**\n     * @name Two.Path#_flagFill\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#fill} needs updating.\n     */\n    _flagFill = true;\n    /**\n     * @name Two.Path#_flagStroke\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#stroke} needs updating.\n     */\n    _flagStroke = true;\n    /**\n     * @name Two.Path#_flagLinewidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#linewidth} needs updating.\n     */\n    _flagLinewidth = true;\n    /**\n     * @name Two.Path#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#opacity} needs updating.\n     */\n    _flagOpacity = true;\n    /**\n     * @name Two.Path#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#visible} needs updating.\n     */\n    _flagVisible = true;\n    /**\n     * @name Two.Path#_flagCap\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#cap} needs updating.\n     */\n    _flagCap = true;\n    /**\n     * @name Two.Path#_flagJoin\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#join} needs updating.\n     */\n    _flagJoin = true;\n    /**\n     * @name Two.Path#_flagMiter\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#miter} needs updating.\n     */\n    _flagMiter = true;\n    /**\n     * @name Two.Path#_flagStrokeAttenuation\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#strokeAttenuation} needs updating.\n     */\n    _flagStrokeAttenuation = true;\n    /**\n     * @name Two.Path#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#mask} needs updating.\n     */\n    _flagMask = false;\n    /**\n     * @name Two.Path#_flagClip\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#clip} needs updating.\n     */\n    _flagClip = false;\n    // Underlying Properties\n    /**\n     * @name Two.Path#_length\n     * @private\n     * @see {@link Two.Path#length}\n     */\n    _length = 0;\n    /**\n     * @name Two.Path#_fill\n     * @private\n     * @see {@link Two.Path#fill}\n     */\n    _fill = \"#fff\";\n    /**\n     * @name Two.Path#_stroke\n     * @private\n     * @see {@link Two.Path#stroke}\n     */\n    _stroke = \"#000\";\n    /**\n     * @name Two.Path#_linewidth\n     * @private\n     * @see {@link Two.Path#linewidth}\n     */\n    _linewidth = 1;\n    /**\n     * @name Two.Path#_opacity\n     * @private\n     * @see {@link Two.Path#opacity}\n     */\n    _opacity = 1;\n    /**\n     * @name Two.Path#_visible\n     * @private\n     * @see {@link Two.Path#visible}\n     */\n    _visible = true;\n    /**\n     * @name Two.Path#_cap\n     * @private\n     * @see {@link Two.Path#cap}\n     */\n    _cap = \"round\";\n    /**\n     * @name Two.Path#_join\n     * @private\n     * @see {@link Two.Path#join}\n     */\n    _join = \"round\";\n    /**\n     * @name Two.Path#_miter\n     * @private\n     * @see {@link Two.Path#miter}\n     */\n    _miter = 4;\n    /**\n     * @name Two.Path#_closed\n     * @private\n     * @see {@link Two.Path#closed}\n     */\n    _closed = true;\n    /**\n     * @name Two.Path#_curved\n     * @private\n     * @see {@link Two.Path#curved}\n     */\n    _curved = false;\n    /**\n     * @name Two.Path#_automatic\n     * @private\n     * @see {@link Two.Path#automatic}\n     */\n    _automatic = true;\n    /**\n     * @name Two.Path#_beginning\n     * @private\n     * @see {@link Two.Path#beginning}\n     */\n    _beginning = 0;\n    /**\n     * @name Two.Path#_ending\n     * @private\n     * @see {@link Two.Path#ending}\n     */\n    _ending = 1;\n    /**\n     * @name Two.Path#_mask\n     * @private\n     * @see {@link Two.Path#mask}\n     */\n    _mask = null;\n    /**\n     * @name Two.Path#_clip\n     * @private\n     * @see {@link Two.Path#clip}\n     */\n    _clip = false;\n    /**\n     * @name Two.Path#_dashes\n     * @private\n     * @see {@link Two.Path#dashes}\n     */\n    _dashes = null;\n    /**\n     * @name Two.Path#_strokeAttenuation\n     * @private\n     * @see {@link Two.Path#strokeAttenuation}\n     */\n    _strokeAttenuation = true;\n    constructor(vertices, closed2, curved, manual) {\n      super();\n      for (let prop in proto10) {\n        Object.defineProperty(this, prop, proto10[prop]);\n      }\n      this._renderer.type = \"path\";\n      this._renderer.flagVertices = FlagVertices.bind(this);\n      this._renderer.bindVertices = BindVertices.bind(this);\n      this._renderer.unbindVertices = UnbindVertices.bind(this);\n      this._renderer.flagFill = FlagFill.bind(this);\n      this._renderer.flagStroke = FlagStroke.bind(this);\n      this._renderer.vertices = [];\n      this._renderer.collection = [];\n      this.closed = !!closed2;\n      this.curved = !!curved;\n      this.beginning = 0;\n      this.ending = 1;\n      this.fill = \"#fff\";\n      this.stroke = \"#000\";\n      this.linewidth = 1;\n      this.opacity = 1;\n      this.className = \"\";\n      this.visible = true;\n      this.cap = \"butt\";\n      this.join = \"miter\";\n      this.miter = 4;\n      this.vertices = vertices;\n      this.automatic = !manual;\n      this.dashes = [];\n      this.dashes.offset = 0;\n    }\n    /**\n     * @name Two.Path.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Path}.\n     */\n    static Properties = [\n      \"fill\",\n      \"stroke\",\n      \"linewidth\",\n      \"opacity\",\n      \"visible\",\n      \"cap\",\n      \"join\",\n      \"miter\",\n      \"closed\",\n      \"curved\",\n      \"automatic\",\n      \"beginning\",\n      \"ending\",\n      \"dashes\",\n      \"strokeAttenuation\"\n    ];\n    static Utils = {\n      getCurveLength: getCurveLength2\n    };\n    /**\n     * @name Two.Path.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Path} to create a new instance\n     * @returns {Two.Path}\n     * @description Create a new {@link Two.Path} from an object notation of a {@link Two.Path}.\n     * @nota-bene Works in conjunction with {@link Two.Path#toObject}\n     */\n    static fromObject(obj) {\n      const fill = typeof obj.fill === \"string\" ? obj.fill : getEffectFromObject(obj.fill);\n      const stroke = typeof obj.stroke === \"string\" ? obj.stroke : getEffectFromObject(obj.stroke);\n      const path = new _Path().copy({ ...obj, fill, stroke });\n      if (\"id\" in obj) {\n        path.id = obj.id;\n      }\n      return path;\n    }\n    /**\n     * @name Two.Path#copy\n     * @function\n     * @param {Two.Path} path - The reference {@link Two.Path}\n     * @description Copy the properties of one {@link Two.Path} onto another.\n     */\n    copy(path) {\n      super.copy.call(this, path);\n      if (path.vertices) {\n        this.vertices = [];\n        for (let j = 0; j < path.vertices.length; j++) {\n          const v = path.vertices[j];\n          if (v instanceof Anchor) {\n            this.vertices.push(path.vertices[j].clone());\n          } else {\n            this.vertices.push(new Anchor().copy(v));\n          }\n        }\n      }\n      for (let i = 0; i < _Path.Properties.length; i++) {\n        const k = _Path.Properties[i];\n        if (k in path) {\n          this[k] = path[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Path#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Path}\n     * @description Create a new instance of {@link Two.Path} with the same properties of the current path.\n     */\n    clone(parent) {\n      const clone = new _Path();\n      for (let j = 0; j < this.vertices.length; j++) {\n        clone.vertices.push(this.vertices[j].clone());\n      }\n      for (let i = 0; i < _Path.Properties.length; i++) {\n        const k = _Path.Properties[i];\n        clone[k] = this[k];\n      }\n      clone.className = this.className;\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    /**\n     * @name Two.Path#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     * @nota-bene Works in conjunction with {@link Two.Path.fromObject}\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.renderer.type = \"path\";\n      result.vertices = this.vertices.map((v) => v.toObject());\n      _.each(\n        _Path.Properties,\n        (k) => {\n          if (typeof this[k] !== \"undefined\") {\n            if (this[k].toObject) {\n              result[k] = this[k].toObject();\n            } else {\n              result[k] = this[k];\n            }\n          }\n        },\n        this\n      );\n      return result;\n    }\n    /**\n     * @name Two.Path#dispose\n     * @function\n     * @returns {Two.Path}\n     * @description Release the path's renderer resources and detach all events.\n     * This method cleans up vertices collection events, individual vertex events,\n     * control point events, and disposes fill/stroke effects (calling dispose()\n     * on Gradients and Textures for thorough cleanup) while preserving the\n     * renderer type for potential re-attachment to a new renderer.\n     */\n    dispose() {\n      super.dispose();\n      if (this.vertices && typeof this.vertices.unbind === \"function\") {\n        try {\n          this.vertices.unbind();\n        } catch (e) {\n        }\n      }\n      if (this.vertices) {\n        for (let i = 0; i < this.vertices.length; i++) {\n          const v = this.vertices[i];\n          if (typeof v.unbind === \"function\") {\n            v.unbind();\n          }\n          if (v.controls) {\n            if (v.controls.left && typeof v.controls.left.unbind === \"function\") {\n              v.controls.left.unbind();\n            }\n            if (v.controls.right && typeof v.controls.right.unbind === \"function\") {\n              v.controls.right.unbind();\n            }\n          }\n        }\n      }\n      if (typeof this.fill === \"object\" && typeof this.fill.dispose === \"function\") {\n        this.fill.dispose();\n      } else if (typeof this.fill === \"object\" && typeof this.fill.unbind === \"function\") {\n        this.fill.unbind();\n      }\n      if (typeof this.stroke === \"object\" && typeof this.stroke.dispose === \"function\") {\n        this.stroke.dispose();\n      } else if (typeof this.stroke === \"object\" && typeof this.stroke.unbind === \"function\") {\n        this.stroke.unbind();\n      }\n      return this;\n    }\n    /**\n     * @name Two.Path#noFill\n     * @function\n     * @description Short hand method to set fill to `none`.\n     */\n    noFill() {\n      this.fill = \"none\";\n      return this;\n    }\n    /**\n     * @name Two.Path#noStroke\n     * @function\n     * @description Short hand method to set stroke to `none`.\n     */\n    noStroke() {\n      this.stroke = \"none\";\n      this.linewidth = 0;\n      return this;\n    }\n    /**\n     * @name Two.Path#corner\n     * @function\n     * @description Orient the vertices of the shape to the upper left-hand corner of the path.\n     */\n    corner() {\n      const rect = this.getBoundingClientRect(true);\n      const hw = rect.width / 2;\n      const hh = rect.height / 2;\n      const cx = rect.left + rect.width / 2;\n      const cy = rect.top + rect.height / 2;\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        v.x -= cx;\n        v.y -= cy;\n        v.x += hw;\n        v.y += hh;\n      }\n      if (this.mask) {\n        this.mask.translation.x -= cx;\n        this.mask.translation.x += hw;\n        this.mask.translation.y -= cy;\n        this.mask.translation.y += hh;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Path#center\n     * @function\n     * @description Orient the vertices of the shape to the center of the path.\n     */\n    center() {\n      const rect = this.getBoundingClientRect(true);\n      const cx = rect.left + rect.width / 2 - this.translation.x;\n      const cy = rect.top + rect.height / 2 - this.translation.y;\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        v.x -= cx;\n        v.y -= cy;\n      }\n      if (this.mask) {\n        this.mask.translation.x -= cx;\n        this.mask.translation.y -= cy;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Path#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n     */\n    getBoundingClientRect(shallow) {\n      let matrix, border, l, i, v0, v1;\n      let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity;\n      this._update(true);\n      matrix = shallow ? this.matrix : this.worldMatrix;\n      border = (this.linewidth || 0) / 2;\n      l = this._renderer.vertices.length;\n      if (this.linewidth > 0 || this.stroke && !/(transparent|none)/i.test(this.stroke)) {\n        if (this.matrix.manual) {\n          const { scaleX, scaleY } = decomposeMatrix(\n            matrix.elements[0],\n            matrix.elements[3],\n            matrix.elements[1],\n            matrix.elements[4],\n            matrix.elements[2],\n            matrix.elements[5]\n          );\n          if (typeof scaleX === \"number\" && typeof scaleY === \"number\") {\n            border = Math.max(scaleX, scaleY) * (this.linewidth || 0) / 2;\n          }\n        } else {\n          border *= typeof this.scale === \"number\" ? this.scale : Math.max(this.scale.x, this.scale.y);\n        }\n      }\n      if (l <= 0) {\n        return {\n          width: 0,\n          height: 0\n        };\n      }\n      for (i = 0; i < l; i++) {\n        v1 = this._renderer.vertices[i];\n        v0 = this._renderer.vertices[(i + l - 1) % l];\n        const [v0x, v0y] = matrix.multiply(v0.x, v0.y);\n        const [v1x, v1y] = matrix.multiply(v1.x, v1.y);\n        if (v0.controls && v1.controls) {\n          let rx = v0.controls.right.x;\n          let ry = v0.controls.right.y;\n          if (v0.relative) {\n            rx += v0.x;\n            ry += v0.y;\n          }\n          let [c0x, c0y] = matrix.multiply(rx, ry);\n          let lx = v1.controls.left.x;\n          let ly = v1.controls.left.y;\n          if (v1.relative) {\n            lx += v1.x;\n            ly += v1.y;\n          }\n          let [c1x, c1y] = matrix.multiply(lx, ly);\n          const bb = getCurveBoundingBox(v0x, v0y, c0x, c0y, c1x, c1y, v1x, v1y);\n          top = min(bb.min.y - border, top);\n          left = min(bb.min.x - border, left);\n          right = max(bb.max.x + border, right);\n          bottom = max(bb.max.y + border, bottom);\n        } else {\n          if (i <= 1) {\n            top = min(v0y - border, top);\n            left = min(v0x - border, left);\n            right = max(v0x + border, right);\n            bottom = max(v0y + border, bottom);\n          }\n          top = min(v1y - border, top);\n          left = min(v1x - border, left);\n          right = max(v1x + border, right);\n          bottom = max(v1y + border, bottom);\n        }\n      }\n      return {\n        top,\n        left,\n        right,\n        bottom,\n        width: right - left,\n        height: bottom - top\n      };\n    }\n    /**\n     * @name Two.Path#contains\n     * @function\n     * @param {Number} x - x coordinate to hit test against\n     * @param {Number} y - y coordinate to hit test against\n     * @param {Object} [options] - Optional options object\n     * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `path.visible = false` shapes\n     * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n     * @returns {Boolean}\n     * @description Check to see if coordinates are within a {@link Two.Path}'s bounding rectangle\n     * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n     */\n    contains(x, y, options) {\n      const opts = options || {};\n      const ignoreVisibility = opts.ignoreVisibility === true;\n      if (!ignoreVisibility && this.visible === false) {\n        return false;\n      }\n      if (!ignoreVisibility && typeof this.opacity === \"number\" && this.opacity <= 0) {\n        return false;\n      }\n      const tolerance = typeof opts.tolerance === \"number\" ? opts.tolerance : 0;\n      this._update(true);\n      const rect = this.getBoundingClientRect();\n      if (!rect || x < rect.left - tolerance || x > rect.right + tolerance || y < rect.top - tolerance || y > rect.bottom + tolerance) {\n        return false;\n      }\n      const matrix = this.worldMatrix;\n      const inverse = matrix && matrix.inverse(hitTestMatrix);\n      if (!inverse) {\n        return super.contains(x, y, opts);\n      }\n      const [localX, localY] = inverse.multiply(x, y, 1);\n      const precision = typeof opts.precision === \"number\" && !Number.isNaN(opts.precision) ? Math.max(1, Math.floor(opts.precision)) : 8;\n      const fillTest = hasVisibleFill(this, opts.fill);\n      const strokeTest = hasVisibleStroke(this, opts.stroke);\n      const { polygons, segments } = buildPathHitParts(this, precision);\n      if (fillTest && polygons.length > 0) {\n        if (pointInPolygons(polygons, localX, localY)) {\n          return true;\n        }\n      }\n      if (strokeTest && segments.length > 0) {\n        const linewidth = typeof this.linewidth === \"number\" ? this.linewidth : 0;\n        if (linewidth > 0) {\n          const distance = distanceToSegments(segments, localX, localY);\n          if (distance <= linewidth / 2 + tolerance) {\n            return true;\n          }\n        }\n      }\n      if (!fillTest && !strokeTest) {\n        return super.contains(x, y, opts);\n      }\n      if (fillTest && polygons.length === 0) {\n        return super.contains(x, y, opts);\n      }\n      return false;\n    }\n    /**\n     * @name Two.Path#getPointAt\n     * @function\n     * @param {Number} t - Percentage value describing where on the {@link Two.Path} to estimate and assign coordinate values.\n     * @param {Two.Vector} [obj] - Object to apply calculated x, y to. If none available returns new `Object`.\n     * @returns {Object}\n     * @description Given a float `t` from 0 to 1, return a point or assign a passed `obj`'s coordinates to that percentage on this {@link Two.Path}'s curve.\n     */\n    getPointAt(t, obj) {\n      let ia, ib, result;\n      let x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right;\n      let target = this.length * Math.min(Math.max(t, 0), 1);\n      const length = this.vertices.length;\n      const last = length - 1;\n      let a = null;\n      let b = null;\n      for (let i = 0, l = this._lengths.length, sum = 0; i < l; i++) {\n        if (sum + this._lengths[i] >= target) {\n          if (this._closed) {\n            ia = mod(i, length);\n            ib = mod(i - 1, length);\n            if (i === 0) {\n              ia = ib;\n              ib = i;\n            }\n          } else {\n            ia = i;\n            ib = Math.min(Math.max(i - 1, 0), last);\n          }\n          a = this.vertices[ia];\n          b = this.vertices[ib];\n          target -= sum;\n          if (this._lengths[i] !== 0) {\n            t = target / this._lengths[i];\n          } else {\n            t = 0;\n          }\n          break;\n        }\n        sum += this._lengths[i];\n      }\n      if (a === null || b === null) {\n        return null;\n      }\n      if (!a) {\n        return b;\n      } else if (!b) {\n        return a;\n      }\n      right = b.controls && b.controls.right;\n      left = a.controls && a.controls.left;\n      x1 = b.x;\n      y1 = b.y;\n      x2 = (right || b).x;\n      y2 = (right || b).y;\n      x3 = (left || a).x;\n      y3 = (left || a).y;\n      x4 = a.x;\n      y4 = a.y;\n      if (right && b.relative) {\n        x2 += b.x;\n        y2 += b.y;\n      }\n      if (left && a.relative) {\n        x3 += a.x;\n        y3 += a.y;\n      }\n      x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n      y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n      const t1x = lerp(x1, x2, t);\n      const t1y = lerp(y1, y2, t);\n      const t2x = lerp(x2, x3, t);\n      const t2y = lerp(y2, y3, t);\n      const t3x = lerp(x3, x4, t);\n      const t3y = lerp(y3, y4, t);\n      const brx = lerp(t1x, t2x, t);\n      const bry = lerp(t1y, t2y, t);\n      const alx = lerp(t2x, t3x, t);\n      const aly = lerp(t2y, t3y, t);\n      if (_.isObject(obj)) {\n        obj.x = x;\n        obj.y = y;\n        if (obj instanceof Anchor) {\n          obj.controls.left.x = brx;\n          obj.controls.left.y = bry;\n          obj.controls.right.x = alx;\n          obj.controls.right.y = aly;\n          if (!(typeof obj.relative === \"boolean\") || obj.relative) {\n            obj.controls.left.x -= x;\n            obj.controls.left.y -= y;\n            obj.controls.right.x -= x;\n            obj.controls.right.y -= y;\n          }\n        }\n        obj.t = t;\n        return obj;\n      }\n      result = new Anchor(\n        x,\n        y,\n        brx - x,\n        bry - y,\n        alx - x,\n        aly - y,\n        this._curved ? Commands.curve : Commands.line\n      );\n      result.t = t;\n      return result;\n    }\n    /**\n     * @name Two.Path#plot\n     * @function\n     * @description Based on closed / curved and sorting of vertices plot where all points should be and where the respective handles should be too.\n     * @nota-bene While this method is public it is internally called by {@link Two.Path#_update} when `automatic = true`.\n     */\n    plot() {\n      if (this.curved) {\n        getCurveFromPoints(this._collection, this.closed);\n        return this;\n      }\n      for (let i = 0; i < this._collection.length; i++) {\n        this._collection[i].command = i === 0 ? Commands.move : Commands.line;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Path#smooth\n     * @function\n     * @param {Object} [options] - Configuration for smoothing.\n     * @param {String} [options.type='continuous'] - Type of smoothing algorithm.\n     * @param {Number} [options.from=0] - Index of vertices to start smoothing\n     * @param {Number} [options.to=1] - Index of vertices to terminate smoothing\n     * @description Adjust vertex handles to generate smooth curves without toggling `automatic`.\n     */\n    smooth(options) {\n      const opts = options || {};\n      const type = opts.type || \"continuous\";\n      const vertices = this._collection;\n      const length = vertices.length;\n      if (length < 2) {\n        return this;\n      }\n      const closed2 = this._closed || length > 0 && vertices[length - 1] && vertices[length - 1].command === Commands.close;\n      const resolveIndex = (value, defaultIndex) => {\n        if (value === void 0 || value === null) {\n          return defaultIndex;\n        }\n        if (typeof value === \"number\") {\n          if (closed2) {\n            return mod(value, length);\n          }\n          let index = value;\n          if (index < 0) {\n            index += length;\n          }\n          return Math.min(Math.max(index, 0), length - 1);\n        }\n        const idx = vertices.indexOf(value);\n        return idx !== -1 ? idx : defaultIndex;\n      };\n      const loop2 = closed2 && opts.from === void 0 && opts.to === void 0;\n      let from = resolveIndex(opts.from, 0);\n      let to = resolveIndex(opts.to, length - 1);\n      if (from > to) {\n        if (closed2) {\n          from -= length;\n        } else {\n          const temp2 = from;\n          from = to;\n          to = temp2;\n        }\n      }\n      const rangeLength = to - from + 1;\n      for (let i = 0; i < rangeLength; i += 1) {\n        const index = mod(from + i, length);\n        const anchor2 = vertices[index];\n        const isOpenStart = !closed2 && index === 0;\n        if (anchor2.command === Commands.move && !isOpenStart) {\n          anchor2.command = Commands.line;\n        }\n      }\n      if (type === \"continuous\" || type === \"asymmetric\") {\n        applyGlobalSmooth(\n          vertices,\n          from,\n          to,\n          closed2,\n          loop2,\n          type === \"asymmetric\"\n        );\n      } else if (type === \"catmull-rom\" || type === \"geometric\") {\n        const range = {\n          type,\n          factor: opts.factor\n        };\n        applyLocalSmooth(vertices, from, to, closed2, loop2, range);\n      } else {\n        throw new Error(\n          `Path.smooth does not support type \"${type}\". Try 'continuous', 'asymmetric', 'catmull-rom', or 'geometric'.`\n        );\n      }\n      this._automatic = false;\n      this._flagVertices = true;\n      this._flagLength = true;\n      return this;\n    }\n    /**\n     * @name Two.Path#subdivide\n     * @function\n     * @param {Number} limit - How many times to recurse subdivisions.\n     * @description Insert a {@link Two.Anchor} at the midpoint between every item in {@link Two.Path#vertices}.\n     */\n    subdivide(limit) {\n      this._update();\n      const vertices = this.vertices;\n      const length = vertices.length;\n      if (length < 2) {\n        return this;\n      }\n      const points = [];\n      let prevOriginal = null;\n      let subpathStartOriginal = null;\n      for (let i = 0; i < length; i += 1) {\n        const currentOriginal = vertices[i];\n        if (!prevOriginal || currentOriginal.command === Commands.move) {\n          const clone = currentOriginal.clone();\n          points.push(clone);\n          prevOriginal = currentOriginal;\n          subpathStartOriginal = currentOriginal;\n          continue;\n        }\n        const isCurve = isSegmentCurved(currentOriginal, prevOriginal);\n        if (isCurve) {\n          const subdivided = getSubdivisions(\n            currentOriginal,\n            prevOriginal,\n            limit\n          );\n          const steps = subdivided.length;\n          const prevClone = points[points.length - 1];\n          let startSegment = prevClone.clone();\n          let endSegment = currentOriginal.clone();\n          let prevCloneRef = prevClone;\n          let prevT = 0;\n          if (steps <= 1) {\n            const currentClone = currentOriginal.clone();\n            points.push(currentClone);\n          } else {\n            for (let j = 1; j < steps; j += 1) {\n              const globalT = j / steps;\n              const denom = 1 - prevT;\n              const localT = denom <= Number.EPSILON ? globalT : (globalT - prevT) / denom;\n              const split = splitSubdivisionSegment(\n                startSegment,\n                endSegment,\n                localT\n              );\n              setHandleComponent(\n                prevCloneRef,\n                \"right\",\n                split.startOut.x - prevCloneRef.x,\n                split.startOut.y - prevCloneRef.y\n              );\n              const newAnchor = split.anchor;\n              points.push(newAnchor);\n              prevCloneRef = newAnchor;\n              startSegment = newAnchor.clone();\n              prevT = globalT;\n              setHandleComponent(\n                endSegment,\n                \"left\",\n                split.endIn.x - endSegment.x,\n                split.endIn.y - endSegment.y\n              );\n            }\n            const currentClone = currentOriginal.clone();\n            currentClone.controls.left.copy(endSegment.controls.left);\n            points.push(currentClone);\n          }\n        } else {\n          const subdivided = getSubdivisions(\n            currentOriginal,\n            prevOriginal,\n            limit\n          );\n          for (let j = 1; j < subdivided.length; j += 1) {\n            const anchor2 = subdivided[j];\n            inheritRelative(anchor2, prevOriginal);\n            clearHandleComponent(anchor2, \"left\");\n            clearHandleComponent(anchor2, \"right\");\n            anchor2.command = Commands.line;\n            points.push(anchor2);\n          }\n          const currentClone = currentOriginal.clone();\n          points.push(currentClone);\n        }\n        prevOriginal = currentOriginal;\n        if (currentOriginal.command === Commands.close) {\n          prevOriginal = subpathStartOriginal;\n        }\n      }\n      this._automatic = false;\n      this._curved = false;\n      this.vertices = points;\n      return this;\n    }\n    /**\n     * @name Two.Path#_updateLength\n     * @function\n     * @private\n     * @param {Number} [limit] -\n     * @param {Boolean} [silent=false] - If set to `true` then the path isn't updated before calculation. Useful for internal use.\n     * @description Recalculate the {@link Two.Path#length} value.\n     */\n    _updateLength(limit, silent) {\n      if (!silent) {\n        this._update();\n      }\n      const length = this.vertices.length;\n      const last = length - 1;\n      const closed2 = false;\n      let b = this.vertices[last];\n      let sum = 0;\n      if (typeof this._lengths === \"undefined\") {\n        this._lengths = [];\n      }\n      _.each(\n        this.vertices,\n        function(a, i) {\n          if (i <= 0 && !closed2 || a.command === Commands.move) {\n            b = a;\n            this._lengths[i] = 0;\n            return;\n          }\n          this._lengths[i] = getCurveLength2(a, b, limit);\n          sum += this._lengths[i];\n          b = a;\n        },\n        this\n      );\n      this._length = sum;\n      this._flagLength = false;\n      return this;\n    }\n    /**\n     * @name Two.Path#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices) {\n        if (this._automatic) {\n          this.plot();\n        }\n        if (this._flagLength) {\n          this._updateLength(void 0, true);\n        }\n        const l = this._collection.length;\n        const closed2 = this._closed;\n        const beginning = Math.min(this._beginning, this._ending);\n        const ending = Math.max(this._beginning, this._ending);\n        const bid = getIdByLength(this, beginning * this._length);\n        const eid = getIdByLength(this, ending * this._length);\n        const low = ceil(bid);\n        const high = floor2(eid);\n        let left, right, prev, next, v, i;\n        this._renderer.vertices.length = 0;\n        for (i = 0; i < l; i++) {\n          if (this._renderer.collection.length <= i) {\n            this._renderer.collection.push(new Anchor());\n          }\n          if (i > high && !right) {\n            v = this._renderer.collection[i].copy(this._collection[i]);\n            this.getPointAt(ending, v);\n            v.command = this._renderer.collection[i].command;\n            this._renderer.vertices.push(v);\n            right = v;\n            prev = this._collection[i - 1];\n            if (prev && prev.controls) {\n              if (v.relative) {\n                v.controls.right.clear();\n              } else {\n                v.controls.right.copy(v);\n              }\n              if (prev.relative) {\n                this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(Vector.zero, 1 - v.t);\n              } else {\n                this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(prev, 1 - v.t);\n              }\n            }\n          } else if (i >= low && i <= high) {\n            v = this._renderer.collection[i].copy(this._collection[i]);\n            this._renderer.vertices.push(v);\n            if (i === high && contains(this, ending)) {\n              right = v;\n              if (!closed2 && right.controls) {\n                if (right.relative) {\n                  right.controls.right.clear();\n                } else {\n                  right.controls.right.copy(right);\n                }\n              }\n            } else if (i === low && contains(this, beginning)) {\n              left = v;\n              left.command = Commands.move;\n              if (!closed2 && left.controls) {\n                if (left.relative) {\n                  left.controls.left.clear();\n                } else {\n                  left.controls.left.copy(left);\n                }\n              }\n            }\n          }\n        }\n        if (low > 0 && !left) {\n          i = low - 1;\n          v = this._renderer.collection[i].copy(this._collection[i]);\n          this.getPointAt(beginning, v);\n          v.command = Commands.move;\n          this._renderer.vertices.unshift(v);\n          next = this._collection[i + 1];\n          if (next && next.controls) {\n            v.controls.left.clear();\n            if (next.relative) {\n              this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(Vector.zero, v.t);\n            } else {\n              vector.copy(next);\n              this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(next, v.t);\n            }\n          }\n        }\n      }\n      Shape.prototype._update.apply(this, arguments);\n      return this;\n    }\n    /**\n     * @name Two.Path#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagCap = this._flagJoin = this._flagMiter = this._flagClip = this._flagStrokeAttenuation = false;\n      Shape.prototype.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto10 = {\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        this._flagLinewidth = true;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n      }\n    },\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._visible = v;\n        this._flagVisible = true;\n      }\n    },\n    cap: {\n      enumerable: true,\n      get: function() {\n        return this._cap;\n      },\n      set: function(v) {\n        this._cap = v;\n        this._flagCap = true;\n      }\n    },\n    join: {\n      enumerable: true,\n      get: function() {\n        return this._join;\n      },\n      set: function(v) {\n        this._join = v;\n        this._flagJoin = true;\n      }\n    },\n    miter: {\n      enumerable: true,\n      get: function() {\n        return this._miter;\n      },\n      set: function(v) {\n        this._miter = v;\n        this._flagMiter = true;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(f) {\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n        }\n        this._fill = f;\n        this._flagFill = true;\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.bind(Events.Types.change, this._renderer.flagFill);\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(f) {\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n        }\n        this._stroke = f;\n        this._flagStroke = true;\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n        }\n      }\n    },\n    /**\n     * @name Two.Path#length\n     * @property {Number} - The sum of distances between all {@link Two.Path#vertices}.\n     */\n    length: {\n      get: function() {\n        if (this._flagLength) {\n          this._updateLength();\n        }\n        return this._length;\n      }\n    },\n    closed: {\n      enumerable: true,\n      get: function() {\n        return this._closed;\n      },\n      set: function(v) {\n        this._closed = !!v;\n        this._flagVertices = true;\n      }\n    },\n    curved: {\n      enumerable: true,\n      get: function() {\n        return this._curved;\n      },\n      set: function(v) {\n        this._curved = !!v;\n        this._flagVertices = true;\n      }\n    },\n    automatic: {\n      enumerable: true,\n      get: function() {\n        return this._automatic;\n      },\n      set: function(v) {\n        if (v === this._automatic) {\n          return;\n        }\n        this._automatic = !!v;\n        const method = this._automatic ? \"ignore\" : \"listen\";\n        _.each(this.vertices, function(v2) {\n          v2[method]();\n        });\n      }\n    },\n    beginning: {\n      enumerable: true,\n      get: function() {\n        return this._beginning;\n      },\n      set: function(v) {\n        this._beginning = v;\n        this._flagVertices = true;\n      }\n    },\n    ending: {\n      enumerable: true,\n      get: function() {\n        return this._ending;\n      },\n      set: function(v) {\n        this._ending = v;\n        this._flagVertices = true;\n      }\n    },\n    vertices: {\n      enumerable: true,\n      get: function() {\n        return this._collection;\n      },\n      set: function(vertices) {\n        const bindVertices = this._renderer.bindVertices;\n        const unbindVertices = this._renderer.unbindVertices;\n        if (this._collection) {\n          this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices);\n        }\n        if (vertices instanceof Collection) {\n          this._collection = vertices;\n        } else {\n          this._collection = new Collection(vertices || []);\n        }\n        this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices);\n        bindVertices(this._collection);\n      }\n    },\n    /**\n     * @name Two.Path#mask\n     * @property {Two.Shape} - The shape whose alpha property becomes a clipping area for the path.\n     * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n     */\n    mask: {\n      enumerable: true,\n      get: function() {\n        return this._mask;\n      },\n      set: function(v) {\n        this._mask = v;\n        this._flagMask = true;\n        if (_.isObject(v) && !v.clip) {\n          v.clip = true;\n        }\n      }\n    },\n    /**\n     * @name Two.Path#clip\n     * @property {Boolean} - Tells Two.js renderer if this object represents a mask for another object (or not).\n     */\n    clip: {\n      enumerable: true,\n      get: function() {\n        return this._clip;\n      },\n      set: function(v) {\n        this._clip = v;\n        this._flagClip = true;\n      }\n    },\n    dashes: {\n      enumerable: true,\n      get: function() {\n        return this._dashes;\n      },\n      set: function(v) {\n        if (typeof v.offset !== \"number\") {\n          v.offset = this.dashes && this._dashes.offset || 0;\n        }\n        this._dashes = v;\n      }\n    },\n    /**\n     * @name Two.Path#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n     * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n     */\n    strokeAttenuation: {\n      enumerable: true,\n      get: function() {\n        return this._strokeAttenuation;\n      },\n      set: function(v) {\n        this._strokeAttenuation = !!v;\n        this._flagStrokeAttenuation = true;\n        this._flagLinewidth = true;\n      }\n    }\n  };\n  function FlagVertices() {\n    this._flagVertices = true;\n    this._flagLength = true;\n    if (this.parent) {\n      this.parent._flagLength = true;\n    }\n  }\n  function BindVertices(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].bind(Events.Types.change, this._renderer.flagVertices);\n    }\n    this._renderer.flagVertices();\n  }\n  function UnbindVertices(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].unbind(Events.Types.change, this._renderer.flagVertices);\n    }\n    this._renderer.flagVertices();\n  }\n  function FlagFill() {\n    this._flagFill = true;\n  }\n  function FlagStroke() {\n    this._flagStroke = true;\n  }\n\n  // src/shapes/rectangle.js\n  var Rectangle = class _Rectangle extends Path {\n    constructor(x, y, width, height) {\n      const points = [\n        new Anchor(),\n        new Anchor(),\n        new Anchor(),\n        new Anchor()\n        // new Anchor() // TODO: Figure out how to handle this for `beginning` / `ending` animations\n      ];\n      super(points, true, false, true);\n      this._renderer.type = \"rectangle\";\n      for (let prop in proto11) {\n        Object.defineProperty(this, prop, proto11[prop]);\n      }\n      this.width = typeof width === \"number\" ? width : 1;\n      this.height = typeof height === \"number\" ? height : 1;\n      this.origin = new Vector();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n      this._update();\n    }\n    /**\n     * @name Two.Rectangle.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Rectangle}.\n     */\n    static Properties = [\"width\", \"height\", \"origin\"];\n    /**\n     * @name Two.Rectangle.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Rectangle} to create a new instance\n     * @returns {Two.Rectangle}\n     * @description Create a new {@link Two.Rectangle} from an object notation of a {@link Two.Rectangle}.\n     * @nota-bene Works in conjunction with {@link Two.Rectangle#toObject}\n     */\n    static fromObject(obj) {\n      const rectangle = new _Rectangle().copy(obj);\n      if (\"id\" in obj) {\n        rectangle.id = obj.id;\n      }\n      return rectangle;\n    }\n    /**\n     * @name Two.Rectangle#copy\n     * @function\n     * @param {Two.Rectangle} rectangle - The reference {@link Two.Rectangle}\n     * @description Copy the properties of one {@link Two.Rectangle} onto another.\n     */\n    copy(rectangle) {\n      super.copy.call(this, rectangle);\n      for (let i = 0; i < _Rectangle.Properties.length; i++) {\n        const k = _Rectangle.Properties[i];\n        if (k in rectangle) {\n          if (typeof rectangle[k] === \"number\") {\n            this[k] = rectangle[k];\n          } else if (this[k] instanceof Vector) {\n            this[k].copy(rectangle[k]);\n          }\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Rectangle#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Rectangle#width} needs updating.\n     */\n    _flagWidth = false;\n    /**\n     * @name Two.Rectangle#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Rectangle#height} needs updating.\n     */\n    _flagHeight = false;\n    /**\n     * @name Two.Rectangle#_width\n     * @private\n     * @see {@link Two.Rectangle#width}\n     */\n    _width = 0;\n    /**\n     * @name Two.Rectangle#_height\n     * @private\n     * @see {@link Two.Rectangle#height}\n     */\n    _height = 0;\n    _origin = null;\n    /**\n     * @name Two.Rectangle#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight) {\n        const xr = this._width / 2;\n        const yr = this._height / 2;\n        if (!this._closed && this.vertices.length === 4) {\n          this.vertices.push(new Anchor());\n        }\n        this.vertices[0].set(-xr, -yr).sub(this._origin).command = Commands.move;\n        this.vertices[1].set(xr, -yr).sub(this._origin).command = Commands.line;\n        this.vertices[2].set(xr, yr).sub(this._origin).command = Commands.line;\n        this.vertices[3].set(-xr, yr).sub(this._origin).command = Commands.line;\n        if (this.vertices[4]) {\n          this.vertices[4].set(-xr, -yr).sub(this._origin).command = Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Rectangle#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagWidth = this._flagHeight = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Rectangle#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Rectangle}\n     * @description Create a new instance of {@link Two.Rectangle} with the same properties of the current path.\n     */\n    clone(parent) {\n      const clone = new _Rectangle(0, 0, this.width, this.height);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        if (clone[k] instanceof Vector) {\n          clone[k].copy(this[k]);\n        } else {\n          clone[k] = this[k];\n        }\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Rectangle#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"rectangle\";\n      object.width = this.width;\n      object.height = this.height;\n      object.origin = this.origin.toObject();\n      return object;\n    }\n  };\n  var proto11 = {\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n      }\n    },\n    origin: {\n      enumerable: true,\n      get: function() {\n        return this._origin;\n      },\n      set: function(v) {\n        if (this._origin) {\n          this._origin.unbind(Events.Types.change, this._renderer.flagVertices);\n        }\n        this._origin = v;\n        this._origin.bind(Events.Types.change, this._renderer.flagVertices);\n        this._renderer.flagVertices();\n      }\n    }\n  };\n\n  // src/effects/sprite.js\n  var Sprite = class _Sprite extends Rectangle {\n    /**\n     * @name Two.Sprite#_flagTexture\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#texture} needs updating.\n     */\n    _flagTexture = false;\n    /**\n     * @name Two.Sprite#_flagColumns\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#columns} need updating.\n     */\n    _flagColumns = false;\n    /**\n     * @name Two.Sprite#_flagRows\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#rows} need updating.\n     */\n    _flagRows = false;\n    /**\n     * @name Two.Sprite#_flagFrameRate\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#flagFrameRate} needs updating.\n     */\n    _flagFrameRate = false;\n    /**\n     * @name Two.Sprite#_flagIndex\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#index} needs updating.\n     */\n    _flagIndex = false;\n    // Private variables\n    /**\n     * @name Two.Sprite#_amount\n     * @private\n     * @property {Number} - Number of frames for a given {@link Two.Sprite}.\n     */\n    _amount = 1;\n    /**\n     * @name Two.Sprite#_duration\n     * @private\n     * @property {Number} - Number of milliseconds a {@link Two.Sprite}.\n     */\n    _duration = 0;\n    /**\n     * @name Two.Sprite#_startTime\n     * @private\n     * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.Sprite} started.\n     */\n    _startTime = 0;\n    /**\n     * @name Two.Sprite#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.Sprite} is animating or not.\n     */\n    _playing = false;\n    /**\n     * @name Two.Sprite#_firstFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.Sprite} should start with.\n     */\n    _firstFrame = 0;\n    /**\n     * @name Two.Sprite#_lastFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.Sprite} should end with.\n     */\n    _lastFrame = 0;\n    /**\n     * @name Two.Sprite#_loop\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.Sprite} should loop or not.\n     */\n    _loop = true;\n    // Exposed through getter-setter\n    /**\n     * @name Two.Sprite#_texture\n     * @private\n     * @see {@link Two.Sprite#texture}\n     */\n    _texture = null;\n    /**\n     * @name Two.Sprite#_columns\n     * @private\n     * @see {@link Two.Sprite#columns}\n     */\n    _columns = 1;\n    /**\n     * @name Two.Sprite#_rows\n     * @private\n     * @see {@link Two.Sprite#rows}\n     */\n    _rows = 1;\n    /**\n     * @name Two.Sprite#_frameRate\n     * @private\n     * @see {@link Two.Sprite#frameRate}\n     */\n    _frameRate = 0;\n    /**\n     * @name Two.Sprite#_index\n     * @private\n     * @property {Number} - The current frame the {@link Two.Sprite} is currently displaying.\n     */\n    _index = 0;\n    /**\n     * @name Two.Sprite#_origin\n     * @private\n     * @see {@link Two.Sprite#origin}\n     */\n    _origin = null;\n    constructor(src, ox, oy, cols, rows, frameRate) {\n      super(ox, oy, 0, 0);\n      this._renderer.type = \"sprite\";\n      for (let prop in proto12) {\n        Object.defineProperty(this, prop, proto12[prop]);\n      }\n      this.noStroke();\n      this.noFill();\n      if (src instanceof Texture) {\n        this.texture = src;\n      } else if (typeof src === \"string\") {\n        this.texture = new Texture(src);\n      }\n      this.origin = new Vector();\n      this._update();\n      if (typeof cols === \"number\") {\n        this.columns = cols;\n      }\n      if (typeof rows === \"number\") {\n        this.rows = rows;\n      }\n      if (typeof frameRate === \"number\") {\n        this.frameRate = frameRate;\n      }\n      this.index = 0;\n    }\n    /**\n     * @name Two.Sprite.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Sprite}.\n     */\n    static Properties = [\n      \"texture\",\n      \"columns\",\n      \"rows\",\n      \"frameRate\",\n      \"index\",\n      \"firstFrame\",\n      \"lastFrame\",\n      \"loop\"\n    ];\n    /**\n     * @name Two.Sprite.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Sprite} to create a new instance\n     * @returns {Two.Sprite}\n     * @description Create a new {@link Two.Sprite} from an object notation of a {@link Two.Sprite}.\n     * @nota-bene Works in conjunction with {@link Two.Sprite#toObject}\n     */\n    static fromObject(obj) {\n      const sprite = new _Sprite().copy(obj);\n      if (\"id\" in obj) {\n        sprite.id = obj.id;\n      }\n      return sprite;\n    }\n    /**\n     * @name Two.Sprite#copy\n     * @function\n     * @param {Two.Sprite} sprite - The reference {@link Two.Sprite}\n     * @description Copy the properties of one {@link Two.Sprite} onto another.\n     */\n    copy(sprite) {\n      super.copy.call(this, sprite);\n      for (let i = 0; i < _Sprite.Properties.length; i++) {\n        const k = _Sprite.Properties[i];\n        if (k in sprite) {\n          this[k] = sprite[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Sprite#play\n     * @function\n     * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n     * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.Sprite#textures}.\n     * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the sprite is looped.\n     * @description Initiate animation playback of a {@link Two.Sprite}.\n     */\n    play(firstFrame, lastFrame, onLastFrame) {\n      this._playing = true;\n      this._firstFrame = 0;\n      this._lastFrame = this.amount - 1;\n      this._startTime = _.performance.now();\n      if (typeof firstFrame === \"number\") {\n        this._firstFrame = firstFrame;\n      }\n      if (typeof lastFrame === \"number\") {\n        this._lastFrame = lastFrame;\n      }\n      if (typeof onLastFrame === \"function\") {\n        this._onLastFrame = onLastFrame;\n      } else {\n        delete this._onLastFrame;\n      }\n      if (this._index !== this._firstFrame) {\n        this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Sprite#pause\n     * @function\n     * @description Halt animation playback of a {@link Two.Sprite}.\n     */\n    pause() {\n      this._playing = false;\n      return this;\n    }\n    /**\n     * @name Two.Sprite#stop\n     * @function\n     * @description Halt animation playback of a {@link Two.Sprite} and set the current frame back to the first frame.\n     */\n    stop() {\n      this._playing = false;\n      this._index = 0;\n      return this;\n    }\n    /**\n     * @name Two.Sprite#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Sprite}\n     * @description Create a new instance of {@link Two.Sprite} with the same properties of the current sprite.\n     */\n    clone(parent) {\n      const clone = new _Sprite(\n        this.texture,\n        this.translation.x,\n        this.translation.y,\n        this.columns,\n        this.rows,\n        this.frameRate\n      );\n      if (this.playing) {\n        clone.play(this._firstFrame, this._lastFrame);\n      }\n      clone.loop = this.loop;\n      clone.firstFrame = this.firstFrame;\n      clone.lastFrame = this.lastFrame;\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Sprite#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"sprite\";\n      object.texture = this.texture.toObject();\n      object.columns = this.columns;\n      object.rows = this.rows;\n      object.frameRate = this.frameRate;\n      object.index = this.index;\n      object.firstFrame = this.firstFrame;\n      object.lastFrame = this.lastFrame;\n      object.loop = this.loop;\n      return object;\n    }\n    /**\n     * @name Two.Sprite#dispose\n     * @function\n     * @returns {Two.Sprite}\n     * @description Release the sprite's renderer resources and detach all events.\n     * This method stops any running animation, clears animation callbacks, disposes\n     * the texture (calling dispose() for thorough cleanup), and inherits comprehensive\n     * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n     * for potential re-attachment.\n     */\n    dispose() {\n      super.dispose();\n      if (this._playing) {\n        this._playing = false;\n      }\n      this._onLastFrame = null;\n      this._startTime = 0;\n      if (this._texture && typeof this._texture.dispose === \"function\") {\n        this._texture.dispose();\n      } else if (this._texture && typeof this._texture.unbind === \"function\") {\n        this._texture.unbind();\n      }\n      return this;\n    }\n    /**\n     * @name Two.Sprite#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      const effect = this._texture;\n      const cols = this._columns;\n      const rows = this._rows;\n      let width, height, elapsed, amount, duration;\n      let index, iw, ih, frames;\n      if (effect) {\n        if (this._flagColumns || this._flagRows) {\n          this._amount = this._columns * this._rows;\n        }\n        if (this._flagFrameRate) {\n          this._duration = 1e3 * this._amount / this._frameRate;\n        }\n        if (this._flagTexture) {\n          this.fill = effect;\n        }\n        if (effect.loaded) {\n          iw = effect.image.width;\n          ih = effect.image.height;\n          width = iw / cols;\n          height = ih / rows;\n          amount = this._amount;\n          if (this.width !== width) {\n            this.width = width;\n          }\n          if (this.height !== height) {\n            this.height = height;\n          }\n          if (this._playing && this._frameRate > 0) {\n            if (_.isNaN(this._lastFrame)) {\n              this._lastFrame = amount - 1;\n            }\n            elapsed = _.performance.now() - this._startTime;\n            frames = this._lastFrame + 1;\n            duration = 1e3 * (frames - this._firstFrame) / this._frameRate;\n            if (this._loop) {\n              elapsed = elapsed % duration;\n            } else {\n              elapsed = Math.min(elapsed, duration);\n            }\n            index = lerp(this._firstFrame, frames, elapsed / duration);\n            index = Math.floor(index);\n            if (index !== this._index) {\n              this._index = index;\n              if (index >= this._lastFrame - 1 && this._onLastFrame) {\n                this._onLastFrame();\n              }\n            }\n          }\n          const col = this._index % cols;\n          const row = Math.floor(this._index / cols);\n          const ox = -width * col + (iw - width) / 2;\n          const oy = -height * row + (ih - height) / 2;\n          if (ox !== effect.offset.x) {\n            effect.offset.x = ox;\n          }\n          if (oy !== effect.offset.y) {\n            effect.offset.y = oy;\n          }\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Sprite#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagTexture = this._flagColumns = this._flagRows = this._flagFrameRate = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto12 = {\n    texture: {\n      enumerable: true,\n      get: function() {\n        return this._texture;\n      },\n      set: function(v) {\n        this._texture = v;\n        this._flagTexture = true;\n      }\n    },\n    columns: {\n      enumerable: true,\n      get: function() {\n        return this._columns;\n      },\n      set: function(v) {\n        this._columns = v;\n        this._flagColumns = true;\n      }\n    },\n    rows: {\n      enumerable: true,\n      get: function() {\n        return this._rows;\n      },\n      set: function(v) {\n        this._rows = v;\n        this._flagRows = true;\n      }\n    },\n    frameRate: {\n      enumerable: true,\n      get: function() {\n        return this._frameRate;\n      },\n      set: function(v) {\n        this._frameRate = v;\n        this._flagFrameRate = true;\n      }\n    },\n    index: {\n      enumerable: true,\n      get: function() {\n        return this._index;\n      },\n      set: function(v) {\n        this._index = v;\n        this._flagIndex = true;\n      }\n    },\n    firstFrame: {\n      enumerable: true,\n      get: function() {\n        return this._firstFrame;\n      },\n      set: function(v) {\n        this._firstFrame = v;\n      }\n    },\n    lastFrame: {\n      enumerable: true,\n      get: function() {\n        return this._lastFrame;\n      },\n      set: function(v) {\n        this._lastFrame = v;\n      }\n    },\n    loop: {\n      enumerable: true,\n      get: function() {\n        return this._loop;\n      },\n      set: function(v) {\n        this._loop = !!v;\n      }\n    }\n  };\n\n  // src/children.js\n  var Children = class extends Collection {\n    /**\n     * @name Two.Group.Children#ids\n     * @property {Object} - Map of all elements in the list keyed by `id`s.\n     */\n    // N.B: Technique to disable enumeration on object\n    #ids = {};\n    get ids() {\n      return this.#ids;\n    }\n    constructor(children) {\n      children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments);\n      super(children);\n      this.attach(children);\n      this.on(Events.Types.insert, this.attach);\n      this.on(Events.Types.remove, this.detach);\n    }\n    /**\n     * @function\n     * @name Two.Group.Children#attach\n     * @param {Two.Shape[]} children - The objects which extend {@link Two.Shape} to be added.\n     * @description Adds elements to the `ids` map.\n     */\n    attach(children) {\n      for (let i = 0; i < children.length; i++) {\n        const child = children[i];\n        if (child && child.id) {\n          this.ids[child.id] = child;\n        }\n      }\n      return this;\n    }\n    /**\n     * @function\n     * @name Two.Group.Children#detach\n     * @param {Two.Shape[]} children - The objects which extend {@link Two.Shape} to be removed.\n     * @description Removes elements to the `ids` map.\n     */\n    detach(children) {\n      for (let i = 0; i < children.length; i++) {\n        delete this.ids[children[i].id];\n      }\n      return this;\n    }\n  };\n\n  // src/shapes/arc-segment.js\n  var ArcSegment = class _ArcSegment extends Path {\n    /**\n     * @name Two.ArcSegment#_flagStartAngle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#startAngle} needs updating.\n     */\n    _flagStartAngle = false;\n    /**\n     * @name Two.ArcSegment#_flagEndAngle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#endAngle} needs updating.\n     */\n    _flagEndAngle = false;\n    /**\n     * @name Two.ArcSegment#_flagInnerRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#innerRadius} needs updating.\n     */\n    _flagInnerRadius = false;\n    /**\n     * @name Two.ArcSegment#_flagOuterRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#outerRadius} needs updating.\n     */\n    _flagOuterRadius = false;\n    /**\n     * @name Two.ArcSegment#_startAngle\n     * @private\n     * @see {@link Two.ArcSegment#startAngle}\n     */\n    _startAngle = 0;\n    /**\n     * @name Two.ArcSegment#_endAngle\n     * @private\n     * @see {@link Two.ArcSegment#endAngle}\n     */\n    _endAngle = TWO_PI;\n    /**\n     * @name Two.ArcSegment#_innerRadius\n     * @private\n     * @see {@link Two.ArcSegment#innerRadius}\n     */\n    _innerRadius = 0;\n    /**\n     * @name Two.ArcSegment#_outerRadius\n     * @private\n     * @see {@link Two.ArcSegment#outerRadius}\n     */\n    _outerRadius = 0;\n    constructor(x, y, ir, or, sa, ea, res) {\n      const amount = res || Constants.Resolution * 3;\n      const points = [];\n      for (let i = 0; i < amount; i++) {\n        points.push(new Anchor());\n      }\n      super(points, true, false, true);\n      this._renderer.type = \"arc-segment\";\n      for (let prop in proto13) {\n        Object.defineProperty(this, prop, proto13[prop]);\n      }\n      if (typeof ir === \"number\") {\n        this.innerRadius = ir;\n      }\n      if (typeof or === \"number\") {\n        this.outerRadius = or;\n      }\n      if (typeof sa === \"number\") {\n        this.startAngle = sa;\n      }\n      if (typeof ea === \"number\") {\n        this.endAngle = ea;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    /**\n     * @name Two.ArcSegment.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.ArcSegment}.\n     */\n    static Properties = [\"startAngle\", \"endAngle\", \"innerRadius\", \"outerRadius\"];\n    /**\n     * @name Two.ArcSegment.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.ArcSegment} to create a new instance\n     * @returns {Two.ArcSegment}\n     * @description Create a new {@link Two.ArcSegment} from an object notation of a {@link Two.ArcSegment}.\n     * @nota-bene Works in conjunction with {@link Two.ArcSegment#toObject}\n     */\n    static fromObject(obj) {\n      const segment = new _ArcSegment().copy(obj);\n      if (\"id\" in obj) {\n        segment.id = obj.id;\n      }\n      return segment;\n    }\n    /**\n     * @name Two.ArcSegment#copy\n     * @function\n     * @param {Two.ArcSegment} arcSegment - The reference {@link Two.ArcSegment}\n     * @description Copy the properties of one {@link Two.ArcSegment} onto another.\n     */\n    copy(arcSegment) {\n      super.copy.call(this, arcSegment);\n      for (let i = 0; i < _ArcSegment.Properties.length; i++) {\n        const k = _ArcSegment.Properties[i];\n        if (k in arcSegment && typeof arcSegment[k] === \"number\") {\n          this[k] = arcSegment[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.ArcSegment#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices || this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius || this._flagOuterRadius) {\n        const sa = this._startAngle;\n        const ea = this._endAngle;\n        const ir = this._innerRadius;\n        const or = this._outerRadius;\n        const connected = mod(sa, TWO_PI) === mod(ea, TWO_PI);\n        const punctured = ir > 0;\n        const vertices = this.vertices;\n        let length = punctured ? vertices.length / 2 : vertices.length;\n        let command, id = 0;\n        let i, last, pct, v, theta, step, x, y, amp;\n        if (connected) {\n          length--;\n        } else if (!punctured) {\n          length -= 2;\n        }\n        for (i = 0, last = length - 1; i < length; i++) {\n          pct = i / last;\n          v = vertices[id];\n          theta = pct * (ea - sa) + sa;\n          step = (ea - sa) / length;\n          x = or * Math.cos(theta);\n          y = or * Math.sin(theta);\n          switch (i) {\n            case 0:\n              command = Commands.move;\n              break;\n            default:\n              command = Commands.curve;\n          }\n          v.command = command;\n          v.x = x;\n          v.y = y;\n          v.controls.left.clear();\n          v.controls.right.clear();\n          if (v.command === Commands.curve) {\n            amp = or * step / Math.PI;\n            v.controls.left.x = amp * Math.cos(theta - HALF_PI);\n            v.controls.left.y = amp * Math.sin(theta - HALF_PI);\n            v.controls.right.x = amp * Math.cos(theta + HALF_PI);\n            v.controls.right.y = amp * Math.sin(theta + HALF_PI);\n            if (i === 1) {\n              v.controls.left.multiplyScalar(2);\n            }\n            if (i === last) {\n              v.controls.right.multiplyScalar(2);\n            }\n          }\n          id++;\n        }\n        if (punctured) {\n          if (connected) {\n            vertices[id].command = Commands.close;\n            id++;\n          } else {\n            length--;\n            last = length - 1;\n          }\n          for (i = 0; i < length; i++) {\n            pct = i / last;\n            v = vertices[id];\n            theta = (1 - pct) * (ea - sa) + sa;\n            step = (ea - sa) / length;\n            x = ir * Math.cos(theta);\n            y = ir * Math.sin(theta);\n            command = Commands.curve;\n            if (i <= 0) {\n              command = connected ? Commands.move : Commands.line;\n            }\n            v.command = command;\n            v.x = x;\n            v.y = y;\n            v.controls.left.clear();\n            v.controls.right.clear();\n            if (v.command === Commands.curve) {\n              amp = ir * step / Math.PI;\n              v.controls.left.x = amp * Math.cos(theta + HALF_PI);\n              v.controls.left.y = amp * Math.sin(theta + HALF_PI);\n              v.controls.right.x = amp * Math.cos(theta - HALF_PI);\n              v.controls.right.y = amp * Math.sin(theta - HALF_PI);\n              if (i === 1) {\n                v.controls.left.multiplyScalar(2);\n              }\n              if (i === last) {\n                v.controls.right.multiplyScalar(2);\n              }\n            }\n            id++;\n          }\n          vertices[id].copy(vertices[0]);\n          vertices[id].command = Commands.line;\n        } else if (!connected) {\n          vertices[id].command = Commands.line;\n          vertices[id].x = 0;\n          vertices[id].y = 0;\n          id++;\n          vertices[id].copy(vertices[0]);\n          vertices[id].command = Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.ArcSegment#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      super.flagReset.call(this);\n      this._flagStartAngle = this._flagEndAngle = this._flagInnerRadius = this._flagOuterRadius = false;\n      return this;\n    }\n    /**\n     * @name Two.ArcSegment#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.ArcSegment}\n     * @description Create a new instance of {@link Two.ArcSegment} with the same properties of the current path.\n     */\n    clone(parent) {\n      const ir = this.innerRadius;\n      const or = this.outerRadius;\n      const sa = this.startAngle;\n      const ea = this.endAngle;\n      const resolution = this.vertices.length;\n      const clone = new _ArcSegment(0, 0, ir, or, sa, ea, resolution);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.ArcSegment#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"arc-segment\";\n      for (let i = 0; i < _ArcSegment.Properties.length; i++) {\n        const k = _ArcSegment.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var proto13 = {\n    startAngle: {\n      enumerable: true,\n      get: function() {\n        return this._startAngle;\n      },\n      set: function(v) {\n        this._startAngle = v;\n        this._flagStartAngle = true;\n      }\n    },\n    endAngle: {\n      enumerable: true,\n      get: function() {\n        return this._endAngle;\n      },\n      set: function(v) {\n        this._endAngle = v;\n        this._flagEndAngle = true;\n      }\n    },\n    innerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._innerRadius;\n      },\n      set: function(v) {\n        this._innerRadius = v;\n        this._flagInnerRadius = true;\n      }\n    },\n    outerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._outerRadius;\n      },\n      set: function(v) {\n        this._outerRadius = v;\n        this._flagOuterRadius = true;\n      }\n    }\n  };\n\n  // src/shapes/circle.js\n  var cos2 = Math.cos;\n  var sin2 = Math.sin;\n  var Circle = class _Circle extends Path {\n    /**\n     * @name Two.Circle#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Circle#radius} needs updating.\n     */\n    _flagRadius = false;\n    /**\n     * @name Two.Circle#_radius\n     * @private\n     * @see {@link Two.Circle#radius}\n     */\n    _radius = 0;\n    constructor(ox, oy, r, resolution) {\n      const amount = resolution ? Math.max(resolution, 2) : 4;\n      const points = [];\n      for (let i = 0; i < amount; i++) {\n        points.push(new Anchor(0, 0, 0, 0, 0, 0));\n      }\n      super(points, true, true, true);\n      this._renderer.type = \"circle\";\n      for (let prop in proto14) {\n        Object.defineProperty(this, prop, proto14[prop]);\n      }\n      if (typeof r === \"number\") {\n        this.radius = r;\n      }\n      this._update();\n      if (typeof ox === \"number\") {\n        this.translation.x = ox;\n      }\n      if (typeof oy === \"number\") {\n        this.translation.y = oy;\n      }\n    }\n    /**\n     * @name Two.Circle.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Circle}.\n     */\n    static Properties = [\"radius\"];\n    /**\n     * @name Two.Circle.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Circle} to create a new instance\n     * @returns {Two.Circle}\n     * @description Create a new {@link Two.Circle} from an object notation of a {@link Two.Circle}.\n     * @nota-bene Works in conjunction with {@link Two.Circle#toObject}\n     */\n    static fromObject(obj) {\n      const circle = new _Circle().copy(obj);\n      if (\"id\" in obj) {\n        circle.id = obj.id;\n      }\n      return circle;\n    }\n    /**\n     * @name Two.Circle#copy\n     * @function\n     * @param {Two.Circle} circle - The reference {@link Two.Circle}\n     * @description Copy the properties of one {@link Two.Circle} onto another.\n     */\n    copy(circle) {\n      super.copy.call(this, circle);\n      for (let i = 0; i < _Circle.Properties.length; i++) {\n        const k = _Circle.Properties[i];\n        if (k in circle && typeof circle[k] === \"number\") {\n          this[k] = circle[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Circle#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices || this._flagRadius) {\n        let length = this.vertices.length;\n        if (!this._closed && length > 2) {\n          length -= 1;\n        }\n        const c = 4 / 3 * Math.tan(Math.PI / (length * 2));\n        const radius = this._radius;\n        const rc = radius * c;\n        for (let i = 0; i < this.vertices.length; i++) {\n          const pct = i / length;\n          const theta = pct * TWO_PI;\n          const x = radius * cos2(theta);\n          const y = radius * sin2(theta);\n          const lx = rc * cos2(theta - HALF_PI);\n          const ly = rc * sin2(theta - HALF_PI);\n          const rx = rc * cos2(theta + HALF_PI);\n          const ry = rc * sin2(theta + HALF_PI);\n          const v = this.vertices[i];\n          v.command = i === 0 ? Commands.move : Commands.curve;\n          v.set(x, y);\n          v.controls.left.set(lx, ly);\n          v.controls.right.set(rx, ry);\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Circle#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagRadius = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Circle#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Circle}\n     * @description Create a new instance of {@link Two.Circle} with the same properties of the current path.\n     */\n    clone(parent) {\n      const clone = new _Circle(0, 0, this.radius, this.vertices.length);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Circle#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"circle\";\n      for (let i = 0; i < _Circle.Properties.length; i++) {\n        const k = _Circle.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var proto14 = {\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        this._radius = v;\n        this._flagRadius = true;\n      }\n    }\n  };\n\n  // src/shapes/ellipse.js\n  var cos3 = Math.cos;\n  var sin3 = Math.sin;\n  var Ellipse = class _Ellipse extends Path {\n    /**\n     * @name Two.Ellipse#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Ellipse#width} needs updating.\n     */\n    _flagWidth = false;\n    /**\n     * @name Two.Ellipse#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Ellipse#height} needs updating.\n     */\n    _flagHeight = false;\n    /**\n     * @name Two.Ellipse#_width\n     * @private\n     * @see {@link Two.Ellipse#width}\n     */\n    _width = 0;\n    /**\n     * @name Two.Ellipse#_height\n     * @private\n     * @see {@link Two.Ellipse#height}\n     */\n    _height = 0;\n    constructor(x, y, rx, ry, resolution) {\n      if (typeof ry !== \"number\" && typeof rx === \"number\") {\n        ry = rx;\n      }\n      const amount = resolution ? Math.max(resolution, 2) : 4;\n      const points = [];\n      for (let i = 0; i < amount; i++) {\n        points.push(new Anchor());\n      }\n      super(points, true, true, true);\n      this._renderer.type = \"ellipse\";\n      for (let prop in proto15) {\n        Object.defineProperty(this, prop, proto15[prop]);\n      }\n      if (typeof rx === \"number\") {\n        this.width = rx * 2;\n      }\n      if (typeof ry === \"number\") {\n        this.height = ry * 2;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    /**\n     * @name Two.Ellipse.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Ellipse}.\n     */\n    static Properties = [\"width\", \"height\"];\n    /**\n     * @name Two.Ellipse.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Ellipse} to create a new instance\n     * @returns {Two.Ellipse}\n     * @description Create a new {@link Two.Ellipse} from an object notation of a {@link Two.Ellipse}.\n     * @nota-bene Works in conjunction with {@link Two.Ellipse#toObject}\n     */\n    static fromObject(obj) {\n      const ellipse = new _Ellipse().copy(obj);\n      if (\"id\" in obj) {\n        ellipse.id = obj.id;\n      }\n      return ellipse;\n    }\n    /**\n     * @name Two.Ellipse#copy\n     * @function\n     * @param {Two.Ellipse} ellipse - The reference {@link Two.Ellipse}\n     * @description Copy the properties of one {@link Two.Ellipse} onto another.\n     */\n    copy(ellipse) {\n      super.copy.call(this, ellipse);\n      for (let i = 0; i < _Ellipse.Properties.length; i++) {\n        const k = _Ellipse.Properties[i];\n        if (k in ellipse && typeof ellipse[k] === \"number\") {\n          this[k] = ellipse[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Ellipse#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight) {\n        let length = this.vertices.length;\n        if (!this._closed && length > 2) {\n          length -= 1;\n        }\n        const c = 4 / 3 * Math.tan(Math.PI / (this.vertices.length * 2));\n        const radiusX = this._width / 2;\n        const radiusY = this._height / 2;\n        for (let i = 0; i < this.vertices.length; i++) {\n          const pct = i / length;\n          const theta = pct * TWO_PI;\n          const x = radiusX * cos3(theta);\n          const y = radiusY * sin3(theta);\n          const lx = radiusX * c * cos3(theta - HALF_PI);\n          const ly = radiusY * c * sin3(theta - HALF_PI);\n          const rx = radiusX * c * cos3(theta + HALF_PI);\n          const ry = radiusY * c * sin3(theta + HALF_PI);\n          const v = this.vertices[i];\n          v.command = i === 0 ? Commands.move : Commands.curve;\n          v.set(x, y);\n          v.controls.left.set(lx, ly);\n          v.controls.right.set(rx, ry);\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Ellipse#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagWidth = this._flagHeight = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Ellipse#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Ellipse}\n     * @description Create a new instance of {@link Two.Ellipse} with the same properties of the current path.\n     */\n    clone(parent) {\n      const rx = this.width / 2;\n      const ry = this.height / 2;\n      const resolution = this.vertices.length;\n      const clone = new _Ellipse(0, 0, rx, ry, resolution);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Ellipse#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"ellipse\";\n      for (let i = 0; i < _Ellipse.Properties.length; i++) {\n        const k = _Ellipse.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var proto15 = {\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n      }\n    }\n  };\n\n  // src/shapes/points.js\n  var ceil2 = Math.ceil;\n  var floor3 = Math.floor;\n  var Points = class _Points extends Shape {\n    _flagVertices = true;\n    _flagLength = true;\n    _flagFill = true;\n    _flagStroke = true;\n    _flagLinewidth = true;\n    _flagOpacity = true;\n    _flagVisible = true;\n    _flagSize = true;\n    _flagSizeAttenuation = true;\n    _flagStrokeAttenuation = true;\n    _length = 0;\n    _fill = \"#fff\";\n    _stroke = \"#000\";\n    _linewidth = 1;\n    _opacity = 1;\n    _visible = true;\n    _size = 1;\n    _sizeAttenuation = false;\n    _beginning = 0;\n    _ending = 1;\n    _dashes = null;\n    _strokeAttenuation = true;\n    constructor(vertices) {\n      super();\n      for (let prop in proto16) {\n        Object.defineProperty(this, prop, proto16[prop]);\n      }\n      this._renderer.type = \"points\";\n      this._renderer.flagVertices = FlagVertices.bind(this);\n      this._renderer.bindVertices = BindVertices.bind(this);\n      this._renderer.unbindVertices = UnbindVertices.bind(this);\n      this._renderer.flagFill = FlagFill.bind(this);\n      this._renderer.flagStroke = FlagStroke.bind(this);\n      this._renderer.vertices = null;\n      this._renderer.collection = null;\n      this.size = 1;\n      this.sizeAttenuation = false;\n      this.beginning = 0;\n      this.ending = 1;\n      this.fill = \"#fff\";\n      this.stroke = \"#000\";\n      this.linewidth = 1;\n      this.opacity = 1;\n      this.className = \"\";\n      this.visible = true;\n      this.vertices = vertices;\n      this.dashes = [];\n      this.dashes.offset = 0;\n    }\n    /**\n     * @name Two.Points.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Points}.\n     */\n    static Properties = [\n      \"fill\",\n      \"stroke\",\n      \"linewidth\",\n      \"opacity\",\n      \"visible\",\n      \"size\",\n      \"sizeAttenuation\",\n      \"beginning\",\n      \"ending\",\n      \"dashes\",\n      \"strokeAttenuation\"\n    ];\n    /**\n     * @name Two.Points.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Points} to create a new instance\n     * @returns {Two.Points}\n     * @description Create a new {@link Two.Points} from an object notation of a {@link Two.Points}.\n     * @nota-bene Works in conjunction with {@link Two.Points#toObject}\n     */\n    static fromObject(obj) {\n      const fill = typeof obj.fill === \"string\" ? obj.fill : getEffectFromObject(obj.fill);\n      const stroke = typeof obj.stroke === \"string\" ? obj.stroke : getEffectFromObject(obj.stroke);\n      const points = new _Points().copy({ ...obj, fill, stroke });\n      if (\"id\" in obj) {\n        points.id = obj.id;\n      }\n      return points;\n    }\n    /**\n     * @name Two.Points#copy\n     * @function\n     * @param {Two.Points} points - The reference {@link Two.Points}\n     * @description Copy the properties of one {@link Two.Points} onto another.\n     */\n    copy(points) {\n      super.copy.call(this, points);\n      for (let j = 0; j < points.vertices.length; j++) {\n        const v = points.vertices[j];\n        if (v instanceof Anchor) {\n          this.vertices.push(points.vertices[j].clone());\n        } else {\n          this.vertices.push(new Anchor().copy(v));\n        }\n      }\n      for (let i = 0; i < _Points.Properties.length; i++) {\n        const k = _Points.Properties[i];\n        if (k in points) {\n          this[k] = points[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Points#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Points}\n     * @description Create a new instance of {@link Two.Points} with the same properties of the current path.\n     */\n    clone(parent) {\n      const clone = new _Points();\n      for (let j = 0; j < this.vertices.length; j++) {\n        clone.vertices.push(this.vertices[j].clone());\n      }\n      for (let i = 0; i < _Points.Properties.length; i++) {\n        const k = _Points.Properties[i];\n        clone[k] = this[k];\n      }\n      clone.className = this.className;\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    /**\n     * @name Two.Points#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the points object.\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.renderer.type = \"points\";\n      result.vertices = this.vertices.map((v) => v.toObject());\n      _.each(\n        _Points.Properties,\n        function(k) {\n          if (typeof this[k] !== \"undefined\") {\n            if (this[k].toObject) {\n              result[k] = this[k].toObject();\n            } else {\n              result[k] = this[k];\n            }\n          }\n        },\n        this\n      );\n      return result;\n    }\n    /**\n     * @name Two.Points#dispose\n     * @function\n     * @returns {Two.Points}\n     * @description Release the points' renderer resources and detach all events.\n     * This method cleans up vertices collection events, individual vertex events,\n     * and disposes fill/stroke effects (calling dispose() on Gradients and\n     * Textures for thorough cleanup) while preserving the renderer type for\n     * potential re-attachment to a new renderer.\n     */\n    dispose() {\n      super.dispose();\n      if (this.vertices && typeof this.vertices.unbind === \"function\") {\n        try {\n          this.vertices.unbind();\n        } catch (e) {\n        }\n      }\n      if (this.vertices) {\n        for (let i = 0; i < this.vertices.length; i++) {\n          const v = this.vertices[i];\n          if (typeof v.unbind === \"function\") {\n            v.unbind();\n          }\n        }\n      }\n      if (typeof this.fill === \"object\" && typeof this.fill.dispose === \"function\") {\n        this.fill.dispose();\n      } else if (typeof this.fill === \"object\" && typeof this.fill.unbind === \"function\") {\n        this.fill.unbind();\n      }\n      if (typeof this.stroke === \"object\" && typeof this.stroke.dispose === \"function\") {\n        this.stroke.dispose();\n      } else if (typeof this.stroke === \"object\" && typeof this.stroke.unbind === \"function\") {\n        this.stroke.unbind();\n      }\n      return this;\n    }\n    /**\n     * @name Two.Points#noFill\n     * @function\n     * @description Short hand method to set fill to `none`.\n     */\n    noFill = Path.prototype.noFill;\n    /**\n     * @name Two.Points#noStroke\n     * @function\n     * @description Short hand method to set stroke to `none`.\n     */\n    noStroke = Path.prototype.noStroke;\n    /**\n     * @name Two.Points#corner\n     * @function\n     * @description Orient the vertices of the shape to the upper left-hand corner of the points object.\n     */\n    corner = Path.prototype.corner;\n    /**\n     * @name Two.Points#center\n     * @function\n     * @description Orient the vertices of the shape to the center of the points object.\n     */\n    center = Path.prototype.center;\n    /**\n     * @name Two.Points#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n     */\n    getBoundingClientRect = Path.prototype.getBoundingClientRect;\n    /**\n     * @name Two.Points#subdivide\n     * @function\n     * @param {Number} limit - How many times to recurse subdivisions.\n     * @description Insert a {@link Two.Vector} at the midpoint between every item in {@link Two.Points#vertices}.\n     */\n    subdivide(limit) {\n      this._update();\n      let points = [];\n      for (let i = 0; i < this.vertices.length; i++) {\n        const a = this.vertices[i];\n        const b = this.vertices[i - 1];\n        if (!b) {\n          continue;\n        }\n        const x1 = a.x;\n        const y1 = a.y;\n        const x2 = b.x;\n        const y2 = b.y;\n        const subdivisions = subdivide(x1, y1, x1, y1, x2, y2, x2, y2, limit);\n        points = points.concat(subdivisions);\n      }\n      this.vertices = points;\n      return this;\n    }\n    /**\n     * @name Two.Points#_updateLength\n     * @function\n     * @private\n     * @param {Number} [limit] -\n     * @param {Boolean} [silent=false] - If set to `true` then the points object isn't updated before calculation. Useful for internal use.\n     * @description Recalculate the {@link Two.Points#length} value.\n     */\n    _updateLength = Path.prototype._updateLength;\n    /**\n     * @name Two.Points#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices) {\n        if (this._flagLength) {\n          this._updateLength(void 0, true);\n        }\n        const beginning = Math.min(this._beginning, this._ending);\n        const ending = Math.max(this._beginning, this._ending);\n        const bid = getIdByLength(this, beginning * this._length);\n        const eid = getIdByLength(this, ending * this._length);\n        const low = ceil2(bid);\n        const high = floor3(eid);\n        let j = 0, v;\n        this._renderer.vertices = [];\n        this._renderer.collection = [];\n        for (let i = 0; i < this._collection.length; i++) {\n          if (i >= low && i <= high) {\n            v = this._collection[i];\n            this._renderer.collection.push(v);\n            this._renderer.vertices[j * 2 + 0] = v.x;\n            this._renderer.vertices[j * 2 + 1] = v.y;\n            j++;\n          }\n        }\n      }\n      super._update.apply(this, arguments);\n      return this;\n    }\n    /**\n     * @name Two.Points#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagSize = this._flagSizeAttenuation = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto16 = {\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        this._flagLinewidth = true;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n      }\n    },\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._visible = v;\n        this._flagVisible = true;\n      }\n    },\n    size: {\n      enumerable: true,\n      get: function() {\n        return this._size;\n      },\n      set: function(v) {\n        this._size = v;\n        this._flagSize = true;\n      }\n    },\n    sizeAttenuation: {\n      enumerable: true,\n      get: function() {\n        return this._sizeAttenuation;\n      },\n      set: function(v) {\n        this._sizeAttenuation = v;\n        this._flagSizeAttenuation = true;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(f) {\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n        }\n        this._fill = f;\n        this._flagFill = true;\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.bind(Events.Types.change, this._renderer.flagFill);\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(f) {\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n        }\n        this._stroke = f;\n        this._flagStroke = true;\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n        }\n      }\n    },\n    /**\n     * @name Two.Points#length\n     * @property {Number} - The sum of distances between all {@link Two.Points#vertices}.\n     */\n    length: {\n      get: function() {\n        if (this._flagLength) {\n          this._updateLength();\n        }\n        return this._length;\n      }\n    },\n    beginning: {\n      enumerable: true,\n      get: function() {\n        return this._beginning;\n      },\n      set: function(v) {\n        this._beginning = v;\n        this._flagVertices = true;\n      }\n    },\n    ending: {\n      enumerable: true,\n      get: function() {\n        return this._ending;\n      },\n      set: function(v) {\n        this._ending = v;\n        this._flagVertices = true;\n      }\n    },\n    vertices: {\n      enumerable: true,\n      get: function() {\n        return this._collection;\n      },\n      set: function(vertices) {\n        const bindVertices = this._renderer.bindVertices;\n        const unbindVertices = this._renderer.unbindVertices;\n        if (this._collection) {\n          this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices);\n        }\n        if (vertices instanceof Collection) {\n          this._collection = vertices;\n        } else {\n          this._collection = new Collection(vertices || []);\n        }\n        this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices);\n        bindVertices(this._collection);\n      }\n    },\n    dashes: {\n      enumerable: true,\n      get: function() {\n        return this._dashes;\n      },\n      set: function(v) {\n        if (typeof v.offset !== \"number\") {\n          v.offset = this.dashes && this._dashes.offset || 0;\n        }\n        this._dashes = v;\n      }\n    },\n    /**\n     * @name Two.Points#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n     * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n     */\n    strokeAttenuation: {\n      enumerable: true,\n      get: function() {\n        return this._strokeAttenuation;\n      },\n      set: function(v) {\n        this._strokeAttenuation = !!v;\n        this._flagStrokeAttenuation = true;\n        this._flagLinewidth = true;\n      }\n    }\n  };\n\n  // src/shapes/polygon.js\n  var cos4 = Math.cos;\n  var sin4 = Math.sin;\n  var Polygon = class _Polygon extends Path {\n    /**\n     * @name Two.Polygon#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#width} needs updating.\n     */\n    _flagWidth = false;\n    /**\n     * @name Two.Polygon#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#height} needs updating.\n     */\n    _flagHeight = false;\n    /**\n     * @name Two.Polygon#_flagSides\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#sides} needs updating.\n     */\n    _flagSides = false;\n    /**\n     * @name Two.Polygon#_radius\n     * @private\n     * @see {@link Two.Polygon#radius}\n     */\n    _radius = 0;\n    /**\n     * @name Two.Polygon#_width\n     * @private\n     * @see {@link Two.Polygon#width}\n     */\n    _width = 0;\n    /**\n     * @name Two.Polygon#_height\n     * @private\n     * @see {@link Two.Polygon#height}\n     */\n    _height = 0;\n    /**\n     * @name Two.Polygon#_sides\n     * @private\n     * @see {@link Two.Polygon#sides}\n     */\n    _sides = 0;\n    constructor(x, y, radius, sides) {\n      sides = Math.max(sides || 0, 3);\n      super();\n      this._renderer.type = \"polygon\";\n      for (let prop in proto17) {\n        Object.defineProperty(this, prop, proto17[prop]);\n      }\n      this.closed = true;\n      this.automatic = false;\n      if (typeof radius === \"number\") {\n        this.radius = radius;\n      }\n      if (typeof sides === \"number\") {\n        this.sides = sides;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    /**\n     * @name Two.Polygon.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Polygon}.\n     */\n    static Properties = [\"width\", \"height\", \"sides\"];\n    /**\n     * @name Two.Polygon.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Polygon} to create a new instance\n     * @returns {Two.Polygon}\n     * @description Create a new {@link Two.Polygon} from an object notation of a {@link Two.Polygon}.\n     * @nota-bene Works in conjunction with {@link Two.Polygon#toObject}\n     */\n    static fromObject(obj) {\n      const polygon = new _Polygon().copy(obj);\n      if (\"id\" in obj) {\n        polygon.id = obj.id;\n      }\n      return polygon;\n    }\n    /**\n     * @name Two.Polygon#copy\n     * @function\n     * @param {Two.Polygon} polygon - The reference {@link Two.Polygon}\n     * @description Copy the properties of one {@link Two.Polygon} onto another.\n     */\n    copy(polygon) {\n      super.copy.call(this, polygon);\n      for (let i = 0; i < _Polygon.Properties.length; i++) {\n        const k = _Polygon.Properties[i];\n        if (k in polygon && typeof polygon[k] === \"number\") {\n          this[k] = polygon[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Polygon#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagSides) {\n        const sides = this._sides;\n        const amount = sides + 1;\n        let length = this.vertices.length;\n        if (length > sides) {\n          this.vertices.splice(sides - 1, length - sides);\n          length = sides;\n        }\n        for (let i = 0; i < amount; i++) {\n          const pct = (i + 0.5) / sides;\n          const theta = TWO_PI * pct + Math.PI / 2;\n          const x = this._width * cos4(theta) / 2;\n          const y = this._height * sin4(theta) / 2;\n          if (i >= length) {\n            this.vertices.push(new Anchor(x, y));\n          } else {\n            this.vertices[i].set(x, y);\n          }\n          this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Polygon#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagWidth = this._flagHeight = this._flagSides = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Polygon#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Polygon}\n     * @description Create a new instance of {@link Two.Polygon} with the same properties of the current path.\n     */\n    clone(parent) {\n      const clone = new _Polygon(0, 0, 0, this.sides);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      clone.width = this.width;\n      clone.height = this.height;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Polygon#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"polygon\";\n      for (let i = 0; i < _Polygon.Properties.length; i++) {\n        const k = _Polygon.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var proto17 = {\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        this._radius = v;\n        this.width = v * 2;\n        this.height = v * 2;\n      }\n    },\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n        this._radius = Math.max(this.width, this.height) / 2;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n        this._radius = Math.max(this.width, this.height) / 2;\n      }\n    },\n    sides: {\n      enumerable: true,\n      get: function() {\n        return this._sides;\n      },\n      set: function(v) {\n        this._sides = v;\n        this._flagSides = true;\n      }\n    }\n  };\n\n  // src/shapes/rounded-rectangle.js\n  var RoundedRectangle = class _RoundedRectangle extends Path {\n    /**\n     * @name Two.RoundedRectangle#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#width} needs updating.\n     */\n    _flagWidth = false;\n    /**\n     * @name Two.RoundedRectangle#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#height} needs updating.\n     */\n    _flagHeight = false;\n    /**\n     * @name Two.RoundedRectangle#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#radius} needs updating.\n     */\n    _flagRadius = false;\n    /**\n     * @name Two.RoundedRectangle#_width\n     * @private\n     * @see {@link Two.RoundedRectangle#width}\n     */\n    _width = 0;\n    /**\n     * @name Two.RoundedRectangle#_height\n     * @private\n     * @see {@link Two.RoundedRectangle#height}\n     */\n    _height = 0;\n    /**\n     * @name Two.RoundedRectangle#_radius\n     * @private\n     * @see {@link Two.RoundedRectangle#radius}\n     */\n    _radius = 12;\n    constructor(x, y, width, height, radius) {\n      if (typeof radius === \"undefined\" && typeof width === \"number\" && typeof height === \"number\") {\n        radius = Math.floor(Math.min(width, height) / 12);\n      }\n      const points = [];\n      for (let i = 0; i < 10; i++) {\n        points.push(\n          new Anchor(0, 0, 0, 0, 0, 0, i === 0 ? Commands.move : Commands.curve)\n        );\n      }\n      super(points);\n      this._renderer.type = \"rounded-rectangle\";\n      for (let prop in proto18) {\n        Object.defineProperty(this, prop, proto18[prop]);\n      }\n      this.closed = true;\n      this.automatic = false;\n      this._renderer.flagRadius = FlagRadius.bind(this);\n      if (typeof width === \"number\") {\n        this.width = width;\n      }\n      if (typeof height === \"number\") {\n        this.height = height;\n      }\n      if (typeof radius === \"number\" || radius instanceof Vector) {\n        this.radius = radius;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    /**\n     * @name Two.RoundedRectangle.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.RoundedRectangle}.\n     */\n    static Properties = [\"width\", \"height\", \"radius\"];\n    /**\n     * @name Two.RoundedRectangle.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.RoundedRectangle} to create a new instance\n     * @returns {Two.RoundedRectangle}\n     * @description Create a new {@link Two.RoundedRectangle} from an object notation of a {@link Two.RoundedRectangle}.\n     * @nota-bene Works in conjunction with {@link Two.RoundedRectangle#toObject}\n     */\n    static fromObject(obj) {\n      const rectangle = new _RoundedRectangle().copy(obj);\n      if (\"id\" in obj) {\n        rectangle.id = obj.id;\n      }\n      return rectangle;\n    }\n    /**\n     * @name Two.RoundedRectangle#copy\n     * @function\n     * @param {Two.RoundedRectangle} roundedRectangle - The reference {@link Two.RoundedRectangle}\n     * @description Copy the properties of one {@link Two.RoundedRectangle} onto another.\n     */\n    copy(roundedRectangle) {\n      super.copy.call(this, roundedRectangle);\n      for (let i = 0; i < _RoundedRectangle.Properties.length; i++) {\n        const k = _RoundedRectangle.Properties[i];\n        if (k in roundedRectangle) {\n          const value = roundedRectangle[k];\n          if (/radius/i.test(k)) {\n            this[k] = typeof value === \"number\" || value instanceof Vector ? value : new Vector().copy(value);\n          } else if (typeof value === \"number\") {\n            this[k] = value;\n          }\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.RoundedRectangle#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagRadius) {\n        const width = this._width;\n        const height = this._height;\n        let rx, ry;\n        if (this._radius instanceof Vector) {\n          rx = this._radius.x;\n          ry = this._radius.y;\n        } else {\n          rx = this._radius;\n          ry = this._radius;\n        }\n        let v;\n        let w = width / 2;\n        let h = height / 2;\n        v = this.vertices[0];\n        v.x = -(w - rx);\n        v.y = -h;\n        v = this.vertices[1];\n        v.x = w - rx;\n        v.y = -h;\n        v.controls.left.clear();\n        v.controls.right.x = rx;\n        v.controls.right.y = 0;\n        v = this.vertices[2];\n        v.x = w;\n        v.y = -(h - ry);\n        v.controls.right.clear();\n        v.controls.left.clear();\n        v = this.vertices[3];\n        v.x = w;\n        v.y = h - ry;\n        v.controls.left.clear();\n        v.controls.right.x = 0;\n        v.controls.right.y = ry;\n        v = this.vertices[4];\n        v.x = w - rx;\n        v.y = h;\n        v.controls.right.clear();\n        v.controls.left.clear();\n        v = this.vertices[5];\n        v.x = -(w - rx);\n        v.y = h;\n        v.controls.left.clear();\n        v.controls.right.x = -rx;\n        v.controls.right.y = 0;\n        v = this.vertices[6];\n        v.x = -w;\n        v.y = h - ry;\n        v.controls.left.clear();\n        v.controls.right.clear();\n        v = this.vertices[7];\n        v.x = -w;\n        v.y = -(h - ry);\n        v.controls.left.clear();\n        v.controls.right.x = 0;\n        v.controls.right.y = -ry;\n        v = this.vertices[8];\n        v.x = -(w - rx);\n        v.y = -h;\n        v.controls.left.clear();\n        v.controls.right.clear();\n        v = this.vertices[9];\n        v.copy(this.vertices[8]);\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.RoundedRectangle#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagWidth = this._flagHeight = this._flagRadius = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.RoundedRectangle#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.RoundedRectangle}\n     * @description Create a new instance of {@link Two.RoundedRectangle} with the same properties of the current path.\n     */\n    clone(parent) {\n      const width = this.width;\n      const height = this.height;\n      const radius = this.radius;\n      const clone = new _RoundedRectangle(0, 0, width, height, radius);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.RoundedRectangle#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"rounded-rectangle\";\n      for (let i = 0; i < _RoundedRectangle.Properties.length; i++) {\n        const k = _RoundedRectangle.Properties[i];\n        object[k] = this[k];\n      }\n      object.radius = typeof this.radius === \"number\" ? this.radius : this.radius.toObject();\n      return object;\n    }\n  };\n  var proto18 = {\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n      }\n    },\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        if (this._radius instanceof Vector) {\n          this._radius.unbind(Events.Types.change, this._renderer.flagRadius);\n        }\n        this._radius = v;\n        if (this._radius instanceof Vector) {\n          this._radius.bind(Events.Types.change, this._renderer.flagRadius);\n        }\n        this._flagRadius = true;\n      }\n    }\n  };\n  function FlagRadius() {\n    this._flagRadius = true;\n  }\n\n  // src/shapes/star.js\n  var cos5 = Math.cos;\n  var sin5 = Math.sin;\n  var Star = class _Star extends Path {\n    /**\n     * @name Two.Star#_flagInnerRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#innerRadius} needs updating.\n     */\n    _flagInnerRadius = false;\n    /**\n     * @name Two.Star#_flagOuterRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#outerRadius} needs updating.\n     */\n    _flagOuterRadius = false;\n    /**\n     * @name Two.Star#_flagSides\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#sides} needs updating.\n     */\n    _flagSides = false;\n    /**\n     * @name Two.Star#_innerRadius\n     * @private\n     * @see {@link Two.Star#innerRadius}\n     */\n    _innerRadius = 0;\n    /**\n     * @name Two.Star#_outerRadius\n     * @private\n     * @see {@link Two.Star#outerRadius}\n     */\n    _outerRadius = 0;\n    /**\n     * @name Two.Star#_sides\n     * @private\n     * @see {@link Two.Star#sides}\n     */\n    _sides = 0;\n    constructor(x, y, innerRadius, outerRadius, sides) {\n      if (arguments.length <= 3) {\n        outerRadius = innerRadius;\n        innerRadius = outerRadius / 2;\n      }\n      if (typeof sides !== \"number\" || sides <= 0) {\n        sides = 5;\n      }\n      super();\n      this._renderer.type = \"star\";\n      for (let prop in proto19) {\n        Object.defineProperty(this, prop, proto19[prop]);\n      }\n      this.closed = true;\n      this.automatic = false;\n      if (typeof innerRadius === \"number\") {\n        this.innerRadius = innerRadius;\n      }\n      if (typeof outerRadius === \"number\") {\n        this.outerRadius = outerRadius;\n      }\n      if (typeof sides === \"number\") {\n        this.sides = sides;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    /**\n     * @name Two.Star.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Star}.\n     */\n    static Properties = [\"innerRadius\", \"outerRadius\", \"sides\"];\n    /**\n     * @name Two.Star.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Star} to create a new instance\n     * @returns {Two.Star}\n     * @description Create a new {@link Two.Star} from an object notation of a {@link Two.Star}.\n     * @nota-bene Works in conjunction with {@link Two.Star#toObject}\n     */\n    static fromObject(obj) {\n      const star = new _Star().copy(obj);\n      if (\"id\" in obj) {\n        star.id = obj.id;\n      }\n      return star;\n    }\n    /**\n     * @name Two.Star#copy\n     * @function\n     * @param {Two.Star} star - The reference {@link Two.Star}\n     * @description Copy the properties of one {@link Two.Star} onto another.\n     */\n    copy(star) {\n      super.copy.call(this, star);\n      for (let i = 0; i < _Star.Properties.length; i++) {\n        const k = _Star.Properties[i];\n        if (k in star && typeof star[k] === \"number\") {\n          this[k] = star[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Star#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (this._flagVertices || this._flagInnerRadius || this._flagOuterRadius || this._flagSides) {\n        const sides = this._sides * 2;\n        const amount = sides + 1;\n        let length = this.vertices.length;\n        if (length > sides) {\n          this.vertices.splice(sides - 1, length - sides);\n          length = sides;\n        }\n        for (let i = 0; i < amount; i++) {\n          const pct = (i + 0.5) / sides;\n          const theta = TWO_PI * pct;\n          const r = (!(i % 2) ? this._innerRadius : this._outerRadius) / 2;\n          const x = r * cos5(theta);\n          const y = r * sin5(theta);\n          if (i >= length) {\n            this.vertices.push(new Anchor(x, y));\n          } else {\n            this.vertices[i].set(x, y);\n          }\n          this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Star#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Star#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Star}\n     * @description Create a new instance of {@link Two.Star} with the same properties of the current path.\n     */\n    clone(parent) {\n      const ir = this.innerRadius;\n      const or = this.outerRadius;\n      const sides = this.sides;\n      const clone = new _Star(0, 0, ir, or, sides);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Star#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"star\";\n      for (let i = 0; i < _Star.Properties.length; i++) {\n        const k = _Star.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var proto19 = {\n    innerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._innerRadius;\n      },\n      set: function(v) {\n        this._innerRadius = v;\n        this._flagInnerRadius = true;\n      }\n    },\n    outerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._outerRadius;\n      },\n      set: function(v) {\n        this._outerRadius = v;\n        this._flagOuterRadius = true;\n      }\n    },\n    sides: {\n      enumerable: true,\n      get: function() {\n        return this._sides;\n      },\n      set: function(v) {\n        this._sides = v;\n        this._flagSides = true;\n      }\n    }\n  };\n\n  // src/text.js\n  var canvas;\n  var min2 = Math.min;\n  var max2 = Math.max;\n  if (root.document) {\n    canvas = document.createElement(\"canvas\");\n  }\n  var Text = class _Text extends Shape {\n    /**\n     * @name Two.Text#_flagValue\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#value} need updating.\n     */\n    _flagValue = true;\n    /**\n     * @name Two.Text#_flagFamily\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#family} need updating.\n     */\n    _flagFamily = true;\n    /**\n     * @name Two.Text#_flagSize\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#size} need updating.\n     */\n    _flagSize = true;\n    /**\n     * @name Two.Text#_flagLeading\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#leading} need updating.\n     */\n    _flagLeading = true;\n    /**\n     * @name Two.Text#_flagAlignment\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#alignment} need updating.\n     */\n    _flagAlignment = true;\n    /**\n     * @name Two.Text#_flagBaseline\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#baseline} need updating.\n     */\n    _flagBaseline = true;\n    /**\n     * @name Two.Text#_flagStyle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#style} need updating.\n     */\n    _flagStyle = true;\n    /**\n     * @name Two.Text#_flagWeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#weight} need updating.\n     */\n    _flagWeight = true;\n    /**\n     * @name Two.Text#_flagDecoration\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#decoration} need updating.\n     */\n    _flagDecoration = true;\n    /**\n     * @name Two.Text#_flagFill\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#fill} need updating.\n     */\n    _flagFill = true;\n    /**\n     * @name Two.Text#_flagStroke\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#stroke} need updating.\n     */\n    _flagStroke = true;\n    /**\n     * @name Two.Text#_flagLinewidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#linewidth} need updating.\n     */\n    _flagLinewidth = true;\n    /**\n     * @name Two.Text#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#opacity} need updating.\n     */\n    _flagOpacity = true;\n    /**\n     * @name Two.Text#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#visible} need updating.\n     */\n    _flagVisible = true;\n    /**\n     * @name Two.Text#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#mask} needs updating.\n     */\n    _flagMask = false;\n    /**\n     * @name Two.Text#_flagClip\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#clip} needs updating.\n     */\n    _flagClip = false;\n    /**\n     * @name Two.Text#_flagDirection\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#direction} needs updating.\n     */\n    _flagDirection = true;\n    /**\n     * @name Two.Text#_flagStrokeAttenuation\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#strokeAttenuation} needs updating.\n     */\n    _flagStrokeAttenuation = true;\n    // Underlying Properties\n    /**\n     * @name Two.Text#value\n     * @property {String} - The characters to be rendered to the the screen. Referred to in the documentation sometimes as the `message`.\n     */\n    _value = \"\";\n    /**\n     * @name Two.Text#family\n     * @property {String} - The font family Two.js should attempt to register for rendering. The default value is `'sans-serif'`. Comma separated font names can be supplied as a \"stack\", similar to the CSS implementation of `font-family`.\n     */\n    _family = \"sans-serif\";\n    /**\n     * @name Two.Text#size\n     * @property {Number} - The font size in Two.js point space. Defaults to `13`.\n     */\n    _size = 13;\n    /**\n     * @name Two.Text#leading\n     * @property {Number} - The height between lines measured from base to base in Two.js point space. Defaults to `17`.\n     */\n    _leading = 17;\n    /**\n     * @name Two.Text#alignment\n     * @property {String} - Alignment of text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'left'`, `'center'`, `'right'`. Defaults to `'center'`.\n     */\n    _alignment = \"center\";\n    /**\n     * @name Two.Text#baseline\n     * @property {String} - The vertical aligment of the text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'top'`, `'middle'`, `'bottom'`, and `'baseline'`. Defaults to `'baseline'`.\n     * @nota-bene In headless environments where the canvas is based on {@link https://github.com/Automattic/node-canvas}, `baseline` seems to be the only valid property.\n     */\n    _baseline = \"middle\";\n    /**\n     * @name Two.Text#style\n     * @property {String} - The font's style. Possible values include '`normal`', `'italic'`. Defaults to `'normal'`.\n     */\n    _style = \"normal\";\n    /**\n     * @name Two.Text#weight\n     * @property {Number} - A number at intervals of 100 to describe the font's weight. This compatibility varies with the typeface's variant weights. Larger values are bolder. Smaller values are thinner. Defaults to `'500'`.\n     */\n    _weight = 500;\n    /**\n     * @name Two.Text#decoration\n     * @property {String} - String to delineate whether text should be decorated with for instance an `'underline'`. Defaults to `'none'`.\n     */\n    _decoration = \"none\";\n    /**\n     * @name Two.Text#direction\n     * @property {String} - String to determine what direction the text should run. Possibly values are `'ltr'` for left-to-right and `'rtl'` for right-to-left. Defaults to `'ltr'`.\n     */\n    _direction = \"ltr\";\n    /**\n     * @name Two.Text#fill\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the text object should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    _fill = \"#000\";\n    /**\n     * @name Two.Text#stroke\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the text object should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    _stroke = \"none\";\n    /**\n     * @name Two.Text#linewidth\n     * @property {Number} - The thickness in pixels of the stroke.\n     */\n    _linewidth = 1;\n    /**\n     * @name Two.Text#opacity\n     * @property {Number} - The opaqueness of the text object.\n     * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n     */\n    _opacity = 1;\n    /**\n     * @name Two.Text#visible\n     * @property {Boolean} - Display the text object or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    _visible = true;\n    /**\n     * @name Two.Text#mask\n     * @property {Two.Shape} - The shape whose alpha property becomes a clipping area for the text.\n     * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n     */\n    _mask = null;\n    /**\n     * @name Two.Text#clip\n     * @property {Two.Shape} - Object to define clipping area.\n     * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n     */\n    _clip = false;\n    /**\n     * @name Two.Text#_dashes\n     * @private\n     * @see {@link Two.Text#dashes}\n     */\n    _dashes = null;\n    /**\n     * @name Two.Text#_strokeAttenuation\n     * @private\n     * @see {@link Two.Text#strokeAttenuation}\n     */\n    _strokeAttenuation = true;\n    constructor(message, x, y, styles) {\n      super();\n      for (let prop in proto20) {\n        Object.defineProperty(this, prop, proto20[prop]);\n      }\n      this._renderer.type = \"text\";\n      this._renderer.flagFill = FlagFill2.bind(this);\n      this._renderer.flagStroke = FlagStroke2.bind(this);\n      this.value = message;\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n      this.dashes = [];\n      this.dashes.offset = 0;\n      if (!_.isObject(styles)) {\n        return this;\n      }\n      for (let i = 0; i < _Text.Properties.length; i++) {\n        const property = _Text.Properties[i];\n        if (property in styles) {\n          this[property] = styles[property];\n        }\n      }\n    }\n    /**\n     * @name Two.Text.Ratio\n     * @property {Number} - Approximate aspect ratio of a typeface's character width to height.\n     */\n    static Ratio = 0.6;\n    /**\n     * @name Two.Text.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Text}.\n     */\n    static Properties = [\n      \"value\",\n      \"family\",\n      \"size\",\n      \"leading\",\n      \"alignment\",\n      \"linewidth\",\n      \"style\",\n      \"weight\",\n      \"decoration\",\n      \"direction\",\n      \"baseline\",\n      \"opacity\",\n      \"visible\",\n      \"fill\",\n      \"stroke\",\n      \"dashes\",\n      \"strokeAttenuation\"\n    ];\n    /**\n     *\n     * @name Two.Measure\n     * @function\n     * @param {Two.Text} [text] - The instance of {@link Two.Text} to measure.\n     * @returns {Object} - The width and height of the {@link Two.Text} instance.\n     */\n    static Measure(text) {\n      if (canvas) {\n        const ctx = canvas.getContext(\"2d\");\n        ctx.font = [\n          text._style,\n          text._weight,\n          `${text._size}px/${text._leading}px`,\n          text._family\n        ].join(\" \");\n        const metrics = ctx.measureText(text.value, 0, 0);\n        const height = metrics.actualBoundingBoxDescent + metrics.actualBoundingBoxAscent;\n        return {\n          width: metrics.width,\n          height\n        };\n      } else {\n        const width = this.value.length * this.size * _Text.Ratio;\n        const height = this.leading;\n        console.warn(\n          \"Two.Text: unable to accurately measure text, so using an approximation.\"\n        );\n        return {\n          width,\n          height\n        };\n      }\n    }\n    /**\n     * @name Two.Text.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Text} to create a new instance\n     * @returns {Two.Text}\n     * @description Create a new {@link Two.Text} from an object notation of a {@link Two.Text}.\n     * @nota-bene Works in conjunction with {@link Two.Text#toObject}\n     */\n    static fromObject(obj) {\n      const fill = typeof obj.fill === \"string\" ? obj.fill : getEffectFromObject(obj.fill);\n      const stroke = typeof obj.stroke === \"string\" ? obj.stroke : getEffectFromObject(obj.stroke);\n      const text = new _Text().copy({ ...obj, fill, stroke });\n      if (\"id\" in obj) {\n        text.id = obj.id;\n      }\n      return text;\n    }\n    /**\n     * @name Two.Text#copy\n     * @function\n     * @param {Two.Text} text\n     * @description Copy the properties of one {@link Two.Text} onto another.\n     */\n    copy(text) {\n      super.copy.call(this, text);\n      for (let i = 0; i < _Text.Properties.length; i++) {\n        const k = _Text.Properties[i];\n        if (k in text) {\n          this[k] = text[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Text#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Text}\n     * @description Create a new instance of {@link Two.Text} with the same properties of the current text object.\n     */\n    clone(parent) {\n      const clone = new _Text(this.value);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      for (let i = 0; i < _Text.Properties.length; i++) {\n        const prop = _Text.Properties[i];\n        clone[prop] = this[prop];\n      }\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    /**\n     * @name Two.Text#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the text object.\n     * @nota-bene Works in conjunction with {@link Two.Text.fromObject}\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.renderer.type = \"text\";\n      for (let i = 0; i < _Text.Properties.length; i++) {\n        const prop = _Text.Properties[i];\n        result[prop] = this[prop];\n      }\n      return result;\n    }\n    /**\n     * @name Two.Text#dispose\n     * @function\n     * @returns {Two.Text}\n     * @description Release the text's renderer resources and detach all events.\n     * This method disposes fill and stroke effects (calling dispose() on\n     * Gradients and Textures for thorough cleanup) while preserving the\n     * renderer type for potential re-attachment to a new renderer.\n     */\n    dispose() {\n      super.dispose();\n      if (typeof this.fill === \"object\" && typeof this.fill.dispose === \"function\") {\n        this.fill.dispose();\n      } else if (typeof this.fill === \"object\" && typeof this.fill.unbind === \"function\") {\n        this.fill.unbind();\n      }\n      if (typeof this.stroke === \"object\" && typeof this.stroke.dispose === \"function\") {\n        this.stroke.dispose();\n      } else if (typeof this.stroke === \"object\" && typeof this.stroke.unbind === \"function\") {\n        this.stroke.unbind();\n      }\n      return this;\n    }\n    /**\n     * @name Two.Text#noFill\n     * @function\n     * @description Short hand method to set fill to `none`.\n     */\n    noFill() {\n      this.fill = \"none\";\n      return this;\n    }\n    /**\n     * @name Two.Text#noStroke\n     * @function\n     * @description Short hand method to set stroke to `none`.\n     */\n    noStroke() {\n      this.stroke = \"none\";\n      this.linewidth = 0;\n      return this;\n    }\n    // A shim to not break `getBoundingClientRect` calls.\n    // TODO: Implement a way to calculate proper bounding\n    // boxes of `Two.Text`.\n    /**\n     * @name Two.Text#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the text object.\n     */\n    getBoundingClientRect(shallow) {\n      let matrix;\n      let left, right, top, bottom;\n      this._update(true);\n      matrix = shallow ? this.matrix : this.worldMatrix;\n      const { width, height } = _Text.Measure(this);\n      const border = (this._linewidth || 0) / 2;\n      switch (this.alignment) {\n        case \"left\":\n          left = -border;\n          right = width + border;\n          break;\n        case \"right\":\n          left = -(width + border);\n          right = border;\n          break;\n        default:\n          left = -(width / 2 + border);\n          right = width / 2 + border;\n      }\n      switch (this.baseline) {\n        case \"middle\":\n          top = -(height / 2 + border);\n          bottom = height / 2 + border;\n          break;\n        default:\n          top = -(height + border);\n          bottom = border;\n      }\n      const [ax, ay] = matrix.multiply(left, top);\n      const [bx, by] = matrix.multiply(left, bottom);\n      const [cx, cy] = matrix.multiply(right, top);\n      const [dx, dy] = matrix.multiply(right, bottom);\n      top = min2(ay, by, cy, dy);\n      left = min2(ax, bx, cx, dx);\n      right = max2(ax, bx, cx, dx);\n      bottom = max2(ay, by, cy, dy);\n      return {\n        top,\n        left,\n        right,\n        bottom,\n        width: right - left,\n        height: bottom - top\n      };\n    }\n    /**\n     * @name Two.Text#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      super.flagReset.call(this);\n      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;\n      return this;\n    }\n  };\n  var proto20 = {\n    value: {\n      enumerable: true,\n      get: function() {\n        return this._value;\n      },\n      set: function(v) {\n        this._value = v;\n        this._flagValue = true;\n      }\n    },\n    family: {\n      enumerable: true,\n      get: function() {\n        return this._family;\n      },\n      set: function(v) {\n        this._family = v;\n        this._flagFamily = true;\n      }\n    },\n    size: {\n      enumerable: true,\n      get: function() {\n        return this._size;\n      },\n      set: function(v) {\n        this._size = v;\n        this._flagSize = true;\n      }\n    },\n    leading: {\n      enumerable: true,\n      get: function() {\n        return this._leading;\n      },\n      set: function(v) {\n        this._leading = v;\n        this._flagLeading = true;\n      }\n    },\n    alignment: {\n      enumerable: true,\n      get: function() {\n        return this._alignment;\n      },\n      set: function(v) {\n        this._alignment = v;\n        this._flagAlignment = true;\n      }\n    },\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        this._flagLinewidth = true;\n      }\n    },\n    style: {\n      enumerable: true,\n      get: function() {\n        return this._style;\n      },\n      set: function(v) {\n        this._style = v;\n        this._flagStyle = true;\n      }\n    },\n    weight: {\n      enumerable: true,\n      get: function() {\n        return this._weight;\n      },\n      set: function(v) {\n        this._weight = v;\n        this._flagWeight = true;\n      }\n    },\n    decoration: {\n      enumerable: true,\n      get: function() {\n        return this._decoration;\n      },\n      set: function(v) {\n        this._decoration = v;\n        this._flagDecoration = true;\n      }\n    },\n    direction: {\n      enumerable: true,\n      get: function() {\n        return this._direction;\n      },\n      set: function(v) {\n        this._direction = v;\n        this._flagDirection = true;\n      }\n    },\n    baseline: {\n      enumerable: true,\n      get: function() {\n        return this._baseline;\n      },\n      set: function(v) {\n        this._baseline = v;\n        this._flagBaseline = true;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n      }\n    },\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._visible = v;\n        this._flagVisible = true;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(f) {\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n        }\n        this._fill = f;\n        this._flagFill = true;\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.bind(Events.Types.change, this._renderer.flagFill);\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(f) {\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n        }\n        this._stroke = f;\n        this._flagStroke = true;\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n        }\n      }\n    },\n    mask: {\n      enumerable: true,\n      get: function() {\n        return this._mask;\n      },\n      set: function(v) {\n        this._mask = v;\n        this._flagMask = true;\n        if (_.isObject(v) && !v.clip) {\n          v.clip = true;\n        }\n      }\n    },\n    clip: {\n      enumerable: true,\n      get: function() {\n        return this._clip;\n      },\n      set: function(v) {\n        this._clip = v;\n        this._flagClip = true;\n      }\n    },\n    dashes: {\n      enumerable: true,\n      get: function() {\n        return this._dashes;\n      },\n      set: function(v) {\n        if (typeof v.offset !== \"number\") {\n          v.offset = this.dashes && this._dashes.offset || 0;\n        }\n        this._dashes = v;\n      }\n    },\n    /**\n     * @name Two.Text#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n     * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n     */\n    strokeAttenuation: {\n      enumerable: true,\n      get: function() {\n        return this._strokeAttenuation;\n      },\n      set: function(v) {\n        this._strokeAttenuation = !!v;\n        this._flagStrokeAttenuation = true;\n        this._flagLinewidth = true;\n      }\n    }\n  };\n  function FlagFill2() {\n    this._flagFill = true;\n  }\n  function FlagStroke2() {\n    this._flagStroke = true;\n  }\n\n  // src/effects/image-sequence.js\n  var ImageSequence = class _ImageSequence extends Rectangle {\n    /**\n     * @name Two.ImageSequence#_flagTextures\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#textures} need updating.\n     */\n    _flagTextures = false;\n    /**\n     * @name Two.ImageSequence#_flagFrameRate\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#frameRate} needs updating.\n     */\n    _flagFrameRate = false;\n    /**\n     * @name Two.ImageSequence#_flagIndex\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#index} needs updating.\n     */\n    _flagIndex = false;\n    // Private variables\n    /**\n     * @name Two.ImageSequence#_amount\n     * @private\n     * @property {Number} - Number of frames for a given {@link Two.ImageSequence}.\n     */\n    _amount = 1;\n    /**\n     * @name Two.ImageSequence#_duration\n     * @private\n     * @property {Number} - Number of milliseconds a {@link Two.ImageSequence}.\n     */\n    _duration = 0;\n    /**\n     * @name Two.ImageSequence#_index\n     * @private\n     * @property {Number} - The current frame the {@link Two.ImageSequence} is currently displaying.\n     */\n    _index = 0;\n    /**\n     * @name Two.ImageSequence#_startTime\n     * @private\n     * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.ImageSequence} started.\n     */\n    _startTime = 0;\n    /**\n     * @name Two.ImageSequence#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} is animating or not.\n     */\n    _playing = false;\n    /**\n     * @name Two.ImageSequence#_firstFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.ImageSequence} should start with.\n     */\n    _firstFrame = 0;\n    /**\n     * @name Two.ImageSequence#_lastFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.ImageSequence} should end with.\n     */\n    _lastFrame = 0;\n    /**\n     * @name Two.ImageSequence#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} should loop or not.\n     */\n    _loop = true;\n    // Exposed through getter-setter\n    /**\n     * @name Two.ImageSequence#_textures\n     * @private\n     * @see {@link Two.ImageSequence#textures}\n     */\n    _textures = null;\n    /**\n     * @name Two.ImageSequence#_frameRate\n     * @private\n     * @see {@link Two.ImageSequence#frameRate}\n     */\n    _frameRate = 0;\n    /**\n     * @name Two.ImageSequence#_origin\n     * @private\n     * @see {@link Two.ImageSequence#origin}\n     */\n    _origin = null;\n    constructor(src, ox, oy, frameRate) {\n      super(ox, oy, 0, 0);\n      this._renderer.type = \"image-sequence\";\n      for (let prop in proto21) {\n        Object.defineProperty(this, prop, proto21[prop]);\n      }\n      this._renderer.flagTextures = FlagTextures.bind(this);\n      this._renderer.bindTextures = BindTextures.bind(this);\n      this._renderer.unbindTextures = UnbindTextures.bind(this);\n      this.noStroke();\n      this.noFill();\n      if (Array.isArray(src)) {\n        this.textures = src.map(GenerateTexture.bind(this));\n      } else if (typeof src === \"string\") {\n        this.textures = [GenerateTexture(src)];\n      }\n      this.origin = new Vector();\n      this._update();\n      if (typeof frameRate === \"number\") {\n        this.frameRate = frameRate;\n      } else {\n        this.frameRate = _ImageSequence.DefaultFrameRate;\n      }\n      this.index = 0;\n    }\n    /**\n     * @name Two.ImageSequence.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.ImageSequence}.\n     */\n    static Properties = [\n      \"textures\",\n      \"frameRate\",\n      \"index\",\n      \"firstFrame\",\n      \"lastFrame\",\n      \"loop\"\n    ];\n    /**\n     * @name Two.ImageSequence.DefaultFrameRate\n     * @property The default frame rate that {@link Two.ImageSequence#frameRate} is set to when instantiated.\n     */\n    static DefaultFrameRate = 30;\n    /**\n     * @name Two.ImageSequence.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.ImageSequence} to create a new instance\n     * @returns {Two.ImageSequence}\n     * @description Create a new {@link Two.ImageSequence} from an object notation of a {@link Two.ImageSequence}.\n     * @nota-bene Works in conjunction with {@link Two.ImageSequence#toObject}\n     */\n    static fromObject(obj) {\n      const sequence = new _ImageSequence().copy(obj);\n      if (\"id\" in obj) {\n        sequence.id = obj.id;\n      }\n      return sequence;\n    }\n    /**\n     * @name Two.ImageSequence#copy\n     * @function\n     * @param {Two.ImageSequence} imageSequence - The reference {@link Two.ImageSequence}\n     * @description Copy the properties of one {@link Two.ImageSequence} onto another.\n     */\n    copy(imageSequence) {\n      super.copy.call(this, imageSequence);\n      for (let i = 0; i < _ImageSequence.Properties.length; i++) {\n        const k = _ImageSequence.Properties[i];\n        if (k in imageSequence) {\n          this[k] = imageSequence[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.ImageSequence#play\n     * @function\n     * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n     * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.ImageSequence#textures}.\n     * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the image sequence is looped.\n     * @description Initiate animation playback of a {@link Two.ImageSequence}.\n     */\n    play(firstFrame, lastFrame, onLastFrame) {\n      this._playing = true;\n      this._firstFrame = 0;\n      this._lastFrame = this.amount - 1;\n      this._startTime = _.performance.now();\n      if (typeof firstFrame === \"number\") {\n        this._firstFrame = firstFrame;\n      }\n      if (typeof lastFrame === \"number\") {\n        this._lastFrame = lastFrame;\n      }\n      if (typeof onLastFrame === \"function\") {\n        this._onLastFrame = onLastFrame;\n      } else {\n        delete this._onLastFrame;\n      }\n      if (this._index !== this._firstFrame) {\n        this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate;\n      }\n      return this;\n    }\n    /**\n     * @name Two.ImageSequence#pause\n     * @function\n     * @description Halt animation playback of a {@link Two.ImageSequence}.\n     */\n    pause() {\n      this._playing = false;\n      return this;\n    }\n    /**\n     * @name Two.ImageSequence#stop\n     * @function\n     * @description Halt animation playback of a {@link Two.ImageSequence} and set the current frame back to the first frame.\n     */\n    stop() {\n      this._playing = false;\n      this._index = this._firstFrame;\n      return this;\n    }\n    /**\n     * @name Two.ImageSequence#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.ImageSequence}\n     * @description Create a new instance of {@link Two.ImageSequence} with the same properties of the current image sequence.\n     */\n    clone(parent) {\n      const clone = new _ImageSequence(\n        this.textures,\n        this.translation.x,\n        this.translation.y,\n        this.frameRate\n      );\n      clone._loop = this._loop;\n      if (this._playing) {\n        clone.play();\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.ImageSequence#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"image-sequence\";\n      object.textures = this.textures.map(function(texture) {\n        return texture.toObject();\n      });\n      object.frameRate = this.frameRate;\n      object.index = this.index;\n      object.firstFrame = this.firstFrame;\n      object.lastFrame = this.lastFrame;\n      object.loop = this.loop;\n      return object;\n    }\n    /**\n     * @name Two.ImageSequence#dispose\n     * @function\n     * @returns {Two.ImageSequence}\n     * @description Release the image sequence's renderer resources and detach all events.\n     * This method stops any running animation, clears animation callbacks, unbinds\n     * textures collection events, and disposes individual textures (calling dispose()\n     * for thorough cleanup) while preserving the renderer type for potential\n     * re-attachment to a new renderer.\n     */\n    dispose() {\n      super.dispose();\n      if (this._playing) {\n        this._playing = false;\n      }\n      this._onLastFrame = null;\n      if (this.textures && typeof this.textures.unbind === \"function\") {\n        try {\n          this.textures.unbind();\n        } catch (e) {\n        }\n      }\n      if (this.textures) {\n        for (let i = 0; i < this.textures.length; i++) {\n          const texture = this.textures[i];\n          if (typeof texture.dispose === \"function\") {\n            texture.dispose();\n          } else if (typeof texture.unbind === \"function\") {\n            texture.unbind();\n          }\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.ImageSequence#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      const effect = this._textures;\n      let width, height, elapsed, amount, duration, texture;\n      let index, frames;\n      if (effect) {\n        if (this._flagTextures) {\n          this._amount = effect.length;\n        }\n        if (this._flagFrameRate) {\n          this._duration = 1e3 * this._amount / this._frameRate;\n        }\n        if (this._playing && this._frameRate > 0) {\n          amount = this._amount;\n          if (_.isNaN(this._lastFrame)) {\n            this._lastFrame = amount - 1;\n          }\n          elapsed = _.performance.now() - this._startTime;\n          frames = this._lastFrame + 1;\n          duration = 1e3 * (frames - this._firstFrame) / this._frameRate;\n          if (this._loop) {\n            elapsed = elapsed % duration;\n          } else {\n            elapsed = Math.min(elapsed, duration);\n          }\n          index = lerp(this._firstFrame, frames, elapsed / duration);\n          index = Math.floor(index);\n          if (index !== this._index) {\n            this._index = index;\n            texture = effect[this._index];\n            if (texture.loaded) {\n              width = texture.image.width;\n              height = texture.image.height;\n              if (this.width !== width) {\n                this.width = width;\n              }\n              if (this.height !== height) {\n                this.height = height;\n              }\n              this.fill = texture;\n              if (index >= this._lastFrame - 1 && this._onLastFrame) {\n                this._onLastFrame();\n              }\n            }\n          }\n        } else if (this._flagIndex || !(this.fill instanceof Texture)) {\n          texture = effect[this._index];\n          if (texture.loaded) {\n            width = texture.image.width;\n            height = texture.image.height;\n            if (this.width !== width) {\n              this.width = width;\n            }\n            if (this.height !== height) {\n              this.height = height;\n            }\n          }\n          this.fill = texture;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.ImageSequence#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      this._flagTextures = this._flagFrameRate = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto21 = {\n    frameRate: {\n      enumerable: true,\n      get: function() {\n        return this._frameRate;\n      },\n      set: function(v) {\n        this._frameRate = v;\n        this._flagFrameRate = true;\n      }\n    },\n    index: {\n      enumerable: true,\n      get: function() {\n        return this._index;\n      },\n      set: function(v) {\n        this._index = v;\n        this._flagIndex = true;\n      }\n    },\n    textures: {\n      enumerable: true,\n      get: function() {\n        return this._textures;\n      },\n      set: function(textures) {\n        const bindTextures = this._renderer.bindTextures;\n        const unbindTextures = this._renderer.unbindTextures;\n        if (this._textures) {\n          this._textures.unbind(Events.Types.insert, bindTextures).unbind(Events.Types.remove, unbindTextures);\n        }\n        this._textures = new Collection((textures || []).slice(0));\n        this._textures.bind(Events.Types.insert, bindTextures).bind(Events.Types.remove, unbindTextures);\n        bindTextures(this._textures);\n      }\n    },\n    firstFrame: {\n      enumerable: true,\n      get: function() {\n        return this._firstFrame;\n      },\n      set: function(v) {\n        this._firstFrame = v;\n      }\n    },\n    lastFrame: {\n      enumerable: true,\n      get: function() {\n        return this._lastFrame;\n      },\n      set: function(v) {\n        this._lastFrame = v;\n      }\n    },\n    loop: {\n      enumerable: true,\n      get: function() {\n        return this._loop;\n      },\n      set: function(v) {\n        this._loop = !!v;\n      }\n    }\n  };\n  function FlagTextures() {\n    this._flagTextures = true;\n  }\n  function BindTextures(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].bind(Events.Types.change, this._renderer.flagTextures);\n    }\n    this._renderer.flagTextures();\n  }\n  function UnbindTextures(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].unbind(Events.Types.change, this._renderer.flagTextures);\n    }\n    this._renderer.flagTextures();\n  }\n  function GenerateTexture(obj) {\n    if (obj instanceof Texture) {\n      return obj;\n    } else if (typeof obj === \"string\") {\n      return new Texture(obj);\n    }\n  }\n\n  // src/group.js\n  var min3 = Math.min;\n  var max3 = Math.max;\n  var cache = {\n    getShapesAtPoint: {\n      results: [],\n      hitOptions: {},\n      context: {\n        x: 0,\n        y: 0,\n        visibleOnly: true,\n        results: null\n      },\n      single: [],\n      output: [],\n      empty: []\n    }\n  };\n  var Group = class _Group extends Shape {\n    /**\n     * @name Two.Group#_flagAdditions\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#additions} needs updating.\n     */\n    _flagAdditions = false;\n    /**\n     * @name Two.Group#_flagSubtractions\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#subtractions} needs updating.\n     */\n    _flagSubtractions = false;\n    /**\n     * @name Two.Group#_flagOrder\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#order} needs updating.\n     */\n    _flagOrder = false;\n    /**\n     * @name Two.Group#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#visible} needs updating.\n     */\n    /**\n     * @name Two.Group#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#opacity} needs updating.\n     */\n    _flagOpacity = true;\n    /**\n     * @name Two.Group#_flagBeginning\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#beginning} needs updating.\n     */\n    _flagBeginning = false;\n    /**\n     * @name Two.Group#_flagEnding\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#ending} needs updating.\n     */\n    _flagEnding = false;\n    /**\n     * @name Two.Group#_flagLength\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#length} needs updating.\n     */\n    _flagLength = false;\n    /**\n     * @name Two.Group#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#mask} needs updating.\n     */\n    _flagMask = false;\n    // Underlying Properties\n    /**\n     * @name Two.Group#fill\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what all child shapes should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    _fill = \"#fff\";\n    /**\n     * @name Two.Group#stroke\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what all child shapes should be outlined in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    _stroke = \"#000\";\n    /**\n     * @name Two.Group#linewidth\n     * @property {Number} - The thickness in pixels of the stroke for all child shapes.\n     */\n    _linewidth = 1;\n    /**\n     * @name Two.Group#opacity\n     * @property {Number} - The opaqueness of all child shapes.\n     * @nota-bene Becomes multiplied by the individual child's opacity property.\n     */\n    _opacity = 1;\n    /**\n     * @name Two.Group#visible\n     * @property {Boolean} - Display the path or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    _visible = true;\n    /**\n     * @name Two.Group#cap\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty}\n     */\n    _cap = \"round\";\n    /**\n     * @name Two.Group#join\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty}\n     */\n    _join = \"round\";\n    /**\n     * @name Two.Group#miter\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty}\n     */\n    _miter = 4;\n    /**\n     * @name Two.Group#closed\n     * @property {Boolean} - Determines whether a final line is drawn between the final point in the `vertices` array and the first point of all child shapes.\n     */\n    _closed = true;\n    /**\n     * @name Two.Group#curved\n     * @property {Boolean} - When the child's path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n     */\n    _curved = false;\n    /**\n     * @name Two.Group#automatic\n     * @property {Boolean} - Determines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n     */\n    _automatic = true;\n    /**\n     * @name Two.Group#beginning\n     * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n     * @description {@link Two.Group#beginning} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#ending}.\n     */\n    _beginning = 0;\n    /**\n     * @name Two.Group#ending\n     * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n     * @description {@link Two.Group#ending} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#beginning}.\n     */\n    _ending = 1;\n    /**\n     * @name Two.Group#length\n     * @property {Number} - The sum of distances between all child lengths.\n     */\n    _length = 0;\n    /**\n     * @name Two.Group#mask\n     * @property {Two.Shape} - The Two.js object to clip from a group's rendering.\n     */\n    _mask = null;\n    /**\n     * @name Two.Group#_strokeAttenuation\n     * @private\n     * @see {@link Two.Group#strokeAttenuation}\n     */\n    _strokeAttenuation = true;\n    constructor(children) {\n      super();\n      for (let prop in proto22) {\n        Object.defineProperty(this, prop, proto22[prop]);\n      }\n      this._renderer.type = \"group\";\n      this.additions = [];\n      this.subtractions = [];\n      this.children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments);\n    }\n    static Children = Children;\n    /**\n     * @name Two.Group.InsertChildren\n     * @function\n     * @param {Two.Shape[]} children - The objects to be inserted.\n     * @description Cached method to let renderers know children have been added to a {@link Two.Group}.\n     */\n    static InsertChildren(children) {\n      for (let i = 0; i < children.length; i++) {\n        replaceParent.call(this, children[i], this);\n      }\n    }\n    /**\n     * @name Two.Group.RemoveChildren\n     * @function\n     * @param {Two.Shape[]} children - The objects to be removed.\n     * @description Cached method to let renderers know children have been removed from a {@link Two.Group}.\n     */\n    static RemoveChildren(children) {\n      for (let i = 0; i < children.length; i++) {\n        replaceParent.call(this, children[i]);\n      }\n    }\n    /**\n     * @name Two.Group.OrderChildren\n     * @function\n     * @description Cached method to let renderers know order has been updated on a {@link Two.Group}.\n     */\n    static OrderChildren(children) {\n      this._flagOrder = true;\n    }\n    /**\n     * @name Two.Group.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Group}.\n     */\n    static Properties = [\n      \"fill\",\n      \"stroke\",\n      \"linewidth\",\n      \"cap\",\n      \"join\",\n      \"miter\",\n      \"closed\",\n      \"curved\",\n      \"automatic\"\n    ];\n    static fromObject(obj) {\n      const group = new _Group();\n      for (let i = 0; i < _Group.Properties.length; i++) {\n        const k = _Group.Properties[i];\n        if (k in obj) {\n          if (/(fill|stroke)/i.test(k)) {\n            group[k] = typeof obj[k] === \"string\" ? obj[k] : getEffectFromObject(obj[k]);\n          } else {\n            group[k] = obj[k];\n          }\n        }\n      }\n      if (\"mask\" in obj) {\n        group.mask = getShapeFromObject(obj.mask);\n      }\n      if (\"id\" in obj) {\n        group.id = obj.id;\n      }\n      group.children = obj.children.map(getShapeFromObject);\n      return group;\n      function getShapeFromObject(child) {\n        if (child && child.renderer) {\n          switch (child.renderer.type) {\n            case \"arc-segment\":\n              return ArcSegment.fromObject(child);\n            case \"circle\":\n              return Circle.fromObject(child);\n            case \"element\":\n              return Element.fromObject(child);\n            case \"ellipse\":\n              return Ellipse.fromObject(child);\n            case \"group\":\n              return _Group.fromObject(child);\n            case \"image\":\n              return Image.fromObject(child);\n            case \"image-sequence\":\n              return ImageSequence.fromObject(child);\n            case \"path\":\n              return Path.fromObject(child);\n            case \"points\":\n              return Points.fromObject(child);\n            case \"polygon\":\n              return Polygon.fromObject(child);\n            case \"rectangle\":\n              return Rectangle.fromObject(child);\n            case \"rounded-rectangle\":\n              return RoundedRectangle.fromObject(child);\n            case \"shape\":\n              return Shape.fromObject(child);\n            case \"sprite\":\n              return Sprite.fromObject(child);\n            case \"star\":\n              return Star.fromObject(child);\n            case \"text\":\n              return Text.fromObject(child);\n          }\n        }\n        return child;\n      }\n    }\n    static IsVisible(element, visibleOnly) {\n      if (!visibleOnly) {\n        return true;\n      }\n      let current = element;\n      while (current) {\n        if (typeof current.visible === \"boolean\" && !current.visible) {\n          return false;\n        }\n        if (typeof current.opacity === \"number\" && current.opacity <= 0) {\n          return false;\n        }\n        current = current.parent;\n      }\n      return true;\n    }\n    static VisitForHitTest(group, context, includeGroups, filter, hitOptions, tolerance, stopOnFirst) {\n      const children = group && group.children;\n      if (!children) {\n        return false;\n      }\n      const results = context.results;\n      for (let i = children.length - 1; i >= 0; i--) {\n        const child = children[i];\n        if (!child) {\n          continue;\n        }\n        if (!_Group.IsVisible(child, context.visibleOnly)) {\n          continue;\n        }\n        const rect = typeof child.getBoundingClientRect === \"function\" ? child.getBoundingClientRect() : null;\n        if (rect && !boundsContains(rect, context.x, context.y, tolerance)) {\n          continue;\n        }\n        if (child instanceof _Group) {\n          if (includeGroups && (!filter || filter(child)) && typeof child.contains === \"function\" && child.contains(context.x, context.y, hitOptions)) {\n            results.push(child);\n            if (stopOnFirst) {\n              return true;\n            }\n          }\n          if (_Group.VisitForHitTest(\n            child,\n            context,\n            includeGroups,\n            filter,\n            hitOptions,\n            tolerance,\n            stopOnFirst\n          )) {\n            return true;\n          }\n          continue;\n        }\n        if (!(child instanceof Shape)) {\n          continue;\n        }\n        if (filter && !filter(child)) {\n          continue;\n        }\n        if (typeof child.contains !== \"function\") {\n          continue;\n        }\n        if (child.contains(context.x, context.y, hitOptions)) {\n          results.push(child);\n          if (stopOnFirst) {\n            return true;\n          }\n        }\n      }\n      return false;\n    }\n    /**\n     * @name Two.Group#copy\n     * @function\n     * @param {Two.Group} [group] - The reference {@link Two.Group}\n     * @returns {Two.Group}\n     * @description Copy the properties of one {@link Two.Group} onto another.\n     */\n    copy(group) {\n      super.copy.call(this, group);\n      console.warn(\n        \"Two.js: attempting to copy group. Two.Group.children copying not supported.\"\n      );\n      for (let i = 0; i < _Group.Properties.length; i++) {\n        const k = _Group.Properties[i];\n        if (k in group) {\n          this[k] = group[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Group#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Group}\n     * @description Create a new instance of {@link Two.Group} with the same properties of the current group.\n     */\n    clone(parent) {\n      const clone = new _Group();\n      const children = this.children.map(function(child) {\n        return child.clone();\n      });\n      clone.add(children);\n      clone.opacity = this.opacity;\n      if (this.mask) {\n        clone.mask = this.mask;\n      }\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.className = this.className;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    /**\n     * @name Two.Group#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the group.\n     */\n    toObject() {\n      const result = super.toObject.call(this);\n      result.renderer.type = \"group\";\n      result.children = [];\n      result.opacity = this.opacity;\n      result.className = this.className;\n      result.mask = this.mask ? this.mask.toObject() : null;\n      _.each(\n        this.children,\n        (child, i) => {\n          result.children[i] = child.toObject();\n        },\n        this\n      );\n      return result;\n    }\n    /**\n     * @name Two.Group#dispose\n     * @function\n     * @returns {Two.Group}\n     * @description Release the group's renderer resources and detach all events.\n     * This method recursively disposes all child objects, unbinds the children\n     * collection events, and preserves the renderer type for potential re-attachment\n     * to a new renderer.\n     */\n    dispose() {\n      super.dispose();\n      if (this.children) {\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          if (typeof child.dispose === \"function\") {\n            child.dispose();\n          }\n        }\n      }\n      if (this.children && typeof this.children.unbind === \"function\") {\n        try {\n          this.children.unbind();\n        } catch (e) {\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Group#getShapesAtPoint\n     * @function\n     * @param {Number} x - X coordinate in world space.\n     * @param {Number} y - Y coordinate in world space.\n     * @param {Object} [options] - Hit test configuration.\n     * @param {Boolean} [options.visibleOnly=true] - Limit results to visible shapes.\n     * @param {Boolean} [options.includeGroups=false] - Include groups in the hit results.\n     * @param {('all'|'deepest')} [options.mode='all'] - Whether to return all intersecting shapes or only the top-most.\n     * @param {Boolean} [options.deepest] - Alias for `mode: 'deepest'`.\n     * @param {Number} [options.precision] - Segmentation precision for curved geometry.\n     * @param {Number} [options.tolerance=0] - Pixel tolerance applied to hit testing.\n     * @param {Boolean} [options.fill] - Override fill testing behaviour.\n     * @param {Boolean} [options.stroke] - Override stroke testing behaviour.\n     * @param {Function} [options.filter] - Predicate to filter shapes from the result set.\n     * @returns {Shape[]} Ordered list of intersecting shapes, front to back.\n     * @description Traverse the group hierarchy and return shapes that contain the specified point.\n     * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n     */\n    getShapesAtPoint(x, y, options) {\n      const opts = options || {};\n      const { results, hitOptions, context, single, empty } = cache.getShapesAtPoint;\n      results.length = 0;\n      const mode = opts.mode === \"deepest\" || opts.deepest ? \"deepest\" : \"all\";\n      const visibleOnly = opts.visibleOnly !== false;\n      const includeGroups = !!opts.includeGroups;\n      const filter = typeof opts.filter === \"function\" ? opts.filter : null;\n      const tolerance = typeof opts.tolerance === \"number\" ? opts.tolerance : 0;\n      if (typeof opts.precision === \"number\") {\n        hitOptions.precision = opts.precision;\n      } else {\n        delete hitOptions.precision;\n      }\n      if (typeof opts.fill !== \"undefined\") {\n        hitOptions.fill = opts.fill;\n      } else {\n        delete hitOptions.fill;\n      }\n      if (typeof opts.stroke !== \"undefined\") {\n        hitOptions.stroke = opts.stroke;\n      } else {\n        delete hitOptions.stroke;\n      }\n      hitOptions.tolerance = tolerance;\n      hitOptions.ignoreVisibility = !visibleOnly;\n      const stopOnFirst = mode === \"deepest\";\n      context.x = x;\n      context.y = y;\n      context.visibleOnly = visibleOnly;\n      context.results = results;\n      _Group.VisitForHitTest(\n        this,\n        context,\n        includeGroups,\n        filter,\n        hitOptions,\n        tolerance,\n        stopOnFirst\n      );\n      if (stopOnFirst) {\n        if (results.length > 0) {\n          const first = results[0];\n          results.length = 0;\n          single[0] = first;\n          single.length = 1;\n          return single;\n        }\n        empty.length = 0;\n        return empty;\n      }\n      const hits = results.slice();\n      results.length = 0;\n      return hits;\n    }\n    /**\n     * @name Two.Group#corner\n     * @function\n     * @description Orient the children of the group to the upper left-hand corner of that group.\n     */\n    corner() {\n      const rect = this.getBoundingClientRect(true);\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.translation.x -= rect.left;\n        child.translation.y -= rect.top;\n      }\n      if (this.mask) {\n        this.mask.translation.x -= rect.left;\n        this.mask.translation.y -= rect.top;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Group#center\n     * @function\n     * @description Orient the children of the group to the center of that group.\n     */\n    center() {\n      const rect = this.getBoundingClientRect(true);\n      const cx = rect.left + rect.width / 2 - this.translation.x;\n      const cy = rect.top + rect.height / 2 - this.translation.y;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (child.isShape) {\n          child.translation.x -= cx;\n          child.translation.y -= cy;\n        }\n      }\n      if (this.mask) {\n        this.mask.translation.x -= cx;\n        this.mask.translation.y -= cy;\n      }\n      return this;\n    }\n    /**\n     * @name Two.Group#getById\n     * @function\n     * @description Recursively search for id. Returns the first element found.\n     * @returns {Two.Shape} - Or `null` if nothing is found.\n     */\n    getById(id) {\n      let found = null;\n      function search(node) {\n        if (node.id === id) {\n          return node;\n        } else if (node.children) {\n          if (node.children.ids[id]) {\n            return node.children.ids[id];\n          }\n          for (let i = 0; i < node.children.length; i++) {\n            found = search(node.children[i]);\n            if (found) {\n              return found;\n            }\n          }\n        }\n        return null;\n      }\n      return search(this);\n    }\n    /**\n     * @name Two.Group#getByClassName\n     * @function\n     * @description Recursively search for classes. Returns an array of matching elements.\n     * @returns {Two.Shape[]} - Or empty array if nothing is found.\n     */\n    getByClassName(className) {\n      const found = [];\n      function search(node) {\n        if (Array.prototype.indexOf.call(node.classList, className) >= 0) {\n          found.push(node);\n        }\n        if (node.children) {\n          for (let i = 0; i < node.children.length; i++) {\n            const child = node.children[i];\n            search(child);\n          }\n        }\n        return found;\n      }\n      return search(this);\n    }\n    /**\n     * @name Two.Group#getByType\n     * @function\n     * @description Recursively search for children of a specific type, e.g. {@link Two.Path}. Pass a reference to this type as the param. Returns an array of matching elements.\n     * @returns {Two.Shape[]} - Empty array if nothing is found.\n     */\n    getByType(type) {\n      const found = [];\n      function search(node) {\n        if (node instanceof type) {\n          found.push(node);\n        }\n        if (node.children) {\n          for (let i = 0; i < node.children.length; i++) {\n            const child = node.children[i];\n            search(child);\n          }\n        }\n        return found;\n      }\n      return search(this);\n    }\n    /**\n     * @name Two.Group#add\n     * @function\n     * @param {Two.Shape[]|...Two.Shape} objects - An array of objects to be added. Can also be supplied as individual arguments.\n     * @description Add objects to the group.\n     */\n    add(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      } else {\n        objects = objects.slice();\n      }\n      for (let i = 0; i < objects.length; i++) {\n        const child = objects[i];\n        if (!(child && child.id)) {\n          continue;\n        }\n        const index = Array.prototype.indexOf.call(this.children, child);\n        if (index >= 0) {\n          this.children.splice(index, 1);\n        }\n        this.children.push(child);\n      }\n      return this;\n    }\n    /**\n     * @name Two.Group#remove\n     * @function\n     * @param {Two.Shape[]|...Two.Shape} [objects=self] - An array of objects to be removed. Can be also removed as individual arguments. If no arguments are passed, then it removes itself from its parent.\n     * @description Remove objects from the group.\n     */\n    remove(objects) {\n      const l = arguments.length, grandparent = this.parent;\n      if (l <= 0 && grandparent) {\n        grandparent.remove(this);\n        return this;\n      }\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      } else {\n        objects = objects.slice();\n      }\n      for (let i = 0; i < objects.length; i++) {\n        const object = objects[i];\n        if (!object || !this.children.ids[object.id]) {\n          continue;\n        }\n        const index = this.children.indexOf(object);\n        if (index >= 0) {\n          this.children.splice(index, 1);\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Group#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the group.\n     */\n    getBoundingClientRect(shallow) {\n      let rect, matrix, tc, lc, rc, bc;\n      this._update(true);\n      let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity;\n      const regex3 = /texture|gradient/i;\n      matrix = shallow ? this.matrix : this.worldMatrix;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (!child.visible || regex3.test(child._renderer.type)) {\n          continue;\n        }\n        rect = child.getBoundingClientRect(shallow);\n        tc = typeof rect.top !== \"number\" || _.isNaN(rect.top) || !isFinite(rect.top);\n        lc = typeof rect.left !== \"number\" || _.isNaN(rect.left) || !isFinite(rect.left);\n        rc = typeof rect.right !== \"number\" || _.isNaN(rect.right) || !isFinite(rect.right);\n        bc = typeof rect.bottom !== \"number\" || _.isNaN(rect.bottom) || !isFinite(rect.bottom);\n        if (tc || lc || rc || bc) {\n          continue;\n        }\n        if (shallow) {\n          const [ax, ay] = matrix.multiply(rect.left, rect.top);\n          const [bx, by] = matrix.multiply(rect.right, rect.top);\n          const [cx, cy] = matrix.multiply(rect.left, rect.bottom);\n          const [dx, dy] = matrix.multiply(rect.right, rect.bottom);\n          top = min3(ay, by, cy, dy, top);\n          left = min3(ax, bx, cx, dx, left);\n          right = max3(ax, bx, cx, dx, right);\n          bottom = max3(ay, by, cy, dy, bottom);\n        } else {\n          top = min3(rect.top, top);\n          left = min3(rect.left, left);\n          right = max3(rect.right, right);\n          bottom = max3(rect.bottom, bottom);\n        }\n      }\n      return {\n        top,\n        left,\n        right,\n        bottom,\n        width: right - left,\n        height: bottom - top\n      };\n    }\n    /**\n     * @name Two.Group#noFill\n     * @function\n     * @description Apply `noFill` method to all child shapes.\n     */\n    noFill() {\n      this.children.forEach(function(child) {\n        child.noFill();\n      });\n      return this;\n    }\n    /**\n     * @name Two.Group#noStroke\n     * @function\n     * @description Apply `noStroke` method to all child shapes.\n     */\n    noStroke() {\n      this.children.forEach(function(child) {\n        child.noStroke();\n      });\n      return this;\n    }\n    /**\n     * @name Two.Group#subdivide\n     * @function\n     * @description Apply `subdivide` method to all child shapes.\n     */\n    subdivide() {\n      const args = arguments;\n      this.children.forEach(function(child) {\n        child.subdivide.apply(child, args);\n      });\n      return this;\n    }\n    /**\n     * @name Two.Group#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      let i, l, child;\n      if (this._flagBeginning || this._flagEnding) {\n        const beginning = Math.min(this._beginning, this._ending);\n        const ending = Math.max(this._beginning, this._ending);\n        const length = this.length;\n        let sum = 0;\n        const bd = beginning * length;\n        const ed = ending * length;\n        for (i = 0; i < this.children.length; i++) {\n          child = this.children[i];\n          l = child.length;\n          if (bd > sum + l) {\n            child.beginning = 1;\n            child.ending = 1;\n          } else if (ed < sum) {\n            child.beginning = 0;\n            child.ending = 0;\n          } else if (bd > sum && bd < sum + l) {\n            child.beginning = (bd - sum) / l;\n            child.ending = 1;\n          } else if (ed > sum && ed < sum + l) {\n            child.beginning = 0;\n            child.ending = (ed - sum) / l;\n          } else {\n            child.beginning = 0;\n            child.ending = 1;\n          }\n          sum += l;\n        }\n      }\n      return super._update.apply(this, arguments);\n    }\n    /**\n     * @name Two.Group#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      if (this._flagAdditions) {\n        this.additions.length = 0;\n        this._flagAdditions = false;\n      }\n      if (this._flagSubtractions) {\n        this.subtractions.length = 0;\n        this._flagSubtractions = false;\n      }\n      this._flagOrder = this._flagMask = this._flagOpacity = this._flagBeginning = this._flagEnding = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto22 = {\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._flagVisible = this._visible !== v || this._flagVisible;\n        this._visible = v;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._flagOpacity = this._opacity !== v || this._flagOpacity;\n        this._opacity = v;\n      }\n    },\n    beginning: {\n      enumerable: true,\n      get: function() {\n        return this._beginning;\n      },\n      set: function(v) {\n        this._flagBeginning = this._beginning !== v || this._flagBeginning;\n        this._beginning = v;\n      }\n    },\n    ending: {\n      enumerable: true,\n      get: function() {\n        return this._ending;\n      },\n      set: function(v) {\n        this._flagEnding = this._ending !== v || this._flagEnding;\n        this._ending = v;\n      }\n    },\n    length: {\n      enumerable: true,\n      get: function() {\n        if (this._flagLength || this._length <= 0) {\n          this._length = 0;\n          if (!this.children) {\n            return this._length;\n          }\n          for (let i = 0; i < this.children.length; i++) {\n            const child = this.children[i];\n            this._length += child.length;\n          }\n        }\n        return this._length;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(v) {\n        this._fill = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.fill = v;\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(v) {\n        this._stroke = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.stroke = v;\n        }\n      }\n    },\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.linewidth = v;\n        }\n      }\n    },\n    join: {\n      enumerable: true,\n      get: function() {\n        return this._join;\n      },\n      set: function(v) {\n        this._join = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.join = v;\n        }\n      }\n    },\n    miter: {\n      enumerable: true,\n      get: function() {\n        return this._miter;\n      },\n      set: function(v) {\n        this._miter = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.miter = v;\n        }\n      }\n    },\n    cap: {\n      enumerable: true,\n      get: function() {\n        return this._cap;\n      },\n      set: function(v) {\n        this._cap = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.cap = v;\n        }\n      }\n    },\n    closed: {\n      enumerable: true,\n      get: function() {\n        return this._closed;\n      },\n      set: function(v) {\n        this._closed = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.closed = v;\n        }\n      }\n    },\n    curved: {\n      enumerable: true,\n      get: function() {\n        return this._curved;\n      },\n      set: function(v) {\n        this._curved = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.curved = v;\n        }\n      }\n    },\n    automatic: {\n      enumerable: true,\n      get: function() {\n        return this._automatic;\n      },\n      set: function(v) {\n        this._automatic = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.automatic = v;\n        }\n      }\n    },\n    children: {\n      enumerable: true,\n      get: function() {\n        return this._children;\n      },\n      set: function(children) {\n        const insertChildren = Group.InsertChildren.bind(this);\n        const removeChildren = Group.RemoveChildren.bind(this);\n        const orderChildren = Group.OrderChildren.bind(this);\n        if (this._children) {\n          this._children.unbind();\n          if (this._children.length > 0) {\n            removeChildren(this._children);\n          }\n        }\n        this._children = new Children(children);\n        this._children.bind(Events.Types.insert, insertChildren);\n        this._children.bind(Events.Types.remove, removeChildren);\n        this._children.bind(Events.Types.order, orderChildren);\n        if (children.length > 0) {\n          insertChildren(children);\n        }\n      }\n    },\n    mask: {\n      enumerable: true,\n      get: function() {\n        return this._mask;\n      },\n      set: function(v) {\n        this._mask = v;\n        this._flagMask = true;\n        if (_.isObject(v) && !v.clip) {\n          v.clip = true;\n        }\n      }\n    },\n    /**\n     * @name Two.Group#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space for all child shapes.\n     * @description When `strokeAttenuation` is `false`, this property is applied to all child shapes, making their stroke widths automatically adjust to compensate for the group's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke widths scale normally with transformations.\n     */\n    strokeAttenuation: {\n      enumerable: true,\n      get: function() {\n        return this._strokeAttenuation;\n      },\n      set: function(v) {\n        this._strokeAttenuation = !!v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          if (child.strokeAttenuation !== void 0) {\n            child.strokeAttenuation = v;\n          }\n        }\n      }\n    }\n  };\n  function replaceParent(child, newParent) {\n    const parent = child.parent;\n    let index;\n    if (parent === newParent) {\n      add();\n      return;\n    }\n    if (parent && parent.children.ids[child.id]) {\n      index = Array.prototype.indexOf.call(parent.children, child);\n      parent.children.splice(index, 1);\n      splice();\n    }\n    if (newParent) {\n      add();\n      return;\n    }\n    splice();\n    if (parent._flagAdditions && parent.additions.length === 0) {\n      parent._flagAdditions = false;\n    }\n    if (parent._flagSubtractions && parent.subtractions.length === 0) {\n      parent._flagSubtractions = false;\n    }\n    delete child.parent;\n    function add() {\n      if (newParent.subtractions.length > 0) {\n        index = Array.prototype.indexOf.call(newParent.subtractions, child);\n        if (index >= 0) {\n          newParent.subtractions.splice(index, 1);\n        }\n      }\n      if (newParent.additions.length > 0) {\n        index = Array.prototype.indexOf.call(newParent.additions, child);\n        if (index >= 0) {\n          newParent.additions.splice(index, 1);\n        }\n      }\n      child.parent = newParent;\n      newParent.additions.push(child);\n      newParent._flagAdditions = true;\n    }\n    function splice() {\n      index = Array.prototype.indexOf.call(parent.additions, child);\n      if (index >= 0) {\n        parent.additions.splice(index, 1);\n      }\n      index = Array.prototype.indexOf.call(parent.subtractions, child);\n      if (index < 0) {\n        parent.subtractions.push(child);\n        parent._flagSubtractions = true;\n      }\n    }\n  }\n\n  // src/shapes/line.js\n  var Line = class extends Path {\n    constructor(x1, y1, x2, y2) {\n      const points = [new Anchor(x1, y1), new Anchor(x2, y2)];\n      super(points);\n      for (let prop in proto23) {\n        Object.defineProperty(this, prop, proto23[prop]);\n      }\n      this.vertices[0].command = Commands.move;\n      this.vertices[1].command = Commands.line;\n      this.automatic = false;\n    }\n    static Properties = [\"left\", \"right\"];\n  };\n  var proto23 = {\n    left: {\n      enumerable: true,\n      get: function() {\n        return this.vertices[0];\n      },\n      set: function(v) {\n        if (_.isObject(v)) {\n          this.vertices.splice(0, 1, v);\n          this.vertices[0].command = Commands.move;\n        } else {\n          const error = new TwoError(\"Two.Line.left argument is not an object.\");\n          console.warn(error.name, error.message);\n        }\n      }\n    },\n    right: {\n      enumerable: true,\n      get: function() {\n        return this.vertices[1];\n      },\n      set: function(v) {\n        if (_.isObject(v)) {\n          this.vertices.splice(1, 1, v);\n          this.vertices[1].command = Commands.line;\n        } else {\n          const error = new TwoError(\"Two.Line.right argument is not an object.\");\n          console.warn(error.name, error.message);\n        }\n      }\n    }\n  };\n\n  // src/utils/interpret-svg.js\n  var regex2 = {\n    path: /[+-]?(?:\\d*\\.\\d+|\\d+)(?:[eE][+-]\\d+)?/g,\n    cssBackgroundImage: /url\\(['\"]?#([\\w\\d-_]*)['\"]?\\)/i,\n    unitSuffix: /[a-zA-Z%]*/i\n  };\n  var alignments = {\n    start: \"left\",\n    middle: \"center\",\n    end: \"right\"\n  };\n  var reservedAttributesToRemove = [\n    \"id\",\n    \"class\",\n    \"transform\",\n    \"xmlns\",\n    \"viewBox\"\n  ];\n  var overwriteAttrs = [\"x\", \"y\", \"width\", \"height\", \"href\", \"xlink:href\"];\n  function getAlignment(anchor2) {\n    return alignments[anchor2];\n  }\n  function getBaseline(node) {\n    const a = node.getAttribute(\"dominant-baseline\");\n    const b = node.getAttribute(\"alignment-baseline\");\n    return a || b;\n  }\n  function getTagName(tag) {\n    return tag.replace(/svg:/gi, \"\").toLowerCase();\n  }\n  function applyTransformsToVector(transforms, vector3) {\n    vector3.x += transforms.translateX;\n    vector3.y += transforms.translateY;\n    vector3.x *= transforms.scaleX;\n    vector3.y *= transforms.scaleY;\n    if (transforms.rotation !== 0) {\n      const l = vector3.length();\n      vector3.x = l * Math.cos(transforms.rotation);\n      vector3.y = l * Math.sin(transforms.rotation);\n    }\n  }\n  function extractCSSText(text, styles) {\n    if (!styles) {\n      styles = {};\n    }\n    const commands = text.split(\";\");\n    for (let i = 0; i < commands.length; i++) {\n      const command = commands[i].split(\":\");\n      const name = command[0];\n      const value = command[1];\n      if (typeof name === \"undefined\" || typeof value === \"undefined\") {\n        continue;\n      }\n      const trimmedName = name.replace(/\\s/g, \"\");\n      const trimmedValue = value.replace(/\\s/g, \"\");\n      styles[trimmedName] = trimmedValue;\n    }\n    return styles;\n  }\n  function getSvgStyles(node) {\n    const styles = {};\n    const attributes = getSvgAttributes(node);\n    const length = Math.max(attributes.length, node.style.length);\n    for (let i = 0; i < length; i++) {\n      const command = node.style[i];\n      const attribute = attributes[i];\n      if (command) {\n        styles[command] = node.style[command];\n      }\n      if (attribute) {\n        styles[attribute] = node.getAttribute(attribute);\n      }\n    }\n    return styles;\n  }\n  function getSvgAttributes(node) {\n    const attributes = node.getAttributeNames();\n    for (let i = 0; i < reservedAttributesToRemove.length; i++) {\n      const keyword = reservedAttributesToRemove[i];\n      const index = Array.prototype.indexOf.call(attributes, keyword);\n      if (index >= 0) {\n        attributes.splice(index, 1);\n      }\n    }\n    return attributes;\n  }\n  function applySvgViewBox(node, value) {\n    const elements = value.split(/[\\s,]/);\n    const x = -parseFloat(elements[0]);\n    const y = -parseFloat(elements[1]);\n    const width = parseFloat(elements[2]);\n    const height = parseFloat(elements[3]);\n    if (x && y) {\n      for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        if (\"translation\" in child) {\n          child.translation.add(x, y);\n        } else if (\"x\" in child) {\n          child.x = x;\n        } else if (\"y\" in child) {\n          child.y = y;\n        }\n      }\n    }\n    const xExists = typeof node.x === \"number\";\n    const yExists = typeof node.y === \"number\";\n    const widthExists = typeof node.width === \"number\";\n    const heightExists = typeof node.height === \"number\";\n    if (xExists) {\n      node.translation.x += node.x;\n    }\n    if (yExists) {\n      node.translation.y += node.y;\n    }\n    if (widthExists || heightExists) {\n      node.scale = new Vector(1, 1);\n    }\n    if (widthExists) {\n      node.scale.x = node.width / width;\n    }\n    if (heightExists) {\n      node.scale.y = node.height / height;\n    }\n    node.mask = new Rectangle(0, 0, width, height);\n    node.mask.origin.set(-width / 2, -height / 2);\n    return node;\n  }\n  function applySvgAttributes(node, elem, parentStyles) {\n    const styles = {}, attributes = {}, extracted = {};\n    let i, m, key, value, prop, attr;\n    let transforms, x, y;\n    let id, scene, ref, tagName;\n    let ca, cb, cc, error;\n    if (node === null) {\n      return styles;\n    }\n    if (root.getComputedStyle) {\n      const computedStyles = root.getComputedStyle(node);\n      i = computedStyles.length;\n      while (i--) {\n        key = computedStyles[i];\n        value = computedStyles[key];\n        if (typeof value !== \"undefined\") {\n          styles[key] = value;\n        }\n      }\n    }\n    for (i = 0; i < node.attributes.length; i++) {\n      attr = node.attributes[i];\n      if (/style/i.test(attr.nodeName)) {\n        extractCSSText(attr.value, extracted);\n      } else {\n        attributes[attr.nodeName] = attr.value;\n      }\n    }\n    if (typeof styles.opacity !== \"undefined\") {\n      styles[\"stroke-opacity\"] = styles.opacity;\n      styles[\"fill-opacity\"] = styles.opacity;\n      delete styles.opacity;\n    }\n    if (parentStyles) {\n      _.defaults(styles, parentStyles);\n    }\n    _.extend(styles, extracted, attributes);\n    styles.visible = !(typeof styles.display === \"undefined\" && /none/i.test(styles.display)) || typeof styles.visibility === \"undefined\" && /hidden/i.test(styles.visibility);\n    for (key in styles) {\n      value = styles[key];\n      switch (key) {\n        case \"gradientTransform\":\n          if (/none/i.test(value)) break;\n          m = node.gradientTransform && node.gradientTransform.baseVal && node.gradientTransform.baseVal.length > 0 ? node.gradientTransform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null;\n          if (m === null) break;\n          transforms = decomposeMatrix(m);\n          switch (elem._renderer.type) {\n            case \"linear-gradient\":\n              applyTransformsToVector(transforms, elem.left);\n              applyTransformsToVector(transforms, elem.right);\n              break;\n            case \"radial-gradient\":\n              elem.center.x += transforms.translateX;\n              elem.center.y += transforms.translateY;\n              elem.focal.x += transforms.translateX;\n              elem.focal.y += transforms.translateY;\n              elem.radius *= Math.max(transforms.scaleX, transforms.scaleY);\n              break;\n          }\n          break;\n        case \"transform\":\n          if (/none/i.test(value)) break;\n          m = node.transform && node.transform.baseVal && node.transform.baseVal.length > 0 ? node.transform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null;\n          if (m === null) break;\n          if (Constants.AutoCalculateImportedMatrices) {\n            transforms = decomposeMatrix(m);\n            elem.translation.set(transforms.translateX, transforms.translateY);\n            elem.rotation = Math.PI * (transforms.rotation / 180);\n            elem.scale = new Vector(transforms.scaleX, transforms.scaleY);\n            x = parseFloat((styles.x + \"\").replace(\"px\"));\n            y = parseFloat((styles.y + \"\").replace(\"px\"));\n            if (x) {\n              elem.translation.x = x;\n            }\n            if (y) {\n              elem.translation.y = y;\n            }\n          } else {\n            m = node.getCTM();\n            elem._matrix.manual = true;\n            elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f);\n          }\n          break;\n        case \"visible\":\n          if (elem instanceof Group) {\n            elem._visible = value;\n            break;\n          }\n          elem.visible = value;\n          break;\n        case \"stroke-linecap\":\n          if (elem instanceof Group) {\n            elem._cap = value;\n            break;\n          }\n          elem.cap = value;\n          break;\n        case \"stroke-linejoin\":\n          if (elem instanceof Group) {\n            elem._join = value;\n            break;\n          }\n          elem.join = value;\n          break;\n        case \"stroke-miterlimit\":\n          if (elem instanceof Group) {\n            elem._miter = value;\n            break;\n          }\n          elem.miter = value;\n          break;\n        case \"stroke-width\":\n          if (elem instanceof Group) {\n            elem._linewidth = parseFloat(value);\n            break;\n          }\n          elem.linewidth = parseFloat(value);\n          break;\n        case \"opacity\":\n        case \"stroke-opacity\":\n        case \"fill-opacity\":\n          if (elem instanceof Group) {\n            elem._opacity = parseFloat(value);\n            break;\n          }\n          elem.opacity = parseFloat(value);\n          break;\n        case \"clip-path\":\n          if (regex2.cssBackgroundImage.test(value)) {\n            id = value.replace(regex2.cssBackgroundImage, \"$1\");\n            if (read.defs.current && read.defs.current.contains(id)) {\n              ref = read.defs.current.get(id);\n              if (ref && ref.childNodes.length > 0) {\n                ref = ref.childNodes[0];\n                tagName = getTagName(ref.nodeName);\n                elem.mask = read[tagName].call(this, ref, {});\n                switch (elem._renderer.type) {\n                  case \"text\":\n                  case \"path\":\n                    elem.position.add(elem.mask.position);\n                    elem.mask.position.clear();\n                    break;\n                }\n              }\n            }\n          }\n          break;\n        case \"fill\":\n        case \"stroke\":\n          prop = (elem instanceof Group ? \"_\" : \"\") + key;\n          if (regex2.cssBackgroundImage.test(value)) {\n            id = value.replace(regex2.cssBackgroundImage, \"$1\");\n            if (read.defs.current && read.defs.current.contains(id)) {\n              ref = read.defs.current.get(id);\n              if (!ref.object) {\n                tagName = getTagName(ref.nodeName);\n                ref.object = read[tagName].call(this, ref, {});\n              }\n              ref = ref.object;\n            } else {\n              scene = getScene(this);\n              ref = scene.getById(id);\n            }\n            elem[prop] = ref;\n          } else {\n            elem[prop] = value;\n          }\n          break;\n        case \"id\":\n          elem.id = value;\n          break;\n        case \"class\":\n        case \"className\":\n          elem.classList = value.split(\" \");\n          elem._flagClassName = true;\n          break;\n        case \"x\":\n        case \"y\":\n          ca = elem instanceof Gradient;\n          cb = elem instanceof LinearGradient;\n          cc = elem instanceof RadialGradient;\n          if (ca || cb || cc) {\n            break;\n          }\n          if (value.match(\"[a-z%]$\") && !value.endsWith(\"px\")) {\n            error = new TwoError(\n              \"only pixel values are supported with the \" + key + \" attribute.\"\n            );\n            console.warn(error.name, error.message);\n          }\n          elem.translation[key] = parseFloat(value);\n          break;\n        case \"font-family\":\n          if (elem instanceof Text) {\n            elem.family = value;\n          }\n          break;\n        case \"font-size\":\n          if (elem instanceof Text) {\n            if (value.match(\"[a-z%]$\") && !value.endsWith(\"px\")) {\n              error = new TwoError(\n                \"only pixel values are supported with the \" + key + \" attribute.\"\n              );\n              console.warn(error.name, error.message);\n            }\n            elem.size = parseFloat(value);\n          }\n          break;\n        case \"font-weight\":\n          if (elem instanceof Text) {\n            elem.weight = value;\n          }\n          break;\n        case \"font-style\":\n          if (elem instanceof Text) {\n            elem.style = value;\n          }\n          break;\n        case \"text-decoration\":\n          if (elem instanceof Text) {\n            elem.decoration = value;\n          }\n          break;\n        case \"line-height\":\n          if (elem instanceof Text) {\n            elem.leading = value;\n          }\n          break;\n      }\n    }\n    if (Object.keys(node.dataset).length) elem.dataset = node.dataset;\n    return styles;\n  }\n  function updateDefsCache(node, defsCache) {\n    for (let i = 0, l = node.childNodes.length; i < l; i++) {\n      const n = node.childNodes[i];\n      if (!n.id) continue;\n      const tagName = getTagName(node.nodeName);\n      if (tagName === \"#text\") continue;\n      defsCache.add(n.id, n);\n    }\n  }\n  function getScene(node) {\n    while (node.parent) {\n      node = node.parent;\n    }\n    return node.scene;\n  }\n  var read = {\n    svg: function(node) {\n      const defs = read.defs.current = new Registry();\n      const elements = node.getElementsByTagName(\"defs\");\n      for (let i = 0; i < elements.length; i++) {\n        updateDefsCache(elements[i], defs);\n      }\n      const svg2 = read.g.call(this, node);\n      const viewBox = node.getAttribute(\"viewBox\");\n      const x = node.getAttribute(\"x\");\n      const y = node.getAttribute(\"y\");\n      const width = node.getAttribute(\"width\");\n      const height = node.getAttribute(\"height\");\n      svg2.defs = defs;\n      const viewBoxExists = viewBox !== null;\n      const xExists = x !== null;\n      const yExists = y !== null;\n      const widthExists = width !== null;\n      const heightExists = height !== null;\n      if (xExists) {\n        svg2.x = parseFloat(x.replace(regex2.unitSuffix, \"\"));\n      }\n      if (yExists) {\n        svg2.y = parseFloat(y.replace(regex2.unitSuffix, \"\"));\n      }\n      if (widthExists) {\n        svg2.width = parseFloat(width.replace(regex2.unitSuffix, \"\"));\n      }\n      if (heightExists) {\n        svg2.height = parseFloat(height.replace(regex2.unitSuffix, \"\"));\n      }\n      if (viewBoxExists) {\n        applySvgViewBox(svg2, viewBox);\n      }\n      delete read.defs.current;\n      return svg2;\n    },\n    defs: function(node) {\n      return null;\n    },\n    use: function(node, styles) {\n      let error;\n      const href = node.getAttribute(\"href\") || node.getAttribute(\"xlink:href\");\n      if (!href) {\n        error = new TwoError(\"encountered <use /> with no href.\");\n        console.warn(error.name, error.message);\n        return null;\n      }\n      const id = href.slice(1);\n      if (!read.defs.current.contains(id)) {\n        error = new TwoError(\n          \"unable to find element for reference \" + href + \".\"\n        );\n        console.warn(error.name, error.message);\n        return null;\n      }\n      const template = read.defs.current.get(id);\n      const fullNode = template.cloneNode(true);\n      for (let i = 0; i < node.attributes.length; i++) {\n        const attr = node.attributes[i];\n        const ca = overwriteAttrs.includes(attr.nodeName);\n        const cb = !fullNode.hasAttribute(attr.nodeName);\n        if (ca || cb) {\n          fullNode.setAttribute(attr.nodeName, attr.value);\n        }\n      }\n      const tagName = getTagName(fullNode.nodeName);\n      return read[tagName].call(this, fullNode, styles);\n    },\n    g: function(node, parentStyles) {\n      const group = new Group();\n      applySvgAttributes.call(this, node, group, parentStyles);\n      this.add(group);\n      const styles = getSvgStyles.call(this, node);\n      for (let i = 0, l = node.childNodes.length; i < l; i++) {\n        const n = node.childNodes[i];\n        const tag = n.nodeName;\n        if (!tag) return;\n        const tagName = getTagName(tag);\n        if (tagName in read) {\n          const o = read[tagName].call(group, n, styles);\n          if (!!o && !o.parent) {\n            group.add(o);\n          }\n        }\n      }\n      return group;\n    },\n    polygon: function(node, parentStyles) {\n      let points;\n      if (typeof node === \"string\") {\n        points = node;\n      } else {\n        points = node.getAttribute(\"points\");\n      }\n      const verts = [];\n      points.replace(\n        /(-?[\\d.eE-]+)[,|\\s](-?[\\d.eE-]+)/g,\n        function(match, p1, p2) {\n          verts.push(new Anchor(parseFloat(p1), parseFloat(p2)));\n        }\n      );\n      const poly = new Path(verts, true);\n      poly.stroke = \"none\";\n      poly.fill = \"black\";\n      applySvgAttributes.call(this, node, poly, parentStyles);\n      return poly;\n    },\n    polyline: function(node, parentStyles) {\n      const poly = read.polygon.call(this, node, parentStyles);\n      poly.closed = false;\n      return poly;\n    },\n    path: function(node, parentStyles) {\n      let path;\n      if (typeof node === \"string\") {\n        path = node;\n        node = null;\n      } else {\n        path = node.getAttribute(\"d\");\n      }\n      let points = [];\n      let closed2 = false, relative = false;\n      if (path) {\n        let coord = new Anchor();\n        let control, coords;\n        let commands = path.match(/[a-df-z][^a-df-z]*/gi);\n        const last = commands.length - 1;\n        _.each(commands.slice(0), function(command, i) {\n          const items = command.slice(1).trim().match(regex2.path);\n          const type = command[0];\n          const lower = type.toLowerCase();\n          let bin, j, l, ct, times;\n          const result = [];\n          if (i === 0) {\n            commands = [];\n          }\n          switch (lower) {\n            case \"h\":\n            case \"v\":\n              if (items.length > 1) {\n                bin = 1;\n              }\n              break;\n            case \"m\":\n            case \"l\":\n            case \"t\":\n              if (items.length > 2) {\n                bin = 2;\n              }\n              break;\n            case \"s\":\n            case \"q\":\n              if (items.length > 4) {\n                bin = 4;\n              }\n              break;\n            case \"c\":\n              if (items.length > 6) {\n                bin = 6;\n              }\n              break;\n            case \"a\":\n              if (items.length > 7) {\n                bin = 7;\n              }\n              break;\n          }\n          if (bin) {\n            for (j = 0, l = items.length, times = 0; j < l; j += bin) {\n              ct = type;\n              if (times > 0) {\n                switch (type) {\n                  case \"m\":\n                    ct = \"l\";\n                    break;\n                  case \"M\":\n                    ct = \"L\";\n                    break;\n                }\n              }\n              result.push(ct + items.slice(j, j + bin).join(\" \"));\n              times++;\n            }\n            commands = Array.prototype.concat.apply(commands, result);\n          } else {\n            commands.push(command);\n          }\n        });\n        _.each(commands, function(command, i) {\n          let result, x, y;\n          const type = command[0];\n          const lower = type.toLowerCase();\n          coords = command.slice(1).trim().match(regex2.path);\n          relative = type === lower;\n          let x1, y1, x2, y2, x3, y3, x4, y4, reflection;\n          let a, b;\n          let anchor2, rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n          switch (lower) {\n            case \"z\":\n              if (i >= last) {\n                closed2 = true;\n              } else {\n                x = coord.x;\n                y = coord.y;\n                result = new Anchor(\n                  x,\n                  y,\n                  void 0,\n                  void 0,\n                  void 0,\n                  void 0,\n                  Commands.close\n                );\n                for (let j = points.length - 1; j >= 0; j--) {\n                  const point = points[j];\n                  if (/m/i.test(point.command)) {\n                    coord = point;\n                    break;\n                  }\n                }\n              }\n              break;\n            case \"m\":\n            case \"l\":\n              control = void 0;\n              x = parseFloat(coords[0]);\n              y = parseFloat(coords[1]);\n              result = new Anchor(\n                x,\n                y,\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                /m/i.test(lower) ? Commands.move : Commands.line\n              );\n              if (relative) {\n                result.addSelf(coord);\n              }\n              coord = result;\n              break;\n            case \"h\":\n            case \"v\":\n              a = /h/i.test(lower) ? \"x\" : \"y\";\n              b = /x/i.test(a) ? \"y\" : \"x\";\n              result = new Anchor(\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                Commands.line\n              );\n              result[a] = parseFloat(coords[0]);\n              result[b] = coord[b];\n              if (relative) {\n                result[a] += coord[a];\n              }\n              coord = result;\n              break;\n            case \"c\":\n            case \"s\":\n              x1 = coord.x;\n              y1 = coord.y;\n              if (!control) {\n                control = new Vector();\n              }\n              if (/c/i.test(lower)) {\n                x2 = parseFloat(coords[0]);\n                y2 = parseFloat(coords[1]);\n                x3 = parseFloat(coords[2]);\n                y3 = parseFloat(coords[3]);\n                x4 = parseFloat(coords[4]);\n                y4 = parseFloat(coords[5]);\n              } else {\n                reflection = getReflection(coord, control, relative);\n                x2 = reflection.x;\n                y2 = reflection.y;\n                x3 = parseFloat(coords[0]);\n                y3 = parseFloat(coords[1]);\n                x4 = parseFloat(coords[2]);\n                y4 = parseFloat(coords[3]);\n              }\n              if (relative) {\n                x2 += x1;\n                y2 += y1;\n                x3 += x1;\n                y3 += y1;\n                x4 += x1;\n                y4 += y1;\n              }\n              coord.controls.right.set(x2 - coord.x, y2 - coord.y);\n              result = new Anchor(\n                x4,\n                y4,\n                x3 - x4,\n                y3 - y4,\n                void 0,\n                void 0,\n                Commands.curve\n              );\n              coord = result;\n              control = result.controls.left;\n              break;\n            case \"t\":\n            case \"q\":\n              x1 = coord.x;\n              y1 = coord.y;\n              if (!control) {\n                control = new Vector();\n              }\n              if (/q/i.test(lower)) {\n                x2 = parseFloat(coords[0]);\n                y2 = parseFloat(coords[1]);\n                x3 = parseFloat(coords[0]);\n                y3 = parseFloat(coords[1]);\n                x4 = parseFloat(coords[2]);\n                y4 = parseFloat(coords[3]);\n              } else {\n                reflection = getReflection(coord, control, relative);\n                x2 = reflection.x;\n                y2 = reflection.y;\n                x3 = reflection.x;\n                y3 = reflection.y;\n                x4 = parseFloat(coords[0]);\n                y4 = parseFloat(coords[1]);\n              }\n              if (relative) {\n                x2 += x1;\n                y2 += y1;\n                x3 += x1;\n                y3 += y1;\n                x4 += x1;\n                y4 += y1;\n              }\n              coord.controls.right.set(\n                (x2 - coord.x) * 0.33,\n                (y2 - coord.y) * 0.33\n              );\n              result = new Anchor(\n                x4,\n                y4,\n                x3 - x4,\n                y3 - y4,\n                void 0,\n                void 0,\n                Commands.curve\n              );\n              coord = result;\n              control = result.controls.left;\n              break;\n            case \"a\":\n              x1 = coord.x;\n              y1 = coord.y;\n              rx = parseFloat(coords[0]);\n              ry = parseFloat(coords[1]);\n              xAxisRotation = parseFloat(coords[2]);\n              largeArcFlag = parseFloat(coords[3]);\n              sweepFlag = parseFloat(coords[4]);\n              x4 = parseFloat(coords[5]);\n              y4 = parseFloat(coords[6]);\n              if (relative) {\n                x4 += x1;\n                y4 += y1;\n              }\n              anchor2 = new Anchor(x4, y4);\n              anchor2.command = Commands.arc;\n              anchor2.rx = rx;\n              anchor2.ry = ry;\n              anchor2.xAxisRotation = xAxisRotation;\n              anchor2.largeArcFlag = largeArcFlag;\n              anchor2.sweepFlag = sweepFlag;\n              result = anchor2;\n              coord = anchor2;\n              control = void 0;\n              break;\n          }\n          if (result) {\n            if (Array.isArray(result)) {\n              points = points.concat(result);\n            } else {\n              points.push(result);\n            }\n          }\n        });\n      }\n      path = new Path(points, closed2, void 0, true);\n      path.stroke = \"none\";\n      path.fill = \"black\";\n      const rect = path.getBoundingClientRect(true);\n      rect.centroid = {\n        x: rect.left + rect.width / 2,\n        y: rect.top + rect.height / 2\n      };\n      _.each(path.vertices, function(v) {\n        v.subSelf(rect.centroid);\n      });\n      applySvgAttributes.call(this, node, path, parentStyles);\n      path.translation.addSelf(rect.centroid);\n      return path;\n    },\n    circle: function(node, parentStyles) {\n      const x = parseFloat(node.getAttribute(\"cx\"));\n      const y = parseFloat(node.getAttribute(\"cy\"));\n      const r = parseFloat(node.getAttribute(\"r\"));\n      const circle = new Circle(0, 0, r);\n      circle.stroke = \"none\";\n      circle.fill = \"black\";\n      applySvgAttributes.call(this, node, circle, parentStyles);\n      circle.translation.x = x;\n      circle.translation.y = y;\n      return circle;\n    },\n    ellipse: function(node, parentStyles) {\n      const x = parseFloat(node.getAttribute(\"cx\"));\n      const y = parseFloat(node.getAttribute(\"cy\"));\n      const width = parseFloat(node.getAttribute(\"rx\"));\n      const height = parseFloat(node.getAttribute(\"ry\"));\n      const ellipse = new Ellipse(0, 0, width, height);\n      ellipse.stroke = \"none\";\n      ellipse.fill = \"black\";\n      applySvgAttributes.call(this, node, ellipse, parentStyles);\n      ellipse.translation.x = x;\n      ellipse.translation.y = y;\n      return ellipse;\n    },\n    rect: function(node, parentStyles) {\n      const rx = parseFloat(node.getAttribute(\"rx\"));\n      const ry = parseFloat(node.getAttribute(\"ry\"));\n      if (!_.isNaN(rx) || !_.isNaN(ry)) {\n        return read[\"rounded-rect\"](node);\n      }\n      const width = parseFloat(node.getAttribute(\"width\"));\n      const height = parseFloat(node.getAttribute(\"height\"));\n      const w2 = width / 2;\n      const h2 = height / 2;\n      const rect = new Rectangle(0, 0, width, height);\n      rect.stroke = \"none\";\n      rect.fill = \"black\";\n      applySvgAttributes.call(this, node, rect, parentStyles);\n      rect.translation.x += w2;\n      rect.translation.y += h2;\n      return rect;\n    },\n    \"rounded-rect\": function(node, parentStyles) {\n      const rx = parseFloat(node.getAttribute(\"rx\")) || 0;\n      const ry = parseFloat(node.getAttribute(\"ry\")) || 0;\n      const width = parseFloat(node.getAttribute(\"width\"));\n      const height = parseFloat(node.getAttribute(\"height\"));\n      const w2 = width / 2;\n      const h2 = height / 2;\n      const radius = new Vector(rx, ry);\n      const rect = new RoundedRectangle(0, 0, width, height, radius);\n      rect.stroke = \"none\";\n      rect.fill = \"black\";\n      applySvgAttributes.call(this, node, rect, parentStyles);\n      rect.translation.x += w2;\n      rect.translation.y += h2;\n      return rect;\n    },\n    line: function(node, parentStyles) {\n      const x1 = parseFloat(node.getAttribute(\"x1\"));\n      const y1 = parseFloat(node.getAttribute(\"y1\"));\n      const x2 = parseFloat(node.getAttribute(\"x2\"));\n      const y2 = parseFloat(node.getAttribute(\"y2\"));\n      const line = new Line(x1, y1, x2, y2).noFill();\n      applySvgAttributes.call(this, node, line, parentStyles);\n      return line;\n    },\n    lineargradient: function(node, parentStyles) {\n      let units = node.getAttribute(\"gradientUnits\");\n      let spread = node.getAttribute(\"spreadMethod\");\n      if (!units) {\n        units = \"objectBoundingBox\";\n      }\n      if (!spread) {\n        spread = \"pad\";\n      }\n      let x1 = parseFloat(node.getAttribute(\"x1\") || 0);\n      let y1 = parseFloat(node.getAttribute(\"y1\") || 0);\n      let x2 = parseFloat(node.getAttribute(\"x2\") || 0);\n      let y2 = parseFloat(node.getAttribute(\"y2\") || 0);\n      const ox = (x2 + x1) / 2;\n      const oy = (y2 + y1) / 2;\n      if (/userSpaceOnUse/i.test(units)) {\n        x1 -= ox;\n        y1 -= oy;\n        x2 -= ox;\n        y2 -= oy;\n      }\n      const stops = [];\n      for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        let offset = child.getAttribute(\"offset\");\n        if (/%/gi.test(offset)) {\n          offset = parseFloat(offset.replace(/%/gi, \"\")) / 100;\n        }\n        offset = parseFloat(offset);\n        let color = child.getAttribute(\"stop-color\");\n        let opacity = child.getAttribute(\"stop-opacity\");\n        let style = child.getAttribute(\"style\");\n        let matches;\n        if (color === null) {\n          matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n          color = matches && matches.length > 1 ? matches[1] : void 0;\n        }\n        if (opacity === null) {\n          matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n          opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n        } else {\n          opacity = parseFloat(opacity);\n        }\n        stops.push(new Stop(offset, color, opacity));\n      }\n      const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n      gradient.spread = spread;\n      gradient.units = units;\n      applySvgAttributes.call(this, node, gradient, parentStyles);\n      return gradient;\n    },\n    radialgradient: function(node, parentStyles) {\n      let units = node.getAttribute(\"gradientUnits\");\n      let spread = node.getAttribute(\"spreadMethod\");\n      if (!units) {\n        units = \"objectBoundingBox\";\n      }\n      if (!spread) {\n        spread = \"pad\";\n      }\n      let cx = parseFloat(node.getAttribute(\"cx\")) || 0;\n      let cy = parseFloat(node.getAttribute(\"cy\")) || 0;\n      let r = parseFloat(node.getAttribute(\"r\"));\n      let fx = parseFloat(node.getAttribute(\"fx\"));\n      let fy = parseFloat(node.getAttribute(\"fy\"));\n      if (_.isNaN(fx)) {\n        fx = cx;\n      }\n      if (_.isNaN(fy)) {\n        fy = cy;\n      }\n      const ox = Math.abs(cx + fx) / 2;\n      const oy = Math.abs(cy + fy) / 2;\n      if (/userSpaceOnUse/i.test(units)) {\n        cx -= ox;\n        cy -= oy;\n        fx -= ox;\n        fy -= oy;\n      }\n      const stops = [];\n      for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        let offset = child.getAttribute(\"offset\");\n        if (/%/gi.test(offset)) {\n          offset = parseFloat(offset.replace(/%/gi, \"\")) / 100;\n        }\n        offset = parseFloat(offset);\n        let color = child.getAttribute(\"stop-color\");\n        let opacity = child.getAttribute(\"stop-opacity\");\n        let style = child.getAttribute(\"style\");\n        let matches;\n        if (color === null) {\n          matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n          color = matches && matches.length > 1 ? matches[1] : void 0;\n        }\n        if (opacity === null) {\n          matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n          opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n        } else {\n          opacity = parseFloat(opacity);\n        }\n        stops.push(new Stop(offset, color, opacity));\n      }\n      const gradient = new RadialGradient(cx, cy, r, stops, fx, fy);\n      gradient.spread = spread;\n      gradient.units = units;\n      applySvgAttributes.call(this, node, gradient, parentStyles);\n      return gradient;\n    },\n    text: function(node, parentStyles) {\n      const alignment = getAlignment(node.getAttribute(\"text-anchor\")) || \"left\";\n      const baseline = getBaseline(node) || \"baseline\";\n      let message = \"\";\n      if (node.childNodes.length > 0 && node.childNodes[0].tagName === \"TSPAN\") {\n        message = node.childNodes[0].textContent;\n      } else {\n        message = node.textContent;\n      }\n      const text = new Text(message);\n      applySvgAttributes.call(this, node, text, parentStyles);\n      text.alignment = alignment;\n      text.baseline = baseline;\n      return text;\n    },\n    clippath: function(node, parentStyles) {\n      if (read.defs.current && !read.defs.current.contains(node.id)) {\n        read.defs.current.add(node.id, node);\n      }\n      return null;\n    },\n    image: function(node, parentStyles) {\n      let error;\n      const href = node.getAttribute(\"href\") || node.getAttribute(\"xlink:href\");\n      if (!href) {\n        error = new TwoError(\"encountered <image /> with no href.\");\n        console.warn(error.name, error.message);\n        return null;\n      }\n      const x = parseFloat(node.getAttribute(\"x\")) || 0;\n      const y = parseFloat(node.getAttribute(\"y\")) || 0;\n      const width = parseFloat(node.getAttribute(\"width\"));\n      const height = parseFloat(node.getAttribute(\"height\"));\n      const sprite = new Sprite(href, x, y);\n      if (!_.isNaN(width)) {\n        sprite.width = width;\n      }\n      if (!_.isNaN(height)) {\n        sprite.height = height;\n      }\n      applySvgAttributes.call(this, node, sprite, parentStyles);\n      return sprite;\n    }\n  };\n\n  // src/utils/xhr.js\n  function xhr(path, callback) {\n    const xhr2 = new XMLHttpRequest();\n    xhr2.open(\"GET\", path);\n    xhr2.onreadystatechange = function() {\n      if (xhr2.readyState === 4 && xhr2.status === 200) {\n        callback(xhr2.responseText);\n      }\n    };\n    xhr2.send();\n    return xhr2;\n  }\n\n  // src/effects/image.js\n  var Image2 = class _Image extends Rectangle {\n    /**\n     * @name Two.Image#_flagTexture\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Image#texture} needs updating.\n     */\n    _flagTexture = false;\n    /**\n     * @name Two.Image#_flagMode\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Image#mode} needs updating.\n     */\n    _flagMode = false;\n    /**\n     * @name Two.Image#_texture\n     * @private\n     * @see {@link Two.Image#texture}\n     */\n    _texture = null;\n    /**\n     * @name Two.Image#_mode\n     * @private\n     * @see {@link Two.Image#mode}\n     */\n    _mode = \"fill\";\n    constructor(src, ox, oy, width, height, mode) {\n      super(ox, oy, width || 1, height || 1);\n      this._renderer.type = \"image\";\n      for (let prop in proto24) {\n        Object.defineProperty(this, prop, proto24[prop]);\n      }\n      this.noStroke();\n      this.noFill();\n      if (src instanceof Texture) {\n        this.texture = src;\n      } else if (typeof src === \"string\") {\n        this.texture = new Texture(src);\n      }\n      if (typeof mode === \"string\") {\n        this.mode = mode;\n      }\n      this._update();\n    }\n    /**\n     * @name Two.Image.Modes\n     * @property {Object} Modes - Different mode types to render an image inspired by Figma.\n     * @property {String} Modes.fill - Scale image to fill the bounds while preserving aspect ratio.\n     * @property {String} Modes.fit - Scale image to fit within bounds while preserving aspect ratio.\n     * @property {String} Modes.crop - Scale image to fill bounds while preserving aspect ratio, cropping excess.\n     * @property {String} Modes.tile - Repeat image at original size to fill the bounds.\n     * @property {String} Modes.stretch - Stretch image to fill dimensions, ignoring aspect ratio.\n     */\n    static Modes = {\n      fill: \"fill\",\n      fit: \"fit\",\n      crop: \"crop\",\n      tile: \"tile\",\n      stretch: \"stretch\"\n    };\n    /**\n     * @name Two.Image.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Image}.\n     */\n    static Properties = [\"texture\", \"mode\"];\n    /**\n     * @name Two.Image.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Image} to create a new instance\n     * @returns {Two.Image}\n     * @description Create a new {@link Two.Image} from an object notation of a {@link Two.Image}.\n     * @nota-bene Works in conjunction with {@link Two.Image#toObject}\n     */\n    static fromObject(obj) {\n      const image = new _Image().copy(obj);\n      if (\"id\" in obj) {\n        image.id = obj.id;\n      }\n      return image;\n    }\n    /**\n     * @name Two.Image#copy\n     * @function\n     * @param {Two.Image} image - The reference {@link Two.Image}\n     * @description Copy the properties of one {@link Two.Image} onto another.\n     */\n    copy(image) {\n      super.copy.call(this, image);\n      for (let i = 0; i < _Image.Properties.length; i++) {\n        const k = _Image.Properties[i];\n        if (k in image) {\n          this[k] = image[k];\n        }\n      }\n      return this;\n    }\n    /**\n     * @name Two.Image#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Image}\n     * @description Create a new instance of {@link Two.Image} with the same properties of the current image.\n     */\n    clone(parent) {\n      const clone = new _Image(\n        this.texture,\n        this.translation.x,\n        this.translation.y,\n        this.width,\n        this.height\n      );\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    /**\n     * @name Two.Image#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the image.\n     */\n    toObject() {\n      const object = super.toObject.call(this);\n      object.renderer.type = \"image\";\n      object.texture = this.texture.toObject();\n      object.mode = this.mode;\n      return object;\n    }\n    /**\n     * @name Two.Image#dispose\n     * @function\n     * @returns {Two.Image}\n     * @description Release the image's renderer resources and detach all events.\n     * This method disposes the texture (calling dispose() for thorough cleanup) and inherits comprehensive\n     * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n     * for potential re-attachment.\n     */\n    dispose() {\n      super.dispose();\n      if (this._texture && typeof this._texture.dispose === \"function\") {\n        this._texture.dispose();\n      } else if (this._texture && typeof this._texture.unbind === \"function\") {\n        this._texture.unbind();\n      }\n      return this;\n    }\n    /**\n     * @name Two.Image#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      const effect = this._texture;\n      if (effect) {\n        if (this._flagTexture) {\n          this.fill = effect;\n        }\n        if (effect.loaded) {\n          const iw = effect.image.width;\n          const ih = effect.image.height;\n          const rw = this.width;\n          const rh = this.height;\n          const scaleX = rw / iw;\n          const scaleY = rh / ih;\n          switch (this._mode) {\n            case _Image.Modes.fill: {\n              const scale = Math.max(scaleX, scaleY);\n              effect.scale = scale;\n              effect.offset.x = 0;\n              effect.offset.y = 0;\n              effect.repeat = \"repeat\";\n              break;\n            }\n            case _Image.Modes.fit: {\n              const scale = Math.min(scaleX, scaleY);\n              effect.scale = scale;\n              effect.offset.x = 0;\n              effect.offset.y = 0;\n              effect.repeat = \"no-repeat\";\n              break;\n            }\n            case _Image.Modes.crop: {\n              break;\n            }\n            case _Image.Modes.tile: {\n              effect.offset.x = (iw - rw) / 2;\n              effect.offset.y = (ih - rh) / 2;\n              effect.repeat = \"repeat\";\n              break;\n            }\n            case _Image.Modes.stretch:\n            default: {\n              effect.scale = new Vector(scaleX, scaleY);\n              effect.offset.x = 0;\n              effect.offset.y = 0;\n              effect.repeat = \"repeat\";\n            }\n          }\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    /**\n     * @name Two.Image#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      super.flagReset.call(this);\n      this._flagTexture = this._flagMode = false;\n      return this;\n    }\n  };\n  var proto24 = {\n    texture: {\n      enumerable: true,\n      get: function() {\n        return this._texture;\n      },\n      set: function(v) {\n        this._texture = v;\n        this._flagTexture = true;\n      }\n    },\n    mode: {\n      enumerable: true,\n      get: function() {\n        return this._mode;\n      },\n      set: function(v) {\n        this._mode = v;\n        this._flagMode = true;\n      }\n    }\n  };\n\n  // src/renderers/canvas.js\n  var emptyArray = [];\n  var max4 = Math.max;\n  var min4 = Math.min;\n  var abs = Math.abs;\n  var sin6 = Math.sin;\n  var cos6 = Math.cos;\n  var acos = Math.acos;\n  var sqrt = Math.sqrt;\n  var canvas2 = {\n    isHidden: /(undefined|none|transparent)/i,\n    alignments: {\n      left: \"start\",\n      middle: \"center\",\n      right: \"end\"\n    },\n    baselines: {\n      top: \"top\",\n      middle: \"middle\",\n      bottom: \"bottom\",\n      baseline: \"alphabetic\"\n    },\n    getRendererType: function(type) {\n      return type in canvas2 ? type : \"path\";\n    },\n    group: {\n      renderChild: function(child) {\n        const prop = canvas2.getRendererType(child._renderer.type);\n        canvas2[prop].render.call(child, this.ctx, true, this.clip);\n      },\n      render: function(ctx) {\n        if (!this._visible) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const matrix = this._matrix.elements;\n        const parent = this.parent;\n        this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1);\n        const mask = this._mask;\n        const defaultMatrix = isDefaultMatrix(matrix);\n        const shouldIsolate = !defaultMatrix || !!mask;\n        if (!this._renderer.context) {\n          this._renderer.context = {};\n        }\n        this._renderer.context.ctx = ctx;\n        if (shouldIsolate) {\n          ctx.save();\n          if (!defaultMatrix) {\n            ctx.transform(\n              matrix[0],\n              matrix[3],\n              matrix[1],\n              matrix[4],\n              matrix[2],\n              matrix[5]\n            );\n          }\n        }\n        if (mask) {\n          const prop = canvas2.getRendererType(mask._renderer.type);\n          canvas2[prop].render.call(mask, ctx, true);\n        }\n        if (this._opacity > 0 && this._scale !== 0) {\n          for (let i = 0; i < this.children.length; i++) {\n            const child = this.children[i];\n            const prop = canvas2.getRendererType(child._renderer.type);\n            canvas2[prop].render.call(child, ctx);\n          }\n        }\n        if (shouldIsolate) {\n          ctx.restore();\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    path: {\n      render: function(ctx, forced, parentClipped) {\n        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;\n        po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n        mask = this._mask;\n        clip = this._clip;\n        opacity = this._opacity * (po || 1);\n        visible = this._visible;\n        if (!forced && (!visible || clip || opacity === 0)) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        matrix = this._matrix.elements;\n        stroke = this._stroke;\n        linewidth = this._linewidth;\n        fill = this._fill;\n        cap = this._cap;\n        join = this._join;\n        miter = this._miter;\n        closed2 = this._closed;\n        commands = this._renderer.vertices;\n        length = commands.length;\n        last = length - 1;\n        defaultMatrix = isDefaultMatrix(matrix);\n        dashes = this.dashes;\n        if (!defaultMatrix) {\n          ctx.save();\n          ctx.transform(\n            matrix[0],\n            matrix[3],\n            matrix[1],\n            matrix[4],\n            matrix[2],\n            matrix[5]\n          );\n        }\n        if (mask) {\n          const prop = canvas2.getRendererType(mask._renderer.type);\n          canvas2[prop].render.call(mask, ctx, true);\n        }\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            const prop = canvas2.getRendererType(fill._renderer.type);\n            canvas2[prop].render.call(fill, ctx, this);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            const prop = canvas2.getRendererType(stroke._renderer.type);\n            canvas2[prop].render.call(stroke, ctx, this);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = getEffectiveStrokeWidth(this);\n          }\n          if (miter) {\n            ctx.miterLimit = miter;\n          }\n          if (join) {\n            ctx.lineJoin = join;\n          }\n          if (!closed2 && cap) {\n            ctx.lineCap = cap;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.beginPath();\n        let rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n        for (let i = 0; i < length; i++) {\n          b = commands[i];\n          x = b.x;\n          y = b.y;\n          switch (b.command) {\n            case Commands.close:\n              ctx.closePath();\n              break;\n            case Commands.arc:\n              rx = b.rx;\n              ry = b.ry;\n              xAxisRotation = b.xAxisRotation;\n              largeArcFlag = b.largeArcFlag;\n              sweepFlag = b.sweepFlag;\n              prev = closed2 ? mod(i - 1, length) : max4(i - 1, 0);\n              a = commands[prev];\n              ax = a.x;\n              ay = a.y;\n              canvas2.renderSvgArcCommand(\n                ctx,\n                ax,\n                ay,\n                rx,\n                ry,\n                largeArcFlag,\n                sweepFlag,\n                xAxisRotation,\n                x,\n                y\n              );\n              break;\n            case Commands.curve:\n              prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n              a = commands[prev];\n              ar = a.controls && a.controls.right || Vector.zero;\n              bl = b.controls && b.controls.left || Vector.zero;\n              if (a._relative) {\n                vx = ar.x + a.x;\n                vy = ar.y + a.y;\n              } else {\n                vx = ar.x;\n                vy = ar.y;\n              }\n              if (b._relative) {\n                ux = bl.x + b.x;\n                uy = bl.y + b.y;\n              } else {\n                ux = bl.x;\n                uy = bl.y;\n              }\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              if (i >= last && closed2) {\n                c = d;\n                br = b.controls && b.controls.right || Vector.zero;\n                cl = c.controls && c.controls.left || Vector.zero;\n                if (b._relative) {\n                  vx = br.x + b.x;\n                  vy = br.y + b.y;\n                } else {\n                  vx = br.x;\n                  vy = br.y;\n                }\n                if (c._relative) {\n                  ux = cl.x + c.x;\n                  uy = cl.y + c.y;\n                } else {\n                  ux = cl.x;\n                  uy = cl.y;\n                }\n                x = c.x;\n                y = c.y;\n                ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              }\n              break;\n            case Commands.line:\n              ctx.lineTo(x, y);\n              break;\n            case Commands.move:\n              d = b;\n              ctx.moveTo(x, y);\n              break;\n          }\n        }\n        if (closed2) {\n          ctx.closePath();\n        }\n        if (!clip && !parentClipped) {\n          if (!canvas2.isHidden.test(fill)) {\n            isOffset = fill._renderer && fill._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n              ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n            }\n            ctx.fill();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n          if (!canvas2.isHidden.test(stroke)) {\n            isOffset = stroke._renderer && stroke._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(\n                -stroke._renderer.offset.x,\n                -stroke._renderer.offset.y\n              );\n              ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n              ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n            }\n            ctx.stroke();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n        }\n        if (!defaultMatrix) {\n          ctx.restore();\n        }\n        if (clip && !parentClipped) {\n          ctx.clip();\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.setLineDash(emptyArray);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    points: {\n      render: function(ctx, forced, parentClipped) {\n        let me, stroke, linewidth, fill, opacity, visible, size, commands, length, b, x, y, defaultMatrix, isOffset, dashes, po;\n        po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n        opacity = this._opacity * (po || 1);\n        visible = this._visible;\n        if (!forced && (!visible || opacity === 0)) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        me = this._matrix.elements;\n        stroke = this._stroke;\n        linewidth = this._linewidth;\n        fill = this._fill;\n        commands = this._renderer.collection;\n        length = commands.length;\n        defaultMatrix = isDefaultMatrix(me);\n        dashes = this.dashes;\n        size = this._size;\n        if (!defaultMatrix) {\n          ctx.save();\n          ctx.transform(me[0], me[3], me[1], me[4], me[2], me[5]);\n        }\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            const prop = canvas2.getRendererType(fill._renderer.type);\n            canvas2[prop].render.call(fill, ctx, this);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            const prop = canvas2.getRendererType(stroke._renderer.type);\n            canvas2[prop].render.call(stroke, ctx, this);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = getEffectiveStrokeWidth(this);\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.beginPath();\n        let radius = size * 0.5, m;\n        if (!this._sizeAttenuation) {\n          m = this.worldMatrix.elements;\n          m = decomposeMatrix(m[0], m[3], m[1], m[4], m[2], m[5]);\n          radius /= Math.max(m.scaleX, m.scaleY);\n        }\n        for (let i = 0; i < length; i++) {\n          b = commands[i];\n          x = b.x;\n          y = b.y;\n          ctx.moveTo(x + radius, y);\n          ctx.arc(x, y, radius, 0, TWO_PI);\n        }\n        if (!parentClipped) {\n          if (!canvas2.isHidden.test(fill)) {\n            isOffset = fill._renderer && fill._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n              ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n            }\n            ctx.fill();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n          if (!canvas2.isHidden.test(stroke)) {\n            isOffset = stroke._renderer && stroke._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(\n                -stroke._renderer.offset.x,\n                -stroke._renderer.offset.y\n              );\n              ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n              ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n            }\n            ctx.stroke();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n        }\n        if (!defaultMatrix) {\n          ctx.restore();\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.setLineDash(emptyArray);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    text: {\n      render: function(ctx, forced, parentClipped) {\n        const po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n        const opacity = this._opacity * po;\n        const visible = this._visible;\n        const mask = this._mask;\n        const clip = this._clip;\n        if (!forced && (!visible || clip || opacity === 0)) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const matrix = this._matrix.elements;\n        const stroke = this._stroke;\n        const linewidth = this._linewidth;\n        const fill = this._fill;\n        const decoration = this._decoration;\n        const direction = this._direction;\n        const defaultMatrix = isDefaultMatrix(matrix);\n        const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;\n        const dashes = this.dashes;\n        const alignment = canvas2.alignments[this._alignment] || this._alignment;\n        const baseline = canvas2.baselines[this._baseline] || this._baseline;\n        let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n        if (!defaultMatrix) {\n          ctx.save();\n          ctx.transform(\n            matrix[0],\n            matrix[3],\n            matrix[1],\n            matrix[4],\n            matrix[2],\n            matrix[5]\n          );\n        }\n        if (mask) {\n          const prop = canvas2.getRendererType(mask._renderer.type);\n          canvas2[prop].render.call(mask, ctx, true);\n        }\n        if (!isOffset) {\n          ctx.font = [\n            this._style,\n            this._weight,\n            this._size + \"px/\" + this._leading + \"px\",\n            this._family\n          ].join(\" \");\n        }\n        ctx.textAlign = alignment;\n        ctx.textBaseline = baseline;\n        ctx.direction = direction;\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            const prop = canvas2.getRendererType(fill._renderer.type);\n            canvas2[prop].render.call(fill, ctx, this);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            const prop = canvas2.getRendererType(stroke._renderer.type);\n            canvas2[prop].render.call(stroke, ctx, this);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = getEffectiveStrokeWidth(this);\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        if (!clip && !parentClipped) {\n          if (!canvas2.isHidden.test(fill)) {\n            if (fill._renderer && fill._renderer.offset) {\n              sx = fill._renderer.scale.x;\n              sy = fill._renderer.scale.y;\n              ctx.save();\n              ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n              ctx.scale(sx, sy);\n              a = this._size / fill._renderer.scale.y;\n              b = this._leading / fill._renderer.scale.y;\n              ctx.font = [\n                this._style,\n                this._weight,\n                a + \"px/\",\n                b + \"px\",\n                this._family\n              ].join(\" \");\n              c = fill._renderer.offset.x / fill._renderer.scale.x;\n              d = fill._renderer.offset.y / fill._renderer.scale.y;\n              ctx.fillText(this.value, c, d);\n              ctx.restore();\n            } else {\n              ctx.fillText(this.value, 0, 0);\n            }\n          }\n          if (!canvas2.isHidden.test(stroke)) {\n            if (stroke._renderer && stroke._renderer.offset) {\n              sx = stroke._renderer.scale.x;\n              sy = stroke._renderer.scale.y;\n              ctx.save();\n              ctx.translate(\n                -stroke._renderer.offset.x,\n                -stroke._renderer.offset.y\n              );\n              ctx.scale(sx, sy);\n              a = this._size / stroke._renderer.scale.y;\n              b = this._leading / stroke._renderer.scale.y;\n              ctx.font = [\n                this._style,\n                this._weight,\n                a + \"px/\",\n                b + \"px\",\n                this._family\n              ].join(\" \");\n              c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n              d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n              e = linewidth / stroke._renderer.scale.x;\n              ctx.lineWidth = e;\n              ctx.strokeText(this.value, c, d);\n              ctx.restore();\n            } else {\n              ctx.strokeText(this.value, 0, 0);\n            }\n          }\n        }\n        if (/(underline|strikethrough)/i.test(decoration)) {\n          const metrics = ctx.measureText(this.value);\n          let scalar = 1;\n          switch (decoration) {\n            case \"underline\":\n              y1 = metrics.actualBoundingBoxDescent;\n              y2 = metrics.actualBoundingBoxDescent;\n              break;\n            case \"strikethrough\":\n              y1 = 0;\n              y2 = 0;\n              scalar = 0.5;\n              break;\n          }\n          switch (baseline) {\n            case \"top\":\n              y1 += this._size * scalar;\n              y2 += this._size * scalar;\n              break;\n            case \"baseline\":\n            case \"bottom\":\n              y1 -= this._size * scalar;\n              y2 -= this._size * scalar;\n              break;\n          }\n          switch (alignment) {\n            case \"left\":\n            case \"start\":\n              x1 = 0;\n              x2 = metrics.width;\n              break;\n            case \"right\":\n            case \"end\":\n              x1 = -metrics.width;\n              x2 = 0;\n              break;\n            default:\n              x1 = -metrics.width / 2;\n              x2 = metrics.width / 2;\n          }\n          ctx.lineWidth = Math.max(Math.floor(this._size / 15), 1);\n          ctx.strokeStyle = ctx.fillStyle;\n          ctx.beginPath();\n          ctx.moveTo(x1, y1);\n          ctx.lineTo(x2, y2);\n          ctx.stroke();\n        }\n        if (!defaultMatrix) {\n          ctx.restore();\n        }\n        if (clip && !parentClipped) {\n          ctx.clip();\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.setLineDash(emptyArray);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    \"linear-gradient\": {\n      render: function(ctx, parent) {\n        if (!parent) {\n          return;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) {\n          let rect;\n          let lx = this.left._x;\n          let ly = this.left._y;\n          let rx = this.right._x;\n          let ry = this.right._y;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            lx = (lx - 0.5) * rect.width;\n            ly = (ly - 0.5) * rect.height;\n            rx = (rx - 0.5) * rect.width;\n            ry = (ry - 0.5) * rect.height;\n          }\n          this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    \"radial-gradient\": {\n      render: function(ctx, parent) {\n        if (!parent) {\n          return;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) {\n          let rect;\n          let cx = this.center._x;\n          let cy = this.center._y;\n          let fx = this.focal._x;\n          let fy = this.focal._y;\n          let radius = this._radius;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            cx = (cx - 0.5) * rect.width * 0.5;\n            cy = (cy - 0.5) * rect.height * 0.5;\n            fx = (fx - 0.5) * rect.width * 0.5;\n            fy = (fy - 0.5) * rect.height * 0.5;\n            radius *= Math.min(rect.width, rect.height);\n          }\n          this._renderer.effect = ctx.createRadialGradient(\n            cx,\n            cy,\n            0,\n            fx,\n            fy,\n            radius\n          );\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    texture: {\n      render: function(ctx) {\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const image = this.image;\n        if (!this._renderer.effect || (this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) {\n          this._renderer.effect = ctx.createPattern(this.image, this._repeat);\n        }\n        if (this._flagOffset || this._flagLoaded || this._flagScale) {\n          if (!(this._renderer.offset instanceof Vector)) {\n            this._renderer.offset = new Vector();\n          }\n          this._renderer.offset.x = -this._offset.x;\n          this._renderer.offset.y = -this._offset.y;\n          if (image) {\n            this._renderer.offset.x += image.width / 2;\n            this._renderer.offset.y += image.height / 2;\n            if (this._scale instanceof Vector) {\n              this._renderer.offset.x *= this._scale.x;\n              this._renderer.offset.y *= this._scale.y;\n            } else {\n              this._renderer.offset.x *= this._scale;\n              this._renderer.offset.y *= this._scale;\n            }\n          }\n        }\n        if (this._flagScale || this._flagLoaded) {\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.copy(this._scale);\n          } else {\n            this._renderer.scale.set(this._scale, this._scale);\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    renderSvgArcCommand: function(ctx, ax, ay, rx, ry, largeArcFlag, sweepFlag, xAxisRotation, x, y) {\n      xAxisRotation = xAxisRotation * Math.PI / 180;\n      rx = abs(rx);\n      ry = abs(ry);\n      const dx2 = (ax - x) / 2;\n      const dy2 = (ay - y) / 2;\n      const x1p = cos6(xAxisRotation) * dx2 + sin6(xAxisRotation) * dy2;\n      const y1p = -sin6(xAxisRotation) * dx2 + cos6(xAxisRotation) * dy2;\n      const x1ps = x1p * x1p;\n      const y1ps = y1p * y1p;\n      let rxs = rx * rx;\n      let rys = ry * ry;\n      const cr = x1ps / rxs + y1ps / rys;\n      if (cr > 1) {\n        const s = sqrt(cr);\n        rx = s * rx;\n        ry = s * ry;\n        rxs = rx * rx;\n        rys = ry * ry;\n      }\n      const dq = rxs * y1ps + rys * x1ps;\n      const pq = (rxs * rys - dq) / dq;\n      let q = sqrt(max4(0, pq));\n      if (largeArcFlag === sweepFlag) q = -q;\n      const cxp = q * rx * y1p / ry;\n      const cyp = -q * ry * x1p / rx;\n      const cx = cos6(xAxisRotation) * cxp - sin6(xAxisRotation) * cyp + (ax + x) / 2;\n      const cy = sin6(xAxisRotation) * cxp + cos6(xAxisRotation) * cyp + (ay + y) / 2;\n      const startAngle = svgAngle2(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n      const delta = svgAngle2(\n        (x1p - cxp) / rx,\n        (y1p - cyp) / ry,\n        (-x1p - cxp) / rx,\n        (-y1p - cyp) / ry\n      ) % TWO_PI;\n      const endAngle = startAngle + delta;\n      const clockwise = sweepFlag === 0;\n      renderArcEstimate(\n        ctx,\n        cx,\n        cy,\n        rx,\n        ry,\n        startAngle,\n        endAngle,\n        clockwise,\n        xAxisRotation\n      );\n    }\n  };\n  var Renderer = class extends Events {\n    constructor(params) {\n      super();\n      const smoothing = params.smoothing !== false;\n      this.domElement = params.domElement || document.createElement(\"canvas\");\n      this.ctx = this.domElement.getContext(\"2d\");\n      this.overdraw = params.overdraw || false;\n      if (typeof this.ctx.imageSmoothingEnabled !== \"undefined\") {\n        this.ctx.imageSmoothingEnabled = smoothing;\n      }\n      this.scene = new Group();\n      this.scene.parent = this;\n    }\n    /**\n     * @name Two.CanvasRenderer.Utils\n     * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />`.\n     */\n    static Utils = canvas2;\n    /**\n     * @name Two.CanvasRenderer#setSize\n     * @function\n     * @fires resize\n     * @param {Number} width - The new width of the renderer.\n     * @param {Number} height - The new height of the renderer.\n     * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n     * @description Change the size of the renderer.\n     */\n    setSize(width, height, ratio) {\n      this.width = width;\n      this.height = height;\n      this.ratio = typeof ratio === \"undefined\" ? getRatio(this.ctx) : ratio;\n      this.domElement.width = width * this.ratio;\n      this.domElement.height = height * this.ratio;\n      if (this.domElement.style) {\n        _.extend(this.domElement.style, {\n          width: width + \"px\",\n          height: height + \"px\"\n        });\n      }\n      return this.trigger(Events.Types.resize, width, height, ratio);\n    }\n    /**\n     * @name Two.CanvasRenderer#render\n     * @function\n     * @description Render the current scene to the `<canvas />`.\n     */\n    render() {\n      const isOne = this.ratio === 1;\n      if (!isOne) {\n        this.ctx.save();\n        this.ctx.scale(this.ratio, this.ratio);\n      }\n      if (!this.overdraw) {\n        this.ctx.clearRect(0, 0, this.width, this.height);\n      }\n      canvas2.group.render.call(this.scene, this.ctx);\n      if (!isOne) {\n        this.ctx.restore();\n      }\n      return this;\n    }\n  };\n  function renderArcEstimate(ctx, ox, oy, rx, ry, startAngle, endAngle, clockwise, xAxisRotation) {\n    const delta = endAngle - startAngle;\n    const epsilon = Curve.Tolerance.epsilon;\n    const samePoints = Math.abs(delta) < epsilon;\n    let deltaAngle = mod(delta, TWO_PI);\n    if (deltaAngle < epsilon) {\n      if (samePoints) {\n        deltaAngle = 0;\n      } else {\n        deltaAngle = TWO_PI;\n      }\n    }\n    if (clockwise === true && !samePoints) {\n      if (deltaAngle === TWO_PI) {\n        deltaAngle = -TWO_PI;\n      } else {\n        deltaAngle = deltaAngle - TWO_PI;\n      }\n    }\n    for (let i = 0; i < Constants.Resolution; i++) {\n      const t = i / (Constants.Resolution - 1);\n      const angle = startAngle + t * deltaAngle;\n      let x = ox + rx * Math.cos(angle);\n      let y = oy + ry * Math.sin(angle);\n      if (xAxisRotation !== 0) {\n        const cos7 = Math.cos(xAxisRotation);\n        const sin7 = Math.sin(xAxisRotation);\n        const tx = x - ox;\n        const ty = y - oy;\n        x = tx * cos7 - ty * sin7 + ox;\n        y = tx * sin7 + ty * cos7 + oy;\n      }\n      ctx.lineTo(x, y);\n    }\n  }\n  function svgAngle2(ux, uy, vx, vy) {\n    const dot = ux * vx + uy * vy;\n    const len = sqrt(ux * ux + uy * uy) * sqrt(vx * vx + vy * vy);\n    let ang = acos(max4(-1, min4(1, dot / len)));\n    if (ux * vy - uy * vx < 0) {\n      ang = -ang;\n    }\n    return ang;\n  }\n  function isDefaultMatrix(m) {\n    return m[0] === 1 && m[3] === 0 && m[1] === 0 && m[4] === 1 && m[2] === 0 && m[5] === 0;\n  }\n\n  // src/renderers/svg.js\n  var svg = {\n    version: 1.1,\n    ns: \"http://www.w3.org/2000/svg\",\n    xlink: \"http://www.w3.org/1999/xlink\",\n    alignments: {\n      left: \"start\",\n      center: \"middle\",\n      right: \"end\"\n    },\n    baselines: {\n      top: \"hanging\",\n      middle: \"middle\",\n      bottom: \"ideographic\",\n      baseline: \"alphabetic\"\n    },\n    // Create an svg namespaced element.\n    createElement: function(name, attrs) {\n      const tag = name;\n      const elem = document.createElementNS(svg.ns, tag);\n      if (tag === \"svg\") {\n        attrs = _.defaults(attrs || {}, {\n          version: svg.version\n        });\n      }\n      if (attrs && Object.keys(attrs).length > 0) {\n        svg.setAttributes(elem, attrs);\n      }\n      return elem;\n    },\n    // Add attributes from an svg element.\n    setAttributes: function(elem, attrs) {\n      const keys = Object.keys(attrs);\n      for (let i = 0; i < keys.length; i++) {\n        if (/href/.test(keys[i])) {\n          elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]);\n        } else {\n          elem.setAttribute(keys[i], attrs[keys[i]]);\n        }\n      }\n      return this;\n    },\n    // Remove attributes from an svg element.\n    removeAttributes: function(elem, attrs) {\n      for (let key in attrs) {\n        elem.removeAttribute(key);\n      }\n      return this;\n    },\n    // Turn a set of vertices into a string for the d property of a path\n    // element. It is imperative that the string collation is as fast as\n    // possible, because this call will be happening multiple times a\n    // second.\n    toString: function(points, closed2) {\n      let l = points.length, last = l - 1, d, string = \"\";\n      for (let i = 0; i < l; i++) {\n        const b = points[i];\n        const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0);\n        const a = points[prev];\n        let command, c;\n        let vx, vy, ux, uy, ar, bl, br, cl;\n        let rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n        let x = toFixed(b.x);\n        let y = toFixed(b.y);\n        switch (b.command) {\n          case Commands.close:\n            command = Commands.close;\n            break;\n          case Commands.arc:\n            rx = b.rx;\n            ry = b.ry;\n            xAxisRotation = b.xAxisRotation;\n            largeArcFlag = b.largeArcFlag;\n            sweepFlag = b.sweepFlag;\n            command = Commands.arc + \" \" + rx + \" \" + ry + \" \" + xAxisRotation + \" \" + largeArcFlag + \" \" + sweepFlag + \" \" + x + \" \" + y;\n            break;\n          case Commands.curve:\n            ar = a.controls && a.controls.right || Vector.zero;\n            bl = b.controls && b.controls.left || Vector.zero;\n            if (a.relative) {\n              vx = toFixed(ar.x + a.x);\n              vy = toFixed(ar.y + a.y);\n            } else {\n              vx = toFixed(ar.x);\n              vy = toFixed(ar.y);\n            }\n            if (b.relative) {\n              ux = toFixed(bl.x + b.x);\n              uy = toFixed(bl.y + b.y);\n            } else {\n              ux = toFixed(bl.x);\n              uy = toFixed(bl.y);\n            }\n            command = (i === 0 ? Commands.move : Commands.curve) + \" \" + vx + \" \" + vy + \" \" + ux + \" \" + uy + \" \" + x + \" \" + y;\n            break;\n          case Commands.move:\n            d = b;\n            command = Commands.move + \" \" + x + \" \" + y;\n            break;\n          default:\n            command = b.command + \" \" + x + \" \" + y;\n        }\n        if (i >= last && closed2) {\n          if (b.command === Commands.curve) {\n            c = d;\n            br = b.controls && b.controls.right || b;\n            cl = c.controls && c.controls.left || c;\n            if (b.relative) {\n              vx = toFixed(br.x + b.x);\n              vy = toFixed(br.y + b.y);\n            } else {\n              vx = toFixed(br.x);\n              vy = toFixed(br.y);\n            }\n            if (c.relative) {\n              ux = toFixed(cl.x + c.x);\n              uy = toFixed(cl.y + c.y);\n            } else {\n              ux = toFixed(cl.x);\n              uy = toFixed(cl.y);\n            }\n            x = toFixed(c.x);\n            y = toFixed(c.y);\n            command += \" C \" + vx + \" \" + vy + \" \" + ux + \" \" + uy + \" \" + x + \" \" + y;\n          }\n          if (b.command !== Commands.close) {\n            command += \" Z\";\n          }\n        }\n        string += command + \" \";\n      }\n      return string;\n    },\n    pointsToString: function(points, size) {\n      let string = \"\";\n      const r = size * 0.5;\n      for (let i = 0; i < points.length; i++) {\n        const x = points[i].x;\n        const y = points[i].y - r;\n        string += Commands.move + \" \" + x + \" \" + y + \" \";\n        string += \"a \" + r + \" \" + r + \" 0 1 0 0.001 0 Z\";\n      }\n      return string;\n    },\n    getClip: function(shape, domElement) {\n      let clip = shape._renderer.clip;\n      if (!clip) {\n        clip = shape._renderer.clip = svg.createElement(\"clipPath\", {\n          \"clip-rule\": \"nonzero\"\n        });\n      }\n      if (clip.parentNode === null) {\n        domElement.defs.appendChild(clip);\n      }\n      return clip;\n    },\n    getRendererType: function(type) {\n      return type in svg ? type : \"path\";\n    },\n    defs: {\n      update: function(domElement) {\n        const { defs } = domElement;\n        if (defs._flagUpdate) {\n          const children = Array.prototype.slice.call(defs.children, 0);\n          for (let i = 0; i < children.length; i++) {\n            const child = children[i];\n            const id = child.id;\n            const selector = `[fill=\"url(#${id})\"],[stroke=\"url(#${id})\"],[clip-path=\"url(#${id})\"]`;\n            const exists = domElement.querySelector(selector);\n            if (!exists) {\n              defs.removeChild(child);\n            }\n          }\n          defs._flagUpdate = false;\n        }\n      }\n    },\n    group: {\n      // TODO: Can speed up.\n      // TODO: How does this effect a f\n      appendChild: function(object) {\n        const elem = object._renderer.elem;\n        if (!elem) {\n          return;\n        }\n        const tag = elem.nodeName;\n        if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) {\n          return;\n        }\n        this.elem.appendChild(elem);\n      },\n      removeChild: function(object) {\n        const elem = object._renderer.elem;\n        if (!elem || elem.parentNode != this.elem) {\n          return;\n        }\n        const tag = elem.nodeName;\n        if (!tag) {\n          return;\n        }\n        if (object._clip) {\n          return;\n        }\n        this.elem.removeChild(elem);\n      },\n      orderChild: function(object) {\n        this.elem.appendChild(object._renderer.elem);\n      },\n      renderChild: function(child) {\n        const prop = svg.getRendererType(child._renderer.type);\n        svg[prop].render.call(child, this);\n      },\n      render: function(domElement) {\n        if (!this._visible && !this._flagVisible || this._opacity === 0 && !this._flagOpacity) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        if (!this._renderer.elem) {\n          this._renderer.elem = svg.createElement(\"g\", {\n            id: this.id\n          });\n          domElement.appendChild(this._renderer.elem);\n        }\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const context = {\n          domElement,\n          elem: this._renderer.elem\n        };\n        if (flagMatrix) {\n          this._renderer.elem.setAttribute(\n            \"transform\",\n            \"matrix(\" + this._matrix.toString() + \")\"\n          );\n        }\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          const prop = svg.getRendererType(child._renderer.type);\n          svg[prop].render.call(child, domElement);\n        }\n        if (this._flagId) {\n          this._renderer.elem.setAttribute(\"id\", this._id);\n        }\n        if (this._flagOpacity) {\n          this._renderer.elem.setAttribute(\"opacity\", this._opacity);\n        }\n        if (this._flagVisible) {\n          this._renderer.elem.setAttribute(\n            \"display\",\n            this._visible ? \"inline\" : \"none\"\n          );\n        }\n        if (this._flagClassName) {\n          this._renderer.elem.setAttribute(\"class\", this.classList.join(\" \"));\n        }\n        if (this._flagAdditions) {\n          this.additions.forEach(svg.group.appendChild, context);\n        }\n        if (this._flagSubtractions) {\n          this.subtractions.forEach(svg.group.removeChild, context);\n        }\n        if (this._flagOrder) {\n          this.children.forEach(svg.group.orderChild, context);\n        }\n        if (this._flagMask) {\n          if (this._mask) {\n            const prop = svg.getRendererType(this._mask._renderer.type);\n            svg[prop].render.call(this._mask, domElement);\n            this._renderer.elem.setAttribute(\n              \"clip-path\",\n              \"url(#\" + this._mask.id + \")\"\n            );\n          } else {\n            this._renderer.elem.removeAttribute(\"clip-path\");\n          }\n        }\n        if (this.dataset) {\n          Object.assign(this._renderer.elem.dataset, this.dataset);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    path: {\n      render: function(domElement) {\n        if (this._opacity === 0 && !this._flagOpacity) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const changed = {};\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagMatrix) {\n          changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n        }\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagVertices) {\n          const vertices = svg.toString(this._renderer.vertices, this._closed);\n          changed.d = vertices;\n        }\n        if (this._fill && this._fill._renderer) {\n          this._renderer.hasFillEffect = true;\n          this._fill._update();\n          const prop = svg.getRendererType(this._fill._renderer.type);\n          svg[prop].render.call(this._fill, domElement, true);\n        }\n        if (this._flagFill) {\n          changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n          if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasFillEffect;\n          }\n        }\n        if (this._stroke && this._stroke._renderer) {\n          this._renderer.hasStrokeEffect = true;\n          this._stroke._update();\n          const prop = svg.getRendererType(this._stroke._renderer.type);\n          svg[prop].render.call(this._stroke, domElement, true);\n        }\n        if (this._flagStroke) {\n          changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n          if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasStrokeEffect;\n          }\n        }\n        if (this._flagLinewidth) {\n          changed[\"stroke-width\"] = getEffectiveStrokeWidth(this);\n        }\n        if (this._flagOpacity) {\n          changed[\"stroke-opacity\"] = this._opacity;\n          changed[\"fill-opacity\"] = this._opacity;\n        }\n        if (this._flagClassName) {\n          changed[\"class\"] = this.classList.join(\" \");\n        }\n        if (this._flagVisible) {\n          changed.visibility = this._visible ? \"visible\" : \"hidden\";\n        }\n        if (this._flagCap) {\n          changed[\"stroke-linecap\"] = this._cap;\n        }\n        if (this._flagJoin) {\n          changed[\"stroke-linejoin\"] = this._join;\n        }\n        if (this._flagMiter) {\n          changed[\"stroke-miterlimit\"] = this._miter;\n        }\n        if (this.dashes && this.dashes.length > 0) {\n          changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n          changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"path\", changed);\n          domElement.appendChild(this._renderer.elem);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._flagClip) {\n          const clip = svg.getClip(this, domElement);\n          const elem = this._renderer.elem;\n          if (this._clip) {\n            elem.removeAttribute(\"id\");\n            clip.setAttribute(\"id\", this.id);\n            clip.appendChild(elem);\n          } else {\n            clip.removeAttribute(\"id\");\n            elem.setAttribute(\"id\", this.id);\n            this.parent._renderer.elem.appendChild(elem);\n          }\n        }\n        if (this._flagMask) {\n          if (this._mask) {\n            const prop = svg.getRendererType(this._mask._renderer.type);\n            svg[prop].render.call(this._mask, domElement);\n            this._renderer.elem.setAttribute(\n              \"clip-path\",\n              \"url(#\" + this._mask.id + \")\"\n            );\n          } else {\n            this._renderer.elem.removeAttribute(\"clip-path\");\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    points: {\n      render: function(domElement) {\n        if (this._opacity === 0 && !this._flagOpacity) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const changed = {};\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagMatrix) {\n          changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n        }\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagVertices || this._flagSize || this._flagSizeAttenuation) {\n          let size = this._size;\n          if (!this._sizeAttenuation) {\n            const me = this.worldMatrix.elements;\n            const m = decomposeMatrix(me[0], me[3], me[1], me[4], me[2], me[5]);\n            size /= Math.max(m.scaleX, m.scaleY);\n          }\n          const vertices = svg.pointsToString(this._renderer.collection, size);\n          changed.d = vertices;\n        }\n        if (this._fill && this._fill._renderer) {\n          this._renderer.hasFillEffect = true;\n          this._fill._update();\n          const prop = svg.getRendererType(this._fill._renderer.type);\n          svg[prop].render.call(this._fill, domElement, true);\n        }\n        if (this._flagFill) {\n          changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n          if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasFillEffect;\n          }\n        }\n        if (this._stroke && this._stroke._renderer) {\n          this._renderer.hasStrokeEffect = true;\n          this._stroke._update();\n          const prop = svg.getRendererType(this._stroke._renderer.type);\n          svg[prop].render.call(this._stroke, domElement, true);\n        }\n        if (this._flagStroke) {\n          changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n          if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasStrokeEffect;\n          }\n        }\n        if (this._flagLinewidth) {\n          changed[\"stroke-width\"] = getEffectiveStrokeWidth(this);\n        }\n        if (this._flagOpacity) {\n          changed[\"stroke-opacity\"] = this._opacity;\n          changed[\"fill-opacity\"] = this._opacity;\n        }\n        if (this._flagClassName) {\n          changed[\"class\"] = this.classList.join(\" \");\n        }\n        if (this._flagVisible) {\n          changed.visibility = this._visible ? \"visible\" : \"hidden\";\n        }\n        if (this.dashes && this.dashes.length > 0) {\n          changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n          changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"path\", changed);\n          domElement.appendChild(this._renderer.elem);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    text: {\n      render: function(domElement) {\n        this._update();\n        const changed = {};\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagMatrix) {\n          changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n        }\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagFamily) {\n          changed[\"font-family\"] = this._family;\n        }\n        if (this._flagSize) {\n          changed[\"font-size\"] = this._size;\n        }\n        if (this._flagLeading) {\n          changed[\"line-height\"] = this._leading;\n        }\n        if (this._flagAlignment) {\n          changed[\"text-anchor\"] = svg.alignments[this._alignment] || this._alignment;\n        }\n        if (this._flagBaseline) {\n          changed[\"dominant-baseline\"] = svg.baselines[this._baseline] || this._baseline;\n        }\n        if (this._flagStyle) {\n          changed[\"font-style\"] = this._style;\n        }\n        if (this._flagWeight) {\n          changed[\"font-weight\"] = this._weight;\n        }\n        if (this._flagDecoration) {\n          changed[\"text-decoration\"] = this._decoration;\n        }\n        if (this._flagDirection) {\n          changed[\"direction\"] = this._direction;\n        }\n        if (this._fill && this._fill._renderer) {\n          this._renderer.hasFillEffect = true;\n          this._fill._update();\n          const prop = svg.getRendererType(this._fill._renderer.type);\n          svg[prop].render.call(this._fill, domElement, true);\n        }\n        if (this._flagFill) {\n          changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n          if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasFillEffect;\n          }\n        }\n        if (this._stroke && this._stroke._renderer) {\n          this._renderer.hasStrokeEffect = true;\n          this._stroke._update();\n          const prop = svg.getRendererType(this._stroke._renderer.type);\n          svg[prop].render.call(this._stroke, domElement, true);\n        }\n        if (this._flagStroke) {\n          changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n          if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasStrokeEffect;\n          }\n        }\n        if (this._flagLinewidth) {\n          changed[\"stroke-width\"] = getEffectiveStrokeWidth(this);\n        }\n        if (this._flagOpacity) {\n          changed.opacity = this._opacity;\n        }\n        if (this._flagClassName) {\n          changed[\"class\"] = this.classList.join(\" \");\n        }\n        if (this._flagVisible) {\n          changed.visibility = this._visible ? \"visible\" : \"hidden\";\n        }\n        if (this.dashes && this.dashes.length > 0) {\n          changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n          changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"text\", changed);\n          domElement.appendChild(this._renderer.elem);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._flagClip) {\n          const clip = svg.getClip(this, domElement);\n          const elem = this._renderer.elem;\n          if (this._clip) {\n            elem.removeAttribute(\"id\");\n            clip.setAttribute(\"id\", this.id);\n            clip.appendChild(elem);\n          } else {\n            clip.removeAttribute(\"id\");\n            elem.setAttribute(\"id\", this.id);\n            this.parent._renderer.elem.appendChild(elem);\n          }\n        }\n        if (this._flagMask) {\n          if (this._mask) {\n            const prop = svg.getRendererType(this._mask._renderer.type);\n            svg[prop].render.call(this._mask, domElement);\n            this._renderer.elem.setAttribute(\n              \"clip-path\",\n              \"url(#\" + this._mask.id + \")\"\n            );\n          } else {\n            this._renderer.elem.removeAttribute(\"clip-path\");\n          }\n        }\n        if (this._flagValue) {\n          this._renderer.elem.textContent = this._value;\n        }\n        return this.flagReset();\n      }\n    },\n    \"linear-gradient\": {\n      render: function(domElement, silent) {\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        if (!silent) {\n          this._update();\n        }\n        const changed = {};\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagEndPoints) {\n          changed.x1 = this.left._x;\n          changed.y1 = this.left._y;\n          changed.x2 = this.right._x;\n          changed.y2 = this.right._y;\n        }\n        if (this._flagSpread) {\n          changed.spreadMethod = this._spread;\n        }\n        if (this._flagUnits) {\n          changed.gradientUnits = this._units;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"linearGradient\", changed);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._renderer.elem.parentNode === null) {\n          domElement.defs.appendChild(this._renderer.elem);\n        }\n        if (this._flagStops) {\n          const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length;\n          if (lengthChanged) {\n            while (this._renderer.elem.lastChild) {\n              this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n            }\n          }\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            const attrs = {};\n            if (stop._flagOffset) {\n              attrs.offset = 100 * stop._offset + \"%\";\n            }\n            if (stop._flagColor) {\n              attrs[\"stop-color\"] = stop._color;\n            }\n            if (stop._flagOpacity) {\n              attrs[\"stop-opacity\"] = stop._opacity;\n            }\n            if (!stop._renderer.elem) {\n              stop._renderer.elem = svg.createElement(\"stop\", attrs);\n            } else {\n              svg.setAttributes(stop._renderer.elem, attrs);\n            }\n            if (lengthChanged) {\n              this._renderer.elem.appendChild(stop._renderer.elem);\n            }\n            stop.flagReset();\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    \"radial-gradient\": {\n      render: function(domElement, silent) {\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        if (!silent) {\n          this._update();\n        }\n        const changed = {};\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagCenter) {\n          changed.cx = this.center._x;\n          changed.cy = this.center._y;\n        }\n        if (this._flagFocal) {\n          changed.fx = this.focal._x;\n          changed.fy = this.focal._y;\n        }\n        if (this._flagRadius) {\n          changed.r = this._radius;\n        }\n        if (this._flagSpread) {\n          changed.spreadMethod = this._spread;\n        }\n        if (this._flagUnits) {\n          changed.gradientUnits = this._units;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"radialGradient\", changed);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._renderer.elem.parentNode === null) {\n          domElement.defs.appendChild(this._renderer.elem);\n        }\n        if (this._flagStops) {\n          const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length;\n          if (lengthChanged) {\n            while (this._renderer.elem.lastChild) {\n              this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n            }\n          }\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            const attrs = {};\n            if (stop._flagOffset) {\n              attrs.offset = 100 * stop._offset + \"%\";\n            }\n            if (stop._flagColor) {\n              attrs[\"stop-color\"] = stop._color;\n            }\n            if (stop._flagOpacity) {\n              attrs[\"stop-opacity\"] = stop._opacity;\n            }\n            if (!stop._renderer.elem) {\n              stop._renderer.elem = svg.createElement(\"stop\", attrs);\n            } else {\n              svg.setAttributes(stop._renderer.elem, attrs);\n            }\n            if (lengthChanged) {\n              this._renderer.elem.appendChild(stop._renderer.elem);\n            }\n            stop.flagReset();\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    texture: {\n      render: function(domElement, silent) {\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        if (!silent) {\n          this._update();\n        }\n        const changed = {};\n        const styles = { x: 0, y: 0 };\n        const image = this.image;\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagLoaded && this.loaded) {\n          switch (image.nodeName.toLowerCase()) {\n            case \"canvas\":\n              styles.href = styles[\"xlink:href\"] = image.toDataURL(\"image/png\");\n              break;\n            case \"img\":\n            case \"image\":\n              styles.href = styles[\"xlink:href\"] = this.src;\n              break;\n          }\n        }\n        if (this._flagOffset || this._flagLoaded || this._flagScale) {\n          changed.x = this._offset.x;\n          changed.y = this._offset.y;\n          if (image) {\n            changed.x -= image.width / 2;\n            changed.y -= image.height / 2;\n            if (this._scale instanceof Vector) {\n              changed.x *= this._scale.x;\n              changed.y *= this._scale.y;\n            } else {\n              changed.x *= this._scale;\n              changed.y *= this._scale;\n            }\n          }\n          if (changed.x > 0) {\n            changed.x *= -1;\n          }\n          if (changed.y > 0) {\n            changed.y *= -1;\n          }\n        }\n        if (this._flagScale || this._flagLoaded || this._flagRepeat) {\n          changed.width = 0;\n          changed.height = 0;\n          if (image) {\n            changed.width = image.width;\n            changed.height = image.height;\n            switch (this._repeat) {\n              case \"no-repeat\":\n                changed.width += 1;\n                changed.height += 1;\n                break;\n            }\n            if (this._scale instanceof Vector) {\n              changed.width *= this._scale.x;\n              changed.height *= this._scale.y;\n            } else {\n              changed.width *= this._scale;\n              changed.height *= this._scale;\n            }\n            if (/no-repeat/i.test(this._repeat)) {\n              styles.preserveAspectRatio = \"xMidYMid\";\n            } else {\n              styles.preserveAspectRatio = \"none\";\n            }\n            styles.width = changed.width;\n            styles.height = changed.height;\n          }\n        }\n        if (this._flagScale || this._flagLoaded) {\n          if (!this._renderer.image) {\n            this._renderer.image = svg.createElement(\"image\", styles);\n          } else {\n            svg.setAttributes(this._renderer.image, styles);\n          }\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          changed.patternUnits = \"userSpaceOnUse\";\n          this._renderer.elem = svg.createElement(\"pattern\", changed);\n        } else if (Object.keys(changed).length !== 0) {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._renderer.elem.parentNode === null) {\n          domElement.defs.appendChild(this._renderer.elem);\n        }\n        if (this._renderer.elem && this._renderer.image && !this._renderer.appended) {\n          this._renderer.elem.appendChild(this._renderer.image);\n          this._renderer.appended = true;\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    }\n  };\n  var Renderer2 = class extends Events {\n    constructor(params) {\n      super();\n      this.domElement = params.domElement || svg.createElement(\"svg\");\n      this.scene = new Group();\n      this.scene.parent = this;\n      this.defs = svg.createElement(\"defs\");\n      this.defs._flagUpdate = false;\n      this.domElement.appendChild(this.defs);\n      this.domElement.defs = this.defs;\n      this.domElement.style.overflow = \"hidden\";\n    }\n    /**\n     * @name Two.SVGRenderer.Utils\n     * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<svg />`.\n     */\n    static Utils = svg;\n    /**\n     * @name Two.SVGRenderer#setSize\n     * @function\n     * @param {Number} width - The new width of the renderer.\n     * @param {Number} height - The new height of the renderer.\n     * @description Change the size of the renderer.\n     * @nota-bene Triggers a `Two.Events.resize`.\n     */\n    setSize(width, height) {\n      this.width = width;\n      this.height = height;\n      svg.setAttributes(this.domElement, {\n        width,\n        height\n      });\n      return this.trigger(Events.Types.resize, width, height);\n    }\n    /**\n     * @name Two.SVGRenderer#render\n     * @function\n     * @description Render the current scene to the `<svg />`.\n     */\n    render() {\n      svg.group.render.call(this.scene, this.domElement);\n      svg.defs.update(this.domElement);\n      return this;\n    }\n  };\n\n  // src/utils/shaders.js\n  var shaders = {\n    create: function(gl, source, type) {\n      const shader = gl.createShader(gl[type]);\n      gl.shaderSource(shader, source);\n      gl.compileShader(shader);\n      const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);\n      if (!compiled) {\n        const error = gl.getShaderInfoLog(shader);\n        gl.deleteShader(shader);\n        throw new TwoError(\"unable to compile shader \" + shader + \": \" + error);\n      }\n      return shader;\n    },\n    types: {\n      vertex: \"VERTEX_SHADER\",\n      fragment: \"FRAGMENT_SHADER\"\n    },\n    path: {\n      vertex: `\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n      uniform vec4 u_rect;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 rectCoords = (a_position * (u_rect.zw - u_rect.xy)) + u_rect.xy;\n        vec2 projected = (u_matrix * vec3(rectCoords, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n      fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec4 texel = texture2D(u_image, v_textureCoords);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `\n    },\n    points: {\n      vertex: `\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform float u_size;\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_PointSize = u_size;\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n      fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n\n      void main() {\n        vec4 texel = texture2D(u_image, gl_PointCoord);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `\n    }\n  };\n\n  // src/renderers/webgl.js\n  var multiplyMatrix = Matrix2.Multiply;\n  var identity = [1, 0, 0, 0, 1, 0, 0, 0, 1];\n  var transformation = new NumArray(9);\n  var CanvasUtils = Renderer.Utils;\n  var vector2 = new Vector();\n  var quad = new NumArray([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);\n  var webgl = {\n    precision: 0.9,\n    isHidden: /(undefined|none|transparent)/i,\n    canvas: root.document ? root.document.createElement(\"canvas\") : { getContext: function() {\n    } },\n    alignments: {\n      left: \"start\",\n      middle: \"center\",\n      right: \"end\"\n    },\n    matrix: new Matrix2(),\n    group: {\n      removeChild: function(child, gl) {\n        if (child.children) {\n          for (let i = 0; i < child.children.length; i++) {\n            webgl.group.removeChild(child.children[i], gl);\n          }\n        }\n        if (child._renderer.texture) {\n          gl.deleteTexture(child._renderer.texture);\n          delete child._renderer.texture;\n        }\n        if (child._renderer.positionBuffer) {\n          gl.deleteBuffer(child._renderer.positionBuffer);\n          delete child._renderer.positionBuffer;\n        }\n      },\n      /**\n       * @function\n       // * @type {(gl: any, programs: any) => any}\n       * @param {WebGLContext} gl\n       * @param {Object} programs\n       */\n      render: function(gl, programs) {\n        if (!this._visible) {\n          return;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const parent = this.parent;\n        const flagParentMatrix = parent._matrix && parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagParentMatrix || flagMatrix) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(\n            transformation,\n            parent._renderer.matrix,\n            this._renderer.matrix\n          );\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.x = this._scale.x;\n            this._renderer.scale.y = this._scale.y;\n          } else {\n            this._renderer.scale.x = this._scale;\n            this._renderer.scale.y = this._scale;\n          }\n          if (!/renderer/i.test(parent._renderer.type)) {\n            this._renderer.scale.x *= parent._renderer.scale.x;\n            this._renderer.scale.y *= parent._renderer.scale.y;\n          }\n          if (flagParentMatrix) {\n            this._flagMatrix = true;\n          }\n        }\n        if (this._mask) {\n          gl.clear(gl.STENCIL_BUFFER_BIT);\n          gl.enable(gl.STENCIL_TEST);\n          gl.stencilFunc(gl.ALWAYS, 1, 0);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n          gl.colorMask(false, false, false, false);\n          const prop = Renderer.Utils.getRendererType(\n            this._mask._renderer.type\n          );\n          webgl[prop].render.call(this._mask, gl, programs, this);\n          gl.stencilFunc(gl.EQUAL, 1, 255);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n          gl.colorMask(true, true, true, true);\n        }\n        this._flagOpacity = parent._flagOpacity || this._flagOpacity;\n        this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1);\n        let i;\n        if (this._flagSubtractions) {\n          for (i = 0; i < this.subtractions.length; i++) {\n            webgl.group.removeChild(this.subtractions[i], gl);\n          }\n        }\n        for (i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          const prop = Renderer.Utils.getRendererType(child._renderer.type);\n          webgl[prop].render.call(child, gl, programs);\n        }\n        if (this._mask) {\n          gl.disable(gl.STENCIL_TEST);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    path: {\n      updateCanvas: function(gl, elem) {\n        let prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y;\n        let isOffset;\n        const commands = elem._renderer.vertices;\n        const canvas3 = this.canvas;\n        const ctx = this.ctx;\n        const ratio = gl.renderer.ratio;\n        const scale = vector2.copy(elem._renderer.scale).multiply(ratio);\n        const stroke = elem._stroke;\n        const linewidth = elem._linewidth;\n        const fill = elem._fill;\n        const opacity = elem._renderer.opacity || elem._opacity;\n        const cap = elem._cap;\n        const join = elem._join;\n        const miter = elem._miter;\n        const closed2 = elem._closed;\n        const dashes = elem.dashes;\n        const length = commands.length;\n        const last = length - 1;\n        canvas3.width = Math.max(\n          Math.ceil(elem._renderer.rect.width * scale.x),\n          1\n        );\n        canvas3.height = Math.max(\n          Math.ceil(elem._renderer.rect.height * scale.y),\n          1\n        );\n        const centroid = elem._renderer.rect.centroid;\n        const cx = centroid.x;\n        const cy = centroid.y;\n        ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            const prop = Renderer.Utils.getRendererType(\n              fill._renderer.type\n            );\n            webgl[prop].render.call(fill, ctx, elem);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            const prop = Renderer.Utils.getRendererType(\n              stroke._renderer.type\n            );\n            webgl[prop].render.call(stroke, ctx, elem);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = getEffectiveStrokeWidth(elem);\n          }\n          if (miter) {\n            ctx.miterLimit = miter;\n          }\n          if (join) {\n            ctx.lineJoin = join;\n          }\n          if (!closed2 && cap) {\n            ctx.lineCap = cap;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        let d, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n        ctx.save();\n        ctx.scale(scale.x, scale.y);\n        ctx.translate(cx, cy);\n        ctx.beginPath();\n        for (let i = 0; i < commands.length; i++) {\n          const b = commands[i];\n          x = b.x;\n          y = b.y;\n          switch (b.command) {\n            case Commands.close:\n              ctx.closePath();\n              break;\n            case Commands.arc:\n              rx = b.rx;\n              ry = b.ry;\n              xAxisRotation = b.xAxisRotation;\n              largeArcFlag = b.largeArcFlag;\n              sweepFlag = b.sweepFlag;\n              prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n              a = commands[prev];\n              ax = a.x;\n              ay = a.y;\n              CanvasUtils.renderSvgArcCommand(\n                ctx,\n                ax,\n                ay,\n                rx,\n                ry,\n                largeArcFlag,\n                sweepFlag,\n                xAxisRotation,\n                x,\n                y\n              );\n              break;\n            case Commands.curve:\n              prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n              a = commands[prev];\n              ar = a.controls && a.controls.right || Vector.zero;\n              bl = b.controls && b.controls.left || Vector.zero;\n              if (a._relative) {\n                vx = ar.x + a.x;\n                vy = ar.y + a.y;\n              } else {\n                vx = ar.x;\n                vy = ar.y;\n              }\n              if (b._relative) {\n                ux = bl.x + b.x;\n                uy = bl.y + b.y;\n              } else {\n                ux = bl.x;\n                uy = bl.y;\n              }\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              if (i >= last && closed2) {\n                c = d;\n                br = b.controls && b.controls.right || Vector.zero;\n                cl = c.controls && c.controls.left || Vector.zero;\n                if (b._relative) {\n                  vx = br.x + b.x;\n                  vy = br.y + b.y;\n                } else {\n                  vx = br.x;\n                  vy = br.y;\n                }\n                if (c._relative) {\n                  ux = cl.x + c.x;\n                  uy = cl.y + c.y;\n                } else {\n                  ux = cl.x;\n                  uy = cl.y;\n                }\n                x = c.x;\n                y = c.y;\n                ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              }\n              break;\n            case Commands.line:\n              ctx.lineTo(x, y);\n              break;\n            case Commands.move:\n              d = b;\n              ctx.moveTo(x, y);\n              break;\n          }\n        }\n        if (closed2) {\n          ctx.closePath();\n        }\n        if (!webgl.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        ctx.restore();\n      },\n      // Returns the rect of a set of verts. Typically takes vertices that are\n      // \"centered\" around 0 and returns them to be anchored upper-left.\n      getBoundingClientRect: function(vertices, border, rect) {\n        let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity, width, height;\n        vertices.forEach(function(v) {\n          const x = v.x, y = v.y, controls = v.controls;\n          let a, b, c, d, cl, cr;\n          top = Math.min(y, top);\n          left = Math.min(x, left);\n          right = Math.max(x, right);\n          bottom = Math.max(y, bottom);\n          if (!v.controls) {\n            return;\n          }\n          cl = controls.left;\n          cr = controls.right;\n          if (!cl || !cr) {\n            return;\n          }\n          a = v._relative ? cl.x + x : cl.x;\n          b = v._relative ? cl.y + y : cl.y;\n          c = v._relative ? cr.x + x : cr.x;\n          d = v._relative ? cr.y + y : cr.y;\n          if (!a || !b || !c || !d) {\n            return;\n          }\n          top = Math.min(b, d, top);\n          left = Math.min(a, c, left);\n          right = Math.max(a, c, right);\n          bottom = Math.max(b, d, bottom);\n        });\n        if (typeof border === \"number\") {\n          top -= border;\n          left -= border;\n          right += border;\n          bottom += border;\n        }\n        width = right - left;\n        height = bottom - top;\n        rect.top = top;\n        rect.left = left;\n        rect.right = right;\n        rect.bottom = bottom;\n        rect.width = width;\n        rect.height = height;\n        if (!rect.centroid) {\n          rect.centroid = {};\n        }\n        rect.centroid.x = -left;\n        rect.centroid.y = -top;\n      },\n      render: function(gl, programs, forcedParent) {\n        if (!this._visible || !this._opacity) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const parent = forcedParent || this.parent;\n        const prop = Renderer.Utils.getRendererType(this._renderer.type);\n        const program = programs[prop];\n        const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const parentChanged = this._renderer.parent !== parent;\n        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;\n        if (flagParentMatrix || flagMatrix || parentChanged) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(\n            transformation,\n            parent._renderer.matrix,\n            this._renderer.matrix\n          );\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          let sx, sy;\n          if (this._scale instanceof Vector) {\n            sx = this._scale.x * parent._renderer.scale.x;\n            sy = this._scale.y * parent._renderer.scale.y;\n          } else {\n            sx = this._scale * parent._renderer.scale.x;\n            sy = this._scale * parent._renderer.scale.y;\n          }\n          this._renderer.scale.x = sx < 0 ? -sx : sx;\n          this._renderer.scale.y = sy < 0 ? -sy : sy;\n          if (parentChanged) {\n            this._renderer.parent = parent;\n          }\n        }\n        if (this._mask) {\n          gl.clear(gl.STENCIL_BUFFER_BIT);\n          gl.enable(gl.STENCIL_TEST);\n          gl.stencilFunc(gl.ALWAYS, 1, 0);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n          gl.colorMask(false, false, false, false);\n          const prop2 = Renderer.Utils.getRendererType(\n            this._mask._renderer.type\n          );\n          webgl[prop2].render.call(this._mask, gl, programs, this);\n          gl.stencilFunc(gl.EQUAL, 1, 255);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n          gl.colorMask(true, true, true, true);\n        }\n        if (flagTexture) {\n          if (!this._renderer.rect) {\n            this._renderer.rect = {};\n          }\n          this._renderer.opacity = this._opacity * parent._renderer.opacity;\n          webgl.path.getBoundingClientRect(\n            this._renderer.vertices,\n            this._linewidth,\n            this._renderer.rect\n          );\n          webgl.updateTexture.call(webgl, gl, this);\n        } else {\n          if (this._fill && this._fill._update) {\n            this._fill._update();\n          }\n          if (this._stroke && this._stroke._update) {\n            this._stroke._update();\n          }\n        }\n        if (this._clip && !forcedParent || !this._renderer.texture) {\n          return this;\n        }\n        if (programs.current !== program) {\n          gl.useProgram(program);\n          gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n          gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n          gl.enableVertexAttribArray(program.position);\n          gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n          if (!programs.resolution.flagged) {\n            gl.uniform2f(\n              gl.getUniformLocation(program, \"u_resolution\"),\n              programs.resolution.width,\n              programs.resolution.height\n            );\n          }\n          programs.current = program;\n        }\n        if (programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n        const rect = this._renderer.rect;\n        gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n        gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n        gl.drawArrays(gl.TRIANGLES, 0, 6);\n        if (this._mask) {\n          gl.disable(gl.STENCIL_TEST);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    points: {\n      // The canvas is a texture that is a rendering of one vertex\n      updateCanvas: function(gl, elem) {\n        let isOffset;\n        const canvas3 = this.canvas;\n        const ctx = this.ctx;\n        const ratio = gl.renderer.ratio;\n        const stroke = elem._stroke;\n        const linewidth = elem._linewidth;\n        const fill = elem._fill;\n        const opacity = elem._renderer.opacity || elem._opacity;\n        const dashes = elem.dashes;\n        const size = elem._size * ratio;\n        let dimension = size;\n        if (!webgl.isHidden.test(stroke)) {\n          dimension += linewidth;\n        }\n        canvas3.width = getPoT(dimension);\n        canvas3.height = canvas3.width;\n        const aspect = dimension / canvas3.width;\n        const cx = canvas3.width / 2;\n        const cy = canvas3.height / 2;\n        ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            const prop = Renderer.Utils.getRendererType(\n              fill._renderer.type\n            );\n            webgl[prop].render.call(fill, ctx, elem);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            const prop = Renderer.Utils.getRendererType(\n              stroke._renderer.type\n            );\n            webgl[prop].render.call(stroke, ctx, elem);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = getEffectiveStrokeWidth(elem) / aspect;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.save();\n        ctx.translate(cx, cy);\n        ctx.scale(webgl.precision, webgl.precision);\n        ctx.beginPath();\n        ctx.arc(0, 0, size / aspect * 0.5, 0, TWO_PI);\n        ctx.restore();\n        if (closed) {\n          ctx.closePath();\n        }\n        if (!webgl.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n      },\n      render: function(gl, programs, forcedParent) {\n        if (!this._visible || !this._opacity) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        let size = this._size;\n        const parent = forcedParent || this.parent;\n        const program = programs[this._renderer.type];\n        const sizeAttenuation = this._sizeAttenuation;\n        const stroke = this._stroke;\n        const linewidth = this._linewidth;\n        const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const parentChanged = this._renderer.parent !== parent;\n        const commands = this._renderer.vertices;\n        const length = this._renderer.collection.length;\n        const flagVertices = this._flagVertices;\n        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;\n        if (flagParentMatrix || flagMatrix || parentChanged) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(\n            transformation,\n            parent._renderer.matrix,\n            this._renderer.matrix\n          );\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          let sx, sy;\n          if (this._scale instanceof Vector) {\n            sx = this._scale.x * parent._renderer.scale.x;\n            sy = this._scale.y * parent._renderer.scale.y;\n          } else {\n            sx = this._scale * parent._renderer.scale.x;\n            sy = this._scale * parent._renderer.scale.y;\n          }\n          this._renderer.scale.x = sx < 0 ? -sx : sx;\n          this._renderer.scale.y = sy < 0 ? -sy : sy;\n          if (parentChanged) {\n            this._renderer.parent = parent;\n          }\n        }\n        if (flagVertices) {\n          const positionBuffer = this._renderer.positionBuffer;\n          if (positionBuffer) {\n            gl.deleteBuffer(positionBuffer);\n          }\n          this._renderer.positionBuffer = gl.createBuffer();\n          gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.positionBuffer);\n          gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n          gl.enableVertexAttribArray(program.position);\n          gl.bufferData(gl.ARRAY_BUFFER, commands, gl.STATIC_DRAW);\n        }\n        if (flagTexture) {\n          this._renderer.opacity = this._opacity * parent._renderer.opacity;\n          webgl.updateTexture.call(webgl, gl, this);\n        } else {\n          if (this._fill && this._fill._update) {\n            this._fill._update();\n          }\n          if (this._stroke && this._stroke._update) {\n            this._stroke._update();\n          }\n        }\n        if (this._clip && !forcedParent || !this._renderer.texture) {\n          return this;\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          size += linewidth;\n        }\n        size /= webgl.precision;\n        if (sizeAttenuation) {\n          size *= Math.max(this._renderer.scale.x, this._renderer.scale.y);\n        }\n        if (programs.current !== program) {\n          gl.useProgram(program);\n          if (!programs.resolution.flagged) {\n            gl.uniform2f(\n              gl.getUniformLocation(program, \"u_resolution\"),\n              programs.resolution.width,\n              programs.resolution.height\n            );\n          }\n          programs.current = program;\n        }\n        if (programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n        gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n        gl.uniform1f(program.size, size * programs.resolution.ratio);\n        gl.drawArrays(gl.POINTS, 0, length);\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    text: {\n      updateCanvas: function(gl, elem) {\n        const canvas3 = this.canvas;\n        const ctx = this.ctx;\n        const ratio = gl.renderer.ratio;\n        const scale = vector2.copy(elem._renderer.scale).multiply(ratio);\n        const stroke = elem._stroke;\n        const linewidth = elem._linewidth;\n        const fill = elem._fill;\n        const opacity = elem._renderer.opacity || elem._opacity;\n        const dashes = elem.dashes;\n        const decoration = elem._decoration;\n        const direction = elem._direction;\n        canvas3.width = Math.max(\n          Math.ceil(elem._renderer.rect.width * scale.x),\n          1\n        );\n        canvas3.height = Math.max(\n          Math.ceil(elem._renderer.rect.height * scale.y),\n          1\n        );\n        const centroid = elem._renderer.rect.centroid;\n        const cx = centroid.x;\n        const cy = centroid.y;\n        let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n        const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;\n        ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n        if (!isOffset) {\n          ctx.font = [\n            elem._style,\n            elem._weight,\n            elem._size + \"px/\" + elem._leading + \"px\",\n            elem._family\n          ].join(\" \");\n        }\n        ctx.textAlign = \"center\";\n        ctx.textBaseline = \"middle\";\n        ctx.textDirection = direction;\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            const prop = Renderer.Utils.getRendererType(\n              fill._renderer.type\n            );\n            webgl[prop].render.call(fill, ctx, elem);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            const prop = Renderer.Utils.getRendererType(\n              stroke._renderer.type\n            );\n            webgl[prop].render.call(stroke, ctx, elem);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = getEffectiveStrokeWidth(elem);\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.save();\n        ctx.scale(scale.x, scale.y);\n        ctx.translate(cx, cy);\n        if (!webgl.isHidden.test(fill)) {\n          if (fill._renderer && fill._renderer.offset) {\n            sx = fill._renderer.scale.x;\n            sy = fill._renderer.scale.y;\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(sx, sy);\n            a = elem._size / fill._renderer.scale.y;\n            b = elem._leading / fill._renderer.scale.y;\n            ctx.font = [\n              elem._style,\n              elem._weight,\n              a + \"px/\",\n              b + \"px\",\n              elem._family\n            ].join(\" \");\n            c = fill._renderer.offset.x / fill._renderer.scale.x;\n            d = fill._renderer.offset.y / fill._renderer.scale.y;\n            ctx.fillText(elem.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.fillText(elem.value, 0, 0);\n          }\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          if (stroke._renderer && stroke._renderer.offset) {\n            sx = stroke._renderer.scale.x;\n            sy = stroke._renderer.scale.y;\n            ctx.save();\n            ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n            ctx.scale(sx, sy);\n            a = elem._size / stroke._renderer.scale.y;\n            b = elem._leading / stroke._renderer.scale.y;\n            ctx.font = [\n              elem._style,\n              elem._weight,\n              a + \"px/\",\n              b + \"px\",\n              elem._family\n            ].join(\" \");\n            c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n            d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n            e = linewidth / stroke._renderer.scale.x;\n            ctx.lineWidth = e;\n            ctx.strokeText(elem.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.strokeText(elem.value, 0, 0);\n          }\n        }\n        if (/(underline|strikethrough)/i.test(decoration)) {\n          const metrics = ctx.measureText(elem.value);\n          switch (decoration) {\n            case \"underline\":\n              y1 = metrics.actualBoundingBoxDescent;\n              y2 = metrics.actualBoundingBoxDescent;\n              break;\n            case \"strikethrough\":\n              y1 = 0;\n              y2 = 0;\n              break;\n          }\n          x1 = -metrics.width / 2;\n          x2 = metrics.width / 2;\n          ctx.lineWidth = Math.max(Math.floor(elem._size / 15), 1);\n          ctx.strokeStyle = ctx.fillStyle;\n          ctx.beginPath();\n          ctx.moveTo(x1, y1);\n          ctx.lineTo(x2, y2);\n          ctx.stroke();\n        }\n        ctx.restore();\n      },\n      getBoundingClientRect: function(elem, rect) {\n        const ctx = webgl.ctx;\n        ctx.font = [\n          elem._style,\n          elem._weight,\n          elem._size + \"px/\" + elem._leading + \"px\",\n          elem._family\n        ].join(\" \");\n        ctx.textAlign = \"center\";\n        ctx.textBaseline = Renderer.Utils.baselines[elem._baseline] || elem._baseline;\n        const metrics = ctx.measureText(elem._value);\n        let width = metrics.width;\n        let height = 1.15 * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);\n        if (this._linewidth && !webgl.isHidden.test(this._stroke)) {\n          width += this._linewidth * 2;\n          height += this._linewidth * 2;\n        }\n        const w = width / 2;\n        const h = height / 2;\n        switch (webgl.alignments[elem._alignment] || elem._alignment) {\n          case webgl.alignments.left:\n            if (elem.direction === \"ltr\") {\n              rect.left = 0;\n              rect.right = width;\n            } else {\n              rect.left = -width;\n              rect.right = 0;\n            }\n            break;\n          case webgl.alignments.right:\n            if (elem.direction === \"ltr\") {\n              rect.left = -width;\n              rect.right = 0;\n            } else {\n              rect.left = 0;\n              rect.right = width;\n            }\n            break;\n          default:\n            rect.left = -w;\n            rect.right = w;\n        }\n        switch (elem._baseline) {\n          case \"bottom\":\n            rect.top = -height;\n            rect.bottom = 0;\n            break;\n          case \"top\":\n            rect.top = 0;\n            rect.bottom = height;\n            break;\n          case \"baseline\":\n            rect.top = -h * 1.5;\n            rect.bottom = h * 0.5;\n            break;\n          default:\n            rect.top = -h;\n            rect.bottom = h;\n        }\n        rect.width = width;\n        rect.height = height;\n        if (!rect.centroid) {\n          rect.centroid = {};\n        }\n        rect.centroid.x = w;\n        rect.centroid.y = h;\n      },\n      render: function(gl, programs, forcedParent) {\n        if (!this._visible || !this._opacity) {\n          return this;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const parent = forcedParent || this.parent;\n        const program = programs[this._renderer.type];\n        const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const parentChanged = this._renderer.parent !== parent;\n        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;\n        if (flagParentMatrix || flagMatrix || parentChanged) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(\n            transformation,\n            parent._renderer.matrix,\n            this._renderer.matrix\n          );\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          let sx, sy;\n          if (this._scale instanceof Vector) {\n            sx = this._scale.x * parent._renderer.scale.x;\n            sy = this._scale.y * parent._renderer.scale.y;\n          } else {\n            sx = this._scale * parent._renderer.scale.x;\n            sy = this._scale * parent._renderer.scale.y;\n          }\n          this._renderer.scale.x = sx < 0 ? -sx : sx;\n          this._renderer.scale.y = sy < 0 ? -sy : sy;\n          if (parentChanged) {\n            this._renderer.parent = parent;\n          }\n        }\n        if (this._mask) {\n          gl.clear(gl.STENCIL_BUFFER_BIT);\n          gl.enable(gl.STENCIL_TEST);\n          gl.stencilFunc(gl.ALWAYS, 1, 0);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n          gl.colorMask(false, false, false, false);\n          const prop = Renderer.Utils.getRendererType(\n            this._mask._renderer.type\n          );\n          webgl[prop].render.call(this._mask, gl, programs, this);\n          gl.stencilFunc(gl.EQUAL, 1, 255);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n          gl.colorMask(true, true, true, true);\n        }\n        if (flagTexture) {\n          if (!this._renderer.rect) {\n            this._renderer.rect = {};\n          }\n          this._renderer.opacity = this._opacity * parent._renderer.opacity;\n          webgl.text.getBoundingClientRect(this, this._renderer.rect);\n          webgl.updateTexture.call(webgl, gl, this);\n        } else {\n          if (this._fill && this._fill._update) {\n            this._fill._update();\n          }\n          if (this._stroke && this._stroke._update) {\n            this._stroke._update();\n          }\n        }\n        if (this._clip && !forcedParent || !this._renderer.texture) {\n          return this;\n        }\n        if (programs.current !== program) {\n          gl.useProgram(program);\n          gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n          gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n          gl.enableVertexAttribArray(program.position);\n          gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n          if (!programs.resolution.flagged) {\n            gl.uniform2f(\n              gl.getUniformLocation(program, \"u_resolution\"),\n              programs.resolution.width,\n              programs.resolution.height\n            );\n          }\n          programs.current = program;\n        }\n        if (programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n        const rect = this._renderer.rect;\n        gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n        gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n        gl.drawArrays(gl.TRIANGLES, 0, 6);\n        if (this._mask) {\n          gl.disable(gl.STENCIL_TEST);\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    \"linear-gradient\": {\n      render: function(ctx, parent) {\n        if (!ctx.canvas.getContext(\"2d\") || !parent) {\n          return;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) {\n          let rect;\n          let lx = this.left._x;\n          let ly = this.left._y;\n          let rx = this.right._x;\n          let ry = this.right._y;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            lx = (lx - 0.5) * rect.width;\n            ly = (ly - 0.5) * rect.height;\n            rx = (rx - 0.5) * rect.width;\n            ry = (ry - 0.5) * rect.height;\n          }\n          this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    \"radial-gradient\": {\n      render: function(ctx, parent) {\n        if (!ctx.canvas.getContext(\"2d\") || !parent) {\n          return;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) {\n          let rect;\n          let cx = this.center._x;\n          let cy = this.center._y;\n          let fx = this.focal._x;\n          let fy = this.focal._y;\n          let radius = this._radius;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            cx = (cx - 0.5) * rect.width * 0.5;\n            cy = (cy - 0.5) * rect.height * 0.5;\n            fx = (fx - 0.5) * rect.width * 0.5;\n            fy = (fy - 0.5) * rect.height * 0.5;\n            radius *= Math.min(rect.width, rect.height);\n          }\n          this._renderer.effect = ctx.createRadialGradient(\n            cx,\n            cy,\n            0,\n            fx,\n            fy,\n            radius\n          );\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    texture: {\n      render: function(ctx, elem) {\n        if (!ctx.canvas.getContext(\"2d\")) {\n          return;\n        }\n        if (_.isFunction(this._renderer.onBeforeRender)) {\n          this._renderer.onBeforeRender();\n        }\n        this._update();\n        const image = this.image;\n        if ((this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) {\n          this._renderer.effect = ctx.createPattern(image, this._repeat);\n        } else if (!this._renderer.effect) {\n          return this.flagReset();\n        }\n        if (this._flagOffset || this._flagLoaded || this._flagScale) {\n          if (!(this._renderer.offset instanceof Vector)) {\n            this._renderer.offset = new Vector();\n          }\n          this._renderer.offset.x = -this._offset.x;\n          this._renderer.offset.y = -this._offset.y;\n          if (image) {\n            this._renderer.offset.x += image.width / 2;\n            this._renderer.offset.y += image.height / 2;\n            if (this._scale instanceof Vector) {\n              this._renderer.offset.x *= this._scale.x;\n              this._renderer.offset.y *= this._scale.y;\n            } else {\n              this._renderer.offset.x *= this._scale;\n              this._renderer.offset.y *= this._scale;\n            }\n          }\n        }\n        if (this._flagScale || this._flagLoaded) {\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          let sx, sy;\n          if (this._scale instanceof Vector) {\n            sx = this._scale.x;\n            sy = this._scale.y;\n          } else {\n            sx = this._scale;\n            sy = this._scale;\n          }\n          this._renderer.scale.x = sx < 0 ? -sx : sx;\n          this._renderer.scale.y = sy < 0 ? -sy : sy;\n        }\n        if (_.isFunction(this._renderer.onAfterRender)) {\n          this._renderer.onAfterRender();\n        }\n        return this.flagReset();\n      }\n    },\n    updateTexture: function(gl, elem) {\n      const prop = Renderer.Utils.getRendererType(elem._renderer.type);\n      this[prop].updateCanvas.call(webgl, gl, elem);\n      if (this.canvas.width <= 0 || this.canvas.height <= 0) {\n        if (elem._renderer.texture) {\n          gl.deleteTexture(elem._renderer.texture);\n        }\n        delete elem._renderer.texture;\n        return;\n      }\n      if (!elem._renderer.texture) {\n        elem._renderer.texture = gl.createTexture();\n      }\n      gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture);\n      gl.texImage2D(\n        gl.TEXTURE_2D,\n        0,\n        gl.RGBA,\n        gl.RGBA,\n        gl.UNSIGNED_BYTE,\n        this.canvas\n      );\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n    },\n    program: {\n      create: function(gl, shaders2) {\n        let program, linked, error;\n        program = gl.createProgram();\n        _.each(shaders2, function(s) {\n          gl.attachShader(program, s);\n        });\n        gl.linkProgram(program);\n        linked = gl.getProgramParameter(program, gl.LINK_STATUS);\n        if (!linked) {\n          error = gl.getProgramInfoLog(program);\n          gl.deleteProgram(program);\n          throw new TwoError(\"unable to link program: \" + error);\n        }\n        return program;\n      }\n    },\n    extensions: {\n      init: function(gl) {\n        const extensions = {};\n        const names = [\n          \"EXT_texture_filter_anisotropic\",\n          \"WEBGL_compressed_texture_s3tc\",\n          \"OES_texture_float_linear\",\n          \"WEBGL_multisampled_render_to_texture\"\n        ];\n        for (let i = 0; i < names.length; i++) {\n          const name = names[i];\n          extensions[name] = webgl.extensions.get(gl, name);\n        }\n        return extensions;\n      },\n      get: function(gl, name) {\n        return gl.getExtension(name) || gl.getExtension(`MOZ_${name}`) || gl.getExtension(`WEBKIT_${name}`);\n      }\n    },\n    TextureRegistry: new Registry()\n  };\n  webgl.ctx = webgl.canvas.getContext(\"2d\");\n  var Renderer3 = class extends Events {\n    constructor(params) {\n      super();\n      let gl, program, vs, fs;\n      this.domElement = params.domElement || document.createElement(\"canvas\");\n      if (typeof params.offscreenElement !== \"undefined\") {\n        webgl.canvas = params.offscreenElement;\n        webgl.ctx = webgl.canvas.getContext(\"2d\");\n      }\n      this.scene = new Group();\n      this.scene.parent = this;\n      this._renderer = {\n        type: \"renderer\",\n        matrix: new NumArray(identity),\n        scale: 1,\n        opacity: 1\n      };\n      this._flagMatrix = true;\n      params = _.defaults(params || {}, {\n        antialias: false,\n        alpha: true,\n        premultipliedAlpha: true,\n        stencil: true,\n        preserveDrawingBuffer: true,\n        overdraw: false\n      });\n      this.overdraw = params.overdraw;\n      gl = this.ctx = this.domElement.getContext(\"webgl\", params) || this.domElement.getContext(\"experimental-webgl\", params);\n      if (!this.ctx) {\n        throw new TwoError(\n          \"unable to create a webgl context. Try using another renderer.\"\n        );\n      }\n      vs = shaders.create(gl, shaders.path.vertex, shaders.types.vertex);\n      fs = shaders.create(gl, shaders.path.fragment, shaders.types.fragment);\n      this.programs = {\n        current: null,\n        buffers: {\n          position: gl.createBuffer()\n        },\n        resolution: {\n          width: 0,\n          height: 0,\n          ratio: 1,\n          flagged: false\n        }\n      };\n      program = this.programs.path = webgl.program.create(gl, [vs, fs]);\n      this.programs.text = this.programs.path;\n      gl.extensions = webgl.extensions.init(gl);\n      gl.renderer = this;\n      program.position = gl.getAttribLocation(program, \"a_position\");\n      program.matrix = gl.getUniformLocation(program, \"u_matrix\");\n      program.rect = gl.getUniformLocation(program, \"u_rect\");\n      const positionBuffer = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n      gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n      gl.enableVertexAttribArray(program.position);\n      gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n      vs = shaders.create(gl, shaders.points.vertex, shaders.types.vertex);\n      fs = shaders.create(gl, shaders.points.fragment, shaders.types.fragment);\n      program = this.programs.points = webgl.program.create(gl, [vs, fs]);\n      program.position = gl.getAttribLocation(program, \"a_position\");\n      program.matrix = gl.getUniformLocation(program, \"u_matrix\");\n      program.size = gl.getUniformLocation(program, \"u_size\");\n      gl.enable(gl.BLEND);\n      gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n      gl.blendEquation(gl.FUNC_ADD);\n      gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n    }\n    /**\n     * @name Two.WebGLRenderer.Utils\n     * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />` through the WebGL API.\n     */\n    static Utils = webgl;\n    /**\n     * @name Two.WebGLRenderer#setSize\n     * @function\n     * @fires resize\n     * @param {Number} width - The new width of the renderer.\n     * @param {Number} height - The new height of the renderer.\n     * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n     * @description Change the size of the renderer.\n     */\n    setSize(width, height, ratio) {\n      let w, h;\n      const ctx = this.ctx;\n      this.width = width;\n      this.height = height;\n      this.ratio = typeof ratio === \"undefined\" ? getRatio(ctx) : ratio;\n      this.domElement.width = width * this.ratio;\n      this.domElement.height = height * this.ratio;\n      if (_.isObject(this.domElement.style)) {\n        _.extend(this.domElement.style, {\n          width: width + \"px\",\n          height: height + \"px\"\n        });\n      }\n      this._renderer.matrix[0] = this._renderer.matrix[4] = this._renderer.scale = this.ratio;\n      this._flagMatrix = true;\n      w = width * this.ratio;\n      h = height * this.ratio;\n      ctx.viewport(0, 0, w, h);\n      this.programs.resolution.width = w;\n      this.programs.resolution.height = h;\n      this.programs.resolution.ratio = this.ratio;\n      this.programs.resolution.flagged = true;\n      return this.trigger(Events.Types.resize, width, height, ratio);\n    }\n    /**\n     * @name Two.WebGLRenderer#render\n     * @function\n     * @description Render the current scene to the `<canvas />`.\n     */\n    render() {\n      const gl = this.ctx;\n      if (!this.overdraw) {\n        gl.clear(gl.COLOR_BUFFER_BIT);\n      }\n      webgl.group.render.call(this.scene, gl, this.programs);\n      this._flagMatrix = false;\n      this.programs.resolution.flagged = true;\n      return this;\n    }\n  };\n\n  // src/two.js\n  var Utils = _.extend(\n    {\n      Error: TwoError,\n      getRatio,\n      read,\n      xhr\n    },\n    _,\n    CanvasPolyfill,\n    curves_exports,\n    math_exports\n  );\n  var Two = class _Two {\n    // Warning: inherit events while overriding static properties\n    /**\n     * @private\n     */\n    _events = new Events();\n    // Getters and setters aren't enumerable\n    get _bound() {\n      return this._events._bound;\n    }\n    set _bound(v) {\n      this._events._bound = v;\n    }\n    addEventListener() {\n      return this._events.addEventListener?.apply(this, arguments);\n    }\n    on() {\n      return this._events.addEventListener?.apply(this, arguments);\n    }\n    bind() {\n      return this._events.addEventListener?.apply(this, arguments);\n    }\n    removeEventListener() {\n      return this._events.removeEventListener?.apply(this, arguments);\n    }\n    off() {\n      return this._events.removeEventListener?.apply(this, arguments);\n    }\n    unbind() {\n      return this._events.removeEventListener?.apply(this, arguments);\n    }\n    dispatchEvent() {\n      return this._events.dispatchEvent?.apply(this, arguments);\n    }\n    trigger() {\n      return this._events.dispatchEvent?.apply(this, arguments);\n    }\n    listen() {\n      return this._events.listen?.apply(this, arguments);\n    }\n    ignore() {\n      return this._events.ignore?.apply(this, arguments);\n    }\n    /**\n     * @name Two#type\n     * @property {String} - A string representing which type of renderer the instance has instantiated.\n     */\n    type = \"\";\n    /**\n     * @name Two#renderer\n     * @property {(Two.SVGRenderer|Two.CanvasRenderer|Two.WebGLRenderer)} - The instantiated rendering class for the instance. For a list of possible rendering types check out Two.Types.\n     */\n    renderer = null;\n    /**\n     * @name Two#scene\n     * @property {Two.Group} - The base level {@link Two.Group} which houses all objects for the instance. Because it is a {@link Two.Group} transformations can be applied to it that will affect all objects in the instance. This is handy as a makeshift inverted camera.\n     */\n    scene = null;\n    /**\n     * @name Two#width\n     * @property {Number} - The width of the instance's dom element.\n     */\n    width = 0;\n    /**\n     * @name Two#height\n     * @property {Number} - The height of the instance's dom element.\n     */\n    height = 0;\n    /**\n     * @name Two#frameCount\n     * @property {Number} - An integer representing how many frames have elapsed.\n     */\n    frameCount = 0;\n    /**\n     * @name Two#timeDelta\n     * @property {Number} - A number representing how much time has elapsed since the last frame in milliseconds.\n     */\n    timeDelta = 0;\n    /**\n     * @name Two#playing\n     * @property {Boolean} - A boolean representing whether or not the instance is being updated through the automatic `requestAnimationFrame`.\n     */\n    playing = false;\n    constructor(options) {\n      const params = _.defaults(options || {}, {\n        fullscreen: false,\n        fitted: false,\n        width: 640,\n        height: 480,\n        type: _Two.Types.svg,\n        autostart: false\n      });\n      _.each(\n        params,\n        function(v, k) {\n          if (/fullscreen/i.test(k) || /autostart/i.test(k)) {\n            return;\n          }\n          this[k] = v;\n        },\n        this\n      );\n      if (_.isElement(params.domElement)) {\n        const tagName = params.domElement.tagName.toLowerCase();\n        if (!/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test(\n          this.type + \"-\" + tagName\n        )) {\n          this.type = _Two.Types[tagName];\n        }\n      }\n      this.renderer = new _Two[this.type](this);\n      this.setPlaying(params.autostart);\n      this.frameCount = 0;\n      if (params.fullscreen) {\n        this.fit = fitToWindow.bind(this);\n        this.fit.domElement = window;\n        this.fit.attached = true;\n        _.extend(document.body.style, {\n          overflow: \"hidden\",\n          margin: 0,\n          padding: 0,\n          top: 0,\n          left: 0,\n          right: 0,\n          bottom: 0,\n          position: \"fixed\"\n        });\n        _.extend(this.renderer.domElement.style, {\n          display: \"block\",\n          top: 0,\n          left: 0,\n          right: 0,\n          bottom: 0,\n          position: \"fixed\"\n        });\n        dom.bind(this.fit.domElement, \"resize\", this.fit);\n        this.fit();\n      } else if (params.fitted) {\n        this.fit = fitToParent.bind(this);\n        _.extend(this.renderer.domElement.style, {\n          display: \"block\"\n        });\n      } else if (typeof params.width === \"number\" && typeof params.height === \"number\") {\n        this.renderer.setSize(params.width, params.height, this.ratio);\n        this.width = params.width;\n        this.height = params.height;\n      }\n      this.renderer.bind(Events.Types.resize, updateDimensions.bind(this));\n      this.scene = this.renderer.scene;\n      _Two.Instances.push(this);\n      if (params.autostart) {\n        raf.init();\n      }\n    }\n    static NextFrameId = Constants.NextFrameId;\n    // Primitive\n    /**\n     * @name Two.Types\n     * @property {Object} - The different rendering types available in the library.\n     */\n    static Types = Constants.Types;\n    /**\n     * @name Two.Version\n     * @property {String} - The current working version of the library, `$version`.\n     */\n    static Version = Constants.Version;\n    /**\n     * @name Two.PublishDate\n     * @property {String} - The automatically generated publish date in the build process to verify version release candidates.\n     */\n    static PublishDate = Constants.PublishDate;\n    /**\n     * @name Two.Identifier\n     * @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.\n     */\n    static Identifier = Constants.Identifier;\n    /**\n     * @name Two.Resolution\n     * @property {Number} - Default amount of vertices to be used for interpreting Arcs and ArcSegments.\n     */\n    static Resolution = Constants.Resolution;\n    /**\n     * @name Two.AutoCalculateImportedMatrices\n     * @property {Boolean} - When importing SVGs through the {@link Two#interpret} and {@link Two#load}, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n     * @nota-bene `false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n     */\n    static AutoCalculateImportedMatrices = Constants.AutoCalculateImportedMatrices;\n    /**\n     * @name Two.Instances\n     * @property {Two[]} - Registered list of all Two.js instances in the current session.\n     */\n    static Instances = Constants.Instances;\n    /**\n     * @function Two.uniqueId\n     * @description Simple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n     * @returns {Number} Ever increasing Number.\n     */\n    static uniqueId = Constants.uniqueId;\n    static Anchor = Anchor;\n    static Collection = Collection;\n    static Events = Events;\n    static Group = Group;\n    static Matrix = Matrix2;\n    static Path = Path;\n    static Registry = Registry;\n    static Element = Element;\n    static Shape = Shape;\n    static Text = Text;\n    static Vector = Vector;\n    static Gradient = Gradient;\n    static Image = Image2;\n    static ImageSequence = ImageSequence;\n    static LinearGradient = LinearGradient;\n    static RadialGradient = RadialGradient;\n    static Sprite = Sprite;\n    static Stop = Stop;\n    static Texture = Texture;\n    static ArcSegment = ArcSegment;\n    static Circle = Circle;\n    static Ellipse = Ellipse;\n    static Line = Line;\n    static Points = Points;\n    static Polygon = Polygon;\n    static Rectangle = Rectangle;\n    static RoundedRectangle = RoundedRectangle;\n    static Star = Star;\n    static CanvasRenderer = Renderer;\n    static SVGRenderer = Renderer2;\n    static WebGLRenderer = Renderer3;\n    /**\n     * @name Two.Commands\n     * @property {Object} - Map of possible path commands. Taken from the SVG specification. Commands include: `move`, `line`, `curve`, `arc`, and `close`.\n     */\n    static Commands = Commands;\n    /**\n     * @name Two.Utils\n     * @property {Object} Utils - A massive object filled with utility functions and properties.\n     * @property {Object} Two.Utils.read - A collection of SVG parsing functions indexed by element name.\n     * @property {Function} Two.Utils.read.path - Parse SVG path element or `d` attribute string.\n     */\n    static Utils = Utils;\n    /**\n     * @name Two#appendTo\n     * @function\n     * @param {Element} elem - The DOM element to append the Two.js stage to.\n     * @description Shorthand method to append your instance of Two.js to the `document`.\n     */\n    appendTo(elem) {\n      elem.appendChild(this.renderer.domElement);\n      if (this.fit) {\n        if (this.fit.domElement !== window) {\n          this.fit.domElement = elem;\n          this.fit.attached = false;\n        }\n        this.update();\n      }\n      return this;\n    }\n    /**\n     * @name Two#play\n     * @function\n     * @fires play\n     * @description Call to start an internal animation loop.\n     * @nota-bene This function initiates a `requestAnimationFrame` loop.\n     */\n    play() {\n      this.playing = true;\n      raf.init();\n      return this.trigger(Events.Types.play);\n    }\n    /**\n     * @name Two#pause\n     * @function\n     * @fires pause\n     * @description Call to stop the internal animation loop for a specific instance of Two.js.\n     */\n    pause() {\n      this.playing = false;\n      return this.trigger(Events.Types.pause);\n    }\n    setPlaying(p) {\n      this.playing = p;\n    }\n    /**\n     * @name Two#release\n     * @function\n     * @param {Two.Element} [obj] - Object to release from event listening. If none provided then the root {@link Two.Group} will be used.\n     * @returns {Two.Element} The object passed for event deallocation.\n     * @description Release a {@link Two.Element}’s events from memory and recurse through its children, effects, and/or vertices.\n     */\n    release(obj) {\n      let i, v, child;\n      if (typeof obj === \"undefined\") {\n        return this.release(this.scene);\n      }\n      if (typeof obj.unbind === \"function\") {\n        obj.unbind();\n      }\n      if (typeof obj.fill === \"object\" && typeof obj.fill.unbind === \"function\") {\n        obj.fill.unbind();\n      }\n      if (typeof obj.stroke === \"object\" && typeof obj.stroke.unbind === \"function\") {\n        obj.stroke.unbind();\n      }\n      if (obj.vertices) {\n        if (typeof obj.vertices.unbind === \"function\") {\n          try {\n            obj.vertices.unbind();\n          } catch (e) {\n          }\n        }\n        for (i = 0; i < obj.vertices.length; i++) {\n          v = obj.vertices[i];\n          if (typeof v.unbind === \"function\") {\n            v.unbind();\n          }\n          if (v.controls) {\n            if (v.controls.left && typeof v.controls.left.unbind === \"function\") {\n              v.controls.left.unbind();\n            }\n            if (v.controls.right && typeof v.controls.right.unbind === \"function\") {\n              v.controls.right.unbind();\n            }\n          }\n        }\n      }\n      if (obj.children) {\n        for (i = 0; i < obj.children.length; i++) {\n          child = obj.children[i];\n          this.release(child);\n        }\n        if (typeof obj.children.unbind === \"function\") {\n          try {\n            obj.children.unbind();\n          } catch (e) {\n          }\n        }\n      }\n      if (obj._renderer) {\n        if (obj._renderer.elem && obj._renderer.elem.parentNode) {\n          obj._renderer.elem.parentNode.removeChild(obj._renderer.elem);\n          delete obj._renderer.elem;\n        }\n        if (this.type === \"WebGLRenderer\" && this.renderer.ctx) {\n          const gl = this.renderer.ctx;\n          if (obj._renderer.texture) {\n            gl.deleteTexture(obj._renderer.texture);\n            delete obj._renderer.texture;\n          }\n          if (obj._renderer.positionBuffer) {\n            gl.deleteBuffer(obj._renderer.positionBuffer);\n            delete obj._renderer.positionBuffer;\n          }\n          if (obj._renderer.effect) {\n            obj._renderer.effect = null;\n          }\n        }\n        if (this.type === \"CanvasRenderer\" && obj._renderer.context) {\n          delete obj._renderer.context;\n        }\n      }\n      return obj;\n    }\n    /**\n     * @name Two#getShapesAtPoint\n     * @function\n     * @param {Number} x - X coordinate in world space.\n     * @param {Number} y - Y coordinate in world space.\n     * @param {Object} [options] - Hit test configuration.\n     * @param {Boolean} [options.visibleOnly=true] - Limit results to visible shapes.\n     * @param {Boolean} [options.includeGroups=false] - Include groups in the hit results.\n     * @param {('all'|'deepest')} [options.mode='all'] - Whether to return all intersecting shapes or only the top-most.\n     * @param {Boolean} [options.deepest] - Alias for `mode: 'deepest'`.\n     * @param {Number} [options.precision] - Segmentation precision for curved geometry.\n     * @param {Number} [options.tolerance=0] - Pixel tolerance applied to hit testing.\n     * @param {Boolean} [options.fill] - Override fill testing behaviour.\n     * @param {Boolean} [options.stroke] - Override stroke testing behaviour.\n     * @param {Function} [options.filter] - Predicate to filter shapes from the result set.\n     * @returns {Two.Shape[]} Ordered list of shapes under the specified point, front to back.\n     * @description Returns shapes underneath the provided coordinates. Coordinates are expected in world space (matching the renderer output).\n     * @nota-bene Delegates to {@link Two.Group#getShapesAtPoint} on the root scene.\n     */\n    getShapesAtPoint(x, y, options) {\n      if (this.scene && typeof this.scene.getShapesAtPoint === \"function\") {\n        return this.scene.getShapesAtPoint(x, y, options);\n      }\n      return [];\n    }\n    /**\n     * @name Two#update\n     * @function\n     * @fires update\n     * @description Update positions and calculations in one pass before rendering. Then render to the canvas.\n     * @nota-bene This function is called automatically if using {@link Two#play} or the `autostart` parameter in construction.\n     */\n    update() {\n      const animated = !!this._lastFrame;\n      const now = _.performance.now();\n      if (animated) {\n        this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3));\n      }\n      this._lastFrame = now;\n      if (this.fit && this.fit.domElement && !this.fit.attached) {\n        dom.bind(this.fit.domElement, \"resize\", this.fit);\n        this.fit.attached = true;\n        this.fit();\n      }\n      const width = this.width;\n      const height = this.height;\n      const renderer = this.renderer;\n      if (width !== renderer.width || height !== renderer.height) {\n        renderer.setSize(width, height, this.ratio);\n      }\n      this.trigger(Events.Types.update, this.frameCount, this.timeDelta);\n      return this.render();\n    }\n    /**\n     * @name Two#render\n     * @function\n     * @fires render\n     * @description Render all drawable and visible objects of the scene.\n     */\n    render() {\n      this.renderer.render();\n      return this.trigger(Events.Types.render, this.frameCount++);\n    }\n    // Convenience Methods\n    /**\n     * @name Two#add\n     * @function\n     * @param {(Two.Shape[]|...Two.Shape)} [objects] - An array of Two.js objects. Alternatively can add objects as individual arguments.\n     * @description A shorthand method to add specific Two.js objects to the scene.\n     */\n    add(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      }\n      this.scene.add(objects);\n      return this;\n    }\n    /**\n     * @name Two#remove\n     * @function\n     * @param {(Two.Shape[]|...Two.Shape)} [objects] - An array of Two.js objects.\n     * @description A shorthand method to remove specific Two.js objects from the scene.\n     */\n    remove(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      }\n      this.scene.remove(objects);\n      return this;\n    }\n    /**\n     * @name Two#clear\n     * @function\n     * @description Removes all objects from the instance's scene. If you intend to have the browser garbage collect this, don't forget to delete the references in your application as well.\n     */\n    clear() {\n      this.scene.remove(this.scene.children);\n      return this;\n    }\n    /**\n     * @name Two#makeLine\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} x2\n     * @param {Number} y2\n     * @returns {Two.Line}\n     * @description Creates a Two.js line and adds it to the scene.\n     */\n    makeLine(x1, y1, x2, y2) {\n      const line = new Line(x1, y1, x2, y2);\n      this.scene.add(line);\n      return line;\n    }\n    /**\n     * @name Two#makeArrow\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} x2\n     * @param {Number} y2\n     * @returns {Two.Path}\n     * @description Creates a Two.js arrow and adds it to the scene.\n     */\n    makeArrow(x1, y1, x2, y2, size) {\n      const headlen = typeof size === \"number\" ? size : 10;\n      const angle = Math.atan2(y2 - y1, x2 - x1);\n      const vertices = [\n        new Anchor(\n          x1,\n          y1,\n          void 0,\n          void 0,\n          void 0,\n          void 0,\n          Commands.move\n        ),\n        new Anchor(\n          x2,\n          y2,\n          void 0,\n          void 0,\n          void 0,\n          void 0,\n          Commands.line\n        ),\n        new Anchor(\n          x2 - headlen * Math.cos(angle - Math.PI / 4),\n          y2 - headlen * Math.sin(angle - Math.PI / 4),\n          void 0,\n          void 0,\n          void 0,\n          void 0,\n          Commands.line\n        ),\n        new Anchor(\n          x2,\n          y2,\n          void 0,\n          void 0,\n          void 0,\n          void 0,\n          Commands.move\n        ),\n        new Anchor(\n          x2 - headlen * Math.cos(angle + Math.PI / 4),\n          y2 - headlen * Math.sin(angle + Math.PI / 4),\n          void 0,\n          void 0,\n          void 0,\n          void 0,\n          Commands.line\n        )\n      ];\n      const path = new Path(vertices, false, false, true);\n      path.noFill();\n      path.cap = \"round\";\n      path.join = \"round\";\n      this.scene.add(path);\n      return path;\n    }\n    /**\n     * @name Two#makeRectangle\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} width\n     * @param {Number} height\n     * @returns {Two.Rectangle}\n     * @description Creates a Two.js rectangle and adds it to the scene.\n     */\n    makeRectangle(x, y, width, height) {\n      const rect = new Rectangle(x, y, width, height);\n      this.scene.add(rect);\n      return rect;\n    }\n    /**\n     * @name Two#makeRoundedRectangle\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} width\n     * @param {Number} height\n     * @param {Number} sides\n     * @returns {Two.RoundedRectangle}\n     * @description Creates a Two.js rounded rectangle and adds it to the scene.\n     */\n    makeRoundedRectangle(x, y, width, height, sides) {\n      const rect = new RoundedRectangle(x, y, width, height, sides);\n      this.scene.add(rect);\n      return rect;\n    }\n    /**\n     * @name Two#makeCircle\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} radius\n     * @param {Number} [resolution=4]\n     * @returns {Two.Circle}\n     * @description Creates a Two.js circle and adds it to the scene.\n     */\n    makeCircle(x, y, radius, resolution) {\n      const circle = new Circle(x, y, radius, resolution);\n      this.scene.add(circle);\n      return circle;\n    }\n    /**\n     * @name Two#makeEllipse\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} rx\n     * @param {Number} ry\n     * @param {Number} [resolution=4]\n     * @returns {Two.Ellipse}\n     * @description Creates a Two.js ellipse and adds it to the scene.\n     */\n    makeEllipse(x, y, rx, ry, resolution) {\n      const ellipse = new Ellipse(x, y, rx, ry, resolution);\n      this.scene.add(ellipse);\n      return ellipse;\n    }\n    /**\n     * @name Two#makeStar\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} outerRadius\n     * @param {Number} innerRadius\n     * @param {Number} sides\n     * @returns {Two.Star}\n     * @description Creates a Two.js star and adds it to the scene.\n     */\n    makeStar(x, y, outerRadius, innerRadius, sides) {\n      const star = new Star(x, y, outerRadius, innerRadius, sides);\n      this.scene.add(star);\n      return star;\n    }\n    /**\n     * @name Two#makeCurve\n     * @function\n     * @param {Two.Anchor[]} [points] - An array of {@link Two.Anchor} points.\n     * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n     * @returns {Two.Path} - Where `path.curved` is set to `true`.\n     * @description Creates a Two.js path that is curved and adds it to the scene.\n     * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n     */\n    makeCurve(points) {\n      const l = arguments.length;\n      if (!Array.isArray(points)) {\n        points = [];\n        for (let i = 0; i < l; i += 2) {\n          const x = arguments[i];\n          if (typeof x !== \"number\") {\n            break;\n          }\n          const y = arguments[i + 1];\n          points.push(new Anchor(x, y));\n        }\n      }\n      const last = arguments[l - 1];\n      const curve = new Path(\n        points,\n        !(typeof last === \"boolean\" ? last : void 0),\n        true\n      );\n      const rect = curve.getBoundingClientRect();\n      curve.center().translation.set(rect.left + rect.width / 2, rect.top + rect.height / 2);\n      this.scene.add(curve);\n      return curve;\n    }\n    /**\n     * @name Two#makePolygon\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} radius\n     * @param {Number} sides\n     * @returns {Two.Polygon}\n     * @description Creates a Two.js polygon and adds it to the scene.\n     */\n    makePolygon(x, y, radius, sides) {\n      const poly = new Polygon(x, y, radius, sides);\n      this.scene.add(poly);\n      return poly;\n    }\n    /**\n     * @name Two#makeArcSegment\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} innerRadius\n     * @param {Number} outerRadius\n     * @param {Number} startAngle\n     * @param {Number} endAngle\n     * @param {Number} [resolution=Two.Resolution] - The number of vertices that should comprise the arc segment.\n     * @returns {Two.ArcSegment}\n     */\n    makeArcSegment(x, y, innerRadius, outerRadius, startAngle, endAngle, resolution) {\n      const arcSegment = new ArcSegment(\n        x,\n        y,\n        innerRadius,\n        outerRadius,\n        startAngle,\n        endAngle,\n        resolution\n      );\n      this.scene.add(arcSegment);\n      return arcSegment;\n    }\n    /**\n     * @name Two#makePoints\n     * @function\n     * @param {Two.Vector[]} [points] - An array of {@link Two.Vector} points\n     * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual agrguments. These will be combined into {@link Two.Vector}s for use in the points object.\n     * @returns {Two.Points}\n     * @description Creates a Two.js points object and adds it to the current scene.\n     */\n    makePoints(p) {\n      const l = arguments.length;\n      let vertices = p;\n      if (!Array.isArray(p)) {\n        vertices = [];\n        for (let i = 0; i < l; i += 2) {\n          const x = arguments[i];\n          if (typeof x !== \"number\") {\n            break;\n          }\n          const y = arguments[i + 1];\n          vertices.push(new Vector(x, y));\n        }\n      }\n      const points = new Points(vertices);\n      this.scene.add(points);\n      return points;\n    }\n    /**\n     * @name Two#makePath\n     * @function\n     * @param {Two.Anchor[]} [points] - An array of {@link Two.Anchor} points\n     * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n     * @returns {Two.Path}\n     * @description Creates a Two.js path and adds it to the scene.\n     * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n     */\n    makePath(p) {\n      const l = arguments.length;\n      let points = p;\n      if (!Array.isArray(p)) {\n        points = [];\n        for (let i = 0; i < l; i += 2) {\n          const x = arguments[i];\n          if (typeof x !== \"number\") {\n            break;\n          }\n          const y = arguments[i + 1];\n          points.push(new Anchor(x, y));\n        }\n      }\n      const last = arguments[l - 1];\n      const path = new Path(\n        points,\n        !(typeof last === \"boolean\" ? last : void 0)\n      );\n      const rect = path.getBoundingClientRect();\n      if (typeof rect.top === \"number\" && typeof rect.left === \"number\" && typeof rect.right === \"number\" && typeof rect.bottom === \"number\") {\n        path.center().translation.set(\n          rect.left + rect.width / 2,\n          rect.top + rect.height / 2\n        );\n      }\n      this.scene.add(path);\n      return path;\n    }\n    /**\n     * @name Two#makeText\n     * @function\n     * @param {String} message\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Object} [styles] - An object to describe any of the {@link Two.Text.Properties} including `fill`, `stroke`, `linewidth`, `family`, `alignment`, `leading`, `opacity`, etc..\n     * @returns {Two.Text}\n     * @description Creates a Two.js text object and adds it to the scene.\n     */\n    makeText(message, x, y, styles) {\n      const text = new Text(message, x, y, styles);\n      this.add(text);\n      return text;\n    }\n    /**\n     * @name Two#makeLinearGradient\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} x2\n     * @param {Number} y2\n     * @param {...Two.Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n     * @returns {Two.LinearGradient}\n     * @description Creates a Two.js linear gradient and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n     */\n    makeLinearGradient(x1, y1, x2, y2) {\n      const stops = Array.prototype.slice.call(arguments, 4);\n      const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n      this.add(gradient);\n      return gradient;\n    }\n    /**\n     * @name Two#makeRadialGradient\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} radius\n     * @param {...Two.Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n     * @returns {Two.RadialGradient}\n     * @description Creates a Two.js linear-gradient object and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n     */\n    makeRadialGradient(x1, y1, radius) {\n      const stops = Array.prototype.slice.call(arguments, 3);\n      const gradient = new RadialGradient(x1, y1, radius, stops);\n      this.add(gradient);\n      return gradient;\n    }\n    /**\n     * @name Two#makeSprite\n     * @function\n     * @param {(String|Two.Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} [columns=1]\n     * @param {Number} [rows=1]\n     * @param {Number} [frameRate=0]\n     * @param {Boolean} [autostart=false]\n     * @returns {Two.Sprite}\n     * @description Creates a Two.js sprite object and adds it to the scene. Sprites can be used for still images as well as animations.\n     */\n    makeSprite(src, x, y, columns, rows, frameRate, autostart) {\n      const sprite = new Sprite(src, x, y, columns, rows, frameRate);\n      if (autostart) {\n        sprite.play();\n      }\n      this.add(sprite);\n      return sprite;\n    }\n    /**\n     * @name Two#makeImage\n     * @function\n     * @param {(String|Two.Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} width\n     * @param {Number} height\n     * @param {String} [mode=\"fill\"]\n     * @returns {Two.Image}\n     * @description Creates a Two.js image object and adds it to the scene. Images are scaled to fit the provided width and height.\n     */\n    makeImage(src, x, y, width, height, mode) {\n      const image = new Image2(src, x, y, width, height, mode);\n      this.add(image);\n      return image;\n    }\n    /**\n     * @name Two#makeImageSequence\n     * @function\n     * @param {(String[]|Two.Texture[])} src - An array of paths or of {@link Two.Textures}.\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} [frameRate=0]\n     * @param {Boolean} [autostart=false]\n     * @returns {Two.ImageSequence}\n     * @description Creates a Two.js image sequence object and adds it to the scene.\n     */\n    makeImageSequence(src, x, y, frameRate, autostart) {\n      const imageSequence = new ImageSequence(src, x, y, frameRate);\n      if (autostart) {\n        imageSequence.play();\n      }\n      this.add(imageSequence);\n      return imageSequence;\n    }\n    /**\n     * @name Two#makeTexture\n     * @function\n     * @param {(String|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement)} [src] - The URL path to an image or a DOM image-like element.\n     * @param {Function} [callback] - Function to be invoked when the image is loaded.\n     * @returns {Two.Texture}\n     * @description Creates a Two.js texture object.\n     */\n    makeTexture(src, callback) {\n      const texture = new Texture(src, callback);\n      return texture;\n    }\n    /**\n     * @name Two#makeGroup\n     * @function\n     * @param {(Two.Shape[]|...Two.Shape)} [objects] - Two.js objects to be added to the group in the form of an array or as individual arguments.\n     * @returns {Two.Group}\n     * @description Creates a Two.js group object and adds it to the scene.\n     */\n    makeGroup(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      }\n      const group = new Group();\n      this.scene.add(group);\n      group.add(objects);\n      return group;\n    }\n    /**\n     * @name Two#interpret\n     * @function\n     * @param {SVGElement} svg - The SVG node to be parsed.\n     * @param {Boolean} shallow - Don't create a top-most group but append all content directly.\n     * @param {Boolean} [add=true] – Automatically add the reconstructed SVG node to scene.\n     * @returns {Two.Group}\n     * @description Interpret an SVG Node and add it to this instance's scene. The distinction should be made that this doesn't `import` svg's, it solely interprets them into something compatible for Two.js - this is slightly different than a direct transcription.\n     */\n    interpret(svg2, shallow, add) {\n      const tag = svg2.tagName.toLowerCase();\n      add = typeof add !== \"undefined\" ? add : true;\n      if (!(tag in read)) {\n        return null;\n      }\n      const node = read[tag].call(this, svg2);\n      if (add) {\n        this.add(shallow && node instanceof Group ? node.children : node);\n      } else if (node.parent) {\n        node.remove();\n      }\n      return node;\n    }\n    /**\n     * @name Two#load\n     * @function\n     * @param {String|SVGElement} pathOrSVGContent - The URL path of an SVG file or an SVG document as text.\n     * @param {Function} [callback] - Function to call once loading has completed.\n     * @returns {Two.Group}\n     * @description Load an SVG file or SVG text and interpret it into Two.js legible objects.\n     */\n    load(pathOrSVGContent, callback) {\n      const group = new Group();\n      let elem, i, child;\n      const attach = function(data) {\n        dom.temp.innerHTML = data;\n        for (i = 0; i < dom.temp.children.length; i++) {\n          elem = dom.temp.children[i];\n          child = this.interpret(elem, false, false);\n          if (child !== null) {\n            group.add(child);\n          }\n        }\n        if (typeof callback === \"function\") {\n          const svg2 = dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children;\n          callback(group, svg2);\n        }\n      }.bind(this);\n      if (/\\.svg$/i.test(pathOrSVGContent)) {\n        xhr(pathOrSVGContent, attach);\n        return group;\n      }\n      attach(pathOrSVGContent);\n      return group;\n    }\n  };\n  function fitToWindow() {\n    const wr = document.body.getBoundingClientRect();\n    const width = this.width = wr.width;\n    const height = this.height = wr.height;\n    this.renderer.setSize(width, height, this.ratio);\n  }\n  function fitToParent() {\n    const parent = this.renderer.domElement.parentElement;\n    if (!parent) {\n      console.warn(\"Two.js: Attempting to fit to parent, but no parent found.\");\n      return;\n    }\n    const wr = parent.getBoundingClientRect();\n    const width = this.width = wr.width;\n    const height = this.height = wr.height;\n    this.renderer.setSize(width, height, this.ratio);\n  }\n  function updateDimensions(width, height) {\n    this.width = width;\n    this.height = height;\n    this.trigger(Events.Types.resize, width, height);\n  }\n  var raf = dom.getRequestAnimationFrame();\n  function loop() {\n    for (let i = 0; i < Two.Instances.length; i++) {\n      const t = Two.Instances[i];\n      if (t.playing) {\n        t.update();\n      }\n    }\n    Two.NextFrameId = raf(loop);\n  }\n  raf.init = function() {\n    loop();\n    raf.init = function() {\n    };\n  };\n  return __toCommonJS(two_exports);\n})().default;\n(function(){if(typeof exports==='object'&&typeof module!=='undefined'){module.exports=Two}})()"
  },
  {
    "path": "build/two.module.js",
    "content": "/*\nMIT License\n\nCopyright (c) 2012 - 2025 @jonobr1 / http://jono.fyi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\nvar __defProp = Object.defineProperty;\nvar __defProps = Object.defineProperties;\nvar __getOwnPropDescs = Object.getOwnPropertyDescriptors;\nvar __getOwnPropSymbols = Object.getOwnPropertySymbols;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __propIsEnum = Object.prototype.propertyIsEnumerable;\nvar __typeError = (msg) => {\n  throw TypeError(msg);\n};\nvar __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\nvar __spreadValues = (a, b) => {\n  for (var prop in b || (b = {}))\n    if (__hasOwnProp.call(b, prop))\n      __defNormalProp(a, prop, b[prop]);\n  if (__getOwnPropSymbols)\n    for (var prop of __getOwnPropSymbols(b)) {\n      if (__propIsEnum.call(b, prop))\n        __defNormalProp(a, prop, b[prop]);\n    }\n  return a;\n};\nvar __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));\nvar __export = (target, all) => {\n  for (var name in all)\n    __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== \"symbol\" ? key + \"\" : key, value);\nvar __accessCheck = (obj, member, msg) => member.has(obj) || __typeError(\"Cannot \" + msg);\nvar __privateGet = (obj, member, getter) => (__accessCheck(obj, member, \"read from private field\"), getter ? getter.call(obj) : member.get(obj));\nvar __privateAdd = (obj, member, value) => member.has(obj) ? __typeError(\"Cannot add the same private member more than once\") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);\nvar __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, \"write to private field\"), setter ? setter.call(obj, value) : member.set(obj, value), value);\n\n// src/utils/canvas-polyfill.js\nvar CanvasPolyfill = {\n  /**\n   * @param {Image}\n   */\n  Image: null,\n  /**\n   * @param {Boolean}\n   */\n  isHeadless: false,\n  /**\n   *\n   * @param {canvas} elem - An element to spoof as a `<canvas />`.\n   * @param {String} [name] - An optional tag and node name to spoof. Defaults to `'canvas'`.\n   * @returns {canvas} - The same `elem` passed in the first argument with updated attributes needed to be used by Two.js.\n   * @description Adds attributes invoked by Two.js in order to execute and run correctly. This is used by headless environments.\n   */\n  shim: function(elem, name) {\n    elem.tagName = elem.nodeName = name || \"canvas\";\n    elem.nodeType = 1;\n    elem.getAttribute = function(prop) {\n      return this[prop];\n    };\n    elem.setAttribute = function(prop, val) {\n      this[prop] = val;\n      return this;\n    };\n    return elem;\n  },\n  /**\n   * @name Two.Utils.polyfill\n   * @function\n   * @param {canvas} canvas - The instanced `Canvas` object provided by `node-canvas`.\n   * @param {Image} [Image] - The prototypical `Image` object provided by `node-canvas`. This is only necessary to pass if you're going to load bitmap imagery.\n   * @returns {canvas} Returns the instanced canvas object you passed from with additional attributes needed for Two.js.\n   * @description Convenience method for defining all the dependencies from the npm package `node-canvas`. See [node-canvas](https://github.com/Automattic/node-canvas) for additional information on setting up HTML5 `<canvas />` drawing in a node.js environment.\n   */\n  polyfill: function(canvas3, Image3) {\n    CanvasPolyfill.shim(canvas3);\n    if (typeof Image3 !== \"undefined\") {\n      CanvasPolyfill.Image = Image3;\n    }\n    CanvasPolyfill.isHeadless = true;\n    return canvas3;\n  }\n};\n\n// src/utils/curves.js\nvar curves_exports = {};\n__export(curves_exports, {\n  Curve: () => Curve,\n  getAnchorsFromArcData: () => getAnchorsFromArcData,\n  getComponentOnCubicBezier: () => getComponentOnCubicBezier,\n  getControlPoints: () => getControlPoints,\n  getCurveBoundingBox: () => getCurveBoundingBox,\n  getCurveFromPoints: () => getCurveFromPoints,\n  getCurveLength: () => getCurveLength,\n  getReflection: () => getReflection,\n  integrate: () => integrate,\n  subdivide: () => subdivide\n});\n\n// src/utils/math.js\nvar math_exports = {};\n__export(math_exports, {\n  HALF_PI: () => HALF_PI,\n  NumArray: () => NumArray,\n  TWO_PI: () => TWO_PI,\n  decomposeMatrix: () => decomposeMatrix,\n  getComputedMatrix: () => getComputedMatrix,\n  getEffectiveStrokeWidth: () => getEffectiveStrokeWidth,\n  getPoT: () => getPoT,\n  lerp: () => lerp,\n  mod: () => mod,\n  setMatrix: () => setMatrix,\n  toFixed: () => toFixed\n});\n\n// src/utils/root.js\nvar root;\nif (typeof window !== \"undefined\") {\n  root = window;\n} else if (typeof global !== \"undefined\") {\n  root = global;\n} else if (typeof self !== \"undefined\") {\n  root = self;\n}\n\n// src/utils/math.js\nvar Matrix;\nvar TWO_PI = Math.PI * 2;\nvar HALF_PI = Math.PI * 0.5;\nfunction decomposeMatrix(matrix, b, c, d, e, f) {\n  let a;\n  if (arguments.length <= 1) {\n    a = matrix.a;\n    b = matrix.b;\n    c = matrix.c;\n    d = matrix.d;\n    e = matrix.e;\n    f = matrix.f;\n  } else {\n    a = matrix;\n  }\n  return {\n    translateX: e,\n    translateY: f,\n    scaleX: Math.sqrt(a * a + b * b),\n    scaleY: Math.sqrt(c * c + d * d),\n    rotation: 180 * Math.atan2(b, a) / Math.PI\n  };\n}\nfunction setMatrix(matrix) {\n  Matrix = matrix;\n}\nfunction getComputedMatrix(object, matrix) {\n  matrix = matrix && matrix.identity() || new Matrix();\n  let parent = object;\n  const matrices = [];\n  while (parent && parent._matrix) {\n    matrices.push(parent._matrix);\n    parent = parent.parent;\n  }\n  matrices.reverse();\n  for (let i = 0; i < matrices.length; i++) {\n    const m = matrices[i];\n    const e = m.elements;\n    matrix.multiply(\n      e[0],\n      e[1],\n      e[2],\n      e[3],\n      e[4],\n      e[5],\n      e[6],\n      e[7],\n      e[8]\n    );\n  }\n  return matrix;\n}\nfunction lerp(a, b, t) {\n  return t * (b - a) + a;\n}\nvar pots = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096];\nfunction getPoT(value) {\n  let i = 0;\n  while (pots[i] && pots[i] < value) {\n    i++;\n  }\n  return pots[i];\n}\nfunction mod(v, l) {\n  while (v < 0) {\n    v += l;\n  }\n  return v % l;\n}\nvar NumArray = root.Float32Array || Array;\nvar floor = Math.floor;\nfunction toFixed(v) {\n  return floor(v * 1e6) / 1e6;\n}\nfunction getEffectiveStrokeWidth(object, worldMatrix) {\n  const linewidth = object._linewidth;\n  if (object.strokeAttenuation) {\n    return linewidth;\n  }\n  if (!worldMatrix) {\n    worldMatrix = object.worldMatrix || getComputedMatrix(object);\n  }\n  const decomposed = decomposeMatrix(\n    worldMatrix.elements[0],\n    worldMatrix.elements[3],\n    worldMatrix.elements[1],\n    worldMatrix.elements[4],\n    worldMatrix.elements[2],\n    worldMatrix.elements[5]\n  );\n  const scale = Math.max(Math.abs(decomposed.scaleX), Math.abs(decomposed.scaleY));\n  return scale > 0 ? linewidth / scale : linewidth;\n}\n\n// src/utils/path-commands.js\nvar Commands = {\n  move: \"M\",\n  line: \"L\",\n  curve: \"C\",\n  arc: \"A\",\n  close: \"Z\"\n};\n\n// src/events.js\nvar Events = class {\n  constructor() {\n    __publicField(this, \"_events\", {});\n    __publicField(this, \"_bound\", false);\n  }\n  /**\n   * @name Two.Events#addEventListener\n   * @function\n   * @param {String} [name] - The name of the event to bind a function to.\n   * @param {Function} [handler] - The function to be invoked when the event is dispatched.\n   * @description Call to add a listener to a specific event name.\n   */\n  addEventListener(name, handler) {\n    const list = this._events[name] || (this._events[name] = []);\n    list.push(handler);\n    this._bound = true;\n    return this;\n  }\n  /**\n   * @name Two.Events#on\n   * @function\n   * @description Alias for {@link Two.Events#addEventListener}.\n   */\n  on() {\n    return this.addEventListener.apply(this, arguments);\n  }\n  /**\n   * @name Two.Events#bind\n   * @function\n   * @description Alias for {@link Two.Events#addEventListener}.\n   */\n  bind() {\n    return this.addEventListener.apply(this, arguments);\n  }\n  /**\n   * @name Two.Events#removeEventListener\n   * @function\n   * @param {String} [name] - The name of the event intended to be removed.\n   * @param {Function} [handler] - The handler intended to be removed.\n   * @description Call to remove listeners from a specific event. If only `name` is passed then all the handlers attached to that `name` will be removed. If no arguments are passed then all handlers for every event on the obejct are removed.\n   */\n  removeEventListener(name, handler) {\n    if (!this._events) {\n      return this;\n    }\n    if (!name && !handler) {\n      this._events = {};\n      this._bound = false;\n      return this;\n    }\n    const names = name ? [name] : Object.keys(this._events);\n    for (let i = 0, l = names.length; i < l; i++) {\n      name = names[i];\n      let list = this._events[name];\n      if (list) {\n        let events = [];\n        if (handler) {\n          for (let j = 0, k = list.length; j < k; j++) {\n            let e = list[j];\n            e = e.handler ? e.handler : e;\n            if (handler !== e) {\n              events.push(e);\n            }\n          }\n        }\n        this._events[name] = events;\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Events#off\n   * @function\n   * @description Alias for {@link Two.Events#removeEventListener}.\n   */\n  off() {\n    return this.removeEventListener.apply(this, arguments);\n  }\n  /**\n   * @name Two.Events#unbind\n   * @function\n   * @description Alias for {@link Two.Events#removeEventListener}.\n   */\n  unbind() {\n    return this.removeEventListener.apply(this, arguments);\n  }\n  /**\n   * @name Two.Events#dispatchEvent\n   * @function\n   * @param {String} name - The name of the event to dispatch.\n   * @param args - Anything can be passed after the name and those will be passed on to handlers attached to the event in the order they are passed.\n   * @description Call to trigger a custom event. Any additional arguments passed after the name will be passed along to the attached handlers.\n   */\n  dispatchEvent(name) {\n    if (!this._events) {\n      return this;\n    }\n    const args = Array.prototype.slice.call(arguments, 1);\n    const events = this._events[name];\n    if (events) {\n      for (let i = 0; i < events.length; i++) {\n        events[i].call(this, ...args);\n      }\n    }\n    return this;\n  }\n  trigger() {\n    return this.dispatchEvent.apply(this, arguments);\n  }\n  listen(obj, name, handler) {\n    const scope = this;\n    if (obj) {\n      e.obj = obj;\n      e.name = name;\n      e.handler = handler;\n      obj.on(name, e);\n    }\n    function e() {\n      handler.apply(scope, arguments);\n    }\n    return scope;\n  }\n  ignore(obj, name, handler) {\n    obj.off(name, handler);\n    return this;\n  }\n};\n/**\n * @name Two.Events.Types\n * @property {Object} - Object of different types of Two.js specific events.\n */\n__publicField(Events, \"Types\", {\n  play: \"play\",\n  pause: \"pause\",\n  update: \"update\",\n  render: \"render\",\n  resize: \"resize\",\n  change: \"change\",\n  remove: \"remove\",\n  insert: \"insert\",\n  order: \"order\",\n  load: \"load\"\n});\n__publicField(Events, \"Methods\", [\n  \"addEventListener\",\n  \"on\",\n  \"removeEventListener\",\n  \"off\",\n  \"unbind\",\n  \"dispatchEvent\",\n  \"trigger\",\n  \"listen\",\n  \"ignore\"\n]);\n\n// src/vector.js\nvar proto = {\n  x: {\n    enumerable: true,\n    get: function() {\n      return this._x;\n    },\n    set: function(v) {\n      if (this._x !== v) {\n        this._x = v;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  },\n  y: {\n    enumerable: true,\n    get: function() {\n      return this._y;\n    },\n    set: function(v) {\n      if (this._y !== v) {\n        this._y = v;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  }\n};\nvar _Vector = class _Vector extends Events {\n  constructor(x = 0, y = 0) {\n    super();\n    /**\n     * @name Two.Vector#_x\n     * @private\n     */\n    __publicField(this, \"_x\", 0);\n    /**\n     * @name Two.Vector#_y\n     * @private\n     */\n    __publicField(this, \"_y\", 0);\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n    this.x = x;\n    this.y = y;\n  }\n  /**\n   * @name Two.Vector.add\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Two.Vector}\n   * @description Add two vectors together.\n   */\n  static add(v1, v2) {\n    return new _Vector(v1.x + v2.x, v1.y + v2.y);\n  }\n  /**\n   * @name Two.Vector.sub\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Two.Vector}\n   * @description Subtract two vectors: `v2` from `v1`.\n   */\n  static sub(v1, v2) {\n    return new _Vector(v1.x - v2.x, v1.y - v2.y);\n  }\n  /**\n   * @name Two.Vector.subtract\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  static subtract(v1, v2) {\n    return _Vector.sub(v1, v2);\n  }\n  /**\n   * @name Two.Vector.ratioBetween\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The ratio betwen two points `v1` and `v2`.\n   */\n  static ratioBetween(v1, v2) {\n    return (v1.x * v2.x + v1.y * v2.y) / (v1.length() * v2.length());\n  }\n  /**\n   * @name Two.Vector.angleBetween\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The angle between points `v1` and `v2`.\n   */\n  static angleBetween(v1, v2) {\n    if (arguments.length >= 4) {\n      const dx2 = arguments[0] - arguments[2];\n      const dy2 = arguments[1] - arguments[3];\n      return Math.atan2(dy2, dx2);\n    }\n    const dx = v1.x - v2.x;\n    const dy = v1.y - v2.y;\n    return Math.atan2(dy, dx);\n  }\n  /**\n   * @name Two.Vector.distanceBetween\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The distance between points `v1` and `v2`. Distance is always positive.\n   */\n  static distanceBetween(v1, v2) {\n    return Math.sqrt(_Vector.distanceBetweenSquared(v1, v2));\n  }\n  /**\n   * @name Two.Vector.distanceBetweenSquared\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The squared distance between points `v1` and `v2`.\n   */\n  static distanceBetweenSquared(v1, v2) {\n    const dx = v1.x - v2.x;\n    const dy = v1.y - v2.y;\n    return dx * dx + dy * dy;\n  }\n  //\n  /**\n   * @name Two.Vector#set\n   * @function\n   * @param {number} x - Value of `x` component\n   * @param {number} y - Value of `y` component\n   */\n  set(x, y) {\n    this.x = x;\n    this.y = y;\n    return this;\n  }\n  /**\n   * @name Two.Vector#copy\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to copy\n   * @description Copy the `x` / `y` components of another object {@link Two.Vector}.\n   */\n  copy(v) {\n    this.x = v.x;\n    this.y = v.y;\n    return this;\n  }\n  /**\n   * @name Two.Vector#clear\n   * @function\n   * @description Set the `x` / `y` component values of the vector to zero.\n   */\n  clear() {\n    this.x = 0;\n    this.y = 0;\n    return this;\n  }\n  /**\n   * @name Two.Vector#clone\n   * @function\n   * @description Create a new vector and copy the existing values onto the newly created instance.\n   * @return {Two.Vector}\n   */\n  clone() {\n    return new _Vector(this.x, this.y);\n  }\n  /**\n   * @name Two.Vector#add\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to add\n   * @description Add an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#add\n   * @function\n   * @param {Number} n - Number to add\n   * @description Add the **same** number to both `x` / `y` component values of the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#add\n   * @function\n   * @param {Number} x - Number to add to `x` component\n   * @param {Number} y - Number to add to `y` component\n   * @description Add `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  add(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === \"number\") {\n        this.x += x;\n        this.y += x;\n      } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n        this.x += x.x;\n        this.y += x.y;\n      }\n    } else {\n      this.x += x;\n      this.y += y;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Vector#addSelf\n   * @function\n   * @description Alias for {@link Two.Vector.add}.\n   */\n  addSelf(v) {\n    return this.add.apply(this, arguments);\n  }\n  /**\n   * @name Two.Vector#sub\n   * @function\n   * @param {Two.Vector} v - The amount as a {@link Two.Vector} to subtract\n   * @description Subtract an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#sub\n   * @function\n   * @param {Number} n - Number to subtract\n   * @description Subtract the **same** number to both `x` / `y` component values of the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#sub\n   * @function\n   * @param {Number} x - Number to subtract from `x` component\n   * @param {Number} y - Number to subtract from `y` component\n   * @description Subtract `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  sub(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === \"number\") {\n        this.x -= x;\n        this.y -= x;\n      } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n        this.x -= x.x;\n        this.y -= x.y;\n      }\n    } else {\n      this.x -= x;\n      this.y -= y;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Vector#subtract\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  subtract() {\n    return this.sub.apply(this, arguments);\n  }\n  /**\n   * @name Two.Vector#subSelf\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  subSelf(v) {\n    return this.sub.apply(this, arguments);\n  }\n  /**\n   * @name Two.Vector#subtractSelf\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  subtractSelf(v) {\n    return this.sub.apply(this, arguments);\n  }\n  /**\n   * @name Two.Vector#multiply\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to multiply\n   * @description Multiply an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#multiply\n   * @function\n   * @param {Number} n - The number to multiply\n   * @description Multiply the **same** number to both x / y component values of the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#multiply\n   * @function\n   * @param {Number} x - The number to multiply to `x` component\n   * @param {Number} y - The number to multiply to `y` component\n   * @description Multiply `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  multiply(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === \"number\") {\n        this.x *= x;\n        this.y *= x;\n      } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n        this.x *= x.x;\n        this.y *= x.y;\n      }\n    } else {\n      this.x *= x;\n      this.y *= y;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Vector#multiplySelf\n   * @function\n   * @description Alias for {@link Two.Vector.multiply}.\n   */\n  multiplySelf(v) {\n    return this.multiply.apply(this, arguments);\n  }\n  /**\n   * @name Two.Vector#multiplyScalar\n   * @function\n   * @param {Number} s - The scalar to multiply by.\n   * @description Mulitiply the vector by a single number. Shorthand to call {@link Two.Vector#multiply} directly.\n   */\n  multiplyScalar(s) {\n    return this.multiply(s);\n  }\n  /**\n   * @name Two.Vector#divide\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to divide\n   * @description Divide an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#divide\n   * @function\n   * @param {Number} n - The number to divide\n   * @description Divide the **same** number to both x / y component values of the instance.\n   * @overloaded\n   */\n  /**\n   * @name Two.Vector#divide\n   * @function\n   * @param {Number} x - The number to divide on the `x` component\n   * @param {Number} y - The number to divide on the `y` component\n   * @description Divide `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  divide(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === \"number\") {\n        this.x /= x;\n        this.y /= x;\n      } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n        this.x /= x.x;\n        this.y /= x.y;\n      }\n    } else {\n      this.x /= x;\n      this.y /= y;\n    }\n    if (isNaN(this.x)) {\n      this.x = 0;\n    }\n    if (isNaN(this.y)) {\n      this.y = 0;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Vector#divideSelf\n   * @function\n   * @description Alias for {@link Two.Vector.divide}.\n   */\n  divideSelf(v) {\n    return this.divide.apply(this, arguments);\n  }\n  /**\n   * @name Two.Vector#divideScalar\n   * @function\n   * @param {Number} s - The scalar to divide by.\n   * @description Divide the vector by a single number. Shorthand to call {@link Two.Vector#divide} directly.\n   */\n  divideScalar(s) {\n    return this.divide(s);\n  }\n  /**\n   * @name Two.Vector#negate\n   * @function\n   * @description Invert each component's sign value.\n   */\n  negate() {\n    return this.multiply(-1);\n  }\n  /**\n   * @name Two.Vector#dot\n   * @function\n   * @returns {Number}\n   * @description Get the [dot product](https://en.wikipedia.org/wiki/Dot_product) of the vector.\n   */\n  dot(v) {\n    return this.x * v.x + this.y * v.y;\n  }\n  /**\n   * @name Two.Vector#length\n   * @function\n   * @returns {Number}\n   * @description Get the length of a vector.\n   */\n  length() {\n    return Math.sqrt(this.lengthSquared());\n  }\n  /**\n   * @name Two.Vector#lengthSquared\n   * @function\n   * @returns {Number}\n   * @description Get the length of the vector to the power of two. Widely used as less expensive than {@link Two.Vector#length} because it isn't square-rooting any numbers.\n   */\n  lengthSquared() {\n    return this.x * this.x + this.y * this.y;\n  }\n  /**\n   * @name Two.Vector#normalize\n   * @function\n   * @description Normalize the vector from negative one to one.\n   */\n  normalize() {\n    return this.divideScalar(this.length());\n  }\n  /**\n   * @name Two.Vector#distanceTo\n   * @function\n   * @returns {Number}\n   * @description Get the distance between two vectors.\n   */\n  distanceTo(v) {\n    return Math.sqrt(this.distanceToSquared(v));\n  }\n  /**\n   * @name Two.Vector#distanceToSquared\n   * @function\n   * @returns {Number}\n   * @description Get the distance between two vectors to the power of two. Widely used as less expensive than {@link Two.Vector#distanceTo} because it isn't square-rooting any numbers.\n   */\n  distanceToSquared(v) {\n    const dx = this.x - v.x;\n    const dy = this.y - v.y;\n    return dx * dx + dy * dy;\n  }\n  /**\n   * @name Two.Vector#setLength\n   * @function\n   * @param {Number} l - length to set vector to.\n   * @description Set the length of a vector.\n   */\n  setLength(l) {\n    return this.normalize().multiplyScalar(l);\n  }\n  /**\n   * @name Two.Vector#equals\n   * @function\n   * @param {Two.Vector} v - The vector to compare against.\n   * @param {Number} [eps=0.0001] - An options epsilon for precision.\n   * @returns {Boolean}\n   * @description Qualify if one vector roughly equal another. With a margin of error defined by epsilon.\n   */\n  equals(v, eps) {\n    eps = typeof eps === \"undefined\" ? 1e-4 : eps;\n    return this.distanceTo(v) < eps;\n  }\n  /**\n   * @name Two.Vector#lerp\n   * @function\n   * @param {Two.Vector} v - The destination vector to step towards.\n   * @param {Number} t - The zero to one value of how close the current vector gets to the destination vector.\n   * @description Linear interpolate one vector to another by an amount `t` defined as a zero to one number.\n   * @see [Matt DesLauriers](https://twitter.com/mattdesl/status/1031305279227478016) has a good thread about this.\n   */\n  lerp(v, t) {\n    const x = (v.x - this.x) * t + this.x;\n    const y = (v.y - this.y) * t + this.y;\n    return this.set(x, y);\n  }\n  /**\n   * @name Two.Vector#isZero\n   * @function\n   * @param {Number} [eps=0.0001] - Optional precision amount to check against.\n   * @returns {Boolean}\n   * @description Check to see if vector is roughly zero, based on the `epsilon` precision value.\n   */\n  isZero(eps) {\n    eps = typeof eps === \"undefined\" ? 1e-4 : eps;\n    return this.length() < eps;\n  }\n  /**\n   * @name Two.Vector#toString\n   * @function\n   * @returns {String}\n   * @description Return a comma-separated string of x, y value. Great for storing in a database.\n   */\n  toString() {\n    return this.x + \", \" + this.y;\n  }\n  /**\n   * @name Two.Vector#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the vector.\n   */\n  toObject() {\n    return { x: toFixed(this.x), y: toFixed(this.y) };\n  }\n  /**\n   * @name Two.Vector#rotate\n   * @function\n   * @param {Number} radians - The amount to rotate the vector by in radians.\n   * @description Rotate a vector.\n   */\n  rotate(radians) {\n    const x = this.x;\n    const y = this.y;\n    const cos7 = Math.cos(radians);\n    const sin7 = Math.sin(radians);\n    this.x = x * cos7 - y * sin7;\n    this.y = x * sin7 + y * cos7;\n    return this;\n  }\n};\n/**\n * @name Two.Vector.zero\n * @readonly\n * @property {Two.Vector} - Handy reference to a vector with component values 0, 0 at all times.\n */\n__publicField(_Vector, \"zero\", new _Vector());\n/**\n * @name Two.Vector.left\n * @readonly\n * @property {Two.Vector} - Handy reference to a vector with component values -1, 0 at all times.\n */\n__publicField(_Vector, \"left\", new _Vector(-1, 0));\n/**\n * @name Two.Vector.right\n * @readonly\n * @property {Two.Vector} - Handy reference to a vector with component values 1, 0 at all times.\n */\n__publicField(_Vector, \"right\", new _Vector(1, 0));\n/**\n * @name Two.Vector.up\n * @readonly\n * @property {Two.Vector} - Handy reference to a vector with component values 0, -1 at all times.\n */\n__publicField(_Vector, \"up\", new _Vector(0, -1));\n/**\n * @name Two.Vector.down\n * @readonly\n * @property {Two.Vector} - Handy reference to a vector with component values 0, 1 at all times.\n */\n__publicField(_Vector, \"down\", new _Vector(0, 1));\nvar Vector = _Vector;\n\n// src/anchor.js\nvar Anchor = class _Anchor extends Vector {\n  constructor(x = 0, y = 0, ax = 0, ay = 0, bx = 0, by = 0, command = Commands.move) {\n    super(x, y);\n    __publicField(this, \"controls\", {\n      left: new Vector(),\n      right: new Vector()\n    });\n    __publicField(this, \"_command\", Commands.move);\n    __publicField(this, \"_relative\", true);\n    __publicField(this, \"_rx\", 0);\n    __publicField(this, \"_ry\", 0);\n    __publicField(this, \"_xAxisRotation\", 0);\n    __publicField(this, \"_largeArcFlag\", 0);\n    __publicField(this, \"_sweepFlag\", 1);\n    for (let prop in proto2) {\n      Object.defineProperty(this, prop, proto2[prop]);\n    }\n    this.command = command;\n    this.relative = true;\n    const broadcast = _Anchor.makeBroadcast(this);\n    this.controls.left.set(ax, ay).addEventListener(Events.Types.change, broadcast);\n    this.controls.right.set(bx, by).addEventListener(Events.Types.change, broadcast);\n  }\n  static makeBroadcast(scope) {\n    return broadcast;\n    function broadcast() {\n      if (scope._bound) {\n        scope.dispatchEvent(Events.Types.change);\n      }\n    }\n  }\n  /**\n   * @name Two.Anchor.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Anchor} to create a new instance\n   * @returns {Two.Anchor}\n   * @description Create a new {@link Two.Anchor} from an object notation of a {@link Two.Anchor}.\n   * @nota-bene Works in conjunction with {@link Two.Anchor#toObject}\n   */\n  static fromObject(obj) {\n    return new _Anchor().copy(obj);\n  }\n  /**\n   * @name Two.Anchor#copy\n   * @function\n   * @param {Two.Anchor} v - The anchor to apply values to.\n   * @description Copy the properties of one {@link Two.Anchor} onto another.\n   */\n  copy(v) {\n    this.x = v.x;\n    this.y = v.y;\n    if (typeof v.command === \"string\") {\n      this.command = v.command;\n    }\n    if (v.controls) {\n      if (v.controls.left) {\n        this.controls.left.copy(v.controls.left);\n      }\n      if (v.controls.right) {\n        this.controls.right.copy(v.controls.right);\n      }\n    }\n    if (typeof v.relative === \"boolean\") {\n      this.relative = v.relative;\n    }\n    if (typeof v.rx === \"number\") {\n      this.rx = v.rx;\n    }\n    if (typeof v.ry === \"number\") {\n      this.ry = v.ry;\n    }\n    if (typeof v.xAxisRotation === \"number\") {\n      this.xAxisRotation = v.xAxisRotation;\n    }\n    if (typeof v.largeArcFlag === \"number\") {\n      this.largeArcFlag = v.largeArcFlag;\n    }\n    if (typeof v.sweepFlag === \"number\") {\n      this.sweepFlag = v.sweepFlag;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Anchor#clone\n   * @function\n   * @returns {Two.Anchor}\n   * @description Create a new {@link Two.Anchor}, set all its values to the current instance and return it for use.\n   */\n  clone() {\n    return new _Anchor().copy(this);\n  }\n  /**\n   * @name Two.Anchor#toObject\n   * @function\n   * @returns {Object} - An object with properties filled out to mirror {@link Two.Anchor}.\n   * @description Create a JSON compatible plain object of the current instance. Intended for use with storing values in a database.\n   * @nota-bene Works in conjunction with {@link Two.Anchor.fromObject}\n   */\n  toObject() {\n    return {\n      x: toFixed(this.x),\n      y: toFixed(this.y),\n      command: this.command,\n      relative: this.relative,\n      controls: {\n        left: this.controls.left.toObject(),\n        right: this.controls.right.toObject()\n      },\n      rx: toFixed(this.rx),\n      ry: toFixed(this.ry),\n      xAxisRotation: toFixed(this.xAxisRotation),\n      largeArcFlag: toFixed(this.largeArcFlag),\n      sweepFlag: toFixed(this.sweepFlag)\n    };\n  }\n  /**\n   * @name Two.Anchor#toString\n   * @function\n   * @returns {String} - A String with comma-separated values reflecting the various values on the current instance.\n   * @description Create a string form of the current instance. Intended for use with storing values in a database. This is lighter to store than the JSON compatible {@link Two.Anchor#toObject}.\n   */\n  toString() {\n    return JSON.stringify(this.toObject());\n  }\n};\nvar proto2 = {\n  command: {\n    enumerable: true,\n    get: function() {\n      return this._command;\n    },\n    set: function(command) {\n      if (this._command !== command) {\n        this._command = command;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  },\n  relative: {\n    enumerable: true,\n    get: function() {\n      return this._relative;\n    },\n    set: function(relative) {\n      if (this._relative !== !!relative) {\n        this._relative = !!relative;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  },\n  rx: {\n    enumerable: true,\n    get: function() {\n      return this._rx;\n    },\n    set: function(rx) {\n      if (this._rx !== rx) {\n        this._rx = rx;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  },\n  ry: {\n    enumerable: true,\n    get: function() {\n      return this._ry;\n    },\n    set: function(ry) {\n      if (this._ry !== ry) {\n        this._ry = ry;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  },\n  xAxisRotation: {\n    enumerable: true,\n    get: function() {\n      return this._xAxisRotation;\n    },\n    set: function(xAxisRotation) {\n      if (this._xAxisRotation !== xAxisRotation) {\n        this._xAxisRotation = xAxisRotation;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  },\n  largeArcFlag: {\n    enumerable: true,\n    get: function() {\n      return this._largeArcFlag;\n    },\n    set: function(largeArcFlag) {\n      if (this._largeArcFlag !== largeArcFlag) {\n        this._largeArcFlag = largeArcFlag;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  },\n  sweepFlag: {\n    get: function() {\n      return this._sweepFlag;\n    },\n    set: function(sweepFlag) {\n      if (this._sweepFlag !== sweepFlag) {\n        this._sweepFlag = sweepFlag;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n  }\n};\n\n// src/constants.js\nvar count = 0;\nvar Constants = {\n  /**\n   * @name Two.NextFrameId\n   * @property {Number}\n   * @description The id of the next `requestAnimationFrame` function. Used to control the (or cancel) the default behavior of Two.js animation loops.\n   */\n  NextFrameId: null,\n  // Primitive\n  /**\n   * @name Two.Types\n   * @property {Object} - The different rendering types available in the library.\n   */\n  Types: {\n    webgl: \"WebGLRenderer\",\n    svg: \"SVGRenderer\",\n    canvas: \"CanvasRenderer\"\n  },\n  /**\n   * @name Two.Version\n   * @property {String} - The current working version of the library.\n   */\n  Version: \"v0.8.23\",\n  /**\n   * @name Two.PublishDate\n   * @property {String} - The automatically generated publish date in the build process to verify version release candidates.\n   */\n  PublishDate: \"2026-01-05T18:28:31.207Z\",\n  /**\n   * @name Two.Identifier\n   * @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.\n   */\n  Identifier: \"two-\",\n  /**\n   * @name Two.Resolution\n   * @property {Number} - Default amount of vertices to be used for interpreting Arcs and ArcSegments.\n   */\n  Resolution: 12,\n  /**\n   * @name Two.AutoCalculateImportedMatrices\n   * @property {Boolean} - When importing SVGs through the {@link Two#interpret} and {@link Two#load}, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n   * @nota-bene `false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n   */\n  AutoCalculateImportedMatrices: true,\n  /**\n   * @name Two.Instances\n   * @property {Two[]} - Registered list of all Two.js instances in the current session.\n   */\n  Instances: [],\n  /**\n   * @function Two.uniqueId\n   * @description Simple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n   * @returns {Number} Ever increasing Number.\n   */\n  uniqueId: function() {\n    return count++;\n  }\n};\n\n// src/utils/curves.js\nvar Curve = {\n  CollinearityEpsilon: Math.pow(10, -30),\n  RecursionLimit: 16,\n  CuspLimit: 0,\n  Tolerance: {\n    distance: 0.25,\n    angle: 0,\n    epsilon: Number.EPSILON\n  },\n  // Lookup tables for abscissas and weights with values for n = 2 .. 16.\n  // As values are symmetric, only store half of them and adapt algorithm\n  // to factor in symmetry.\n  abscissas: [\n    [0.5773502691896],\n    [0, 0.7745966692415],\n    [0.3399810435849, 0.8611363115941],\n    [0, 0.5384693101057, 0.9061798459387],\n    [0.2386191860832, 0.6612093864663, 0.9324695142032],\n    [0, 0.4058451513774, 0.7415311855994, 0.9491079123428],\n    [0.1834346424956, 0.5255324099163, 0.7966664774136, 0.9602898564975],\n    [0, 0.3242534234038, 0.6133714327006, 0.8360311073266, 0.9681602395076],\n    [\n      0.1488743389816,\n      0.4333953941292,\n      0.679409568299,\n      0.865063366689,\n      0.9739065285172\n    ],\n    [\n      0,\n      0.2695431559523,\n      0.5190961292068,\n      0.730152005574,\n      0.8870625997681,\n      0.9782286581461\n    ],\n    [\n      0.1252334085115,\n      0.3678314989982,\n      0.5873179542866,\n      0.7699026741943,\n      0.9041172563705,\n      0.9815606342467\n    ],\n    [\n      0,\n      0.2304583159551,\n      0.4484927510364,\n      0.6423493394403,\n      0.8015780907333,\n      0.917598399223,\n      0.9841830547186\n    ],\n    [\n      0.1080549487073,\n      0.3191123689279,\n      0.5152486363582,\n      0.6872929048117,\n      0.8272013150698,\n      0.9284348836636,\n      0.9862838086968\n    ],\n    [\n      0,\n      0.2011940939974,\n      0.3941513470776,\n      0.5709721726085,\n      0.7244177313602,\n      0.8482065834104,\n      0.9372733924007,\n      0.9879925180205\n    ],\n    [\n      0.0950125098376,\n      0.2816035507793,\n      0.4580167776572,\n      0.6178762444026,\n      0.755404408355,\n      0.8656312023878,\n      0.9445750230732,\n      0.9894009349916\n    ]\n  ],\n  weights: [\n    [1],\n    [0.8888888888889, 0.5555555555556],\n    [0.6521451548625, 0.3478548451375],\n    [0.5688888888889, 0.4786286704994, 0.2369268850562],\n    [0.4679139345727, 0.3607615730481, 0.1713244923792],\n    [0.4179591836735, 0.3818300505051, 0.2797053914893, 0.1294849661689],\n    [0.3626837833784, 0.3137066458779, 0.2223810344534, 0.1012285362904],\n    [\n      0.3302393550013,\n      0.31234707704,\n      0.2606106964029,\n      0.1806481606949,\n      0.0812743883616\n    ],\n    [\n      0.2955242247148,\n      0.26926671931,\n      0.219086362516,\n      0.1494513491506,\n      0.0666713443087\n    ],\n    [\n      0.2729250867779,\n      0.2628045445102,\n      0.233193764592,\n      0.1862902109277,\n      0.1255803694649,\n      0.0556685671162\n    ],\n    [\n      0.2491470458134,\n      0.2334925365384,\n      0.2031674267231,\n      0.1600783285433,\n      0.1069393259953,\n      0.0471753363865\n    ],\n    [\n      0.2325515532309,\n      0.2262831802629,\n      0.2078160475369,\n      0.1781459807619,\n      0.1388735102198,\n      0.0921214998377,\n      0.0404840047653\n    ],\n    [\n      0.2152638534632,\n      0.2051984637213,\n      0.1855383974779,\n      0.1572031671582,\n      0.1215185706879,\n      0.0801580871598,\n      0.0351194603318\n    ],\n    [\n      0.2025782419256,\n      0.1984314853271,\n      0.1861610000156,\n      0.166269205817,\n      0.1395706779262,\n      0.1071592204672,\n      0.0703660474881,\n      0.0307532419961\n    ],\n    [\n      0.1894506104551,\n      0.1826034150449,\n      0.169156519395,\n      0.1495959888166,\n      0.1246289712555,\n      0.0951585116825,\n      0.0622535239386,\n      0.0271524594118\n    ]\n  ]\n};\nfunction getComponentOnCubicBezier(t, a, b, c, d) {\n  const k = 1 - t;\n  return k * k * k * a + 3 * k * k * t * b + 3 * k * t * t * c + t * t * t * d;\n}\nfunction subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n  limit = limit || Curve.RecursionLimit;\n  const amount = limit + 1;\n  if (Math.abs(x1 - x4) < 1e-3 && Math.abs(y1 - y4) < 1e-3) {\n    return [new Anchor(x4, y4)];\n  }\n  const result = [];\n  for (let i = 0; i < amount; i++) {\n    const t = i / amount;\n    const x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n    const y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n    result.push(new Anchor(x, y));\n  }\n  return result;\n}\nfunction getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n  if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) {\n    const dx = x4 - x1;\n    const dy = y4 - y1;\n    return Math.sqrt(dx * dx + dy * dy);\n  }\n  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);\n  function integrand(t) {\n    const dx = (ax * t + bx) * t + cx, dy = (ay * t + by) * t + cy;\n    return Math.sqrt(dx * dx + dy * dy);\n  }\n  return integrate(integrand, 0, 1, limit || Curve.RecursionLimit);\n}\nfunction getCurveBoundingBox(x1, y1, x2, y2, x3, y3, x4, y4) {\n  const tvalues = [];\n  const bounds = [[], []];\n  let a, b, c, t, t1, t2, b2ac, sqrtb2ac;\n  for (let i = 0; i < 2; ++i) {\n    if (i === 0) {\n      b = 6 * x1 - 12 * x2 + 6 * x3;\n      a = -3 * x1 + 9 * x2 - 9 * x3 + 3 * x4;\n      c = 3 * x2 - 3 * x1;\n    } else {\n      b = 6 * y1 - 12 * y2 + 6 * y3;\n      a = -3 * y1 + 9 * y2 - 9 * y3 + 3 * y4;\n      c = 3 * y2 - 3 * y1;\n    }\n    if (Math.abs(a) < 1e-3) {\n      if (Math.abs(b) < 1e-3) {\n        continue;\n      }\n      t = -c / b;\n      if (0 < t && t < 1) {\n        tvalues.push(t);\n      }\n      continue;\n    }\n    b2ac = b * b - 4 * c * a;\n    sqrtb2ac = Math.sqrt(b2ac);\n    if (b2ac < 0) {\n      continue;\n    }\n    t1 = (-b + sqrtb2ac) / (2 * a);\n    if (0 < t1 && t1 < 1) {\n      tvalues.push(t1);\n    }\n    t2 = (-b - sqrtb2ac) / (2 * a);\n    if (0 < t2 && t2 < 1) {\n      tvalues.push(t2);\n    }\n  }\n  let j = tvalues.length;\n  let jlen = j;\n  let mt;\n  while (j--) {\n    t = tvalues[j];\n    mt = 1 - t;\n    bounds[0][j] = mt * mt * mt * x1 + 3 * mt * mt * t * x2 + 3 * mt * t * t * x3 + t * t * t * x4;\n    bounds[1][j] = mt * mt * mt * y1 + 3 * mt * mt * t * y2 + 3 * mt * t * t * y3 + t * t * t * y4;\n  }\n  bounds[0][jlen] = x1;\n  bounds[1][jlen] = y1;\n  bounds[0][jlen + 1] = x4;\n  bounds[1][jlen + 1] = y4;\n  bounds[0].length = bounds[1].length = jlen + 2;\n  return {\n    min: { x: Math.min.apply(0, bounds[0]), y: Math.min.apply(0, bounds[1]) },\n    max: { x: Math.max.apply(0, bounds[0]), y: Math.max.apply(0, bounds[1]) }\n  };\n}\nfunction integrate(f, a, b, n) {\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;\n  while (i < m) {\n    const Ax = A * x[i];\n    sum += w[i++] * (f(B + Ax) + f(B - Ax));\n  }\n  return A * sum;\n}\nfunction getCurveFromPoints(points, closed2) {\n  const l = points.length, last = l - 1;\n  for (let i = 0; i < l; i++) {\n    const point = points[i];\n    const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0);\n    const next = closed2 ? mod(i + 1, l) : Math.min(i + 1, last);\n    const a = points[prev];\n    const b = point;\n    const c = points[next];\n    getControlPoints(a, b, c);\n    b.command = i === 0 ? Commands.move : Commands.curve;\n  }\n}\nfunction getControlPoints(a, b, c) {\n  const a1 = Vector.angleBetween(a, b);\n  const a2 = Vector.angleBetween(c, b);\n  let d1 = Vector.distanceBetween(a, b);\n  let d2 = Vector.distanceBetween(c, b);\n  let mid = (a1 + a2) / 2;\n  if (d1 < 1e-3 || d2 < 1e-3) {\n    if (typeof b.relative === \"boolean\" && !b.relative) {\n      b.controls.left.copy(b);\n      b.controls.right.copy(b);\n    }\n    return b;\n  }\n  d1 *= 0.33;\n  d2 *= 0.33;\n  if (a2 < a1) {\n    mid += HALF_PI;\n  } else {\n    mid -= HALF_PI;\n  }\n  b.controls.left.x = Math.cos(mid) * d1;\n  b.controls.left.y = Math.sin(mid) * d1;\n  mid -= Math.PI;\n  b.controls.right.x = Math.cos(mid) * d2;\n  b.controls.right.y = Math.sin(mid) * d2;\n  if (typeof b.relative === \"boolean\" && !b.relative) {\n    b.controls.left.x += b.x;\n    b.controls.left.y += b.y;\n    b.controls.right.x += b.x;\n    b.controls.right.y += b.y;\n  }\n  return b;\n}\nfunction getReflection(a, b, relative) {\n  return new Vector(\n    2 * a.x - (b.x + a.x) - (relative ? a.x : 0),\n    2 * a.y - (b.y + a.y) - (relative ? a.y : 0)\n  );\n}\nfunction getAnchorsFromArcData(center, xAxisRotation, rx, ry, ts, td, ccw) {\n  const resolution = Constants.Resolution;\n  const anchors = [];\n  for (let i = 0; i < resolution; i++) {\n    let pct = (i + 1) / resolution;\n    if (ccw) {\n      pct = 1 - pct;\n    }\n    const theta = pct * td + ts;\n    const x = rx * Math.cos(theta);\n    const y = ry * Math.sin(theta);\n    const anchor2 = new Anchor(x, y);\n    anchor2.command = Commands.line;\n    anchors.push(anchor2);\n  }\n}\n\n// src/utils/underscore.js\nvar slice = Array.prototype.slice;\nfunction isArrayLike(collection) {\n  if (collection === null || collection === void 0) return false;\n  const length = collection.length;\n  return typeof length == \"number\" && length >= 0 && length < 4294967296;\n}\nvar _ = {\n  isNaN: function(obj) {\n    return typeof obj === \"number\" && obj !== +obj;\n  },\n  isElement: function(obj) {\n    return !!(obj && obj.nodeType === 1);\n  },\n  isObject: function(obj) {\n    const type = typeof obj;\n    return type === \"function\" || type === \"object\" && !!obj;\n  },\n  isFunction: function(obj) {\n    return typeof obj === \"function\";\n  },\n  extend: function(base) {\n    const sources = slice.call(arguments, 1);\n    for (let i = 0; i < sources.length; i++) {\n      const obj = sources[i];\n      for (let k in obj) {\n        base[k] = obj[k];\n      }\n    }\n    return base;\n  },\n  defaults: function(base) {\n    const sources = slice.call(arguments, 1);\n    for (let i = 0; i < sources.length; i++) {\n      const obj = sources[i];\n      for (let k in obj) {\n        if (base[k] === void 0) {\n          base[k] = obj[k];\n        }\n      }\n    }\n    return base;\n  },\n  each: function(obj, iteratee, context) {\n    const ctx = context || this;\n    const keys = !isArrayLike(obj) && Object.keys(obj);\n    const length = (keys || obj).length;\n    for (let i = 0; i < length; i++) {\n      const k = keys ? keys[i] : i;\n      iteratee.call(ctx, obj[k], k, obj);\n    }\n    return obj;\n  },\n  /**\n   * @name Two.Utils.performance\n   * @property {Date} - A special `Date` like object to get the current millis of the session. Used internally to calculate time between frames.\n   * e.g: `Utils.performance.now() // milliseconds since epoch`\n   */\n  performance: root.performance && root.performance.now ? root.performance : Date\n};\n\n// src/utils/dom.js\nvar dom = {\n  hasEventListeners: typeof root.addEventListener === \"function\",\n  bind: function(elem, event, func, bool) {\n    if (this.hasEventListeners) {\n      elem.addEventListener(event, func, !!bool);\n    } else {\n      elem.attachEvent(\"on\" + event, func);\n    }\n    return dom;\n  },\n  unbind: function(elem, event, func, bool) {\n    if (dom.hasEventListeners) {\n      elem.removeEventListeners(event, func, !!bool);\n    } else {\n      elem.detachEvent(\"on\" + event, func);\n    }\n    return dom;\n  },\n  getRequestAnimationFrame: function() {\n    const vendors = [\"ms\", \"moz\", \"webkit\", \"o\"];\n    let lastTime = 0;\n    let request = root.requestAnimationFrame;\n    if (!request) {\n      for (let i = 0; i < vendors.length; i++) {\n        request = root[vendors[i] + \"RequestAnimationFrame\"] || request;\n      }\n      request = request || fallbackRequest;\n    }\n    function fallbackRequest(callback, element) {\n      const currTime = (/* @__PURE__ */ new Date()).getTime();\n      const timeToCall = Math.max(0, 16 - (currTime - lastTime));\n      const id = root.setTimeout(nextRequest, timeToCall);\n      lastTime = currTime + timeToCall;\n      function nextRequest() {\n        callback(currTime + timeToCall);\n      }\n      return id;\n    }\n    return request;\n  }\n};\nvar temp = root.document ? root.document.createElement(\"div\") : {};\ntemp.id = \"help-two-load\";\nObject.defineProperty(dom, \"temp\", {\n  enumerable: true,\n  get: function() {\n    if (_.isElement(temp) && !root.document.head.contains(temp)) {\n      temp.style.display = \"none\";\n      root.document.head.appendChild(temp);\n    }\n    return temp;\n  }\n});\n\n// src/utils/error.js\nvar TwoError = class extends Error {\n  constructor(message) {\n    super();\n    __publicField(this, \"name\", \"Two.js\");\n    __publicField(this, \"message\");\n    this.message = message;\n  }\n};\n\n// src/utils/device-pixel-ratio.js\nvar devicePixelRatio = root.devicePixelRatio || 1;\nfunction getBackingStoreRatio(ctx) {\n  return ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;\n}\nfunction getRatio(ctx) {\n  return devicePixelRatio / getBackingStoreRatio(ctx);\n}\n\n// src/registry.js\nvar Registry = class {\n  constructor() {\n    __publicField(this, \"map\", {});\n  }\n  /**\n   * @name Two.Registry#add\n   * @function\n   * @param {String} id - A unique identifier.\n   * @param obj - Any type of variable to be registered to the directory.\n   * @description Adds any value to the directory. Assigned by the `id`.\n   */\n  add(id, obj) {\n    this.map[id] = obj;\n    return this;\n  }\n  /**\n   * @name Two.Registry#remove\n   * @function\n   * @param {String} id - A unique identifier.\n   * @description Remove any value from the directory by its `id`.\n   */\n  remove(id) {\n    delete this.map[id];\n    return this;\n  }\n  /**\n   * @name Two.Registry#get\n   * @function\n   * @param {String} id - A unique identifier.\n   * @returns {?Object} The associated value. If unavailable then `undefined` is returned.\n   * @description Get a registered value by its `id`.\n   */\n  get(id) {\n    return this.map[id];\n  }\n  /**\n   * @name Two.Registry#contains\n   * @function\n   * @param {String} id - A unique identifier.\n   * @returns {Boolean}\n   * @description Convenience method to see if a value is registered to an `id` already.\n   */\n  contains(id) {\n    return id in this.map;\n  }\n};\n\n// src/collection.js\nvar _events;\nvar Collection = class extends Array {\n  constructor() {\n    super();\n    // Warning: Multiple inheritance hack\n    /**\n     * @private\n     */\n    __privateAdd(this, _events, new Events());\n    if (arguments[0] && Array.isArray(arguments[0])) {\n      if (arguments[0].length > 0) {\n        this.push.apply(this, arguments[0]);\n      }\n    } else if (arguments.length > 0) {\n      this.push.apply(this, arguments);\n    }\n  }\n  // N.B: Technique to disable enumeration on object\n  get _events() {\n    return __privateGet(this, _events);\n  }\n  set _events(e) {\n    __privateSet(this, _events, e);\n  }\n  // Getters and setters aren't enumerable\n  get _bound() {\n    return __privateGet(this, _events)._bound;\n  }\n  set _bound(v) {\n    __privateGet(this, _events)._bound = v;\n  }\n  addEventListener() {\n    var _a;\n    return (_a = __privateGet(this, _events).addEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  on() {\n    var _a;\n    return (_a = __privateGet(this, _events).on) == null ? void 0 : _a.apply(this, arguments);\n  }\n  bind() {\n    var _a;\n    return (_a = __privateGet(this, _events).bind) == null ? void 0 : _a.apply(this, arguments);\n  }\n  removeEventListener() {\n    var _a;\n    return (_a = __privateGet(this, _events).removeEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  off() {\n    var _a;\n    return (_a = __privateGet(this, _events).off) == null ? void 0 : _a.apply(this, arguments);\n  }\n  unbind() {\n    var _a;\n    return (_a = __privateGet(this, _events).unbind) == null ? void 0 : _a.apply(this, arguments);\n  }\n  dispatchEvent() {\n    var _a;\n    return (_a = __privateGet(this, _events).dispatchEvent) == null ? void 0 : _a.apply(this, arguments);\n  }\n  trigger() {\n    var _a;\n    return (_a = __privateGet(this, _events).trigger) == null ? void 0 : _a.apply(this, arguments);\n  }\n  listen() {\n    var _a;\n    return (_a = __privateGet(this, _events).listen) == null ? void 0 : _a.apply(this, arguments);\n  }\n  ignore() {\n    var _a;\n    return (_a = __privateGet(this, _events).ignore) == null ? void 0 : _a.apply(this, arguments);\n  }\n  pop() {\n    const popped = super.pop.apply(this, arguments);\n    this.trigger(Events.Types.remove, [popped]);\n    return popped;\n  }\n  shift() {\n    const shifted = super.shift.apply(this, arguments);\n    this.trigger(Events.Types.remove, [shifted]);\n    return shifted;\n  }\n  push() {\n    const pushed = super.push.apply(this, arguments);\n    this.trigger(Events.Types.insert, arguments);\n    return pushed;\n  }\n  unshift() {\n    const unshifted = super.unshift.apply(this, arguments);\n    this.trigger(Events.Types.insert, arguments);\n    return unshifted;\n  }\n  splice() {\n    const spliced = super.splice.apply(this, arguments);\n    this.trigger(Events.Types.remove, spliced);\n    if (arguments.length > 2) {\n      const inserted = this.slice(\n        arguments[0],\n        arguments[0] + arguments.length - 2\n      );\n      this.trigger(Events.Types.insert, inserted);\n      this.trigger(Events.Types.order);\n    }\n    return spliced;\n  }\n  sort() {\n    super.sort.apply(this, arguments);\n    this.trigger(Events.Types.order);\n    return this;\n  }\n  reverse() {\n    super.reverse.apply(this, arguments);\n    this.trigger(Events.Types.order);\n    return this;\n  }\n  indexOf() {\n    return super.indexOf.apply(this, arguments);\n  }\n  map(func, scope) {\n    const results = [];\n    for (let key = 0; key < this.length; key++) {\n      const value = this[key];\n      let result;\n      if (scope) {\n        result = func.call(scope, value, key);\n      } else {\n        result = func(value, key);\n      }\n      results.push(result);\n    }\n    return results;\n  }\n};\n_events = new WeakMap();\n\n// src/element.js\nvar _Element = class _Element extends Events {\n  constructor() {\n    super();\n    /**\n     * @name Two.Element#_flagId\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Element#id} needs updating.\n     */\n    __publicField(this, \"_flagId\", false);\n    /**\n     * @name Two.Element#_flagClassName\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#className} need updating.\n     */\n    __publicField(this, \"_flagClassName\", false);\n    /**\n     * @name Two.Element#renderer\n     * @property {Object} - Object access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n     * @nota-bene With the {@link Two.SVGRenderer} you can access the underlying SVG element created via `shape.renderer.elem`.\n     */\n    __publicField(this, \"_renderer\", {});\n    /**\n     * @name Two.Element#id\n     * @property {String} - Session specific unique identifier.\n     * @nota-bene In the {@link Two.SVGRenderer} change this to change the underlying SVG element's id too.\n     */\n    __publicField(this, \"_id\", Constants.Identifier + Constants.uniqueId());\n    /**\n     * @name Two.Element#className\n     * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n     * @nota-bene Only rendered to DOM in the SVG renderer.\n     */\n    __publicField(this, \"_className\", \"\");\n    /**\n     * @name Two.Element#classList\n     * @property {String[]}\n     * @description A list of class strings stored if imported / interpreted  from an SVG element.\n     */\n    __publicField(this, \"classList\", []);\n    for (let prop in proto3) {\n      Object.defineProperty(this, prop, proto3[prop]);\n    }\n  }\n  /**\n   * @name Two.Element.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Element} to create a new instance\n   * @returns {Two.Element}\n   * @description Create a new {@link Two.Element} from an object notation of a {@link Two.Element}.\n   * @nota-bene Works in conjunction with {@link Two.Element#toObject}\n   */\n  static fromObject(obj) {\n    const elem = new _Element().copy(obj);\n    if (\"id\" in obj) {\n      elem.id = obj.id;\n    }\n    return elem;\n  }\n  /**\n   * @name Two.Element#flagReset\n   * @function\n   * @description Called internally by Two.js's renderer to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagId = this._flagClassName = false;\n    return this;\n  }\n  copy(element) {\n    if (element.renderer && typeof element.renderer.type === \"string\") {\n      this.renderer.type = element.renderer.type;\n    }\n    if (typeof element.className === \"string\") {\n      this.className = element.className;\n    }\n    return this;\n  }\n  toObject() {\n    return {\n      renderer: { type: this.renderer.type },\n      id: this.id,\n      className: this.className\n    };\n  }\n  /**\n   * @name Two.Element#dispose\n   * @function\n   * @description Release the element's renderer object and detach any events.\n   * This cleans up renderer-specific resources and unbinds all event listeners.\n   */\n  dispose() {\n    if (typeof this.unbind === \"function\") {\n      this.unbind();\n    }\n    if (this._renderer) {\n      if (this._renderer.elem && this._renderer.elem.parentNode) {\n        this._renderer.elem.parentNode.removeChild(this._renderer.elem);\n        delete this._renderer.elem;\n      }\n      if (this.type === \"WebGLRenderer\" && this.renderer.ctx) {\n        const gl = this.renderer.ctx;\n        if (this._renderer.texture) {\n          gl.deleteTexture(this._renderer.texture);\n          delete this._renderer.texture;\n        }\n        if (this._renderer.positionBuffer) {\n          gl.deleteBuffer(this._renderer.positionBuffer);\n          delete this._renderer.positionBuffer;\n        }\n        if (this._renderer.effect) {\n          this._renderer.effect = null;\n        }\n      }\n      if (this.type === \"CanvasRenderer\" && this._renderer.context) {\n        delete this._renderer.context;\n      }\n    }\n    const rendererType = this._renderer.type;\n    this._renderer = { type: rendererType };\n    return this;\n  }\n};\n__publicField(_Element, \"Properties\", [\"renderer\", \"id\", \"className\"]);\nvar Element = _Element;\nvar proto3 = {\n  renderer: {\n    enumerable: false,\n    get: function() {\n      return this._renderer;\n    }\n  },\n  id: {\n    enumerable: true,\n    get: function() {\n      return this._id;\n    },\n    set: function(v) {\n      const id = this._id;\n      if (v === this._id) {\n        return;\n      }\n      this._id = v;\n      this._flagId = true;\n      if (this.parent) {\n        delete this.parent.children.ids[id];\n        this.parent.children.ids[this._id] = this;\n      }\n    }\n  },\n  className: {\n    enumerable: true,\n    get: function() {\n      return this._className;\n    },\n    set: function(v) {\n      if (this._className !== v) {\n        this._flagClassName = true;\n        this.classList = v.split(/\\s+?/);\n        this._className = v;\n      }\n    }\n  }\n};\n\n// src/effects/texture.js\nvar anchor;\nvar regex = {\n  video: /\\.(mp4|webm|ogg)$/i,\n  image: /\\.(jpe?g|png|gif|tiff|webp)$/i,\n  effect: /texture|gradient/i\n};\nif (root.document) {\n  anchor = document.createElement(\"a\");\n}\nvar _Texture = class _Texture extends Element {\n  constructor(src, callback) {\n    super();\n    /**\n     * @name Two.Texture#_flagSrc\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#src} needs updating.\n     */\n    __publicField(this, \"_flagSrc\", false);\n    /**\n     * @name Two.Texture#_flagImage\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#image} needs updating.\n     */\n    __publicField(this, \"_flagImage\", false);\n    /**\n     * @name Two.Texture#_flagVideo\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#video} needs updating.\n     */\n    __publicField(this, \"_flagVideo\", false);\n    /**\n     * @name Two.Texture#_flagLoaded\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#loaded} needs updating.\n     */\n    __publicField(this, \"_flagLoaded\", false);\n    /**\n     * @name Two.Texture#_flagRepeat\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#repeat} needs updating.\n     */\n    __publicField(this, \"_flagRepeat\", false);\n    /**\n     * @name Two.Texture#_flagOffset\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#offset} needs updating.\n     */\n    __publicField(this, \"_flagOffset\", false);\n    /**\n     * @name Two.Texture#_flagScale\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#scale} needs updating.\n     */\n    __publicField(this, \"_flagScale\", false);\n    /**\n     * @name Two.Texture#_src\n     * @private\n     * @see {@link Two.Texture#src}\n     */\n    __publicField(this, \"_src\", \"\");\n    /**\n     * @name Two.Texture#_image\n     * @private\n     * @see {@link Two.Texture#image}\n     */\n    __publicField(this, \"_image\", null);\n    /**\n     * @name Two.Texture#_loaded\n     * @private\n     * @see {@link Two.Texture#loaded}\n     */\n    __publicField(this, \"_loaded\", false);\n    /**\n     * @name Two.Texture#_repeat\n     * @private\n     * @see {@link Two.Texture#repeat}\n     */\n    __publicField(this, \"_repeat\", \"no-repeat\");\n    /**\n     * @name Two.Texture#_scale\n     * @private\n     * @see {@link Two.Texture#scale}\n     */\n    __publicField(this, \"_scale\", 1);\n    /**\n     * @name Two.Texture#_offset\n     * @private\n     * @see {@link Two.Texture#offset}\n     */\n    __publicField(this, \"_offset\", null);\n    for (let prop in proto4) {\n      Object.defineProperty(this, prop, proto4[prop]);\n    }\n    this._renderer.type = \"texture\";\n    this._renderer.flagOffset = FlagOffset.bind(this);\n    this._renderer.flagScale = FlagScale.bind(this);\n    this.loaded = false;\n    this.repeat = \"no-repeat\";\n    this.offset = new Vector();\n    if (typeof callback === \"function\") {\n      const loaded = function() {\n        this.unbind(Events.Types.load, loaded);\n        if (typeof callback === \"function\") {\n          callback();\n        }\n      }.bind(this);\n      this.bind(Events.Types.load, loaded);\n    }\n    if (typeof src === \"string\") {\n      this.src = src;\n    } else if (typeof src === \"object\") {\n      const elemString = Object.prototype.toString.call(src);\n      if (elemString === \"[object HTMLImageElement]\" || elemString === \"[object HTMLCanvasElement]\" || elemString === \"[object HTMLVideoElement]\" || elemString === \"[object Image]\") {\n        this.image = src;\n      }\n    }\n    this._update();\n  }\n  /**\n   * @name Two.Texture.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Texture} to create a new instance\n   * @returns {Two.Texture}\n   * @description Create a new {@link Two.Texture} from an object notation of a {@link Two.Texture}.\n   * @nota-bene Works in conjunction with {@link Two.Texture#toObject}\n   */\n  fromObject(obj) {\n    const texture = new _Texture().copy(obj);\n    if (\"id\" in obj) {\n      texture.id = obj.id;\n    }\n    return texture;\n  }\n  /**\n   * @name Two.Texture.getAbsoluteURL\n   * @property {Function} - Serializes a URL as an absolute path for canonical attribution in {@link Two.Texture.ImageRegistry}.\n   * @param {String} path\n   * @returns {String} - The serialized absolute path.\n   */\n  static getAbsoluteURL(path) {\n    if (!anchor) {\n      return path;\n    }\n    anchor.href = path;\n    return anchor.href;\n  }\n  /**\n   * @name Two.Texture.loadHeadlessBuffer\n   * @property {Function} - Loads an image as a buffer in headless environments.\n   * @param {Two.Texture} texture - The {@link Two.Texture} to be loaded.\n   * @param {Function} onLoad - The callback function to be triggered once the image is loaded.\n   * @nota-bene - This function uses node's `fs.readFileSync` to spoof the `<img />` loading process in the browser.\n   */\n  static loadHeadlessBuffer(texture, onLoad) {\n    texture.image.onload = onLoad;\n    texture.image.src = texture.src;\n  }\n  /**\n   * @name Two.Texture.getTag\n   * @property {Function} - Retrieves the tag name of an image, video, or canvas node.\n   * @param {HTMLImageElement} image - The image to infer the tag name from.\n   * @returns {String} - Returns the tag name of an image, video, or canvas node.\n   */\n  static getTag(image) {\n    return image && image.nodeName && image.nodeName.toLowerCase() || // Headless environments\n    \"img\";\n  }\n  /**\n   * @name Two.Texture.getImage\n   * @property {Function} - Convenience function to set {@link Two.Texture#image} properties with canonical versions set in {@link Two.Texture.ImageRegistry}.\n   * @param {String} src - The URL path of the image.\n   * @returns {HTMLImageElement} - Returns either a cached version of the image or a new one that is registered in {@link Two.Texture.ImageRegistry}.\n   */\n  static getImage(src) {\n    const absoluteSrc = _Texture.getAbsoluteURL(src);\n    if (_Texture.ImageRegistry.contains(absoluteSrc)) {\n      return _Texture.ImageRegistry.get(absoluteSrc);\n    }\n    let image;\n    if (CanvasPolyfill.Image) {\n      image = new CanvasPolyfill.Image();\n      CanvasPolyfill.shim(image, \"img\");\n    } else if (root.document) {\n      if (regex.video.test(absoluteSrc)) {\n        image = document.createElement(\"video\");\n      } else {\n        image = document.createElement(\"img\");\n      }\n    } else {\n      console.warn(\"Two.js: no prototypical image defined for Two.Texture\");\n    }\n    image.crossOrigin = \"anonymous\";\n    image.referrerPolicy = \"no-referrer\";\n    return image;\n  }\n  /**\n   * @name Two.Texture.load\n   * @function\n   * @param {Two.Texture} texture - The texture to load.\n   * @param {Function} callback - The function to be called once the texture is loaded.\n   */\n  static load(texture, callback) {\n    let image = texture.image;\n    let tag = _Texture.getTag(image);\n    if (texture._flagImage) {\n      if (/canvas/i.test(tag)) {\n        _Texture.Register.canvas(texture, callback);\n      } else {\n        texture._src = !CanvasPolyfill.isHeadless && image.getAttribute(\"two-src\") || image.src;\n        _Texture.Register[tag](texture, callback);\n      }\n    }\n    if (texture._flagSrc) {\n      if (!image) {\n        image = _Texture.getImage(texture.src);\n        texture.image = image;\n      }\n      tag = _Texture.getTag(image);\n      _Texture.Register[tag](texture, callback);\n    }\n  }\n  /**\n   * @name Two.Texture#clone\n   * @function\n   * @returns {Two.Texture}\n   * @description Create a new instance of {@link Two.Texture} with the same properties of the current texture.\n   */\n  clone() {\n    const clone = new _Texture(this.src);\n    clone.repeat = this.repeat;\n    clone.offset.copy(this.offset);\n    clone.scale = this.scale;\n    return clone;\n  }\n  /**\n   * @name Two.Texture#copy\n   * @function\n   * @param {Two.Texture} texture - The reference {@link Two.Texture}\n   * @description Copy the properties of one {@link Two.Texture} onto another.\n   */\n  copy(texture) {\n    this.src = texture.src;\n    this.repeat = texture.repeat;\n    this.offset = typeof texture.offset === \"number\" || texture.offset instanceof Vector ? texture.offset : new Vector().copy(texture.offset);\n    this.scale = typeof texture.scale === \"number\" || texture.scale instanceof Vector ? texture.scale : new Vector().copy(texture.scale);\n    return this;\n  }\n  /**\n   * @name Two.Texture#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the texture.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = \"texture\";\n    result.src = this.src;\n    result.repeat = this.repeat;\n    result.offset = this.offset.toObject();\n    result.scale = typeof this.scale === \"number\" ? this.scale : this.scale.toObject();\n    return result;\n  }\n  /**\n   * @name Two.Texture#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagSrc || this._flagImage) {\n      this.trigger(Events.Types.change);\n      if (this._flagSrc || this._flagImage) {\n        this.loaded = false;\n        _Texture.load(\n          this,\n          function() {\n            this.loaded = true;\n            this.trigger(Events.Types.change).trigger(Events.Types.load);\n          }.bind(this)\n        );\n      }\n    }\n    if (this._image && this._image.readyState >= 4) {\n      this._flagVideo = true;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Texture#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagSrc = this._flagImage = this._flagLoaded = this._flagRepeat = this._flagVideo = this._flagScale = this._flagOffset = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Texture#dispose\n   * @function\n   * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n   */\n  dispose() {\n    super.dispose();\n    if (\"elem\" in this._renderer) {\n      const elem = this._renderer.elem;\n      elem.parentNode.removeChild(elem);\n    }\n    if (\"effect\" in this._renderer) {\n      this._renderer.effect = null;\n    }\n    return this;\n  }\n};\n/**\n * @name Two.Texture.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Texture}.\n */\n__publicField(_Texture, \"Properties\", [\"src\", \"loaded\", \"repeat\", \"scale\", \"offset\", \"image\"]);\n/**\n * @name Two.Texture.RegularExpressions\n * @property {Object} - A map of compatible DOM Elements categorized by media format.\n */\n__publicField(_Texture, \"RegularExpressions\", regex);\n/**\n * @name Two.Texture.ImageRegistry\n * @property {Two.Registry} - A canonical listing of image data used in a single session of Two.js.\n * @nota-bene This object is used to cache image data between different textures.\n */\n__publicField(_Texture, \"ImageRegistry\", new Registry());\n/**\n * @name Two.Texture.Register\n * @interface\n * @description A collection of functions to register different types of textures. Used internally by a {@link Two.Texture}.\n */\n__publicField(_Texture, \"Register\", {\n  canvas: function(texture, callback) {\n    texture._src = \"#\" + texture.id;\n    _Texture.ImageRegistry.add(texture.src, texture.image);\n    if (typeof callback === \"function\") {\n      callback();\n    }\n  },\n  img: function(texture, callback) {\n    const image = texture.image;\n    const loaded = function(e) {\n      if (!CanvasPolyfill.isHeadless && image.removeEventListener && typeof image.removeEventListener === \"function\") {\n        image.removeEventListener(\"load\", loaded, false);\n        image.removeEventListener(\"error\", error, false);\n      }\n      if (typeof callback === \"function\") {\n        callback();\n      }\n    };\n    const error = function(e) {\n      if (!CanvasPolyfill.isHeadless && typeof image.removeEventListener === \"function\") {\n        image.removeEventListener(\"load\", loaded, false);\n        image.removeEventListener(\"error\", error, false);\n      }\n      throw new TwoError(\"unable to load \" + texture.src);\n    };\n    if (typeof image.width === \"number\" && image.width > 0 && typeof image.height === \"number\" && image.height > 0) {\n      loaded();\n    } else if (!CanvasPolyfill.isHeadless && typeof image.addEventListener === \"function\") {\n      image.addEventListener(\"load\", loaded, false);\n      image.addEventListener(\"error\", error, false);\n    }\n    texture._src = _Texture.getAbsoluteURL(texture._src);\n    if (!CanvasPolyfill.isHeadless && image && image.getAttribute(\"two-src\")) {\n      return;\n    }\n    if (!CanvasPolyfill.isHeadless) {\n      image.setAttribute(\"two-src\", texture.src);\n    }\n    _Texture.ImageRegistry.add(texture.src, image);\n    if (CanvasPolyfill.isHeadless) {\n      _Texture.loadHeadlessBuffer(texture, loaded);\n    } else {\n      texture.image.src = texture.src;\n    }\n  },\n  video: function(texture, callback) {\n    if (CanvasPolyfill.isHeadless) {\n      throw new TwoError(\n        \"video textures are not implemented in headless environments.\"\n      );\n    }\n    const loaded = function(e) {\n      texture.image.removeEventListener(\"canplaythrough\", loaded, false);\n      texture.image.removeEventListener(\"error\", error, false);\n      texture.image.width = texture.image.videoWidth;\n      texture.image.height = texture.image.videoHeight;\n      if (typeof callback === \"function\") {\n        callback();\n      }\n    };\n    const error = function(e) {\n      texture.image.removeEventListener(\"canplaythrough\", loaded, false);\n      texture.image.removeEventListener(\"error\", error, false);\n      throw new TwoError(\"unable to load \" + texture.src);\n    };\n    texture._src = _Texture.getAbsoluteURL(texture._src);\n    if (!texture.image.getAttribute(\"two-src\")) {\n      texture.image.setAttribute(\"two-src\", texture.src);\n      _Texture.ImageRegistry.add(texture.src, texture.image);\n    }\n    if (texture.image.readyState >= 4) {\n      loaded();\n    } else {\n      texture.image.addEventListener(\"canplaythrough\", loaded, false);\n      texture.image.addEventListener(\"error\", error, false);\n      texture.image.src = texture.src;\n      texture.image.load();\n    }\n  }\n});\nvar Texture = _Texture;\nvar proto4 = {\n  src: {\n    enumerable: true,\n    get: function() {\n      return this._src;\n    },\n    set: function(v) {\n      this._src = v;\n      this._flagSrc = true;\n    }\n  },\n  loaded: {\n    enumerable: true,\n    get: function() {\n      return this._loaded;\n    },\n    set: function(v) {\n      this._loaded = v;\n      this._flagLoaded = true;\n    }\n  },\n  repeat: {\n    enumerable: true,\n    get: function() {\n      return this._repeat;\n    },\n    set: function(v) {\n      this._repeat = v;\n      this._flagRepeat = true;\n    }\n  },\n  image: {\n    enumerable: true,\n    get: function() {\n      return this._image;\n    },\n    set: function(image) {\n      const tag = Texture.getTag(image);\n      let index;\n      switch (tag) {\n        case \"canvas\":\n          index = \"#\" + image.id;\n          break;\n        default:\n          index = image.src;\n      }\n      if (Texture.ImageRegistry.contains(index)) {\n        this._image = Texture.ImageRegistry.get(image.src);\n      } else {\n        this._image = image;\n      }\n      this._flagImage = true;\n    }\n  },\n  offset: {\n    enumerable: true,\n    get: function() {\n      return this._offset;\n    },\n    set: function(v) {\n      if (this._offset) {\n        this._offset.unbind(Events.Types.change, this._renderer.flagOffset);\n      }\n      this._offset = v;\n      this._offset.bind(Events.Types.change, this._renderer.flagOffset);\n      this._flagOffset = true;\n    }\n  },\n  scale: {\n    enumerable: true,\n    get: function() {\n      return this._scale;\n    },\n    set: function(v) {\n      if (this._scale instanceof Vector) {\n        this._scale.unbind(Events.Types.change, this._renderer.flagScale);\n      }\n      this._scale = v;\n      if (this._scale instanceof Vector) {\n        this._scale.bind(Events.Types.change, this._renderer.flagScale);\n      }\n      this._flagScale = true;\n    }\n  }\n};\nfunction FlagOffset() {\n  this._flagOffset = true;\n}\nfunction FlagScale() {\n  this._flagScale = true;\n}\n\n// src/effects/stop.js\nvar _Stop = class _Stop extends Element {\n  constructor(offset, color, opacity) {\n    super();\n    /**\n     * @name Two.Stop#_flagOffset\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#offset} needs updating.\n     */\n    __publicField(this, \"_flagOffset\", true);\n    /**\n     * @name Two.Stop#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#opacity} needs updating.\n     */\n    __publicField(this, \"_flagOpacity\", true);\n    /**\n     * @name Two.Stop#_flagColor\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#color} needs updating.\n     */\n    __publicField(this, \"_flagColor\", true);\n    /**\n     * @name Two.Stop#_offset\n     * @private\n     * @see {@link Two.Stop#offset}\n     */\n    __publicField(this, \"_offset\", 0);\n    /**\n     * @name Two.Stop#_opacity\n     * @private\n     * @see {@link Two.Stop#opacity}\n     */\n    __publicField(this, \"_opacity\", 1);\n    /**\n     * @name Two.Stop#_color\n     * @private\n     * @see {@link Two.Stop#color}\n     */\n    __publicField(this, \"_color\", \"#fff\");\n    for (let prop in proto5) {\n      Object.defineProperty(this, prop, proto5[prop]);\n    }\n    this._renderer.type = \"stop\";\n    this.offset = typeof offset === \"number\" ? offset : _Stop.Index <= 0 ? 0 : 1;\n    this.opacity = typeof opacity === \"number\" ? opacity : 1;\n    this.color = typeof color === \"string\" ? color : _Stop.Index <= 0 ? \"#fff\" : \"#000\";\n    _Stop.Index = (_Stop.Index + 1) % 2;\n  }\n  /**\n   * @name Two.Stop.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Stop} to create a new instance\n   * @returns {Two.Stop}\n   * @description Create a new {@link Two.Stop} from an object notation of a {@link Two.Stop}.\n   * @nota-bene Works in conjunction with {@link Two.Stop#toObject}\n   */\n  static fromObject(obj) {\n    const stop = new _Stop().copy(obj);\n    if (\"id\" in obj) {\n      stop.id = obj.id;\n    }\n    return stop;\n  }\n  /**\n   * @name Two.Stop#copy\n   * @function\n   * @param {Two.Stop} stop - The reference {@link Two.Stop}\n   * @description Copy the properties of one {@link Two.Stop} onto another.\n   */\n  copy(stop) {\n    super.copy.call(this, stop);\n    for (let i = 0; i < _Stop.Properties.length; i++) {\n      const k = _Stop.Properties[i];\n      if (k in stop) {\n        this[k] = stop[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Stop#clone\n   * @function\n   * @param {Two.Gradient} [parent] - The parent gradient to add the clone to.\n   * @returns {Two.Stop}\n   * @description Create a new instance of {@link Two.Stop} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new _Stop();\n    _.each(\n      _Stop.Properties,\n      function(property) {\n        clone[property] = this[property];\n      },\n      this\n    );\n    if (parent && parent.stops) {\n      parent.stops.push(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Stop#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = \"stop\";\n    _.each(\n      _Stop.Properties,\n      (k) => {\n        result[k] = this[k];\n      },\n      this\n    );\n    return result;\n  }\n  /**\n   * @name Two.Stop#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagOffset = this._flagColor = this._flagOpacity = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n/**\n * @name Two.Stop.Index\n * @property {Number} - The current index being referenced for calculating a stop's default offset value.\n */\n__publicField(_Stop, \"Index\", 0);\n/**\n * @name Two.Stop.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Stop}.\n */\n__publicField(_Stop, \"Properties\", [\"offset\", \"opacity\", \"color\"]);\nvar Stop = _Stop;\nvar proto5 = {\n  offset: {\n    enumerable: true,\n    get: function() {\n      return this._offset;\n    },\n    set: function(v) {\n      this._offset = v;\n      this._flagOffset = true;\n      if (this.parent) {\n        this.parent._flagStops = true;\n      }\n    }\n  },\n  opacity: {\n    enumerable: true,\n    get: function() {\n      return this._opacity;\n    },\n    set: function(v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n      if (this.parent) {\n        this.parent._flagStops = true;\n      }\n    }\n  },\n  color: {\n    enumerable: true,\n    get: function() {\n      return this._color;\n    },\n    set: function(v) {\n      this._color = v;\n      this._flagColor = true;\n      if (this.parent) {\n        this.parent._flagStops = true;\n      }\n    }\n  }\n};\n\n// src/effects/gradient.js\nvar _Gradient = class _Gradient extends Element {\n  constructor(stops) {\n    super();\n    __publicField(this, \"_flagStops\", false);\n    __publicField(this, \"_flagSpread\", false);\n    __publicField(this, \"_flagUnits\", false);\n    __publicField(this, \"_spread\", \"\");\n    __publicField(this, \"_units\", \"\");\n    for (let prop in proto6) {\n      Object.defineProperty(this, prop, proto6[prop]);\n    }\n    this._renderer.type = \"gradient\";\n    this._renderer.flagStops = FlagStops.bind(this);\n    this._renderer.bindStops = BindStops.bind(this);\n    this._renderer.unbindStops = UnbindStops.bind(this);\n    this.spread = \"pad\";\n    this.units = \"objectBoundingBox\";\n    if (stops) {\n      this.stops = stops;\n    }\n  }\n  /**\n   * @name Two.Gradient.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Gradient} to create a new instance\n   * @returns {Two.Gradient}\n   * @description Create a new {@link Two.Gradient} from an object notation of a {@link Two.Gradient}.\n   * @nota-bene Works in conjunction with {@link Two.Gradient#toObject}\n   */\n  static fromObject(obj) {\n    let stops = obj.stops;\n    if (stops && stops.length > 0) {\n      stops = stops.map((o) => o instanceof Stop ? o : new Stop().copy(o));\n    }\n    const gradient = new _Gradient(stops).copy(obj);\n    if (\"id\" in obj) {\n      gradient.id = obj.id;\n    }\n    return gradient;\n  }\n  /**\n   * @name Two.Gradient#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Gradient}\n   * @description Create a new instance of {@link Two.Gradient} with the same properties of the current path.\n   */\n  clone(parent) {\n    const stops = this.stops.map((s) => {\n      return s.clone();\n    });\n    const clone = new _Gradient(stops);\n    _.each(\n      _Gradient.Properties,\n      (k) => {\n        clone[k] = this[k];\n      },\n      this\n    );\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Gradient#copy\n   * @function\n   * @param {Two.Gradient} gradient - The reference {@link Two.Gradient}\n   * @description Copy the properties of one {@link Two.Gradient} onto another.\n   */\n  copy(gradient) {\n    super.copy.call(this, gradient);\n    for (let i = 0; i < _Gradient.Properties.length; i++) {\n      const k = _Gradient.Properties[i];\n      if (k in gradient) {\n        this[k] = gradient[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Gradient#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = {\n      stops: this.stops.map((s) => {\n        return s.toObject();\n      })\n    };\n    _.each(\n      _Gradient.Properties,\n      (k) => {\n        result[k] = this[k];\n      },\n      this\n    );\n    return result;\n  }\n  /**\n   * @name Two.Gradient#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagSpread || this._flagStops) {\n      this.trigger(Events.Types.change);\n    }\n    return this;\n  }\n  /**\n   * @name Two.Gradient#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagSpread = this._flagUnits = this._flagStops = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Gradient#dispose\n   * @function\n   * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n   */\n  dispose() {\n    if (\"elem\" in this._renderer) {\n      const elem = this._renderer.elem;\n      elem.parentNode.removeChild(elem);\n    }\n    if (\"effect\" in this._renderer) {\n      this._renderer.effect = null;\n    }\n    return this;\n  }\n};\n/**\n * @name Two.Gradient.Stop\n * @see {@link Two.Stop}\n */\n__publicField(_Gradient, \"Stop\", Stop);\n/**\n * @name Two.Gradient.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Gradient}.\n */\n__publicField(_Gradient, \"Properties\", [\"spread\", \"stops\", \"units\"]);\nvar Gradient = _Gradient;\nvar proto6 = {\n  spread: {\n    enumerable: true,\n    get: function() {\n      return this._spread;\n    },\n    set: function(v) {\n      this._spread = v;\n      this._flagSpread = true;\n    }\n  },\n  units: {\n    enumerable: true,\n    get: function() {\n      return this._units;\n    },\n    set: function(v) {\n      this._units = v;\n      this._flagUnits = true;\n    }\n  },\n  stops: {\n    enumerable: true,\n    get: function() {\n      return this._stops;\n    },\n    set: function(stops) {\n      const bindStops = this._renderer.bindStops;\n      const unbindStops = this._renderer.unbindStops;\n      if (this._stops) {\n        this._stops.unbind(Events.Types.insert, bindStops).unbind(Events.Types.remove, unbindStops);\n      }\n      this._stops = new Collection((stops || []).slice(0));\n      this._stops.bind(Events.Types.insert, bindStops).bind(Events.Types.remove, unbindStops);\n      bindStops(this._stops);\n    }\n  }\n};\nfunction FlagStops() {\n  this._flagStops = true;\n}\nfunction BindStops(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].bind(Events.Types.change, this._renderer.flagStops);\n    items[i].parent = this;\n  }\n  this._renderer.flagStops();\n}\nfunction UnbindStops(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].unbind(Events.Types.change, this._renderer.flagStops);\n    delete items[i].parent;\n  }\n  this._renderer.flagStops();\n}\n\n// src/effects/linear-gradient.js\nvar _LinearGradient = class _LinearGradient extends Gradient {\n  constructor(x1, y1, x2, y2, stops) {\n    super(stops);\n    /**\n     * @name Two.LinearGradient#_flagEndPoints\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.LinearGradient#left} or {@link Two.LinearGradient#right} changed and needs to update.\n     */\n    __publicField(this, \"_flagEndPoints\", false);\n    __publicField(this, \"_left\", null);\n    __publicField(this, \"_right\", null);\n    for (let prop in proto7) {\n      Object.defineProperty(this, prop, proto7[prop]);\n    }\n    this._renderer.type = \"linear-gradient\";\n    this._renderer.flagEndPoints = FlagEndPoints.bind(this);\n    this.left = new Vector();\n    this.right = new Vector();\n    if (typeof x1 === \"number\") {\n      this.left.x = x1;\n    }\n    if (typeof y1 === \"number\") {\n      this.left.y = y1;\n    }\n    if (typeof x2 === \"number\") {\n      this.right.x = x2;\n    }\n    if (typeof y2 === \"number\") {\n      this.right.y = y2;\n    }\n  }\n  /**\n   * @name Two.LinearGradient.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.LinearGradient} to create a new instance\n   * @returns {Two.LinearGradient}\n   * @description Create a new {@link Two.LinearGradient} from an object notation of a {@link Two.LinearGradient}.\n   * @nota-bene Works in conjunction with {@link Two.LinearGradient#toObject}\n   */\n  static fromObject(obj) {\n    const gradient = new _LinearGradient().copy(obj);\n    if (\"id\" in obj) {\n      gradient.id = obj.id;\n    }\n    return gradient;\n  }\n  /**\n   * @name Two.LinearGradient#copy\n   * @function\n   * @param {Two.LinearGradient} gradient - The reference {@link Two.LinearGradient}\n   * @description Copy the properties of one {@link Two.LinearGradient} onto another.\n   */\n  copy(gradient) {\n    super.copy.call(this, gradient);\n    for (let i = 0; i < _LinearGradient.Properties.length; i++) {\n      const k = _LinearGradient.Properties[i];\n      if (k in gradient) {\n        this[k] = gradient[k] instanceof Vector ? gradient[k] : new Vector().copy(gradient[k]);\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.LinearGradient#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Gradient}\n   * @description Create a new instance of {@link Two.LinearGradient} with the same properties of the current path.\n   */\n  clone(parent) {\n    const stops = this.stops.map(function(stop) {\n      return stop.clone();\n    });\n    const clone = new _LinearGradient(\n      this.left._x,\n      this.left._y,\n      this.right._x,\n      this.right._y,\n      stops\n    );\n    _.each(\n      Gradient.Properties,\n      function(k) {\n        clone[k] = this[k];\n      },\n      this\n    );\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.LinearGradient#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.left = this.left.toObject();\n    result.right = this.right.toObject();\n    return result;\n  }\n  /**\n   * @name Two.LinearGradient#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagEndPoints || this._flagSpread || this._flagStops) {\n      this.trigger(Events.Types.change);\n    }\n    return this;\n  }\n  /**\n   * @name Two.LinearGradient#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagEndPoints = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n/**\n * @name Two.LinearGradient.Stop\n * @see {@link Two.Stop}\n */\n__publicField(_LinearGradient, \"Stop\", Stop);\n__publicField(_LinearGradient, \"Properties\", [\"left\", \"right\"]);\nvar LinearGradient = _LinearGradient;\nvar proto7 = {\n  left: {\n    enumerable: true,\n    get: function() {\n      return this._left;\n    },\n    set: function(v) {\n      if (this._left instanceof Vector) {\n        this._left.unbind(Events.Types.change, this._renderer.flagEndPoints);\n      }\n      this._left = v;\n      this._left.bind(Events.Types.change, this._renderer.flagEndPoints);\n      this._flagEndPoints = true;\n    }\n  },\n  right: {\n    enumerable: true,\n    get: function() {\n      return this._right;\n    },\n    set: function(v) {\n      if (this._right instanceof Vector) {\n        this._right.unbind(Events.Types.change, this._renderer.flagEndPoints);\n      }\n      this._right = v;\n      this._right.bind(Events.Types.change, this._renderer.flagEndPoints);\n      this._flagEndPoints = true;\n    }\n  }\n};\nfunction FlagEndPoints() {\n  this._flagEndPoints = true;\n}\n\n// src/effects/radial-gradient.js\nvar _RadialGradient = class _RadialGradient extends Gradient {\n  constructor(cx, cy, r, stops, fx, fy) {\n    super(stops);\n    /**\n     * @name Two.RadialGradient#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#radius} changed and needs to update.\n     */\n    __publicField(this, \"_flagRadius\", false);\n    /**\n     * @name Two.RadialGradient#_flagCenter\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#center} changed and needs to update.\n     */\n    __publicField(this, \"_flagCenter\", false);\n    /**\n     * @name Two.RadialGradient#_flagFocal\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#focal} changed and needs to update.\n     */\n    __publicField(this, \"_flagFocal\", false);\n    __publicField(this, \"_radius\", 0);\n    __publicField(this, \"_center\", null);\n    __publicField(this, \"_focal\", null);\n    for (let prop in proto8) {\n      Object.defineProperty(this, prop, proto8[prop]);\n    }\n    this._renderer.type = \"radial-gradient\";\n    this._renderer.flagCenter = FlagCenter.bind(this);\n    this._renderer.flagFocal = FlagFocal.bind(this);\n    this.center = new Vector();\n    this.radius = typeof r === \"number\" ? r : 1;\n    this.focal = new Vector();\n    if (typeof cx === \"number\") {\n      this.center.x = cx;\n    }\n    if (typeof cy === \"number\") {\n      this.center.y = cy;\n    }\n    this.focal.copy(this.center);\n    if (typeof fx === \"number\") {\n      this.focal.x = fx;\n    }\n    if (typeof fy === \"number\") {\n      this.focal.y = fy;\n    }\n  }\n  /**\n   * @name Two.RadialGradient.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.RadialGradient} to create a new instance\n   * @returns {Two.RadialGradient}\n   * @description Create a new {@link Two.RadialGradient} from an object notation of a {@link Two.RadialGradient}.\n   * @nota-bene Works in conjunction with {@link Two.RadialGradient#toObject}\n   */\n  static fromObject(obj) {\n    const gradient = new _RadialGradient().copy(obj);\n    if (\"id\" in obj) {\n      gradient.id = obj.id;\n    }\n    return gradient;\n  }\n  /**\n   * @name Two.RadialGradient#copy\n   * @function\n   * @param {Two.RadialGradient} gradient - The reference {@link Two.RadialGradient}\n   * @description Copy the properties of one {@link Two.RadialGradient} onto another.\n   */\n  copy(gradient) {\n    super.copy.call(this, gradient);\n    for (let i = 0; i < _RadialGradient.Properties.length; i++) {\n      const k = _RadialGradient.Properties[i];\n      if (k in gradient) {\n        if (/(center|focal)i/.test(k)) {\n          this[k] = gradient[k] instanceof Vector ? gradient[k] : new Vector().copy(gradient[k]);\n        } else if (typeof gradient[k] === \"number\") {\n          this[k] = gradient[MediaKeySystemAccess];\n        }\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.RadialGradient#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.RadialGradient}\n   * @description Create a new instance of {@link Two.RadialGradient} with the same properties of the current path.\n   */\n  clone(parent) {\n    const stops = this.stops.map(function(stop) {\n      return stop.clone();\n    });\n    const clone = new _RadialGradient(\n      this.center._x,\n      this.center._y,\n      this._radius,\n      stops,\n      this.focal._x,\n      this.focal._y\n    );\n    _.each(\n      Gradient.Properties.concat(_RadialGradient.Properties),\n      function(k) {\n        clone[k] = this[k];\n      },\n      this\n    );\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.RadialGradient#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    _.each(\n      _RadialGradient.Properties,\n      function(k) {\n        result[k] = this[k];\n      },\n      this\n    );\n    result.center = this.center.toObject();\n    result.focal = this.focal.toObject();\n    return result;\n  }\n  /**\n   * @name Two.RadialGradient#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagRadius || this._flatCenter || this._flagFocal || this._flagSpread || this._flagStops) {\n      this.trigger(Events.Types.change);\n    }\n    return this;\n  }\n  /**\n   * @name Two.RadialGradient#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagRadius = this._flagCenter = this._flagFocal = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n/**\n * @name Two.RadialGradient.Stop\n * @see {@link Two.Stop}\n */\n__publicField(_RadialGradient, \"Stop\", Stop);\n/**\n * @name Two.RadialGradient.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.RadialGradient}.\n */\n__publicField(_RadialGradient, \"Properties\", [\"center\", \"radius\", \"focal\"]);\nvar RadialGradient = _RadialGradient;\nvar proto8 = {\n  radius: {\n    enumerable: true,\n    get: function() {\n      return this._radius;\n    },\n    set: function(v) {\n      this._radius = v;\n      this._flagRadius = true;\n    }\n  },\n  center: {\n    enumerable: true,\n    get: function() {\n      return this._center;\n    },\n    set: function(v) {\n      if (this._center) {\n        this._center.unbind(Events.Types.change, this._renderer.flagCenter);\n      }\n      this._center = v;\n      this._center.bind(Events.Types.change, this._renderer.flagCenter);\n      this._flagCenter = true;\n    }\n  },\n  focal: {\n    enumerable: true,\n    get: function() {\n      return this._focal;\n    },\n    set: function(v) {\n      if (this._focal) {\n        this._focal.unbind(Events.Types.change, this._renderer.flagFocal);\n      }\n      this._focal = v;\n      this._focal.bind(Events.Types.change, this._renderer.flagFocal);\n      this._flagFocal = true;\n    }\n  }\n};\nfunction FlagCenter() {\n  this._flagCenter = true;\n}\nfunction FlagFocal() {\n  this._flagFocal = true;\n}\n\n// src/utils/shape.js\nfunction contains(path, t) {\n  if (t === 0 || t === 1) {\n    return true;\n  }\n  const length = path._length;\n  const target = length * t;\n  let elapsed = 0;\n  for (let i = 0; i < path._lengths.length; i++) {\n    const dist = path._lengths[i];\n    if (elapsed >= target) {\n      return target - elapsed >= 0;\n    }\n    elapsed += dist;\n  }\n  return false;\n}\nfunction getIdByLength(path, target) {\n  const total = path._length;\n  if (target <= 0) {\n    return 0;\n  } else if (target >= total) {\n    return path._lengths.length - 1;\n  }\n  for (let i = 0, sum = 0; i < path._lengths.length; i++) {\n    if (sum + path._lengths[i] >= target) {\n      target -= sum;\n      return Math.max(i - 1, 0) + target / path._lengths[i];\n    }\n    sum += path._lengths[i];\n  }\n  return -1;\n}\nfunction getCurveLength2(a, b, limit) {\n  let x1, x2, x3, x4, y1, y2, y3, y4;\n  const right = b.controls && b.controls.right;\n  const left = a.controls && a.controls.left;\n  x1 = b.x;\n  y1 = b.y;\n  x2 = (right || b).x;\n  y2 = (right || b).y;\n  x3 = (left || a).x;\n  y3 = (left || a).y;\n  x4 = a.x;\n  y4 = a.y;\n  if (right && b._relative) {\n    x2 += b.x;\n    y2 += b.y;\n  }\n  if (left && a._relative) {\n    x3 += a.x;\n    y3 += a.y;\n  }\n  return getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n}\nfunction getSubdivisions(a, b, limit) {\n  let x1, x2, x3, x4, y1, y2, y3, y4;\n  const right = b.controls && b.controls.right;\n  const left = a.controls && a.controls.left;\n  x1 = b.x;\n  y1 = b.y;\n  x2 = (right || b).x;\n  y2 = (right || b).y;\n  x3 = (left || a).x;\n  y3 = (left || a).y;\n  x4 = a.x;\n  y4 = a.y;\n  if (right && b._relative) {\n    x2 += b.x;\n    y2 += b.y;\n  }\n  if (left && a._relative) {\n    x3 += a.x;\n    y3 += a.y;\n  }\n  return subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n}\nfunction getEffectFromObject(obj) {\n  switch (obj.renderer.type) {\n    case \"texture\":\n      return Texture.fromObject(obj);\n    case \"gradient\":\n      return Gradient.fromObject(obj);\n    case \"linear-gradient\":\n      return LinearGradient.fromObject(obj);\n    case \"radial-gradient\":\n      return RadialGradient.fromObject(obj);\n  }\n  return obj;\n}\n\n// src/matrix.js\nvar cos = Math.cos;\nvar sin = Math.sin;\nvar tan = Math.tan;\nvar array = [];\nvar _Matrix = class _Matrix extends Events {\n  constructor(a, b, c, d, e, f) {\n    super();\n    /**\n     * @name Two.Matrix#elements\n     * @property {Number[]} - The underlying data stored as an array.\n     */\n    __publicField(this, \"elements\", new NumArray(9));\n    /**\n     * @name Two.Matrix#manual\n     * @property {Boolean} - Determines whether Two.js automatically calculates the values for the matrix or if the developer intends to manage the matrix.\n     * @nota-bene - Setting to `true` nullifies {@link Two.Shape#translation}, {@link Two.Shape#rotation}, and {@link Two.Shape#scale}.\n     */\n    __publicField(this, \"manual\", false);\n    let elements = a;\n    if (!Array.isArray(elements)) {\n      elements = Array.prototype.slice.call(arguments);\n    }\n    this.identity();\n    if (elements.length > 0) {\n      this.set(elements);\n    }\n  }\n  /**\n   * @name Two.Matrix.Multiply\n   * @function\n   * @param {Number[]} A - The first {@link Two.Matrix} to multiply\n   * @param {Number[]} B - The second {@link Two.Matrix} to multiply\n   * @param {Number[]} [C] - An optional {@link Two.Matrix} to apply the result to\n   * @returns {Number[]} - If an optional `C` matrix isn't passed then a new one is created and returned.\n   * @description Multiply two matrices together and return the result.\n   */\n  static Multiply(A, B, C) {\n    if (B.length <= 3) {\n      const e = A;\n      let x, y, z;\n      const a = B[0] || 0, b = B[1] || 0, c = B[2] || 0;\n      x = e[0] * a + e[1] * b + e[2] * c;\n      y = e[3] * a + e[4] * b + e[5] * c;\n      z = e[6] * a + e[7] * b + e[8] * c;\n      return [x, y, z];\n    }\n    const A0 = A[0], A1 = A[1], A2 = A[2];\n    const A3 = A[3], A4 = A[4], A5 = A[5];\n    const A6 = A[6], A7 = A[7], A8 = A[8];\n    const B0 = B[0], B1 = B[1], B2 = B[2];\n    const B3 = B[3], B4 = B[4], B5 = B[5];\n    const B6 = B[6], B7 = B[7], B8 = B[8];\n    C = C || new NumArray(9);\n    C[0] = A0 * B0 + A1 * B3 + A2 * B6;\n    C[1] = A0 * B1 + A1 * B4 + A2 * B7;\n    C[2] = A0 * B2 + A1 * B5 + A2 * B8;\n    C[3] = A3 * B0 + A4 * B3 + A5 * B6;\n    C[4] = A3 * B1 + A4 * B4 + A5 * B7;\n    C[5] = A3 * B2 + A4 * B5 + A5 * B8;\n    C[6] = A6 * B0 + A7 * B3 + A8 * B6;\n    C[7] = A6 * B1 + A7 * B4 + A8 * B7;\n    C[8] = A6 * B2 + A7 * B5 + A8 * B8;\n    return C;\n  }\n  /**\n   * @name Two.Matrix.fromObject\n   * @function\n   * @param {Object} obj - The object notation of a Two.Matrix to create a new instance\n   * @returns {Two.Matrix}\n   * @description Create a new {@link Two.Matrix} from an object notation of a {@link Two.Matrix}.\n   * @nota-bene Works in conjunction with {@link Two.Matrix#toObject}\n   */\n  static fromObject(obj) {\n    return new _Matrix().copy(obj);\n  }\n  /**\n   * @name Two.Matrix#set\n   * @function\n   * @param {Number} a - The value for element at the first column and first row\n   * @param {Number} b - The value for element at the second column and first row\n   * @param {Number} c - The value for element at the third column and first row\n   * @param {Number} d - The value for element at the first column and second row\n   * @param {Number} e - The value for element at the second column and second row\n   * @param {Number} f - The value for element at the third column and second row\n   * @param {Number} g - The value for element at the first column and third row\n   * @param {Number} h - The value for element at the second column and third row\n   * @param {Number} i - The value for element at the third column and third row\n   * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n   */\n  /**\n   * @name Two.Matrix#set\n   * @function\n   * @param {Number[]} a - The array of elements to apply\n   * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n   */\n  set(a, b, c, d, e, f, g, h, i) {\n    if (typeof b === \"undefined\") {\n      const elements = a;\n      a = elements[0];\n      b = elements[1];\n      c = elements[2];\n      d = elements[3];\n      e = elements[4];\n      f = elements[5];\n      g = elements[6];\n      h = elements[7];\n      i = elements[8];\n    }\n    this.elements[0] = a;\n    this.elements[1] = b;\n    this.elements[2] = c;\n    this.elements[3] = d;\n    this.elements[4] = e;\n    this.elements[5] = f;\n    this.elements[6] = g;\n    this.elements[7] = h;\n    this.elements[8] = i;\n    return this.trigger(Events.Types.change);\n  }\n  /**\n   * @name Two.Matrix#copy\n   * @function\n   * @param {Two.Matrix} m - The matrix to copy\n   * @description Copy the matrix of one to the current instance.\n   */\n  copy(m) {\n    this.elements[0] = m.elements[0];\n    this.elements[1] = m.elements[1];\n    this.elements[2] = m.elements[2];\n    this.elements[3] = m.elements[3];\n    this.elements[4] = m.elements[4];\n    this.elements[5] = m.elements[5];\n    this.elements[6] = m.elements[6];\n    this.elements[7] = m.elements[7];\n    this.elements[8] = m.elements[8];\n    this.manual = m.manual;\n    return this.trigger(Events.Types.change);\n  }\n  /**\n   * @name Two.Matrix#identity\n   * @function\n   * @description Turn matrix to the identity, like resetting.\n   */\n  identity() {\n    this.elements[0] = _Matrix.Identity[0];\n    this.elements[1] = _Matrix.Identity[1];\n    this.elements[2] = _Matrix.Identity[2];\n    this.elements[3] = _Matrix.Identity[3];\n    this.elements[4] = _Matrix.Identity[4];\n    this.elements[5] = _Matrix.Identity[5];\n    this.elements[6] = _Matrix.Identity[6];\n    this.elements[7] = _Matrix.Identity[7];\n    this.elements[8] = _Matrix.Identity[8];\n    return this.trigger(Events.Types.change);\n  }\n  /**\n   * @name Two.Matrix#multiply\n   * @function\n   * @param {Number} s - The scalar to be multiplied.\n   * @description Multiply all components of the matrix against a single scalar value.\n   * @overloaded\n   */\n  /**\n   * @name Two.Matrix#multiply\n   * @function\n   * @param {Number} x - The `x` component to be multiplied.\n   * @param {Number} y - The `y` component to be multiplied.\n   * @param {Number} z - The `z` component to be multiplied.\n   * @description Multiply all components of a matrix against a 3 component vector.\n   * @overloaded\n   */\n  /**\n   * @name Two.Matrix#multiply\n   * @function\n   * @param {Number} a - The value at the first column and first row of the matrix to be multiplied.\n   * @param {Number} b - The value at the second column and first row of the matrix to be multiplied.\n   * @param {Number} c - The value at the third column and first row of the matrix to be multiplied.\n   * @param {Number} d - The value at the first column and second row of the matrix to be multiplied.\n   * @param {Number} e - The value at the second column and second row of the matrix to be multiplied.\n   * @param {Number} f - The value at the third column and second row of the matrix to be multiplied.\n   * @param {Number} g - The value at the first column and third row of the matrix to be multiplied.\n   * @param {Number} h - The value at the second column and third row of the matrix to be multiplied.\n   * @param {Number} i - The value at the third column and third row of the matrix to be multiplied.\n   * @description Multiply all components of a matrix against another matrix.\n   * @overloaded\n   */\n  multiply(a, b, c, d, e, f, g, h, i) {\n    if (typeof b === \"undefined\") {\n      this.elements[0] *= a;\n      this.elements[1] *= a;\n      this.elements[2] *= a;\n      this.elements[3] *= a;\n      this.elements[4] *= a;\n      this.elements[5] *= a;\n      this.elements[6] *= a;\n      this.elements[7] *= a;\n      this.elements[8] *= a;\n      return this.trigger(Events.Types.change);\n    }\n    if (typeof c === \"undefined\") {\n      c = 1;\n    }\n    if (typeof d === \"undefined\") {\n      a = a || 0;\n      b = b || 0;\n      c = c || 0;\n      e = this.elements;\n      const x = e[0] * a + e[1] * b + e[2] * c;\n      const y = e[3] * a + e[4] * b + e[5] * c;\n      const z = e[6] * a + e[7] * b + e[8] * c;\n      return [x, y, z];\n    }\n    const A = this.elements;\n    const B = [a, b, c, d, e, f, g, h, i];\n    const A0 = A[0], A1 = A[1], A2 = A[2];\n    const A3 = A[3], A4 = A[4], A5 = A[5];\n    const A6 = A[6], A7 = A[7], A8 = A[8];\n    const B0 = B[0], B1 = B[1], B2 = B[2];\n    const B3 = B[3], B4 = B[4], B5 = B[5];\n    const B6 = B[6], B7 = B[7], B8 = B[8];\n    this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6;\n    this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7;\n    this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8;\n    this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6;\n    this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7;\n    this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8;\n    this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6;\n    this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7;\n    this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8;\n    return this.trigger(Events.Types.change);\n  }\n  /**\n   * @name Two.Matrix#inverse\n   * @function\n   * @param {Two.Matrix} [output] - The optional matrix to apply the inversion to.\n   * @description Return an inverted version of the matrix. If no optional one is passed a new matrix is created and returned.\n   */\n  inverse(output) {\n    const a = this.elements;\n    output = output || new _Matrix();\n    const a00 = a[0], a01 = a[1], a02 = a[2];\n    const a10 = a[3], a11 = a[4], a12 = a[5];\n    const a20 = a[6], a21 = a[7], a22 = a[8];\n    const b01 = a22 * a11 - a12 * a21;\n    const b11 = -a22 * a10 + a12 * a20;\n    const b21 = a21 * a10 - a11 * a20;\n    let det = a00 * b01 + a01 * b11 + a02 * b21;\n    if (!det) {\n      return null;\n    }\n    det = 1 / det;\n    output.elements[0] = b01 * det;\n    output.elements[1] = (-a22 * a01 + a02 * a21) * det;\n    output.elements[2] = (a12 * a01 - a02 * a11) * det;\n    output.elements[3] = b11 * det;\n    output.elements[4] = (a22 * a00 - a02 * a20) * det;\n    output.elements[5] = (-a12 * a00 + a02 * a10) * det;\n    output.elements[6] = b21 * det;\n    output.elements[7] = (-a21 * a00 + a01 * a20) * det;\n    output.elements[8] = (a11 * a00 - a01 * a10) * det;\n    return output;\n  }\n  /**\n   * @name Two.Matrix#scale\n   * @function\n   * @param {Number} s - The one dimensional scale to apply to the matrix.\n   * @description Uniformly scale the transformation matrix.\n   */\n  /**\n   * @name Two.Matrix#scale\n   * @function\n   * @param {Number} sx - The horizontal scale factor.\n   * @param {Number} sy - The vertical scale factor\n   * @description Scale the transformation matrix in two dimensions.\n   */\n  scale(sx, sy) {\n    const l = arguments.length;\n    if (l <= 1) {\n      sy = sx;\n    }\n    return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1);\n  }\n  /**\n   * @name Two.Matrix#rotate\n   * @function\n   * @param {Number} n - The amount to rotate in Number.\n   * @description Rotate the matrix.\n   */\n  rotate(n) {\n    const c = cos(n);\n    const s = sin(n);\n    return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1);\n  }\n  /**\n   * @name Two.Matrix#translate\n   * @function\n   * @param {Number} x - The horizontal translation value to apply\n   * @param {Number} y - The vertical translation value to apply\n   * @description Translate the matrix to specific `x` / `y` values.\n   */\n  translate(x, y) {\n    return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1);\n  }\n  /**\n   * @name Two.Matrix#skewX\n   * @function\n   * @param {Number} n - The amount to skew\n   * @description Skew the matrix by an angle in the x axis direction.\n   */\n  skewX(n) {\n    const a = tan(n);\n    return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1);\n  }\n  /**\n   * @name Two.Matrix#skewY\n   * @function\n   * @param {Number} n - The amount to skew\n   * @description Skew the matrix by an angle in the y axis direction.\n   */\n  skewY(n) {\n    const a = tan(n);\n    return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1);\n  }\n  /**\n   * @name Two.Matrix#toString\n   * @function\n   * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n   * @returns {String} - The transformation matrix as a 6 component string separated by spaces.\n   * @description Create a transform string. Used for the Two.js rendering APIs.\n   */\n  toString(fullMatrix) {\n    array.length = 0;\n    this.toTransformArray(fullMatrix, array);\n    return array.map(toFixed).join(\" \");\n  }\n  /**\n   * @name Two.Matrix#toTransformArray\n   * @function\n   * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 in the format for 2D transformations.\n   * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n   * @description Create a transform array. Used for the Two.js rendering APIs.\n   */\n  toTransformArray(fullMatrix, output) {\n    const elements = this.elements;\n    const hasOutput = !!output;\n    const a = elements[0];\n    const b = elements[1];\n    const c = elements[2];\n    const d = elements[3];\n    const e = elements[4];\n    const f = elements[5];\n    if (fullMatrix) {\n      const g = elements[6];\n      const h = elements[7];\n      const i = elements[8];\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = d;\n        output[2] = g;\n        output[3] = b;\n        output[4] = e;\n        output[5] = h;\n        output[6] = c;\n        output[7] = f;\n        output[8] = i;\n        return;\n      }\n      return [a, d, g, b, e, h, c, f, i];\n    }\n    if (hasOutput) {\n      output[0] = a;\n      output[1] = d;\n      output[2] = b;\n      output[3] = e;\n      output[4] = c;\n      output[5] = f;\n      return;\n    }\n    return [\n      a,\n      d,\n      b,\n      e,\n      c,\n      f\n      // Specific format see LN:19\n    ];\n  }\n  /**\n   * @name Two.Matrix#toArray\n   * @function\n   * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n   * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n   * @description Create a transform array. Used for the Two.js rendering APIs.\n   */\n  toArray(fullMatrix, output) {\n    const elements = this.elements;\n    const hasOutput = !!output;\n    const a = elements[0];\n    const b = elements[1];\n    const c = elements[2];\n    const d = elements[3];\n    const e = elements[4];\n    const f = elements[5];\n    if (fullMatrix) {\n      const g = elements[6];\n      const h = elements[7];\n      const i = elements[8];\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = b;\n        output[2] = c;\n        output[3] = d;\n        output[4] = e;\n        output[5] = f;\n        output[6] = g;\n        output[7] = h;\n        output[8] = i;\n        return;\n      }\n      return [a, b, c, d, e, f, g, h, i];\n    }\n    if (hasOutput) {\n      output[0] = a;\n      output[1] = b;\n      output[2] = c;\n      output[3] = d;\n      output[4] = e;\n      output[5] = f;\n      return;\n    }\n    return [a, b, c, d, e, f];\n  }\n  /**\n   * @name Two.Matrix#toObject\n   * @function\n   * @description Create a JSON compatible object that represents information of the matrix.\n   * @nota-bene Works in conjunction with {@link Two.Matrix.fromObject}\n   */\n  toObject() {\n    return {\n      renderer: { type: \"matrix\" },\n      elements: this.toArray(true),\n      manual: !!this.manual\n    };\n  }\n  /**\n   * @name Two.Matrix#clone\n   * @function\n   * @description Clone the current matrix.\n   */\n  clone() {\n    return new _Matrix().copy(this);\n  }\n};\n//\n/**\n * @name Two.Matrix.Identity\n * @property {Number[]} - A stored reference to the default value of a 3 x 3 matrix.\n */\n__publicField(_Matrix, \"Identity\", [1, 0, 0, 0, 1, 0, 0, 0, 1]);\nvar Matrix2 = _Matrix;\nsetMatrix(Matrix2);\n\n// src/shape.js\nvar _Shape = class _Shape extends Element {\n  constructor() {\n    super();\n    /**\n     * @name Two.Shape#_flagMatrix\n     * @private\n     * @property {Boolean} - Determines whether the matrix needs updating.\n     */\n    __publicField(this, \"_flagMatrix\", true);\n    /**\n     * @name Two.Shape#_flagScale\n     * @private\n     * @property {Boolean} - Determines whether the scale needs updating.\n     */\n    __publicField(this, \"_flagScale\", false);\n    // Underlying Properties\n    /**\n     * @name Two.Shape#_matrix\n     * @private\n     * @property {Two.Matrix} - The matrix value of the shape's position, rotation, and scale.\n     */\n    __publicField(this, \"_matrix\", null);\n    /**\n     * @name Two.Shape#_worldMatrix\n     * @private\n     * @property {Two.Matrix} - The matrix value of the shape's position, rotation, and scale in the scene.\n     */\n    __publicField(this, \"_worldMatrix\", null);\n    /**\n     * @name Two.Shape#_position\n     * @private\n     * @property {Two.Vector} - The translation values as a {@link Two.Vector}.\n     */\n    __publicField(this, \"_position\", null);\n    /**\n     * @name Two.Shape#_rotation\n     * @private\n     * @property {Number} - The rotation value in radians.\n     */\n    __publicField(this, \"_rotation\", 0);\n    /**\n     * @name Two.Shape#_scale\n     * @private\n     * @property {Number|Two.Vector} - The scale value in Number. Can be a vector for non-uniform scaling.\n     */\n    __publicField(this, \"_scale\", 1);\n    /**\n     * @name Two.Shape#_skewX\n     * @private\n     * @property {Number} - The rotation value in Number.\n     */\n    __publicField(this, \"_skewX\", 0);\n    /**\n     * @name Two.Shape#_skewY\n     * @private\n     * @property {Number} - The rotation value in Number.\n     */\n    __publicField(this, \"_skewY\", 0);\n    for (let prop in proto9) {\n      Object.defineProperty(this, prop, proto9[prop]);\n    }\n    this._renderer.flagMatrix = FlagMatrix.bind(this);\n    this.isShape = true;\n    this.matrix = new Matrix2();\n    this.worldMatrix = new Matrix2();\n    this.position = new Vector();\n    this.rotation = 0;\n    this.scale = 1;\n    this.skewX = 0;\n    this.skewY = 0;\n  }\n  /**\n   * @name Two.Shape.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Shape} to create a new instance\n   * @returns {Two.Shape}\n   * @description Create a new {@link Two.Shape} from an object notation of a {@link Two.Shape}.\n   * @nota-bene Works in conjunction with {@link Two.Shape#toObject}\n   */\n  static fromObject(obj) {\n    const shape = new _Shape().copy(obj);\n    if (\"id\" in obj) {\n      shape.id = obj.id;\n    }\n    return shape;\n  }\n  get renderer() {\n    return this._renderer;\n  }\n  set renderer(v) {\n    this._renderer = v;\n  }\n  /**\n   * @name Two.Shape#translation\n   * @description Alias for {@link Two.Shape#position}.\n   */\n  get translation() {\n    return proto9.position.get.apply(this, arguments);\n  }\n  set translation(v) {\n    proto9.position.set.apply(this, arguments);\n  }\n  /**\n   * @name Two.Shape#addTo\n   * @function\n   * @param {Two.Group} group - The parent the shape adds itself to.\n   * @description Convenience method to add itself to the scenegraph.\n   */\n  addTo(group) {\n    group.add(this);\n    return this;\n  }\n  /**\n   * @name Two.Shape#remove\n   * @function\n   * @description Remove self from the scene / parent.\n   */\n  remove() {\n    if (!this.parent) {\n      return this;\n    }\n    this.parent.remove(this);\n    return this;\n  }\n  /**\n   * @name Two.Shape#contains\n   * @function\n   * @param {Number} x - x coordinate to hit test against\n   * @param {Number} y - y coordinate to hit test against\n   * @param {Object} [options] - Optional options object\n   * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `shape.visible = false` shapes\n   * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n   * @returns {Boolean}\n   * @description Check to see if coordinates are within a {@link Two.Shape}'s bounding rectangle\n   * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n   */\n  contains(x, y, options) {\n    const opts = options || {};\n    const ignoreVisibility = opts.ignoreVisibility === true;\n    if (!ignoreVisibility && \"visible\" in this && this.visible === false) {\n      return false;\n    }\n    if (!ignoreVisibility && \"opacity\" in this && typeof this.opacity === \"number\" && this.opacity <= 0) {\n      return false;\n    }\n    if (typeof this.getBoundingClientRect !== \"function\") {\n      return false;\n    }\n    const tolerance = typeof opts.tolerance === \"number\" ? opts.tolerance : 0;\n    this._update(true);\n    const rect = this.getBoundingClientRect();\n    if (!rect) {\n      return false;\n    }\n    return x >= rect.left - tolerance && x <= rect.right + tolerance && y >= rect.top - tolerance && y <= rect.bottom + tolerance;\n  }\n  /**\n   * @name Two.Shape#copy\n   * @function\n   * @param {Two.Shape} shape\n   * @description Copy the properties of one {@link Two.Shape} onto another.\n   */\n  copy(shape) {\n    super.copy.call(this, shape);\n    if (\"position\" in shape) {\n      if (shape.position instanceof Vector) {\n        this.position = shape.position;\n      } else {\n        this.position.copy(shape.position);\n      }\n    }\n    if (\"rotation\" in shape) {\n      this.rotation = shape.rotation;\n    }\n    if (\"scale\" in shape) {\n      this.scale = typeof shape.scale === \"number\" || shape.scale instanceof Vector ? shape.scale : new Vector(shape.scale.x, shape.scale.y);\n    }\n    if (\"skewX\" in shape) {\n      this.skewX = shape.skewX;\n    }\n    if (\"skewY\" in shape) {\n      this.skewY = shape.skewY;\n    }\n    if (\"matrix\" in shape && shape.matrix.manual) {\n      this.matrix.copy(shape.matrix);\n      this.matrix.manual = true;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Shape#clone\n   * @function\n   * @param {Two.Group} [parent] - Optional argument to automatically add the shape to a scenegraph.\n   * @returns {Two.Shape}\n   * @description Create a new {@link Two.Shape} with the same values as the current shape.\n   */\n  clone(parent) {\n    const clone = new _Shape();\n    clone.position.copy(this.position);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone._update();\n  }\n  /**\n   * @name Two.Shape#toObject\n   * @function\n   * @description Create a JSON compatible object that represents information of the shape.\n   * @nota-bene Works in conjunction with {@link Two.Shape.fromObject}\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer = { type: \"shape\" };\n    result.isShape = true;\n    result.translation = this.translation.toObject();\n    result.rotation = this.translation.rotation;\n    result.scale = this.scale instanceof Vector ? this.scale.toObject() : this.scale;\n    result.skewX = this.skewX;\n    result.skewY = this.skewY;\n    result.matrix = this.matrix.toObject();\n    return result;\n  }\n  /**\n   * @name Two.Shape#dispose\n   * @function\n   * @description Release the shape's bound objects by unbinding relevant events.\n   */\n  dispose() {\n    super.dispose();\n    if (typeof this.translation === \"object\" && typeof this.translation.unbind === \"function\") {\n      this.translation.unbind();\n    }\n    if (typeof this.scale === \"object\" && typeof this.scale.unbind === \"function\") {\n      this.scale.unbind();\n    }\n  }\n  /**\n   * @name Two.Shape#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update(bubbles) {\n    if (!this._matrix.manual && this._flagMatrix) {\n      this._matrix.identity().translate(this.position.x, this.position.y);\n      this._matrix.rotate(this.rotation);\n      if (this._scale instanceof Vector) {\n        this._matrix.scale(this._scale.x, this._scale.y);\n      } else {\n        this._matrix.scale(this._scale);\n      }\n      this._matrix.skewX(this.skewX);\n      this._matrix.skewY(this.skewY);\n    }\n    if (bubbles) {\n      if (this.parent && this.parent._update) {\n        this.parent._update();\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Shape#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagMatrix = this._flagScale = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n__publicField(_Shape, \"Properties\", [\n  \"position\",\n  \"rotation\",\n  \"scale\",\n  \"skewX\",\n  \"skewY\",\n  \"matrix\",\n  \"worldMatrix\"\n]);\nvar Shape = _Shape;\nvar proto9 = {\n  position: {\n    enumerable: true,\n    get: function() {\n      return this._position;\n    },\n    set: function(v) {\n      if (this._position) {\n        this._position.unbind(Events.Types.change, this._renderer.flagMatrix);\n      }\n      this._position = v;\n      this._position.bind(Events.Types.change, this._renderer.flagMatrix);\n      FlagMatrix.call(this);\n    }\n  },\n  rotation: {\n    enumerable: true,\n    get: function() {\n      return this._rotation;\n    },\n    set: function(v) {\n      this._rotation = v;\n      this._flagMatrix = true;\n    }\n  },\n  scale: {\n    enumerable: true,\n    get: function() {\n      return this._scale;\n    },\n    set: function(v) {\n      if (this._scale instanceof Vector) {\n        this._scale.unbind(Events.Types.change, this._renderer.flagMatrix);\n      }\n      this._scale = v;\n      if (this._scale instanceof Vector) {\n        this._scale.bind(Events.Types.change, this._renderer.flagMatrix);\n      }\n      this._flagMatrix = true;\n      this._flagScale = true;\n    }\n  },\n  skewX: {\n    enumerable: true,\n    get: function() {\n      return this._skewX;\n    },\n    set: function(v) {\n      this._skewX = v;\n      this._flagMatrix = true;\n    }\n  },\n  skewY: {\n    enumerable: true,\n    get: function() {\n      return this._skewY;\n    },\n    set: function(v) {\n      this._skewY = v;\n      this._flagMatrix = true;\n    }\n  },\n  matrix: {\n    enumerable: true,\n    get: function() {\n      return this._matrix;\n    },\n    set: function(v) {\n      this._matrix = v;\n      this._flagMatrix = true;\n    }\n  },\n  worldMatrix: {\n    enumerable: true,\n    get: function() {\n      getComputedMatrix(this, this._worldMatrix);\n      return this._worldMatrix;\n    },\n    set: function(v) {\n      this._worldMatrix = v;\n    }\n  }\n};\nfunction FlagMatrix() {\n  this._flagMatrix = true;\n}\n\n// src/utils/hit-test.js\nvar TRANSPARENT_REGEX = /^(?:none|transparent)$/i;\nvar DEFAULT_PRECISION = 8;\nvar EPSILON = Number.EPSILON;\nfunction createPoint(x, y) {\n  return { x, y };\n}\nfunction pointsEqual(a, b, epsilon = EPSILON) {\n  return Math.abs(a.x - b.x) <= epsilon && Math.abs(a.y - b.y) <= epsilon;\n}\nfunction svgAngle(ux, uy, vx, vy) {\n  const dot = ux * vx + uy * vy;\n  const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy) || 1e-12;\n  let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));\n  if (ux * vy - uy * vx < 0) {\n    ang = -ang;\n  }\n  return ang;\n}\nfunction sampleArcPoints(prev, anchor2, precision) {\n  if (!prev) {\n    return [createPoint(anchor2.x, anchor2.y)];\n  }\n  let rx = anchor2.rx;\n  let ry = anchor2.ry;\n  if (!(rx && ry)) {\n    return [createPoint(anchor2.x, anchor2.y)];\n  }\n  const xAxisRotation = (anchor2.xAxisRotation || 0) * Math.PI / 180;\n  const largeArcFlag = anchor2.largeArcFlag ? 1 : 0;\n  const sweepFlag = anchor2.sweepFlag ? 1 : 0;\n  rx = Math.abs(rx);\n  ry = Math.abs(ry);\n  const ax = prev.x;\n  const ay = prev.y;\n  const x = anchor2.x;\n  const y = anchor2.y;\n  const dx2 = (ax - x) / 2;\n  const dy2 = (ay - y) / 2;\n  const cosRot = Math.cos(xAxisRotation);\n  const sinRot = Math.sin(xAxisRotation);\n  let x1p = cosRot * dx2 + sinRot * dy2;\n  let y1p = -sinRot * dx2 + cosRot * dy2;\n  let rxs = rx * rx;\n  let rys = ry * ry;\n  const cr = x1p * x1p / rxs + y1p * y1p / rys;\n  if (cr > 1) {\n    const s = Math.sqrt(cr);\n    rx *= s;\n    ry *= s;\n    rxs = rx * rx;\n    rys = ry * ry;\n  }\n  const dq = rxs * y1p * y1p + rys * x1p * x1p;\n  const pq = dq === 0 ? 0 : (rxs * rys - dq) / dq;\n  let q = Math.sqrt(Math.max(0, pq));\n  if (largeArcFlag === sweepFlag) {\n    q = -q;\n  }\n  const cxp = q * rx * y1p / ry;\n  const cyp = -q * ry * x1p / rx;\n  const cx = cosRot * cxp - sinRot * cyp + (ax + x) / 2;\n  const cy = sinRot * cxp + cosRot * cyp + (ay + y) / 2;\n  const startAngle = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n  const delta = svgAngle(\n    (x1p - cxp) / rx,\n    (y1p - cyp) / ry,\n    (-x1p - cxp) / rx,\n    (-y1p - cyp) / ry\n  ) % TWO_PI;\n  const endAngle = startAngle + delta;\n  const clockwise = sweepFlag === 0;\n  const angleDelta = (() => {\n    const raw = endAngle - startAngle;\n    const samePoints = Math.abs(raw) < Number.EPSILON;\n    let deltaAngle = mod(raw, TWO_PI);\n    if (deltaAngle < Number.EPSILON) {\n      deltaAngle = samePoints ? 0 : TWO_PI;\n    }\n    if (clockwise && !samePoints) {\n      deltaAngle = deltaAngle === TWO_PI ? -TWO_PI : deltaAngle - TWO_PI;\n    }\n    return deltaAngle;\n  })();\n  const steps = Math.max(Constants.Resolution, Math.max(precision * 2, 1));\n  const points = [];\n  for (let i = 1; i <= steps; i++) {\n    const t = i / steps;\n    const angle = startAngle + t * angleDelta;\n    let px = cx + rx * Math.cos(angle);\n    let py = cy + ry * Math.sin(angle);\n    if (xAxisRotation !== 0) {\n      const tx = px - cx;\n      const ty = py - cy;\n      const cosR = Math.cos(xAxisRotation);\n      const sinR = Math.sin(xAxisRotation);\n      px = tx * cosR - ty * sinR + cx;\n      py = tx * sinR + ty * cosR + cy;\n    }\n    points.push(createPoint(px, py));\n  }\n  return points;\n}\nfunction buildPathHitParts(path, precision = DEFAULT_PRECISION) {\n  const polygons = [];\n  const segments = [];\n  const vertices = path._renderer && path._renderer.vertices && path._renderer.vertices.length > 0 ? path._renderer.vertices : path.vertices;\n  if (!vertices || vertices.length === 0) {\n    return { polygons, segments };\n  }\n  const limit = Math.max(1, Math.floor(precision));\n  let currentPolygon = null;\n  let firstPoint = null;\n  let lastPoint = null;\n  let prevVertex = null;\n  const closePolygon = (forceClose = false) => {\n    if (!currentPolygon) {\n      return;\n    }\n    if (forceClose && firstPoint && lastPoint && !pointsEqual(firstPoint, lastPoint)) {\n      const closingPoint = createPoint(firstPoint.x, firstPoint.y);\n      segments.push({ a: lastPoint, b: closingPoint });\n      currentPolygon.push(closingPoint);\n      lastPoint = closingPoint;\n    }\n    if (currentPolygon.length >= 3 && firstPoint && lastPoint && pointsEqual(firstPoint, lastPoint)) {\n      polygons.push(currentPolygon);\n    }\n    currentPolygon = null;\n    firstPoint = null;\n    lastPoint = null;\n  };\n  const appendPoint = (pt) => {\n    if (!lastPoint) {\n      lastPoint = pt;\n      if (currentPolygon) {\n        currentPolygon.push(pt);\n      }\n      return;\n    }\n    if (pointsEqual(lastPoint, pt)) {\n      return;\n    }\n    segments.push({ a: lastPoint, b: pt });\n    if (currentPolygon) {\n      currentPolygon.push(pt);\n    }\n    lastPoint = pt;\n  };\n  for (let i = 0; i < vertices.length; i++) {\n    const vertex = vertices[i];\n    const command = vertex.command || (i === 0 ? Commands.move : Commands.line);\n    if (command === Commands.move) {\n      closePolygon(false);\n      const pt = createPoint(vertex.x, vertex.y);\n      currentPolygon = [pt];\n      firstPoint = pt;\n      lastPoint = pt;\n      prevVertex = vertex;\n      continue;\n    }\n    if (!prevVertex) {\n      prevVertex = vertices[Math.max(i - 1, 0)];\n    }\n    if (command === Commands.line) {\n      appendPoint(createPoint(vertex.x, vertex.y));\n    } else if (command === Commands.curve) {\n      const subdivisions = getSubdivisions(vertex, prevVertex, limit);\n      for (let j = 1; j < subdivisions.length; j++) {\n        const sv = subdivisions[j];\n        appendPoint(createPoint(sv.x, sv.y));\n      }\n      appendPoint(createPoint(vertex.x, vertex.y));\n    } else if (command === Commands.arc) {\n      const arcPoints = sampleArcPoints(prevVertex, vertex, limit);\n      for (let j = 0; j < arcPoints.length; j++) {\n        appendPoint(arcPoints[j]);\n      }\n    } else if (command === Commands.close) {\n      closePolygon(true);\n      prevVertex = vertex;\n      continue;\n    } else {\n      appendPoint(createPoint(vertex.x, vertex.y));\n    }\n    prevVertex = vertex;\n  }\n  if (currentPolygon) {\n    const shouldForceClose = !!path._closed || !!path.closed || firstPoint && lastPoint && !pointsEqual(firstPoint, lastPoint);\n    closePolygon(shouldForceClose);\n  }\n  return { polygons, segments };\n}\nfunction pointInPolygons(polygons, x, y) {\n  let inside = false;\n  for (let i = 0; i < polygons.length; i++) {\n    const polygon = polygons[i];\n    if (!polygon || polygon.length < 3) {\n      continue;\n    }\n    let lastIndex = polygon.length - 1;\n    for (let j = 0; j < polygon.length; j++) {\n      const v0 = polygon[lastIndex];\n      const v1 = polygon[j];\n      const intersects = v1.y > y !== v0.y > y && x < (v0.x - v1.x) * (y - v1.y) / (v0.y - v1.y || 1e-12) + v1.x;\n      if (intersects) {\n        inside = !inside;\n      }\n      lastIndex = j;\n    }\n  }\n  return inside;\n}\nfunction distanceToSegmentSquared(x, y, a, b) {\n  const dx = b.x - a.x;\n  const dy = b.y - a.y;\n  if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {\n    const ddx2 = x - a.x;\n    const ddy2 = y - a.y;\n    return ddx2 * ddx2 + ddy2 * ddy2;\n  }\n  const t = ((x - a.x) * dx + (y - a.y) * dy) / (dx * dx + dy * dy);\n  const clamped = Math.max(0, Math.min(1, t));\n  const cx = a.x + clamped * dx;\n  const cy = a.y + clamped * dy;\n  const ddx = x - cx;\n  const ddy = y - cy;\n  return ddx * ddx + ddy * ddy;\n}\nfunction distanceToSegments(segments, x, y) {\n  if (!segments || segments.length === 0) {\n    return Infinity;\n  }\n  let minDistance = Infinity;\n  for (let i = 0; i < segments.length; i++) {\n    const segment = segments[i];\n    const distance = distanceToSegmentSquared(x, y, segment.a, segment.b);\n    if (distance < minDistance) {\n      minDistance = distance;\n    }\n  }\n  return Math.sqrt(minDistance);\n}\nfunction hasVisibleFill(shape, override) {\n  if (typeof override === \"boolean\") {\n    return override;\n  }\n  const fill = shape.fill;\n  if (!fill && fill !== 0) {\n    return false;\n  }\n  if (typeof fill === \"string\") {\n    return !TRANSPARENT_REGEX.test(fill);\n  }\n  return true;\n}\nfunction hasVisibleStroke(shape, override) {\n  const linewidth = typeof shape.linewidth === \"number\" ? shape.linewidth : shape._linewidth || 0;\n  if (typeof override === \"boolean\") {\n    return override && linewidth > 0;\n  }\n  if (!(linewidth > 0)) {\n    return false;\n  }\n  const stroke = shape.stroke;\n  if (!stroke && stroke !== 0) {\n    return false;\n  }\n  if (typeof stroke === \"string\") {\n    return !TRANSPARENT_REGEX.test(stroke);\n  }\n  return true;\n}\nfunction boundsContains(rect, x, y, tolerance = 0) {\n  if (!rect) {\n    return false;\n  }\n  const left = rect.left - tolerance;\n  const right = rect.right + tolerance;\n  const top = rect.top - tolerance;\n  const bottom = rect.bottom + tolerance;\n  return x >= left && x <= right && y >= top && y <= bottom;\n}\n\n// src/utils/path.js\nvar EPSILON2 = Number.EPSILON;\nfunction isRelativeAnchor(anchor2) {\n  return !(typeof anchor2.relative === \"boolean\") || !!anchor2.relative;\n}\nfunction setHandleComponent(anchor2, side, dx, dy) {\n  const controls = anchor2.controls;\n  if (!controls || !controls[side]) {\n    return;\n  }\n  if (Math.abs(dx) < EPSILON2 && Math.abs(dy) < EPSILON2) {\n    if (isRelativeAnchor(anchor2)) {\n      controls[side].clear();\n    } else {\n      controls[side].set(anchor2.x, anchor2.y);\n    }\n    return;\n  }\n  if (isRelativeAnchor(anchor2)) {\n    controls[side].set(dx, dy);\n  } else {\n    controls[side].set(anchor2.x + dx, anchor2.y + dy);\n  }\n}\nfunction clearHandleComponent(anchor2, side) {\n  setHandleComponent(anchor2, side, 0, 0);\n}\nfunction getHandleOffset(anchor2, side) {\n  const controls = anchor2.controls;\n  if (!controls || !controls[side]) {\n    return { x: 0, y: 0 };\n  }\n  if (isRelativeAnchor(anchor2)) {\n    return { x: controls[side].x, y: controls[side].y };\n  }\n  return {\n    x: controls[side].x - anchor2.x,\n    y: controls[side].y - anchor2.y\n  };\n}\nfunction hasNonZeroHandle(anchor2, side) {\n  const offset = getHandleOffset(anchor2, side);\n  return Math.abs(offset.x) > EPSILON2 || Math.abs(offset.y) > EPSILON2;\n}\nfunction updateAnchorCommand(anchor2) {\n  if (anchor2.command === Commands.move || anchor2.command === Commands.close) {\n    return;\n  }\n  anchor2.command = hasNonZeroHandle(anchor2, \"left\") || hasNonZeroHandle(anchor2, \"right\") ? Commands.curve : Commands.line;\n}\nfunction inheritRelative(anchor2, reference) {\n  if (typeof reference.relative === \"boolean\") {\n    anchor2.relative = reference.relative;\n  }\n}\nfunction isSegmentCurved(a, b) {\n  return hasNonZeroHandle(b, \"right\") || hasNonZeroHandle(a, \"left\") || hasNonZeroHandle(a, \"right\") || hasNonZeroHandle(b, \"left\") || a.command === Commands.curve || b.command === Commands.curve;\n}\nfunction lerpPoint(a, b, t) {\n  return {\n    x: lerp(a.x, b.x, t),\n    y: lerp(a.y, b.y, t)\n  };\n}\nfunction getAbsoluteHandle(anchor2, side) {\n  const controls = anchor2.controls && anchor2.controls[side];\n  if (!controls) {\n    return { x: anchor2.x, y: anchor2.y };\n  }\n  if (isRelativeAnchor(anchor2)) {\n    return { x: anchor2.x + controls.x, y: anchor2.y + controls.y };\n  }\n  return { x: controls.x, y: controls.y };\n}\nfunction splitSubdivisionSegment(start, end, t) {\n  const right = start.controls && start.controls.right;\n  const left = end.controls && end.controls.left;\n  const p0 = { x: start.x, y: start.y };\n  const p1 = right ? getAbsoluteHandle(start, \"right\") : __spreadValues({}, p0);\n  const p3 = { x: end.x, y: end.y };\n  const p2 = left ? getAbsoluteHandle(end, \"left\") : __spreadValues({}, p3);\n  const q0 = lerpPoint(p0, p1, t);\n  const q1 = lerpPoint(p1, p2, t);\n  const q2 = lerpPoint(p2, p3, t);\n  const r0 = lerpPoint(q0, q1, t);\n  const r1 = lerpPoint(q1, q2, t);\n  const point = lerpPoint(r0, r1, t);\n  const anchor2 = new Anchor(point.x, point.y);\n  inheritRelative(anchor2, start);\n  setHandleComponent(anchor2, \"left\", r0.x - point.x, r0.y - point.y);\n  setHandleComponent(anchor2, \"right\", r1.x - point.x, r1.y - point.y);\n  anchor2.command = Commands.curve;\n  return {\n    anchor: anchor2,\n    startOut: q0,\n    endIn: q2\n  };\n}\nfunction applyGlobalSmooth(vertices, from, to, closed2, loop2, asymmetric) {\n  const length = vertices.length;\n  const amount = to - from + 1;\n  let n = amount - 1;\n  let padding = loop2 ? Math.min(amount, 4) : 1;\n  let paddingLeft = padding;\n  let paddingRight = padding;\n  if (!closed2) {\n    paddingLeft = Math.min(1, from);\n    paddingRight = Math.min(1, length - to - 1);\n  }\n  n += paddingLeft + paddingRight;\n  if (n <= 1) {\n    return;\n  }\n  const knots = new Array(n + 1);\n  for (let i = 0, j = from - paddingLeft; i <= n; i += 1, j += 1) {\n    const index = mod(j, length);\n    knots[i] = vertices[index];\n  }\n  let x = knots[0].x + 2 * knots[1].x;\n  let y = knots[0].y + 2 * knots[1].y;\n  let f = 2;\n  const n1 = n - 1;\n  const rx = [x];\n  const ry = [y];\n  const rf = [f];\n  const px = new Array(n + 1);\n  const py = new Array(n + 1);\n  for (let i = 1; i < n; i += 1) {\n    const internal = i < n1;\n    const a = internal ? 1 : asymmetric ? 1 : 2;\n    const b = internal ? 4 : asymmetric ? 2 : 7;\n    const u = internal ? 4 : asymmetric ? 3 : 8;\n    const v = internal ? 2 : asymmetric ? 0 : 1;\n    const m = a / f;\n    f = rf[i] = b - m;\n    x = rx[i] = u * knots[i].x + v * knots[i + 1].x - m * x;\n    y = ry[i] = u * knots[i].y + v * knots[i + 1].y - m * y;\n  }\n  px[n1] = rx[n1] / rf[n1];\n  py[n1] = ry[n1] / rf[n1];\n  for (let i = n - 2; i >= 0; i -= 1) {\n    px[i] = (rx[i] - px[i + 1]) / rf[i];\n    py[i] = (ry[i] - py[i + 1]) / rf[i];\n  }\n  px[n] = (3 * knots[n].x - px[n1]) / 2;\n  py[n] = (3 * knots[n].y - py[n1]) / 2;\n  const max5 = n - paddingRight;\n  for (let i = paddingLeft, j = from; i <= max5; i += 1, j += 1) {\n    const index = mod(j, length);\n    const anchor2 = vertices[index];\n    const hx = px[i] - anchor2.x;\n    const hy = py[i] - anchor2.y;\n    if (loop2 || i < max5) {\n      setHandleComponent(anchor2, \"right\", hx, hy);\n    } else {\n      clearHandleComponent(anchor2, \"right\");\n    }\n    if (loop2 || i > paddingLeft) {\n      setHandleComponent(anchor2, \"left\", -hx, -hy);\n    } else {\n      clearHandleComponent(anchor2, \"left\");\n    }\n    updateAnchorCommand(anchor2);\n  }\n}\nfunction applyCatmullRom(anchor2, prev, next, factor, clampIn, clampOut) {\n  const p0 = prev || anchor2;\n  const p1 = anchor2;\n  const p2 = next || anchor2;\n  const d1 = Vector.distanceBetween(p0, p1);\n  const d2 = Vector.distanceBetween(p1, p2);\n  const a = factor === void 0 ? 0.5 : factor;\n  const d1a = Math.pow(d1, a);\n  const d2a = Math.pow(d2, a);\n  const d1_2a = d1a * d1a;\n  const d2_2a = d2a * d2a;\n  if (!clampIn && prev) {\n    const A = 2 * d2_2a + 3 * d2a * d1a + d1_2a;\n    const N = 3 * d2a * (d2a + d1a);\n    if (N !== 0) {\n      const hx = (d2_2a * p0.x + A * p1.x - d1_2a * p2.x) / N - p1.x;\n      const hy = (d2_2a * p0.y + A * p1.y - d1_2a * p2.y) / N - p1.y;\n      setHandleComponent(anchor2, \"left\", hx, hy);\n    } else {\n      clearHandleComponent(anchor2, \"left\");\n    }\n  } else {\n    clearHandleComponent(anchor2, \"left\");\n  }\n  if (!clampOut && next) {\n    const A = 2 * d1_2a + 3 * d1a * d2a + d2_2a;\n    const N = 3 * d1a * (d1a + d2a);\n    if (N !== 0) {\n      const hx = (d1_2a * p2.x + A * p1.x - d2_2a * p0.x) / N - p1.x;\n      const hy = (d1_2a * p2.y + A * p1.y - d2_2a * p0.y) / N - p1.y;\n      setHandleComponent(anchor2, \"right\", hx, hy);\n    } else {\n      clearHandleComponent(anchor2, \"right\");\n    }\n  } else {\n    clearHandleComponent(anchor2, \"right\");\n  }\n  updateAnchorCommand(anchor2);\n}\nfunction applyGeometric(anchor2, prev, next, factor, clampIn, clampOut) {\n  if (!(prev && next)) {\n    if (!prev) {\n      clearHandleComponent(anchor2, \"left\");\n    }\n    if (!next) {\n      clearHandleComponent(anchor2, \"right\");\n    }\n    updateAnchorCommand(anchor2);\n    return;\n  }\n  const p0 = prev;\n  const p1 = anchor2;\n  const p2 = next;\n  const d1 = Vector.distanceBetween(p0, p1);\n  const d2 = Vector.distanceBetween(p1, p2);\n  const total = d1 + d2;\n  const tension = factor === void 0 ? 0.4 : factor;\n  const vector3 = { x: p0.x - p2.x, y: p0.y - p2.y };\n  if (!clampIn && total !== 0) {\n    const k = tension * d1 / total;\n    setHandleComponent(anchor2, \"left\", vector3.x * k, vector3.y * k);\n  } else {\n    clearHandleComponent(anchor2, \"left\");\n  }\n  if (!clampOut && total !== 0) {\n    const k = tension * d1 / total - tension;\n    setHandleComponent(anchor2, \"right\", vector3.x * k, vector3.y * k);\n  } else {\n    clearHandleComponent(anchor2, \"right\");\n  }\n  updateAnchorCommand(anchor2);\n}\nfunction applyLocalSmooth(vertices, from, to, closed2, loop2, options) {\n  const type = options.type || \"catmull-rom\";\n  const factor = options.factor;\n  const length = vertices.length;\n  for (let i = from; i <= to; i += 1) {\n    const index = mod(i, length);\n    const anchor2 = vertices[index];\n    if (anchor2.command === Commands.move) {\n      clearHandleComponent(anchor2, \"left\");\n      clearHandleComponent(anchor2, \"right\");\n      continue;\n    }\n    const prevIndex = i === from && !loop2 ? null : i - 1;\n    const nextIndex = i === to && !loop2 ? null : i + 1;\n    const prev = prevIndex === null ? null : vertices[mod(prevIndex, length)];\n    const next = nextIndex === null ? null : vertices[mod(nextIndex, length)];\n    const clampIn = prevIndex === null;\n    const clampOut = nextIndex === null;\n    if (type === \"geometric\") {\n      applyGeometric(anchor2, prev, next, factor, clampIn, clampOut);\n    } else {\n      applyCatmullRom(anchor2, prev, next, factor, clampIn, clampOut);\n    }\n  }\n}\n\n// src/path.js\nvar min = Math.min;\nvar max = Math.max;\nvar ceil = Math.ceil;\nvar floor2 = Math.floor;\nvar vector = new Vector();\nvar hitTestMatrix = new Matrix2();\nvar _Path = class _Path extends Shape {\n  constructor(vertices, closed2, curved, manual) {\n    super();\n    /**\n     * @name Two.Path#_flagVertices\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#vertices} need updating.\n     */\n    __publicField(this, \"_flagVertices\", true);\n    /**\n     * @name Two.Path#_flagLength\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#length} needs updating.\n     */\n    __publicField(this, \"_flagLength\", true);\n    /**\n     * @name Two.Path#_flagFill\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#fill} needs updating.\n     */\n    __publicField(this, \"_flagFill\", true);\n    /**\n     * @name Two.Path#_flagStroke\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#stroke} needs updating.\n     */\n    __publicField(this, \"_flagStroke\", true);\n    /**\n     * @name Two.Path#_flagLinewidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#linewidth} needs updating.\n     */\n    __publicField(this, \"_flagLinewidth\", true);\n    /**\n     * @name Two.Path#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#opacity} needs updating.\n     */\n    __publicField(this, \"_flagOpacity\", true);\n    /**\n     * @name Two.Path#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#visible} needs updating.\n     */\n    __publicField(this, \"_flagVisible\", true);\n    /**\n     * @name Two.Path#_flagCap\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#cap} needs updating.\n     */\n    __publicField(this, \"_flagCap\", true);\n    /**\n     * @name Two.Path#_flagJoin\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#join} needs updating.\n     */\n    __publicField(this, \"_flagJoin\", true);\n    /**\n     * @name Two.Path#_flagMiter\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#miter} needs updating.\n     */\n    __publicField(this, \"_flagMiter\", true);\n    /**\n     * @name Two.Path#_flagStrokeAttenuation\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#strokeAttenuation} needs updating.\n     */\n    __publicField(this, \"_flagStrokeAttenuation\", true);\n    /**\n     * @name Two.Path#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#mask} needs updating.\n     */\n    __publicField(this, \"_flagMask\", false);\n    /**\n     * @name Two.Path#_flagClip\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#clip} needs updating.\n     */\n    __publicField(this, \"_flagClip\", false);\n    // Underlying Properties\n    /**\n     * @name Two.Path#_length\n     * @private\n     * @see {@link Two.Path#length}\n     */\n    __publicField(this, \"_length\", 0);\n    /**\n     * @name Two.Path#_fill\n     * @private\n     * @see {@link Two.Path#fill}\n     */\n    __publicField(this, \"_fill\", \"#fff\");\n    /**\n     * @name Two.Path#_stroke\n     * @private\n     * @see {@link Two.Path#stroke}\n     */\n    __publicField(this, \"_stroke\", \"#000\");\n    /**\n     * @name Two.Path#_linewidth\n     * @private\n     * @see {@link Two.Path#linewidth}\n     */\n    __publicField(this, \"_linewidth\", 1);\n    /**\n     * @name Two.Path#_opacity\n     * @private\n     * @see {@link Two.Path#opacity}\n     */\n    __publicField(this, \"_opacity\", 1);\n    /**\n     * @name Two.Path#_visible\n     * @private\n     * @see {@link Two.Path#visible}\n     */\n    __publicField(this, \"_visible\", true);\n    /**\n     * @name Two.Path#_cap\n     * @private\n     * @see {@link Two.Path#cap}\n     */\n    __publicField(this, \"_cap\", \"round\");\n    /**\n     * @name Two.Path#_join\n     * @private\n     * @see {@link Two.Path#join}\n     */\n    __publicField(this, \"_join\", \"round\");\n    /**\n     * @name Two.Path#_miter\n     * @private\n     * @see {@link Two.Path#miter}\n     */\n    __publicField(this, \"_miter\", 4);\n    /**\n     * @name Two.Path#_closed\n     * @private\n     * @see {@link Two.Path#closed}\n     */\n    __publicField(this, \"_closed\", true);\n    /**\n     * @name Two.Path#_curved\n     * @private\n     * @see {@link Two.Path#curved}\n     */\n    __publicField(this, \"_curved\", false);\n    /**\n     * @name Two.Path#_automatic\n     * @private\n     * @see {@link Two.Path#automatic}\n     */\n    __publicField(this, \"_automatic\", true);\n    /**\n     * @name Two.Path#_beginning\n     * @private\n     * @see {@link Two.Path#beginning}\n     */\n    __publicField(this, \"_beginning\", 0);\n    /**\n     * @name Two.Path#_ending\n     * @private\n     * @see {@link Two.Path#ending}\n     */\n    __publicField(this, \"_ending\", 1);\n    /**\n     * @name Two.Path#_mask\n     * @private\n     * @see {@link Two.Path#mask}\n     */\n    __publicField(this, \"_mask\", null);\n    /**\n     * @name Two.Path#_clip\n     * @private\n     * @see {@link Two.Path#clip}\n     */\n    __publicField(this, \"_clip\", false);\n    /**\n     * @name Two.Path#_dashes\n     * @private\n     * @see {@link Two.Path#dashes}\n     */\n    __publicField(this, \"_dashes\", null);\n    /**\n     * @name Two.Path#_strokeAttenuation\n     * @private\n     * @see {@link Two.Path#strokeAttenuation}\n     */\n    __publicField(this, \"_strokeAttenuation\", true);\n    for (let prop in proto10) {\n      Object.defineProperty(this, prop, proto10[prop]);\n    }\n    this._renderer.type = \"path\";\n    this._renderer.flagVertices = FlagVertices.bind(this);\n    this._renderer.bindVertices = BindVertices.bind(this);\n    this._renderer.unbindVertices = UnbindVertices.bind(this);\n    this._renderer.flagFill = FlagFill.bind(this);\n    this._renderer.flagStroke = FlagStroke.bind(this);\n    this._renderer.vertices = [];\n    this._renderer.collection = [];\n    this.closed = !!closed2;\n    this.curved = !!curved;\n    this.beginning = 0;\n    this.ending = 1;\n    this.fill = \"#fff\";\n    this.stroke = \"#000\";\n    this.linewidth = 1;\n    this.opacity = 1;\n    this.className = \"\";\n    this.visible = true;\n    this.cap = \"butt\";\n    this.join = \"miter\";\n    this.miter = 4;\n    this.vertices = vertices;\n    this.automatic = !manual;\n    this.dashes = [];\n    this.dashes.offset = 0;\n  }\n  /**\n   * @name Two.Path.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Path} to create a new instance\n   * @returns {Two.Path}\n   * @description Create a new {@link Two.Path} from an object notation of a {@link Two.Path}.\n   * @nota-bene Works in conjunction with {@link Two.Path#toObject}\n   */\n  static fromObject(obj) {\n    const fill = typeof obj.fill === \"string\" ? obj.fill : getEffectFromObject(obj.fill);\n    const stroke = typeof obj.stroke === \"string\" ? obj.stroke : getEffectFromObject(obj.stroke);\n    const path = new _Path().copy(__spreadProps(__spreadValues({}, obj), { fill, stroke }));\n    if (\"id\" in obj) {\n      path.id = obj.id;\n    }\n    return path;\n  }\n  /**\n   * @name Two.Path#copy\n   * @function\n   * @param {Two.Path} path - The reference {@link Two.Path}\n   * @description Copy the properties of one {@link Two.Path} onto another.\n   */\n  copy(path) {\n    super.copy.call(this, path);\n    if (path.vertices) {\n      this.vertices = [];\n      for (let j = 0; j < path.vertices.length; j++) {\n        const v = path.vertices[j];\n        if (v instanceof Anchor) {\n          this.vertices.push(path.vertices[j].clone());\n        } else {\n          this.vertices.push(new Anchor().copy(v));\n        }\n      }\n    }\n    for (let i = 0; i < _Path.Properties.length; i++) {\n      const k = _Path.Properties[i];\n      if (k in path) {\n        this[k] = path[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Path#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Path}\n   * @description Create a new instance of {@link Two.Path} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new _Path();\n    for (let j = 0; j < this.vertices.length; j++) {\n      clone.vertices.push(this.vertices[j].clone());\n    }\n    for (let i = 0; i < _Path.Properties.length; i++) {\n      const k = _Path.Properties[i];\n      clone[k] = this[k];\n    }\n    clone.className = this.className;\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone._update();\n  }\n  /**\n   * @name Two.Path#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   * @nota-bene Works in conjunction with {@link Two.Path.fromObject}\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = \"path\";\n    result.vertices = this.vertices.map((v) => v.toObject());\n    _.each(\n      _Path.Properties,\n      (k) => {\n        if (typeof this[k] !== \"undefined\") {\n          if (this[k].toObject) {\n            result[k] = this[k].toObject();\n          } else {\n            result[k] = this[k];\n          }\n        }\n      },\n      this\n    );\n    return result;\n  }\n  /**\n   * @name Two.Path#dispose\n   * @function\n   * @returns {Two.Path}\n   * @description Release the path's renderer resources and detach all events.\n   * This method cleans up vertices collection events, individual vertex events,\n   * control point events, and disposes fill/stroke effects (calling dispose()\n   * on Gradients and Textures for thorough cleanup) while preserving the\n   * renderer type for potential re-attachment to a new renderer.\n   */\n  dispose() {\n    super.dispose();\n    if (this.vertices && typeof this.vertices.unbind === \"function\") {\n      try {\n        this.vertices.unbind();\n      } catch (e) {\n      }\n    }\n    if (this.vertices) {\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        if (typeof v.unbind === \"function\") {\n          v.unbind();\n        }\n        if (v.controls) {\n          if (v.controls.left && typeof v.controls.left.unbind === \"function\") {\n            v.controls.left.unbind();\n          }\n          if (v.controls.right && typeof v.controls.right.unbind === \"function\") {\n            v.controls.right.unbind();\n          }\n        }\n      }\n    }\n    if (typeof this.fill === \"object\" && typeof this.fill.dispose === \"function\") {\n      this.fill.dispose();\n    } else if (typeof this.fill === \"object\" && typeof this.fill.unbind === \"function\") {\n      this.fill.unbind();\n    }\n    if (typeof this.stroke === \"object\" && typeof this.stroke.dispose === \"function\") {\n      this.stroke.dispose();\n    } else if (typeof this.stroke === \"object\" && typeof this.stroke.unbind === \"function\") {\n      this.stroke.unbind();\n    }\n    return this;\n  }\n  /**\n   * @name Two.Path#noFill\n   * @function\n   * @description Short hand method to set fill to `none`.\n   */\n  noFill() {\n    this.fill = \"none\";\n    return this;\n  }\n  /**\n   * @name Two.Path#noStroke\n   * @function\n   * @description Short hand method to set stroke to `none`.\n   */\n  noStroke() {\n    this.stroke = \"none\";\n    this.linewidth = 0;\n    return this;\n  }\n  /**\n   * @name Two.Path#corner\n   * @function\n   * @description Orient the vertices of the shape to the upper left-hand corner of the path.\n   */\n  corner() {\n    const rect = this.getBoundingClientRect(true);\n    const hw = rect.width / 2;\n    const hh = rect.height / 2;\n    const cx = rect.left + rect.width / 2;\n    const cy = rect.top + rect.height / 2;\n    for (let i = 0; i < this.vertices.length; i++) {\n      const v = this.vertices[i];\n      v.x -= cx;\n      v.y -= cy;\n      v.x += hw;\n      v.y += hh;\n    }\n    if (this.mask) {\n      this.mask.translation.x -= cx;\n      this.mask.translation.x += hw;\n      this.mask.translation.y -= cy;\n      this.mask.translation.y += hh;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Path#center\n   * @function\n   * @description Orient the vertices of the shape to the center of the path.\n   */\n  center() {\n    const rect = this.getBoundingClientRect(true);\n    const cx = rect.left + rect.width / 2 - this.translation.x;\n    const cy = rect.top + rect.height / 2 - this.translation.y;\n    for (let i = 0; i < this.vertices.length; i++) {\n      const v = this.vertices[i];\n      v.x -= cx;\n      v.y -= cy;\n    }\n    if (this.mask) {\n      this.mask.translation.x -= cx;\n      this.mask.translation.y -= cy;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Path#getBoundingClientRect\n   * @function\n   * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n   * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n   * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n   */\n  getBoundingClientRect(shallow) {\n    let matrix, border, l, i, v0, v1;\n    let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity;\n    this._update(true);\n    matrix = shallow ? this.matrix : this.worldMatrix;\n    border = (this.linewidth || 0) / 2;\n    l = this._renderer.vertices.length;\n    if (this.linewidth > 0 || this.stroke && !/(transparent|none)/i.test(this.stroke)) {\n      if (this.matrix.manual) {\n        const { scaleX, scaleY } = decomposeMatrix(\n          matrix.elements[0],\n          matrix.elements[3],\n          matrix.elements[1],\n          matrix.elements[4],\n          matrix.elements[2],\n          matrix.elements[5]\n        );\n        if (typeof scaleX === \"number\" && typeof scaleY === \"number\") {\n          border = Math.max(scaleX, scaleY) * (this.linewidth || 0) / 2;\n        }\n      } else {\n        border *= typeof this.scale === \"number\" ? this.scale : Math.max(this.scale.x, this.scale.y);\n      }\n    }\n    if (l <= 0) {\n      return {\n        width: 0,\n        height: 0\n      };\n    }\n    for (i = 0; i < l; i++) {\n      v1 = this._renderer.vertices[i];\n      v0 = this._renderer.vertices[(i + l - 1) % l];\n      const [v0x, v0y] = matrix.multiply(v0.x, v0.y);\n      const [v1x, v1y] = matrix.multiply(v1.x, v1.y);\n      if (v0.controls && v1.controls) {\n        let rx = v0.controls.right.x;\n        let ry = v0.controls.right.y;\n        if (v0.relative) {\n          rx += v0.x;\n          ry += v0.y;\n        }\n        let [c0x, c0y] = matrix.multiply(rx, ry);\n        let lx = v1.controls.left.x;\n        let ly = v1.controls.left.y;\n        if (v1.relative) {\n          lx += v1.x;\n          ly += v1.y;\n        }\n        let [c1x, c1y] = matrix.multiply(lx, ly);\n        const bb = getCurveBoundingBox(v0x, v0y, c0x, c0y, c1x, c1y, v1x, v1y);\n        top = min(bb.min.y - border, top);\n        left = min(bb.min.x - border, left);\n        right = max(bb.max.x + border, right);\n        bottom = max(bb.max.y + border, bottom);\n      } else {\n        if (i <= 1) {\n          top = min(v0y - border, top);\n          left = min(v0x - border, left);\n          right = max(v0x + border, right);\n          bottom = max(v0y + border, bottom);\n        }\n        top = min(v1y - border, top);\n        left = min(v1x - border, left);\n        right = max(v1x + border, right);\n        bottom = max(v1y + border, bottom);\n      }\n    }\n    return {\n      top,\n      left,\n      right,\n      bottom,\n      width: right - left,\n      height: bottom - top\n    };\n  }\n  /**\n   * @name Two.Path#contains\n   * @function\n   * @param {Number} x - x coordinate to hit test against\n   * @param {Number} y - y coordinate to hit test against\n   * @param {Object} [options] - Optional options object\n   * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `path.visible = false` shapes\n   * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n   * @returns {Boolean}\n   * @description Check to see if coordinates are within a {@link Two.Path}'s bounding rectangle\n   * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n   */\n  contains(x, y, options) {\n    const opts = options || {};\n    const ignoreVisibility = opts.ignoreVisibility === true;\n    if (!ignoreVisibility && this.visible === false) {\n      return false;\n    }\n    if (!ignoreVisibility && typeof this.opacity === \"number\" && this.opacity <= 0) {\n      return false;\n    }\n    const tolerance = typeof opts.tolerance === \"number\" ? opts.tolerance : 0;\n    this._update(true);\n    const rect = this.getBoundingClientRect();\n    if (!rect || x < rect.left - tolerance || x > rect.right + tolerance || y < rect.top - tolerance || y > rect.bottom + tolerance) {\n      return false;\n    }\n    const matrix = this.worldMatrix;\n    const inverse = matrix && matrix.inverse(hitTestMatrix);\n    if (!inverse) {\n      return super.contains(x, y, opts);\n    }\n    const [localX, localY] = inverse.multiply(x, y, 1);\n    const precision = typeof opts.precision === \"number\" && !Number.isNaN(opts.precision) ? Math.max(1, Math.floor(opts.precision)) : 8;\n    const fillTest = hasVisibleFill(this, opts.fill);\n    const strokeTest = hasVisibleStroke(this, opts.stroke);\n    const { polygons, segments } = buildPathHitParts(this, precision);\n    if (fillTest && polygons.length > 0) {\n      if (pointInPolygons(polygons, localX, localY)) {\n        return true;\n      }\n    }\n    if (strokeTest && segments.length > 0) {\n      const linewidth = typeof this.linewidth === \"number\" ? this.linewidth : 0;\n      if (linewidth > 0) {\n        const distance = distanceToSegments(segments, localX, localY);\n        if (distance <= linewidth / 2 + tolerance) {\n          return true;\n        }\n      }\n    }\n    if (!fillTest && !strokeTest) {\n      return super.contains(x, y, opts);\n    }\n    if (fillTest && polygons.length === 0) {\n      return super.contains(x, y, opts);\n    }\n    return false;\n  }\n  /**\n   * @name Two.Path#getPointAt\n   * @function\n   * @param {Number} t - Percentage value describing where on the {@link Two.Path} to estimate and assign coordinate values.\n   * @param {Two.Vector} [obj] - Object to apply calculated x, y to. If none available returns new `Object`.\n   * @returns {Object}\n   * @description Given a float `t` from 0 to 1, return a point or assign a passed `obj`'s coordinates to that percentage on this {@link Two.Path}'s curve.\n   */\n  getPointAt(t, obj) {\n    let ia, ib, result;\n    let x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right;\n    let target = this.length * Math.min(Math.max(t, 0), 1);\n    const length = this.vertices.length;\n    const last = length - 1;\n    let a = null;\n    let b = null;\n    for (let i = 0, l = this._lengths.length, sum = 0; i < l; i++) {\n      if (sum + this._lengths[i] >= target) {\n        if (this._closed) {\n          ia = mod(i, length);\n          ib = mod(i - 1, length);\n          if (i === 0) {\n            ia = ib;\n            ib = i;\n          }\n        } else {\n          ia = i;\n          ib = Math.min(Math.max(i - 1, 0), last);\n        }\n        a = this.vertices[ia];\n        b = this.vertices[ib];\n        target -= sum;\n        if (this._lengths[i] !== 0) {\n          t = target / this._lengths[i];\n        } else {\n          t = 0;\n        }\n        break;\n      }\n      sum += this._lengths[i];\n    }\n    if (a === null || b === null) {\n      return null;\n    }\n    if (!a) {\n      return b;\n    } else if (!b) {\n      return a;\n    }\n    right = b.controls && b.controls.right;\n    left = a.controls && a.controls.left;\n    x1 = b.x;\n    y1 = b.y;\n    x2 = (right || b).x;\n    y2 = (right || b).y;\n    x3 = (left || a).x;\n    y3 = (left || a).y;\n    x4 = a.x;\n    y4 = a.y;\n    if (right && b.relative) {\n      x2 += b.x;\n      y2 += b.y;\n    }\n    if (left && a.relative) {\n      x3 += a.x;\n      y3 += a.y;\n    }\n    x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n    y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n    const t1x = lerp(x1, x2, t);\n    const t1y = lerp(y1, y2, t);\n    const t2x = lerp(x2, x3, t);\n    const t2y = lerp(y2, y3, t);\n    const t3x = lerp(x3, x4, t);\n    const t3y = lerp(y3, y4, t);\n    const brx = lerp(t1x, t2x, t);\n    const bry = lerp(t1y, t2y, t);\n    const alx = lerp(t2x, t3x, t);\n    const aly = lerp(t2y, t3y, t);\n    if (_.isObject(obj)) {\n      obj.x = x;\n      obj.y = y;\n      if (obj instanceof Anchor) {\n        obj.controls.left.x = brx;\n        obj.controls.left.y = bry;\n        obj.controls.right.x = alx;\n        obj.controls.right.y = aly;\n        if (!(typeof obj.relative === \"boolean\") || obj.relative) {\n          obj.controls.left.x -= x;\n          obj.controls.left.y -= y;\n          obj.controls.right.x -= x;\n          obj.controls.right.y -= y;\n        }\n      }\n      obj.t = t;\n      return obj;\n    }\n    result = new Anchor(\n      x,\n      y,\n      brx - x,\n      bry - y,\n      alx - x,\n      aly - y,\n      this._curved ? Commands.curve : Commands.line\n    );\n    result.t = t;\n    return result;\n  }\n  /**\n   * @name Two.Path#plot\n   * @function\n   * @description Based on closed / curved and sorting of vertices plot where all points should be and where the respective handles should be too.\n   * @nota-bene While this method is public it is internally called by {@link Two.Path#_update} when `automatic = true`.\n   */\n  plot() {\n    if (this.curved) {\n      getCurveFromPoints(this._collection, this.closed);\n      return this;\n    }\n    for (let i = 0; i < this._collection.length; i++) {\n      this._collection[i].command = i === 0 ? Commands.move : Commands.line;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Path#smooth\n   * @function\n   * @param {Object} [options] - Configuration for smoothing.\n   * @param {String} [options.type='continuous'] - Type of smoothing algorithm.\n   * @param {Number} [options.from=0] - Index of vertices to start smoothing\n   * @param {Number} [options.to=1] - Index of vertices to terminate smoothing\n   * @description Adjust vertex handles to generate smooth curves without toggling `automatic`.\n   */\n  smooth(options) {\n    const opts = options || {};\n    const type = opts.type || \"continuous\";\n    const vertices = this._collection;\n    const length = vertices.length;\n    if (length < 2) {\n      return this;\n    }\n    const closed2 = this._closed || length > 0 && vertices[length - 1] && vertices[length - 1].command === Commands.close;\n    const resolveIndex = (value, defaultIndex) => {\n      if (value === void 0 || value === null) {\n        return defaultIndex;\n      }\n      if (typeof value === \"number\") {\n        if (closed2) {\n          return mod(value, length);\n        }\n        let index = value;\n        if (index < 0) {\n          index += length;\n        }\n        return Math.min(Math.max(index, 0), length - 1);\n      }\n      const idx = vertices.indexOf(value);\n      return idx !== -1 ? idx : defaultIndex;\n    };\n    const loop2 = closed2 && opts.from === void 0 && opts.to === void 0;\n    let from = resolveIndex(opts.from, 0);\n    let to = resolveIndex(opts.to, length - 1);\n    if (from > to) {\n      if (closed2) {\n        from -= length;\n      } else {\n        const temp2 = from;\n        from = to;\n        to = temp2;\n      }\n    }\n    const rangeLength = to - from + 1;\n    for (let i = 0; i < rangeLength; i += 1) {\n      const index = mod(from + i, length);\n      const anchor2 = vertices[index];\n      const isOpenStart = !closed2 && index === 0;\n      if (anchor2.command === Commands.move && !isOpenStart) {\n        anchor2.command = Commands.line;\n      }\n    }\n    if (type === \"continuous\" || type === \"asymmetric\") {\n      applyGlobalSmooth(\n        vertices,\n        from,\n        to,\n        closed2,\n        loop2,\n        type === \"asymmetric\"\n      );\n    } else if (type === \"catmull-rom\" || type === \"geometric\") {\n      const range = {\n        type,\n        factor: opts.factor\n      };\n      applyLocalSmooth(vertices, from, to, closed2, loop2, range);\n    } else {\n      throw new Error(\n        `Path.smooth does not support type \"${type}\". Try 'continuous', 'asymmetric', 'catmull-rom', or 'geometric'.`\n      );\n    }\n    this._automatic = false;\n    this._flagVertices = true;\n    this._flagLength = true;\n    return this;\n  }\n  /**\n   * @name Two.Path#subdivide\n   * @function\n   * @param {Number} limit - How many times to recurse subdivisions.\n   * @description Insert a {@link Two.Anchor} at the midpoint between every item in {@link Two.Path#vertices}.\n   */\n  subdivide(limit) {\n    this._update();\n    const vertices = this.vertices;\n    const length = vertices.length;\n    if (length < 2) {\n      return this;\n    }\n    const points = [];\n    let prevOriginal = null;\n    let subpathStartOriginal = null;\n    for (let i = 0; i < length; i += 1) {\n      const currentOriginal = vertices[i];\n      if (!prevOriginal || currentOriginal.command === Commands.move) {\n        const clone = currentOriginal.clone();\n        points.push(clone);\n        prevOriginal = currentOriginal;\n        subpathStartOriginal = currentOriginal;\n        continue;\n      }\n      const isCurve = isSegmentCurved(currentOriginal, prevOriginal);\n      if (isCurve) {\n        const subdivided = getSubdivisions(\n          currentOriginal,\n          prevOriginal,\n          limit\n        );\n        const steps = subdivided.length;\n        const prevClone = points[points.length - 1];\n        let startSegment = prevClone.clone();\n        let endSegment = currentOriginal.clone();\n        let prevCloneRef = prevClone;\n        let prevT = 0;\n        if (steps <= 1) {\n          const currentClone = currentOriginal.clone();\n          points.push(currentClone);\n        } else {\n          for (let j = 1; j < steps; j += 1) {\n            const globalT = j / steps;\n            const denom = 1 - prevT;\n            const localT = denom <= Number.EPSILON ? globalT : (globalT - prevT) / denom;\n            const split = splitSubdivisionSegment(\n              startSegment,\n              endSegment,\n              localT\n            );\n            setHandleComponent(\n              prevCloneRef,\n              \"right\",\n              split.startOut.x - prevCloneRef.x,\n              split.startOut.y - prevCloneRef.y\n            );\n            const newAnchor = split.anchor;\n            points.push(newAnchor);\n            prevCloneRef = newAnchor;\n            startSegment = newAnchor.clone();\n            prevT = globalT;\n            setHandleComponent(\n              endSegment,\n              \"left\",\n              split.endIn.x - endSegment.x,\n              split.endIn.y - endSegment.y\n            );\n          }\n          const currentClone = currentOriginal.clone();\n          currentClone.controls.left.copy(endSegment.controls.left);\n          points.push(currentClone);\n        }\n      } else {\n        const subdivided = getSubdivisions(\n          currentOriginal,\n          prevOriginal,\n          limit\n        );\n        for (let j = 1; j < subdivided.length; j += 1) {\n          const anchor2 = subdivided[j];\n          inheritRelative(anchor2, prevOriginal);\n          clearHandleComponent(anchor2, \"left\");\n          clearHandleComponent(anchor2, \"right\");\n          anchor2.command = Commands.line;\n          points.push(anchor2);\n        }\n        const currentClone = currentOriginal.clone();\n        points.push(currentClone);\n      }\n      prevOriginal = currentOriginal;\n      if (currentOriginal.command === Commands.close) {\n        prevOriginal = subpathStartOriginal;\n      }\n    }\n    this._automatic = false;\n    this._curved = false;\n    this.vertices = points;\n    return this;\n  }\n  /**\n   * @name Two.Path#_updateLength\n   * @function\n   * @private\n   * @param {Number} [limit] -\n   * @param {Boolean} [silent=false] - If set to `true` then the path isn't updated before calculation. Useful for internal use.\n   * @description Recalculate the {@link Two.Path#length} value.\n   */\n  _updateLength(limit, silent) {\n    if (!silent) {\n      this._update();\n    }\n    const length = this.vertices.length;\n    const last = length - 1;\n    const closed2 = false;\n    let b = this.vertices[last];\n    let sum = 0;\n    if (typeof this._lengths === \"undefined\") {\n      this._lengths = [];\n    }\n    _.each(\n      this.vertices,\n      function(a, i) {\n        if (i <= 0 && !closed2 || a.command === Commands.move) {\n          b = a;\n          this._lengths[i] = 0;\n          return;\n        }\n        this._lengths[i] = getCurveLength2(a, b, limit);\n        sum += this._lengths[i];\n        b = a;\n      },\n      this\n    );\n    this._length = sum;\n    this._flagLength = false;\n    return this;\n  }\n  /**\n   * @name Two.Path#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices) {\n      if (this._automatic) {\n        this.plot();\n      }\n      if (this._flagLength) {\n        this._updateLength(void 0, true);\n      }\n      const l = this._collection.length;\n      const closed2 = this._closed;\n      const beginning = Math.min(this._beginning, this._ending);\n      const ending = Math.max(this._beginning, this._ending);\n      const bid = getIdByLength(this, beginning * this._length);\n      const eid = getIdByLength(this, ending * this._length);\n      const low = ceil(bid);\n      const high = floor2(eid);\n      let left, right, prev, next, v, i;\n      this._renderer.vertices.length = 0;\n      for (i = 0; i < l; i++) {\n        if (this._renderer.collection.length <= i) {\n          this._renderer.collection.push(new Anchor());\n        }\n        if (i > high && !right) {\n          v = this._renderer.collection[i].copy(this._collection[i]);\n          this.getPointAt(ending, v);\n          v.command = this._renderer.collection[i].command;\n          this._renderer.vertices.push(v);\n          right = v;\n          prev = this._collection[i - 1];\n          if (prev && prev.controls) {\n            if (v.relative) {\n              v.controls.right.clear();\n            } else {\n              v.controls.right.copy(v);\n            }\n            if (prev.relative) {\n              this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(Vector.zero, 1 - v.t);\n            } else {\n              this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(prev, 1 - v.t);\n            }\n          }\n        } else if (i >= low && i <= high) {\n          v = this._renderer.collection[i].copy(this._collection[i]);\n          this._renderer.vertices.push(v);\n          if (i === high && contains(this, ending)) {\n            right = v;\n            if (!closed2 && right.controls) {\n              if (right.relative) {\n                right.controls.right.clear();\n              } else {\n                right.controls.right.copy(right);\n              }\n            }\n          } else if (i === low && contains(this, beginning)) {\n            left = v;\n            left.command = Commands.move;\n            if (!closed2 && left.controls) {\n              if (left.relative) {\n                left.controls.left.clear();\n              } else {\n                left.controls.left.copy(left);\n              }\n            }\n          }\n        }\n      }\n      if (low > 0 && !left) {\n        i = low - 1;\n        v = this._renderer.collection[i].copy(this._collection[i]);\n        this.getPointAt(beginning, v);\n        v.command = Commands.move;\n        this._renderer.vertices.unshift(v);\n        next = this._collection[i + 1];\n        if (next && next.controls) {\n          v.controls.left.clear();\n          if (next.relative) {\n            this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(Vector.zero, v.t);\n          } else {\n            vector.copy(next);\n            this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(next, v.t);\n          }\n        }\n      }\n    }\n    Shape.prototype._update.apply(this, arguments);\n    return this;\n  }\n  /**\n   * @name Two.Path#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagCap = this._flagJoin = this._flagMiter = this._flagClip = this._flagStrokeAttenuation = false;\n    Shape.prototype.flagReset.call(this);\n    return this;\n  }\n};\n/**\n * @name Two.Path.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Path}.\n */\n__publicField(_Path, \"Properties\", [\n  \"fill\",\n  \"stroke\",\n  \"linewidth\",\n  \"opacity\",\n  \"visible\",\n  \"cap\",\n  \"join\",\n  \"miter\",\n  \"closed\",\n  \"curved\",\n  \"automatic\",\n  \"beginning\",\n  \"ending\",\n  \"dashes\",\n  \"strokeAttenuation\"\n]);\n__publicField(_Path, \"Utils\", {\n  getCurveLength: getCurveLength2\n});\nvar Path = _Path;\nvar proto10 = {\n  linewidth: {\n    enumerable: true,\n    get: function() {\n      return this._linewidth;\n    },\n    set: function(v) {\n      this._linewidth = v;\n      this._flagLinewidth = true;\n    }\n  },\n  opacity: {\n    enumerable: true,\n    get: function() {\n      return this._opacity;\n    },\n    set: function(v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n    }\n  },\n  visible: {\n    enumerable: true,\n    get: function() {\n      return this._visible;\n    },\n    set: function(v) {\n      this._visible = v;\n      this._flagVisible = true;\n    }\n  },\n  cap: {\n    enumerable: true,\n    get: function() {\n      return this._cap;\n    },\n    set: function(v) {\n      this._cap = v;\n      this._flagCap = true;\n    }\n  },\n  join: {\n    enumerable: true,\n    get: function() {\n      return this._join;\n    },\n    set: function(v) {\n      this._join = v;\n      this._flagJoin = true;\n    }\n  },\n  miter: {\n    enumerable: true,\n    get: function() {\n      return this._miter;\n    },\n    set: function(v) {\n      this._miter = v;\n      this._flagMiter = true;\n    }\n  },\n  fill: {\n    enumerable: true,\n    get: function() {\n      return this._fill;\n    },\n    set: function(f) {\n      if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n        this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n      }\n      this._fill = f;\n      this._flagFill = true;\n      if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n        this._fill.bind(Events.Types.change, this._renderer.flagFill);\n      }\n    }\n  },\n  stroke: {\n    enumerable: true,\n    get: function() {\n      return this._stroke;\n    },\n    set: function(f) {\n      if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n        this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n      }\n      this._stroke = f;\n      this._flagStroke = true;\n      if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n        this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n      }\n    }\n  },\n  /**\n   * @name Two.Path#length\n   * @property {Number} - The sum of distances between all {@link Two.Path#vertices}.\n   */\n  length: {\n    get: function() {\n      if (this._flagLength) {\n        this._updateLength();\n      }\n      return this._length;\n    }\n  },\n  closed: {\n    enumerable: true,\n    get: function() {\n      return this._closed;\n    },\n    set: function(v) {\n      this._closed = !!v;\n      this._flagVertices = true;\n    }\n  },\n  curved: {\n    enumerable: true,\n    get: function() {\n      return this._curved;\n    },\n    set: function(v) {\n      this._curved = !!v;\n      this._flagVertices = true;\n    }\n  },\n  automatic: {\n    enumerable: true,\n    get: function() {\n      return this._automatic;\n    },\n    set: function(v) {\n      if (v === this._automatic) {\n        return;\n      }\n      this._automatic = !!v;\n      const method = this._automatic ? \"ignore\" : \"listen\";\n      _.each(this.vertices, function(v2) {\n        v2[method]();\n      });\n    }\n  },\n  beginning: {\n    enumerable: true,\n    get: function() {\n      return this._beginning;\n    },\n    set: function(v) {\n      this._beginning = v;\n      this._flagVertices = true;\n    }\n  },\n  ending: {\n    enumerable: true,\n    get: function() {\n      return this._ending;\n    },\n    set: function(v) {\n      this._ending = v;\n      this._flagVertices = true;\n    }\n  },\n  vertices: {\n    enumerable: true,\n    get: function() {\n      return this._collection;\n    },\n    set: function(vertices) {\n      const bindVertices = this._renderer.bindVertices;\n      const unbindVertices = this._renderer.unbindVertices;\n      if (this._collection) {\n        this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices);\n      }\n      if (vertices instanceof Collection) {\n        this._collection = vertices;\n      } else {\n        this._collection = new Collection(vertices || []);\n      }\n      this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices);\n      bindVertices(this._collection);\n    }\n  },\n  /**\n   * @name Two.Path#mask\n   * @property {Two.Shape} - The shape whose alpha property becomes a clipping area for the path.\n   * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n   */\n  mask: {\n    enumerable: true,\n    get: function() {\n      return this._mask;\n    },\n    set: function(v) {\n      this._mask = v;\n      this._flagMask = true;\n      if (_.isObject(v) && !v.clip) {\n        v.clip = true;\n      }\n    }\n  },\n  /**\n   * @name Two.Path#clip\n   * @property {Boolean} - Tells Two.js renderer if this object represents a mask for another object (or not).\n   */\n  clip: {\n    enumerable: true,\n    get: function() {\n      return this._clip;\n    },\n    set: function(v) {\n      this._clip = v;\n      this._flagClip = true;\n    }\n  },\n  dashes: {\n    enumerable: true,\n    get: function() {\n      return this._dashes;\n    },\n    set: function(v) {\n      if (typeof v.offset !== \"number\") {\n        v.offset = this.dashes && this._dashes.offset || 0;\n      }\n      this._dashes = v;\n    }\n  },\n  /**\n   * @name Two.Path#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n   * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function() {\n      return this._strokeAttenuation;\n    },\n    set: function(v) {\n      this._strokeAttenuation = !!v;\n      this._flagStrokeAttenuation = true;\n      this._flagLinewidth = true;\n    }\n  }\n};\nfunction FlagVertices() {\n  this._flagVertices = true;\n  this._flagLength = true;\n  if (this.parent) {\n    this.parent._flagLength = true;\n  }\n}\nfunction BindVertices(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].bind(Events.Types.change, this._renderer.flagVertices);\n  }\n  this._renderer.flagVertices();\n}\nfunction UnbindVertices(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].unbind(Events.Types.change, this._renderer.flagVertices);\n  }\n  this._renderer.flagVertices();\n}\nfunction FlagFill() {\n  this._flagFill = true;\n}\nfunction FlagStroke() {\n  this._flagStroke = true;\n}\n\n// src/shapes/rectangle.js\nvar _Rectangle = class _Rectangle extends Path {\n  constructor(x, y, width, height) {\n    const points = [\n      new Anchor(),\n      new Anchor(),\n      new Anchor(),\n      new Anchor()\n      // new Anchor() // TODO: Figure out how to handle this for `beginning` / `ending` animations\n    ];\n    super(points, true, false, true);\n    /**\n     * @name Two.Rectangle#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Rectangle#width} needs updating.\n     */\n    __publicField(this, \"_flagWidth\", false);\n    /**\n     * @name Two.Rectangle#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Rectangle#height} needs updating.\n     */\n    __publicField(this, \"_flagHeight\", false);\n    /**\n     * @name Two.Rectangle#_width\n     * @private\n     * @see {@link Two.Rectangle#width}\n     */\n    __publicField(this, \"_width\", 0);\n    /**\n     * @name Two.Rectangle#_height\n     * @private\n     * @see {@link Two.Rectangle#height}\n     */\n    __publicField(this, \"_height\", 0);\n    __publicField(this, \"_origin\", null);\n    this._renderer.type = \"rectangle\";\n    for (let prop in proto11) {\n      Object.defineProperty(this, prop, proto11[prop]);\n    }\n    this.width = typeof width === \"number\" ? width : 1;\n    this.height = typeof height === \"number\" ? height : 1;\n    this.origin = new Vector();\n    if (typeof x === \"number\") {\n      this.translation.x = x;\n    }\n    if (typeof y === \"number\") {\n      this.translation.y = y;\n    }\n    this._update();\n  }\n  /**\n   * @name Two.Rectangle.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Rectangle} to create a new instance\n   * @returns {Two.Rectangle}\n   * @description Create a new {@link Two.Rectangle} from an object notation of a {@link Two.Rectangle}.\n   * @nota-bene Works in conjunction with {@link Two.Rectangle#toObject}\n   */\n  static fromObject(obj) {\n    const rectangle = new _Rectangle().copy(obj);\n    if (\"id\" in obj) {\n      rectangle.id = obj.id;\n    }\n    return rectangle;\n  }\n  /**\n   * @name Two.Rectangle#copy\n   * @function\n   * @param {Two.Rectangle} rectangle - The reference {@link Two.Rectangle}\n   * @description Copy the properties of one {@link Two.Rectangle} onto another.\n   */\n  copy(rectangle) {\n    super.copy.call(this, rectangle);\n    for (let i = 0; i < _Rectangle.Properties.length; i++) {\n      const k = _Rectangle.Properties[i];\n      if (k in rectangle) {\n        if (typeof rectangle[k] === \"number\") {\n          this[k] = rectangle[k];\n        } else if (this[k] instanceof Vector) {\n          this[k].copy(rectangle[k]);\n        }\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Rectangle#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagWidth || this._flagHeight) {\n      const xr = this._width / 2;\n      const yr = this._height / 2;\n      if (!this._closed && this.vertices.length === 4) {\n        this.vertices.push(new Anchor());\n      }\n      this.vertices[0].set(-xr, -yr).sub(this._origin).command = Commands.move;\n      this.vertices[1].set(xr, -yr).sub(this._origin).command = Commands.line;\n      this.vertices[2].set(xr, yr).sub(this._origin).command = Commands.line;\n      this.vertices[3].set(-xr, yr).sub(this._origin).command = Commands.line;\n      if (this.vertices[4]) {\n        this.vertices[4].set(-xr, -yr).sub(this._origin).command = Commands.line;\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Rectangle#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Rectangle#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Rectangle}\n   * @description Create a new instance of {@link Two.Rectangle} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new _Rectangle(0, 0, this.width, this.height);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      if (clone[k] instanceof Vector) {\n        clone[k].copy(this[k]);\n      } else {\n        clone[k] = this[k];\n      }\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Rectangle#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"rectangle\";\n    object.width = this.width;\n    object.height = this.height;\n    object.origin = this.origin.toObject();\n    return object;\n  }\n};\n/**\n * @name Two.Rectangle.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Rectangle}.\n */\n__publicField(_Rectangle, \"Properties\", [\"width\", \"height\", \"origin\"]);\nvar Rectangle = _Rectangle;\nvar proto11 = {\n  width: {\n    enumerable: true,\n    get: function() {\n      return this._width;\n    },\n    set: function(v) {\n      this._width = v;\n      this._flagWidth = true;\n    }\n  },\n  height: {\n    enumerable: true,\n    get: function() {\n      return this._height;\n    },\n    set: function(v) {\n      this._height = v;\n      this._flagHeight = true;\n    }\n  },\n  origin: {\n    enumerable: true,\n    get: function() {\n      return this._origin;\n    },\n    set: function(v) {\n      if (this._origin) {\n        this._origin.unbind(Events.Types.change, this._renderer.flagVertices);\n      }\n      this._origin = v;\n      this._origin.bind(Events.Types.change, this._renderer.flagVertices);\n      this._renderer.flagVertices();\n    }\n  }\n};\n\n// src/effects/sprite.js\nvar _Sprite = class _Sprite extends Rectangle {\n  constructor(src, ox, oy, cols, rows, frameRate) {\n    super(ox, oy, 0, 0);\n    /**\n     * @name Two.Sprite#_flagTexture\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#texture} needs updating.\n     */\n    __publicField(this, \"_flagTexture\", false);\n    /**\n     * @name Two.Sprite#_flagColumns\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#columns} need updating.\n     */\n    __publicField(this, \"_flagColumns\", false);\n    /**\n     * @name Two.Sprite#_flagRows\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#rows} need updating.\n     */\n    __publicField(this, \"_flagRows\", false);\n    /**\n     * @name Two.Sprite#_flagFrameRate\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#flagFrameRate} needs updating.\n     */\n    __publicField(this, \"_flagFrameRate\", false);\n    /**\n     * @name Two.Sprite#_flagIndex\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#index} needs updating.\n     */\n    __publicField(this, \"_flagIndex\", false);\n    // Private variables\n    /**\n     * @name Two.Sprite#_amount\n     * @private\n     * @property {Number} - Number of frames for a given {@link Two.Sprite}.\n     */\n    __publicField(this, \"_amount\", 1);\n    /**\n     * @name Two.Sprite#_duration\n     * @private\n     * @property {Number} - Number of milliseconds a {@link Two.Sprite}.\n     */\n    __publicField(this, \"_duration\", 0);\n    /**\n     * @name Two.Sprite#_startTime\n     * @private\n     * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.Sprite} started.\n     */\n    __publicField(this, \"_startTime\", 0);\n    /**\n     * @name Two.Sprite#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.Sprite} is animating or not.\n     */\n    __publicField(this, \"_playing\", false);\n    /**\n     * @name Two.Sprite#_firstFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.Sprite} should start with.\n     */\n    __publicField(this, \"_firstFrame\", 0);\n    /**\n     * @name Two.Sprite#_lastFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.Sprite} should end with.\n     */\n    __publicField(this, \"_lastFrame\", 0);\n    /**\n     * @name Two.Sprite#_loop\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.Sprite} should loop or not.\n     */\n    __publicField(this, \"_loop\", true);\n    // Exposed through getter-setter\n    /**\n     * @name Two.Sprite#_texture\n     * @private\n     * @see {@link Two.Sprite#texture}\n     */\n    __publicField(this, \"_texture\", null);\n    /**\n     * @name Two.Sprite#_columns\n     * @private\n     * @see {@link Two.Sprite#columns}\n     */\n    __publicField(this, \"_columns\", 1);\n    /**\n     * @name Two.Sprite#_rows\n     * @private\n     * @see {@link Two.Sprite#rows}\n     */\n    __publicField(this, \"_rows\", 1);\n    /**\n     * @name Two.Sprite#_frameRate\n     * @private\n     * @see {@link Two.Sprite#frameRate}\n     */\n    __publicField(this, \"_frameRate\", 0);\n    /**\n     * @name Two.Sprite#_index\n     * @private\n     * @property {Number} - The current frame the {@link Two.Sprite} is currently displaying.\n     */\n    __publicField(this, \"_index\", 0);\n    /**\n     * @name Two.Sprite#_origin\n     * @private\n     * @see {@link Two.Sprite#origin}\n     */\n    __publicField(this, \"_origin\", null);\n    this._renderer.type = \"sprite\";\n    for (let prop in proto12) {\n      Object.defineProperty(this, prop, proto12[prop]);\n    }\n    this.noStroke();\n    this.noFill();\n    if (src instanceof Texture) {\n      this.texture = src;\n    } else if (typeof src === \"string\") {\n      this.texture = new Texture(src);\n    }\n    this.origin = new Vector();\n    this._update();\n    if (typeof cols === \"number\") {\n      this.columns = cols;\n    }\n    if (typeof rows === \"number\") {\n      this.rows = rows;\n    }\n    if (typeof frameRate === \"number\") {\n      this.frameRate = frameRate;\n    }\n    this.index = 0;\n  }\n  /**\n   * @name Two.Sprite.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Sprite} to create a new instance\n   * @returns {Two.Sprite}\n   * @description Create a new {@link Two.Sprite} from an object notation of a {@link Two.Sprite}.\n   * @nota-bene Works in conjunction with {@link Two.Sprite#toObject}\n   */\n  static fromObject(obj) {\n    const sprite = new _Sprite().copy(obj);\n    if (\"id\" in obj) {\n      sprite.id = obj.id;\n    }\n    return sprite;\n  }\n  /**\n   * @name Two.Sprite#copy\n   * @function\n   * @param {Two.Sprite} sprite - The reference {@link Two.Sprite}\n   * @description Copy the properties of one {@link Two.Sprite} onto another.\n   */\n  copy(sprite) {\n    super.copy.call(this, sprite);\n    for (let i = 0; i < _Sprite.Properties.length; i++) {\n      const k = _Sprite.Properties[i];\n      if (k in sprite) {\n        this[k] = sprite[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Sprite#play\n   * @function\n   * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n   * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.Sprite#textures}.\n   * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the sprite is looped.\n   * @description Initiate animation playback of a {@link Two.Sprite}.\n   */\n  play(firstFrame, lastFrame, onLastFrame) {\n    this._playing = true;\n    this._firstFrame = 0;\n    this._lastFrame = this.amount - 1;\n    this._startTime = _.performance.now();\n    if (typeof firstFrame === \"number\") {\n      this._firstFrame = firstFrame;\n    }\n    if (typeof lastFrame === \"number\") {\n      this._lastFrame = lastFrame;\n    }\n    if (typeof onLastFrame === \"function\") {\n      this._onLastFrame = onLastFrame;\n    } else {\n      delete this._onLastFrame;\n    }\n    if (this._index !== this._firstFrame) {\n      this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Sprite#pause\n   * @function\n   * @description Halt animation playback of a {@link Two.Sprite}.\n   */\n  pause() {\n    this._playing = false;\n    return this;\n  }\n  /**\n   * @name Two.Sprite#stop\n   * @function\n   * @description Halt animation playback of a {@link Two.Sprite} and set the current frame back to the first frame.\n   */\n  stop() {\n    this._playing = false;\n    this._index = 0;\n    return this;\n  }\n  /**\n   * @name Two.Sprite#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Sprite}\n   * @description Create a new instance of {@link Two.Sprite} with the same properties of the current sprite.\n   */\n  clone(parent) {\n    const clone = new _Sprite(\n      this.texture,\n      this.translation.x,\n      this.translation.y,\n      this.columns,\n      this.rows,\n      this.frameRate\n    );\n    if (this.playing) {\n      clone.play(this._firstFrame, this._lastFrame);\n    }\n    clone.loop = this.loop;\n    clone.firstFrame = this.firstFrame;\n    clone.lastFrame = this.lastFrame;\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Sprite#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"sprite\";\n    object.texture = this.texture.toObject();\n    object.columns = this.columns;\n    object.rows = this.rows;\n    object.frameRate = this.frameRate;\n    object.index = this.index;\n    object.firstFrame = this.firstFrame;\n    object.lastFrame = this.lastFrame;\n    object.loop = this.loop;\n    return object;\n  }\n  /**\n   * @name Two.Sprite#dispose\n   * @function\n   * @returns {Two.Sprite}\n   * @description Release the sprite's renderer resources and detach all events.\n   * This method stops any running animation, clears animation callbacks, disposes\n   * the texture (calling dispose() for thorough cleanup), and inherits comprehensive\n   * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n   * for potential re-attachment.\n   */\n  dispose() {\n    super.dispose();\n    if (this._playing) {\n      this._playing = false;\n    }\n    this._onLastFrame = null;\n    this._startTime = 0;\n    if (this._texture && typeof this._texture.dispose === \"function\") {\n      this._texture.dispose();\n    } else if (this._texture && typeof this._texture.unbind === \"function\") {\n      this._texture.unbind();\n    }\n    return this;\n  }\n  /**\n   * @name Two.Sprite#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    const effect = this._texture;\n    const cols = this._columns;\n    const rows = this._rows;\n    let width, height, elapsed, amount, duration;\n    let index, iw, ih, frames;\n    if (effect) {\n      if (this._flagColumns || this._flagRows) {\n        this._amount = this._columns * this._rows;\n      }\n      if (this._flagFrameRate) {\n        this._duration = 1e3 * this._amount / this._frameRate;\n      }\n      if (this._flagTexture) {\n        this.fill = effect;\n      }\n      if (effect.loaded) {\n        iw = effect.image.width;\n        ih = effect.image.height;\n        width = iw / cols;\n        height = ih / rows;\n        amount = this._amount;\n        if (this.width !== width) {\n          this.width = width;\n        }\n        if (this.height !== height) {\n          this.height = height;\n        }\n        if (this._playing && this._frameRate > 0) {\n          if (_.isNaN(this._lastFrame)) {\n            this._lastFrame = amount - 1;\n          }\n          elapsed = _.performance.now() - this._startTime;\n          frames = this._lastFrame + 1;\n          duration = 1e3 * (frames - this._firstFrame) / this._frameRate;\n          if (this._loop) {\n            elapsed = elapsed % duration;\n          } else {\n            elapsed = Math.min(elapsed, duration);\n          }\n          index = lerp(this._firstFrame, frames, elapsed / duration);\n          index = Math.floor(index);\n          if (index !== this._index) {\n            this._index = index;\n            if (index >= this._lastFrame - 1 && this._onLastFrame) {\n              this._onLastFrame();\n            }\n          }\n        }\n        const col = this._index % cols;\n        const row = Math.floor(this._index / cols);\n        const ox = -width * col + (iw - width) / 2;\n        const oy = -height * row + (ih - height) / 2;\n        if (ox !== effect.offset.x) {\n          effect.offset.x = ox;\n        }\n        if (oy !== effect.offset.y) {\n          effect.offset.y = oy;\n        }\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Sprite#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagTexture = this._flagColumns = this._flagRows = this._flagFrameRate = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n/**\n * @name Two.Sprite.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Sprite}.\n */\n__publicField(_Sprite, \"Properties\", [\n  \"texture\",\n  \"columns\",\n  \"rows\",\n  \"frameRate\",\n  \"index\",\n  \"firstFrame\",\n  \"lastFrame\",\n  \"loop\"\n]);\nvar Sprite = _Sprite;\nvar proto12 = {\n  texture: {\n    enumerable: true,\n    get: function() {\n      return this._texture;\n    },\n    set: function(v) {\n      this._texture = v;\n      this._flagTexture = true;\n    }\n  },\n  columns: {\n    enumerable: true,\n    get: function() {\n      return this._columns;\n    },\n    set: function(v) {\n      this._columns = v;\n      this._flagColumns = true;\n    }\n  },\n  rows: {\n    enumerable: true,\n    get: function() {\n      return this._rows;\n    },\n    set: function(v) {\n      this._rows = v;\n      this._flagRows = true;\n    }\n  },\n  frameRate: {\n    enumerable: true,\n    get: function() {\n      return this._frameRate;\n    },\n    set: function(v) {\n      this._frameRate = v;\n      this._flagFrameRate = true;\n    }\n  },\n  index: {\n    enumerable: true,\n    get: function() {\n      return this._index;\n    },\n    set: function(v) {\n      this._index = v;\n      this._flagIndex = true;\n    }\n  },\n  firstFrame: {\n    enumerable: true,\n    get: function() {\n      return this._firstFrame;\n    },\n    set: function(v) {\n      this._firstFrame = v;\n    }\n  },\n  lastFrame: {\n    enumerable: true,\n    get: function() {\n      return this._lastFrame;\n    },\n    set: function(v) {\n      this._lastFrame = v;\n    }\n  },\n  loop: {\n    enumerable: true,\n    get: function() {\n      return this._loop;\n    },\n    set: function(v) {\n      this._loop = !!v;\n    }\n  }\n};\n\n// src/children.js\nvar _ids;\nvar Children = class extends Collection {\n  constructor(children) {\n    children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments);\n    super(children);\n    /**\n     * @name Two.Group.Children#ids\n     * @property {Object} - Map of all elements in the list keyed by `id`s.\n     */\n    // N.B: Technique to disable enumeration on object\n    __privateAdd(this, _ids, {});\n    this.attach(children);\n    this.on(Events.Types.insert, this.attach);\n    this.on(Events.Types.remove, this.detach);\n  }\n  get ids() {\n    return __privateGet(this, _ids);\n  }\n  /**\n   * @function\n   * @name Two.Group.Children#attach\n   * @param {Two.Shape[]} children - The objects which extend {@link Two.Shape} to be added.\n   * @description Adds elements to the `ids` map.\n   */\n  attach(children) {\n    for (let i = 0; i < children.length; i++) {\n      const child = children[i];\n      if (child && child.id) {\n        this.ids[child.id] = child;\n      }\n    }\n    return this;\n  }\n  /**\n   * @function\n   * @name Two.Group.Children#detach\n   * @param {Two.Shape[]} children - The objects which extend {@link Two.Shape} to be removed.\n   * @description Removes elements to the `ids` map.\n   */\n  detach(children) {\n    for (let i = 0; i < children.length; i++) {\n      delete this.ids[children[i].id];\n    }\n    return this;\n  }\n};\n_ids = new WeakMap();\n\n// src/shapes/arc-segment.js\nvar _ArcSegment = class _ArcSegment extends Path {\n  constructor(x, y, ir, or, sa, ea, res) {\n    const amount = res || Constants.Resolution * 3;\n    const points = [];\n    for (let i = 0; i < amount; i++) {\n      points.push(new Anchor());\n    }\n    super(points, true, false, true);\n    /**\n     * @name Two.ArcSegment#_flagStartAngle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#startAngle} needs updating.\n     */\n    __publicField(this, \"_flagStartAngle\", false);\n    /**\n     * @name Two.ArcSegment#_flagEndAngle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#endAngle} needs updating.\n     */\n    __publicField(this, \"_flagEndAngle\", false);\n    /**\n     * @name Two.ArcSegment#_flagInnerRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#innerRadius} needs updating.\n     */\n    __publicField(this, \"_flagInnerRadius\", false);\n    /**\n     * @name Two.ArcSegment#_flagOuterRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#outerRadius} needs updating.\n     */\n    __publicField(this, \"_flagOuterRadius\", false);\n    /**\n     * @name Two.ArcSegment#_startAngle\n     * @private\n     * @see {@link Two.ArcSegment#startAngle}\n     */\n    __publicField(this, \"_startAngle\", 0);\n    /**\n     * @name Two.ArcSegment#_endAngle\n     * @private\n     * @see {@link Two.ArcSegment#endAngle}\n     */\n    __publicField(this, \"_endAngle\", TWO_PI);\n    /**\n     * @name Two.ArcSegment#_innerRadius\n     * @private\n     * @see {@link Two.ArcSegment#innerRadius}\n     */\n    __publicField(this, \"_innerRadius\", 0);\n    /**\n     * @name Two.ArcSegment#_outerRadius\n     * @private\n     * @see {@link Two.ArcSegment#outerRadius}\n     */\n    __publicField(this, \"_outerRadius\", 0);\n    this._renderer.type = \"arc-segment\";\n    for (let prop in proto13) {\n      Object.defineProperty(this, prop, proto13[prop]);\n    }\n    if (typeof ir === \"number\") {\n      this.innerRadius = ir;\n    }\n    if (typeof or === \"number\") {\n      this.outerRadius = or;\n    }\n    if (typeof sa === \"number\") {\n      this.startAngle = sa;\n    }\n    if (typeof ea === \"number\") {\n      this.endAngle = ea;\n    }\n    this._update();\n    if (typeof x === \"number\") {\n      this.translation.x = x;\n    }\n    if (typeof y === \"number\") {\n      this.translation.y = y;\n    }\n  }\n  /**\n   * @name Two.ArcSegment.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.ArcSegment} to create a new instance\n   * @returns {Two.ArcSegment}\n   * @description Create a new {@link Two.ArcSegment} from an object notation of a {@link Two.ArcSegment}.\n   * @nota-bene Works in conjunction with {@link Two.ArcSegment#toObject}\n   */\n  static fromObject(obj) {\n    const segment = new _ArcSegment().copy(obj);\n    if (\"id\" in obj) {\n      segment.id = obj.id;\n    }\n    return segment;\n  }\n  /**\n   * @name Two.ArcSegment#copy\n   * @function\n   * @param {Two.ArcSegment} arcSegment - The reference {@link Two.ArcSegment}\n   * @description Copy the properties of one {@link Two.ArcSegment} onto another.\n   */\n  copy(arcSegment) {\n    super.copy.call(this, arcSegment);\n    for (let i = 0; i < _ArcSegment.Properties.length; i++) {\n      const k = _ArcSegment.Properties[i];\n      if (k in arcSegment && typeof arcSegment[k] === \"number\") {\n        this[k] = arcSegment[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.ArcSegment#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius || this._flagOuterRadius) {\n      const sa = this._startAngle;\n      const ea = this._endAngle;\n      const ir = this._innerRadius;\n      const or = this._outerRadius;\n      const connected = mod(sa, TWO_PI) === mod(ea, TWO_PI);\n      const punctured = ir > 0;\n      const vertices = this.vertices;\n      let length = punctured ? vertices.length / 2 : vertices.length;\n      let command, id = 0;\n      let i, last, pct, v, theta, step, x, y, amp;\n      if (connected) {\n        length--;\n      } else if (!punctured) {\n        length -= 2;\n      }\n      for (i = 0, last = length - 1; i < length; i++) {\n        pct = i / last;\n        v = vertices[id];\n        theta = pct * (ea - sa) + sa;\n        step = (ea - sa) / length;\n        x = or * Math.cos(theta);\n        y = or * Math.sin(theta);\n        switch (i) {\n          case 0:\n            command = Commands.move;\n            break;\n          default:\n            command = Commands.curve;\n        }\n        v.command = command;\n        v.x = x;\n        v.y = y;\n        v.controls.left.clear();\n        v.controls.right.clear();\n        if (v.command === Commands.curve) {\n          amp = or * step / Math.PI;\n          v.controls.left.x = amp * Math.cos(theta - HALF_PI);\n          v.controls.left.y = amp * Math.sin(theta - HALF_PI);\n          v.controls.right.x = amp * Math.cos(theta + HALF_PI);\n          v.controls.right.y = amp * Math.sin(theta + HALF_PI);\n          if (i === 1) {\n            v.controls.left.multiplyScalar(2);\n          }\n          if (i === last) {\n            v.controls.right.multiplyScalar(2);\n          }\n        }\n        id++;\n      }\n      if (punctured) {\n        if (connected) {\n          vertices[id].command = Commands.close;\n          id++;\n        } else {\n          length--;\n          last = length - 1;\n        }\n        for (i = 0; i < length; i++) {\n          pct = i / last;\n          v = vertices[id];\n          theta = (1 - pct) * (ea - sa) + sa;\n          step = (ea - sa) / length;\n          x = ir * Math.cos(theta);\n          y = ir * Math.sin(theta);\n          command = Commands.curve;\n          if (i <= 0) {\n            command = connected ? Commands.move : Commands.line;\n          }\n          v.command = command;\n          v.x = x;\n          v.y = y;\n          v.controls.left.clear();\n          v.controls.right.clear();\n          if (v.command === Commands.curve) {\n            amp = ir * step / Math.PI;\n            v.controls.left.x = amp * Math.cos(theta + HALF_PI);\n            v.controls.left.y = amp * Math.sin(theta + HALF_PI);\n            v.controls.right.x = amp * Math.cos(theta - HALF_PI);\n            v.controls.right.y = amp * Math.sin(theta - HALF_PI);\n            if (i === 1) {\n              v.controls.left.multiplyScalar(2);\n            }\n            if (i === last) {\n              v.controls.right.multiplyScalar(2);\n            }\n          }\n          id++;\n        }\n        vertices[id].copy(vertices[0]);\n        vertices[id].command = Commands.line;\n      } else if (!connected) {\n        vertices[id].command = Commands.line;\n        vertices[id].x = 0;\n        vertices[id].y = 0;\n        id++;\n        vertices[id].copy(vertices[0]);\n        vertices[id].command = Commands.line;\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.ArcSegment#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    super.flagReset.call(this);\n    this._flagStartAngle = this._flagEndAngle = this._flagInnerRadius = this._flagOuterRadius = false;\n    return this;\n  }\n  /**\n   * @name Two.ArcSegment#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.ArcSegment}\n   * @description Create a new instance of {@link Two.ArcSegment} with the same properties of the current path.\n   */\n  clone(parent) {\n    const ir = this.innerRadius;\n    const or = this.outerRadius;\n    const sa = this.startAngle;\n    const ea = this.endAngle;\n    const resolution = this.vertices.length;\n    const clone = new _ArcSegment(0, 0, ir, or, sa, ea, resolution);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.ArcSegment#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"arc-segment\";\n    for (let i = 0; i < _ArcSegment.Properties.length; i++) {\n      const k = _ArcSegment.Properties[i];\n      object[k] = this[k];\n    }\n    return object;\n  }\n};\n/**\n * @name Two.ArcSegment.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.ArcSegment}.\n */\n__publicField(_ArcSegment, \"Properties\", [\"startAngle\", \"endAngle\", \"innerRadius\", \"outerRadius\"]);\nvar ArcSegment = _ArcSegment;\nvar proto13 = {\n  startAngle: {\n    enumerable: true,\n    get: function() {\n      return this._startAngle;\n    },\n    set: function(v) {\n      this._startAngle = v;\n      this._flagStartAngle = true;\n    }\n  },\n  endAngle: {\n    enumerable: true,\n    get: function() {\n      return this._endAngle;\n    },\n    set: function(v) {\n      this._endAngle = v;\n      this._flagEndAngle = true;\n    }\n  },\n  innerRadius: {\n    enumerable: true,\n    get: function() {\n      return this._innerRadius;\n    },\n    set: function(v) {\n      this._innerRadius = v;\n      this._flagInnerRadius = true;\n    }\n  },\n  outerRadius: {\n    enumerable: true,\n    get: function() {\n      return this._outerRadius;\n    },\n    set: function(v) {\n      this._outerRadius = v;\n      this._flagOuterRadius = true;\n    }\n  }\n};\n\n// src/shapes/circle.js\nvar cos2 = Math.cos;\nvar sin2 = Math.sin;\nvar _Circle = class _Circle extends Path {\n  constructor(ox, oy, r, resolution) {\n    const amount = resolution ? Math.max(resolution, 2) : 4;\n    const points = [];\n    for (let i = 0; i < amount; i++) {\n      points.push(new Anchor(0, 0, 0, 0, 0, 0));\n    }\n    super(points, true, true, true);\n    /**\n     * @name Two.Circle#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Circle#radius} needs updating.\n     */\n    __publicField(this, \"_flagRadius\", false);\n    /**\n     * @name Two.Circle#_radius\n     * @private\n     * @see {@link Two.Circle#radius}\n     */\n    __publicField(this, \"_radius\", 0);\n    this._renderer.type = \"circle\";\n    for (let prop in proto14) {\n      Object.defineProperty(this, prop, proto14[prop]);\n    }\n    if (typeof r === \"number\") {\n      this.radius = r;\n    }\n    this._update();\n    if (typeof ox === \"number\") {\n      this.translation.x = ox;\n    }\n    if (typeof oy === \"number\") {\n      this.translation.y = oy;\n    }\n  }\n  /**\n   * @name Two.Circle.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Circle} to create a new instance\n   * @returns {Two.Circle}\n   * @description Create a new {@link Two.Circle} from an object notation of a {@link Two.Circle}.\n   * @nota-bene Works in conjunction with {@link Two.Circle#toObject}\n   */\n  static fromObject(obj) {\n    const circle = new _Circle().copy(obj);\n    if (\"id\" in obj) {\n      circle.id = obj.id;\n    }\n    return circle;\n  }\n  /**\n   * @name Two.Circle#copy\n   * @function\n   * @param {Two.Circle} circle - The reference {@link Two.Circle}\n   * @description Copy the properties of one {@link Two.Circle} onto another.\n   */\n  copy(circle) {\n    super.copy.call(this, circle);\n    for (let i = 0; i < _Circle.Properties.length; i++) {\n      const k = _Circle.Properties[i];\n      if (k in circle && typeof circle[k] === \"number\") {\n        this[k] = circle[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Circle#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagRadius) {\n      let length = this.vertices.length;\n      if (!this._closed && length > 2) {\n        length -= 1;\n      }\n      const c = 4 / 3 * Math.tan(Math.PI / (length * 2));\n      const radius = this._radius;\n      const rc = radius * c;\n      for (let i = 0; i < this.vertices.length; i++) {\n        const pct = i / length;\n        const theta = pct * TWO_PI;\n        const x = radius * cos2(theta);\n        const y = radius * sin2(theta);\n        const lx = rc * cos2(theta - HALF_PI);\n        const ly = rc * sin2(theta - HALF_PI);\n        const rx = rc * cos2(theta + HALF_PI);\n        const ry = rc * sin2(theta + HALF_PI);\n        const v = this.vertices[i];\n        v.command = i === 0 ? Commands.move : Commands.curve;\n        v.set(x, y);\n        v.controls.left.set(lx, ly);\n        v.controls.right.set(rx, ry);\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Circle#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagRadius = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Circle#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Circle}\n   * @description Create a new instance of {@link Two.Circle} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new _Circle(0, 0, this.radius, this.vertices.length);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Circle#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"circle\";\n    for (let i = 0; i < _Circle.Properties.length; i++) {\n      const k = _Circle.Properties[i];\n      object[k] = this[k];\n    }\n    return object;\n  }\n};\n/**\n * @name Two.Circle.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Circle}.\n */\n__publicField(_Circle, \"Properties\", [\"radius\"]);\nvar Circle = _Circle;\nvar proto14 = {\n  radius: {\n    enumerable: true,\n    get: function() {\n      return this._radius;\n    },\n    set: function(v) {\n      this._radius = v;\n      this._flagRadius = true;\n    }\n  }\n};\n\n// src/shapes/ellipse.js\nvar cos3 = Math.cos;\nvar sin3 = Math.sin;\nvar _Ellipse = class _Ellipse extends Path {\n  constructor(x, y, rx, ry, resolution) {\n    if (typeof ry !== \"number\" && typeof rx === \"number\") {\n      ry = rx;\n    }\n    const amount = resolution ? Math.max(resolution, 2) : 4;\n    const points = [];\n    for (let i = 0; i < amount; i++) {\n      points.push(new Anchor());\n    }\n    super(points, true, true, true);\n    /**\n     * @name Two.Ellipse#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Ellipse#width} needs updating.\n     */\n    __publicField(this, \"_flagWidth\", false);\n    /**\n     * @name Two.Ellipse#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Ellipse#height} needs updating.\n     */\n    __publicField(this, \"_flagHeight\", false);\n    /**\n     * @name Two.Ellipse#_width\n     * @private\n     * @see {@link Two.Ellipse#width}\n     */\n    __publicField(this, \"_width\", 0);\n    /**\n     * @name Two.Ellipse#_height\n     * @private\n     * @see {@link Two.Ellipse#height}\n     */\n    __publicField(this, \"_height\", 0);\n    this._renderer.type = \"ellipse\";\n    for (let prop in proto15) {\n      Object.defineProperty(this, prop, proto15[prop]);\n    }\n    if (typeof rx === \"number\") {\n      this.width = rx * 2;\n    }\n    if (typeof ry === \"number\") {\n      this.height = ry * 2;\n    }\n    this._update();\n    if (typeof x === \"number\") {\n      this.translation.x = x;\n    }\n    if (typeof y === \"number\") {\n      this.translation.y = y;\n    }\n  }\n  /**\n   * @name Two.Ellipse.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Ellipse} to create a new instance\n   * @returns {Two.Ellipse}\n   * @description Create a new {@link Two.Ellipse} from an object notation of a {@link Two.Ellipse}.\n   * @nota-bene Works in conjunction with {@link Two.Ellipse#toObject}\n   */\n  static fromObject(obj) {\n    const ellipse = new _Ellipse().copy(obj);\n    if (\"id\" in obj) {\n      ellipse.id = obj.id;\n    }\n    return ellipse;\n  }\n  /**\n   * @name Two.Ellipse#copy\n   * @function\n   * @param {Two.Ellipse} ellipse - The reference {@link Two.Ellipse}\n   * @description Copy the properties of one {@link Two.Ellipse} onto another.\n   */\n  copy(ellipse) {\n    super.copy.call(this, ellipse);\n    for (let i = 0; i < _Ellipse.Properties.length; i++) {\n      const k = _Ellipse.Properties[i];\n      if (k in ellipse && typeof ellipse[k] === \"number\") {\n        this[k] = ellipse[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Ellipse#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagWidth || this._flagHeight) {\n      let length = this.vertices.length;\n      if (!this._closed && length > 2) {\n        length -= 1;\n      }\n      const c = 4 / 3 * Math.tan(Math.PI / (this.vertices.length * 2));\n      const radiusX = this._width / 2;\n      const radiusY = this._height / 2;\n      for (let i = 0; i < this.vertices.length; i++) {\n        const pct = i / length;\n        const theta = pct * TWO_PI;\n        const x = radiusX * cos3(theta);\n        const y = radiusY * sin3(theta);\n        const lx = radiusX * c * cos3(theta - HALF_PI);\n        const ly = radiusY * c * sin3(theta - HALF_PI);\n        const rx = radiusX * c * cos3(theta + HALF_PI);\n        const ry = radiusY * c * sin3(theta + HALF_PI);\n        const v = this.vertices[i];\n        v.command = i === 0 ? Commands.move : Commands.curve;\n        v.set(x, y);\n        v.controls.left.set(lx, ly);\n        v.controls.right.set(rx, ry);\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Ellipse#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Ellipse#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Ellipse}\n   * @description Create a new instance of {@link Two.Ellipse} with the same properties of the current path.\n   */\n  clone(parent) {\n    const rx = this.width / 2;\n    const ry = this.height / 2;\n    const resolution = this.vertices.length;\n    const clone = new _Ellipse(0, 0, rx, ry, resolution);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Ellipse#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"ellipse\";\n    for (let i = 0; i < _Ellipse.Properties.length; i++) {\n      const k = _Ellipse.Properties[i];\n      object[k] = this[k];\n    }\n    return object;\n  }\n};\n/**\n * @name Two.Ellipse.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Ellipse}.\n */\n__publicField(_Ellipse, \"Properties\", [\"width\", \"height\"]);\nvar Ellipse = _Ellipse;\nvar proto15 = {\n  width: {\n    enumerable: true,\n    get: function() {\n      return this._width;\n    },\n    set: function(v) {\n      this._width = v;\n      this._flagWidth = true;\n    }\n  },\n  height: {\n    enumerable: true,\n    get: function() {\n      return this._height;\n    },\n    set: function(v) {\n      this._height = v;\n      this._flagHeight = true;\n    }\n  }\n};\n\n// src/shapes/points.js\nvar ceil2 = Math.ceil;\nvar floor3 = Math.floor;\nvar _Points = class _Points extends Shape {\n  constructor(vertices) {\n    super();\n    __publicField(this, \"_flagVertices\", true);\n    __publicField(this, \"_flagLength\", true);\n    __publicField(this, \"_flagFill\", true);\n    __publicField(this, \"_flagStroke\", true);\n    __publicField(this, \"_flagLinewidth\", true);\n    __publicField(this, \"_flagOpacity\", true);\n    __publicField(this, \"_flagVisible\", true);\n    __publicField(this, \"_flagSize\", true);\n    __publicField(this, \"_flagSizeAttenuation\", true);\n    __publicField(this, \"_flagStrokeAttenuation\", true);\n    __publicField(this, \"_length\", 0);\n    __publicField(this, \"_fill\", \"#fff\");\n    __publicField(this, \"_stroke\", \"#000\");\n    __publicField(this, \"_linewidth\", 1);\n    __publicField(this, \"_opacity\", 1);\n    __publicField(this, \"_visible\", true);\n    __publicField(this, \"_size\", 1);\n    __publicField(this, \"_sizeAttenuation\", false);\n    __publicField(this, \"_beginning\", 0);\n    __publicField(this, \"_ending\", 1);\n    __publicField(this, \"_dashes\", null);\n    __publicField(this, \"_strokeAttenuation\", true);\n    /**\n     * @name Two.Points#noFill\n     * @function\n     * @description Short hand method to set fill to `none`.\n     */\n    __publicField(this, \"noFill\", Path.prototype.noFill);\n    /**\n     * @name Two.Points#noStroke\n     * @function\n     * @description Short hand method to set stroke to `none`.\n     */\n    __publicField(this, \"noStroke\", Path.prototype.noStroke);\n    /**\n     * @name Two.Points#corner\n     * @function\n     * @description Orient the vertices of the shape to the upper left-hand corner of the points object.\n     */\n    __publicField(this, \"corner\", Path.prototype.corner);\n    /**\n     * @name Two.Points#center\n     * @function\n     * @description Orient the vertices of the shape to the center of the points object.\n     */\n    __publicField(this, \"center\", Path.prototype.center);\n    /**\n     * @name Two.Points#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n     */\n    __publicField(this, \"getBoundingClientRect\", Path.prototype.getBoundingClientRect);\n    /**\n     * @name Two.Points#_updateLength\n     * @function\n     * @private\n     * @param {Number} [limit] -\n     * @param {Boolean} [silent=false] - If set to `true` then the points object isn't updated before calculation. Useful for internal use.\n     * @description Recalculate the {@link Two.Points#length} value.\n     */\n    __publicField(this, \"_updateLength\", Path.prototype._updateLength);\n    for (let prop in proto16) {\n      Object.defineProperty(this, prop, proto16[prop]);\n    }\n    this._renderer.type = \"points\";\n    this._renderer.flagVertices = FlagVertices.bind(this);\n    this._renderer.bindVertices = BindVertices.bind(this);\n    this._renderer.unbindVertices = UnbindVertices.bind(this);\n    this._renderer.flagFill = FlagFill.bind(this);\n    this._renderer.flagStroke = FlagStroke.bind(this);\n    this._renderer.vertices = null;\n    this._renderer.collection = null;\n    this.size = 1;\n    this.sizeAttenuation = false;\n    this.beginning = 0;\n    this.ending = 1;\n    this.fill = \"#fff\";\n    this.stroke = \"#000\";\n    this.linewidth = 1;\n    this.opacity = 1;\n    this.className = \"\";\n    this.visible = true;\n    this.vertices = vertices;\n    this.dashes = [];\n    this.dashes.offset = 0;\n  }\n  /**\n   * @name Two.Points.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Points} to create a new instance\n   * @returns {Two.Points}\n   * @description Create a new {@link Two.Points} from an object notation of a {@link Two.Points}.\n   * @nota-bene Works in conjunction with {@link Two.Points#toObject}\n   */\n  static fromObject(obj) {\n    const fill = typeof obj.fill === \"string\" ? obj.fill : getEffectFromObject(obj.fill);\n    const stroke = typeof obj.stroke === \"string\" ? obj.stroke : getEffectFromObject(obj.stroke);\n    const points = new _Points().copy(__spreadProps(__spreadValues({}, obj), { fill, stroke }));\n    if (\"id\" in obj) {\n      points.id = obj.id;\n    }\n    return points;\n  }\n  /**\n   * @name Two.Points#copy\n   * @function\n   * @param {Two.Points} points - The reference {@link Two.Points}\n   * @description Copy the properties of one {@link Two.Points} onto another.\n   */\n  copy(points) {\n    super.copy.call(this, points);\n    for (let j = 0; j < points.vertices.length; j++) {\n      const v = points.vertices[j];\n      if (v instanceof Anchor) {\n        this.vertices.push(points.vertices[j].clone());\n      } else {\n        this.vertices.push(new Anchor().copy(v));\n      }\n    }\n    for (let i = 0; i < _Points.Properties.length; i++) {\n      const k = _Points.Properties[i];\n      if (k in points) {\n        this[k] = points[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Points#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Points}\n   * @description Create a new instance of {@link Two.Points} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new _Points();\n    for (let j = 0; j < this.vertices.length; j++) {\n      clone.vertices.push(this.vertices[j].clone());\n    }\n    for (let i = 0; i < _Points.Properties.length; i++) {\n      const k = _Points.Properties[i];\n      clone[k] = this[k];\n    }\n    clone.className = this.className;\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone._update();\n  }\n  /**\n   * @name Two.Points#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the points object.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = \"points\";\n    result.vertices = this.vertices.map((v) => v.toObject());\n    _.each(\n      _Points.Properties,\n      function(k) {\n        if (typeof this[k] !== \"undefined\") {\n          if (this[k].toObject) {\n            result[k] = this[k].toObject();\n          } else {\n            result[k] = this[k];\n          }\n        }\n      },\n      this\n    );\n    return result;\n  }\n  /**\n   * @name Two.Points#dispose\n   * @function\n   * @returns {Two.Points}\n   * @description Release the points' renderer resources and detach all events.\n   * This method cleans up vertices collection events, individual vertex events,\n   * and disposes fill/stroke effects (calling dispose() on Gradients and\n   * Textures for thorough cleanup) while preserving the renderer type for\n   * potential re-attachment to a new renderer.\n   */\n  dispose() {\n    super.dispose();\n    if (this.vertices && typeof this.vertices.unbind === \"function\") {\n      try {\n        this.vertices.unbind();\n      } catch (e) {\n      }\n    }\n    if (this.vertices) {\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        if (typeof v.unbind === \"function\") {\n          v.unbind();\n        }\n      }\n    }\n    if (typeof this.fill === \"object\" && typeof this.fill.dispose === \"function\") {\n      this.fill.dispose();\n    } else if (typeof this.fill === \"object\" && typeof this.fill.unbind === \"function\") {\n      this.fill.unbind();\n    }\n    if (typeof this.stroke === \"object\" && typeof this.stroke.dispose === \"function\") {\n      this.stroke.dispose();\n    } else if (typeof this.stroke === \"object\" && typeof this.stroke.unbind === \"function\") {\n      this.stroke.unbind();\n    }\n    return this;\n  }\n  /**\n   * @name Two.Points#subdivide\n   * @function\n   * @param {Number} limit - How many times to recurse subdivisions.\n   * @description Insert a {@link Two.Vector} at the midpoint between every item in {@link Two.Points#vertices}.\n   */\n  subdivide(limit) {\n    this._update();\n    let points = [];\n    for (let i = 0; i < this.vertices.length; i++) {\n      const a = this.vertices[i];\n      const b = this.vertices[i - 1];\n      if (!b) {\n        continue;\n      }\n      const x1 = a.x;\n      const y1 = a.y;\n      const x2 = b.x;\n      const y2 = b.y;\n      const subdivisions = subdivide(x1, y1, x1, y1, x2, y2, x2, y2, limit);\n      points = points.concat(subdivisions);\n    }\n    this.vertices = points;\n    return this;\n  }\n  /**\n   * @name Two.Points#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices) {\n      if (this._flagLength) {\n        this._updateLength(void 0, true);\n      }\n      const beginning = Math.min(this._beginning, this._ending);\n      const ending = Math.max(this._beginning, this._ending);\n      const bid = getIdByLength(this, beginning * this._length);\n      const eid = getIdByLength(this, ending * this._length);\n      const low = ceil2(bid);\n      const high = floor3(eid);\n      let j = 0, v;\n      this._renderer.vertices = [];\n      this._renderer.collection = [];\n      for (let i = 0; i < this._collection.length; i++) {\n        if (i >= low && i <= high) {\n          v = this._collection[i];\n          this._renderer.collection.push(v);\n          this._renderer.vertices[j * 2 + 0] = v.x;\n          this._renderer.vertices[j * 2 + 1] = v.y;\n          j++;\n        }\n      }\n    }\n    super._update.apply(this, arguments);\n    return this;\n  }\n  /**\n   * @name Two.Points#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagSize = this._flagSizeAttenuation = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n/**\n * @name Two.Points.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Points}.\n */\n__publicField(_Points, \"Properties\", [\n  \"fill\",\n  \"stroke\",\n  \"linewidth\",\n  \"opacity\",\n  \"visible\",\n  \"size\",\n  \"sizeAttenuation\",\n  \"beginning\",\n  \"ending\",\n  \"dashes\",\n  \"strokeAttenuation\"\n]);\nvar Points = _Points;\nvar proto16 = {\n  linewidth: {\n    enumerable: true,\n    get: function() {\n      return this._linewidth;\n    },\n    set: function(v) {\n      this._linewidth = v;\n      this._flagLinewidth = true;\n    }\n  },\n  opacity: {\n    enumerable: true,\n    get: function() {\n      return this._opacity;\n    },\n    set: function(v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n    }\n  },\n  visible: {\n    enumerable: true,\n    get: function() {\n      return this._visible;\n    },\n    set: function(v) {\n      this._visible = v;\n      this._flagVisible = true;\n    }\n  },\n  size: {\n    enumerable: true,\n    get: function() {\n      return this._size;\n    },\n    set: function(v) {\n      this._size = v;\n      this._flagSize = true;\n    }\n  },\n  sizeAttenuation: {\n    enumerable: true,\n    get: function() {\n      return this._sizeAttenuation;\n    },\n    set: function(v) {\n      this._sizeAttenuation = v;\n      this._flagSizeAttenuation = true;\n    }\n  },\n  fill: {\n    enumerable: true,\n    get: function() {\n      return this._fill;\n    },\n    set: function(f) {\n      if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n        this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n      }\n      this._fill = f;\n      this._flagFill = true;\n      if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n        this._fill.bind(Events.Types.change, this._renderer.flagFill);\n      }\n    }\n  },\n  stroke: {\n    enumerable: true,\n    get: function() {\n      return this._stroke;\n    },\n    set: function(f) {\n      if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n        this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n      }\n      this._stroke = f;\n      this._flagStroke = true;\n      if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n        this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n      }\n    }\n  },\n  /**\n   * @name Two.Points#length\n   * @property {Number} - The sum of distances between all {@link Two.Points#vertices}.\n   */\n  length: {\n    get: function() {\n      if (this._flagLength) {\n        this._updateLength();\n      }\n      return this._length;\n    }\n  },\n  beginning: {\n    enumerable: true,\n    get: function() {\n      return this._beginning;\n    },\n    set: function(v) {\n      this._beginning = v;\n      this._flagVertices = true;\n    }\n  },\n  ending: {\n    enumerable: true,\n    get: function() {\n      return this._ending;\n    },\n    set: function(v) {\n      this._ending = v;\n      this._flagVertices = true;\n    }\n  },\n  vertices: {\n    enumerable: true,\n    get: function() {\n      return this._collection;\n    },\n    set: function(vertices) {\n      const bindVertices = this._renderer.bindVertices;\n      const unbindVertices = this._renderer.unbindVertices;\n      if (this._collection) {\n        this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices);\n      }\n      if (vertices instanceof Collection) {\n        this._collection = vertices;\n      } else {\n        this._collection = new Collection(vertices || []);\n      }\n      this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices);\n      bindVertices(this._collection);\n    }\n  },\n  dashes: {\n    enumerable: true,\n    get: function() {\n      return this._dashes;\n    },\n    set: function(v) {\n      if (typeof v.offset !== \"number\") {\n        v.offset = this.dashes && this._dashes.offset || 0;\n      }\n      this._dashes = v;\n    }\n  },\n  /**\n   * @name Two.Points#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n   * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function() {\n      return this._strokeAttenuation;\n    },\n    set: function(v) {\n      this._strokeAttenuation = !!v;\n      this._flagStrokeAttenuation = true;\n      this._flagLinewidth = true;\n    }\n  }\n};\n\n// src/shapes/polygon.js\nvar cos4 = Math.cos;\nvar sin4 = Math.sin;\nvar _Polygon = class _Polygon extends Path {\n  constructor(x, y, radius, sides) {\n    sides = Math.max(sides || 0, 3);\n    super();\n    /**\n     * @name Two.Polygon#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#width} needs updating.\n     */\n    __publicField(this, \"_flagWidth\", false);\n    /**\n     * @name Two.Polygon#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#height} needs updating.\n     */\n    __publicField(this, \"_flagHeight\", false);\n    /**\n     * @name Two.Polygon#_flagSides\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#sides} needs updating.\n     */\n    __publicField(this, \"_flagSides\", false);\n    /**\n     * @name Two.Polygon#_radius\n     * @private\n     * @see {@link Two.Polygon#radius}\n     */\n    __publicField(this, \"_radius\", 0);\n    /**\n     * @name Two.Polygon#_width\n     * @private\n     * @see {@link Two.Polygon#width}\n     */\n    __publicField(this, \"_width\", 0);\n    /**\n     * @name Two.Polygon#_height\n     * @private\n     * @see {@link Two.Polygon#height}\n     */\n    __publicField(this, \"_height\", 0);\n    /**\n     * @name Two.Polygon#_sides\n     * @private\n     * @see {@link Two.Polygon#sides}\n     */\n    __publicField(this, \"_sides\", 0);\n    this._renderer.type = \"polygon\";\n    for (let prop in proto17) {\n      Object.defineProperty(this, prop, proto17[prop]);\n    }\n    this.closed = true;\n    this.automatic = false;\n    if (typeof radius === \"number\") {\n      this.radius = radius;\n    }\n    if (typeof sides === \"number\") {\n      this.sides = sides;\n    }\n    this._update();\n    if (typeof x === \"number\") {\n      this.translation.x = x;\n    }\n    if (typeof y === \"number\") {\n      this.translation.y = y;\n    }\n  }\n  /**\n   * @name Two.Polygon.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Polygon} to create a new instance\n   * @returns {Two.Polygon}\n   * @description Create a new {@link Two.Polygon} from an object notation of a {@link Two.Polygon}.\n   * @nota-bene Works in conjunction with {@link Two.Polygon#toObject}\n   */\n  static fromObject(obj) {\n    const polygon = new _Polygon().copy(obj);\n    if (\"id\" in obj) {\n      polygon.id = obj.id;\n    }\n    return polygon;\n  }\n  /**\n   * @name Two.Polygon#copy\n   * @function\n   * @param {Two.Polygon} polygon - The reference {@link Two.Polygon}\n   * @description Copy the properties of one {@link Two.Polygon} onto another.\n   */\n  copy(polygon) {\n    super.copy.call(this, polygon);\n    for (let i = 0; i < _Polygon.Properties.length; i++) {\n      const k = _Polygon.Properties[i];\n      if (k in polygon && typeof polygon[k] === \"number\") {\n        this[k] = polygon[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Polygon#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagSides) {\n      const sides = this._sides;\n      const amount = sides + 1;\n      let length = this.vertices.length;\n      if (length > sides) {\n        this.vertices.splice(sides - 1, length - sides);\n        length = sides;\n      }\n      for (let i = 0; i < amount; i++) {\n        const pct = (i + 0.5) / sides;\n        const theta = TWO_PI * pct + Math.PI / 2;\n        const x = this._width * cos4(theta) / 2;\n        const y = this._height * sin4(theta) / 2;\n        if (i >= length) {\n          this.vertices.push(new Anchor(x, y));\n        } else {\n          this.vertices[i].set(x, y);\n        }\n        this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Polygon#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = this._flagSides = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Polygon#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Polygon}\n   * @description Create a new instance of {@link Two.Polygon} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new _Polygon(0, 0, 0, this.sides);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    clone.width = this.width;\n    clone.height = this.height;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Polygon#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"polygon\";\n    for (let i = 0; i < _Polygon.Properties.length; i++) {\n      const k = _Polygon.Properties[i];\n      object[k] = this[k];\n    }\n    return object;\n  }\n};\n/**\n * @name Two.Polygon.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Polygon}.\n */\n__publicField(_Polygon, \"Properties\", [\"width\", \"height\", \"sides\"]);\nvar Polygon = _Polygon;\nvar proto17 = {\n  radius: {\n    enumerable: true,\n    get: function() {\n      return this._radius;\n    },\n    set: function(v) {\n      this._radius = v;\n      this.width = v * 2;\n      this.height = v * 2;\n    }\n  },\n  width: {\n    enumerable: true,\n    get: function() {\n      return this._width;\n    },\n    set: function(v) {\n      this._width = v;\n      this._flagWidth = true;\n      this._radius = Math.max(this.width, this.height) / 2;\n    }\n  },\n  height: {\n    enumerable: true,\n    get: function() {\n      return this._height;\n    },\n    set: function(v) {\n      this._height = v;\n      this._flagHeight = true;\n      this._radius = Math.max(this.width, this.height) / 2;\n    }\n  },\n  sides: {\n    enumerable: true,\n    get: function() {\n      return this._sides;\n    },\n    set: function(v) {\n      this._sides = v;\n      this._flagSides = true;\n    }\n  }\n};\n\n// src/shapes/rounded-rectangle.js\nvar _RoundedRectangle = class _RoundedRectangle extends Path {\n  constructor(x, y, width, height, radius) {\n    if (typeof radius === \"undefined\" && typeof width === \"number\" && typeof height === \"number\") {\n      radius = Math.floor(Math.min(width, height) / 12);\n    }\n    const points = [];\n    for (let i = 0; i < 10; i++) {\n      points.push(\n        new Anchor(0, 0, 0, 0, 0, 0, i === 0 ? Commands.move : Commands.curve)\n      );\n    }\n    super(points);\n    /**\n     * @name Two.RoundedRectangle#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#width} needs updating.\n     */\n    __publicField(this, \"_flagWidth\", false);\n    /**\n     * @name Two.RoundedRectangle#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#height} needs updating.\n     */\n    __publicField(this, \"_flagHeight\", false);\n    /**\n     * @name Two.RoundedRectangle#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#radius} needs updating.\n     */\n    __publicField(this, \"_flagRadius\", false);\n    /**\n     * @name Two.RoundedRectangle#_width\n     * @private\n     * @see {@link Two.RoundedRectangle#width}\n     */\n    __publicField(this, \"_width\", 0);\n    /**\n     * @name Two.RoundedRectangle#_height\n     * @private\n     * @see {@link Two.RoundedRectangle#height}\n     */\n    __publicField(this, \"_height\", 0);\n    /**\n     * @name Two.RoundedRectangle#_radius\n     * @private\n     * @see {@link Two.RoundedRectangle#radius}\n     */\n    __publicField(this, \"_radius\", 12);\n    this._renderer.type = \"rounded-rectangle\";\n    for (let prop in proto18) {\n      Object.defineProperty(this, prop, proto18[prop]);\n    }\n    this.closed = true;\n    this.automatic = false;\n    this._renderer.flagRadius = FlagRadius.bind(this);\n    if (typeof width === \"number\") {\n      this.width = width;\n    }\n    if (typeof height === \"number\") {\n      this.height = height;\n    }\n    if (typeof radius === \"number\" || radius instanceof Vector) {\n      this.radius = radius;\n    }\n    this._update();\n    if (typeof x === \"number\") {\n      this.translation.x = x;\n    }\n    if (typeof y === \"number\") {\n      this.translation.y = y;\n    }\n  }\n  /**\n   * @name Two.RoundedRectangle.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.RoundedRectangle} to create a new instance\n   * @returns {Two.RoundedRectangle}\n   * @description Create a new {@link Two.RoundedRectangle} from an object notation of a {@link Two.RoundedRectangle}.\n   * @nota-bene Works in conjunction with {@link Two.RoundedRectangle#toObject}\n   */\n  static fromObject(obj) {\n    const rectangle = new _RoundedRectangle().copy(obj);\n    if (\"id\" in obj) {\n      rectangle.id = obj.id;\n    }\n    return rectangle;\n  }\n  /**\n   * @name Two.RoundedRectangle#copy\n   * @function\n   * @param {Two.RoundedRectangle} roundedRectangle - The reference {@link Two.RoundedRectangle}\n   * @description Copy the properties of one {@link Two.RoundedRectangle} onto another.\n   */\n  copy(roundedRectangle) {\n    super.copy.call(this, roundedRectangle);\n    for (let i = 0; i < _RoundedRectangle.Properties.length; i++) {\n      const k = _RoundedRectangle.Properties[i];\n      if (k in roundedRectangle) {\n        const value = roundedRectangle[k];\n        if (/radius/i.test(k)) {\n          this[k] = typeof value === \"number\" || value instanceof Vector ? value : new Vector().copy(value);\n        } else if (typeof value === \"number\") {\n          this[k] = value;\n        }\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.RoundedRectangle#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagRadius) {\n      const width = this._width;\n      const height = this._height;\n      let rx, ry;\n      if (this._radius instanceof Vector) {\n        rx = this._radius.x;\n        ry = this._radius.y;\n      } else {\n        rx = this._radius;\n        ry = this._radius;\n      }\n      let v;\n      let w = width / 2;\n      let h = height / 2;\n      v = this.vertices[0];\n      v.x = -(w - rx);\n      v.y = -h;\n      v = this.vertices[1];\n      v.x = w - rx;\n      v.y = -h;\n      v.controls.left.clear();\n      v.controls.right.x = rx;\n      v.controls.right.y = 0;\n      v = this.vertices[2];\n      v.x = w;\n      v.y = -(h - ry);\n      v.controls.right.clear();\n      v.controls.left.clear();\n      v = this.vertices[3];\n      v.x = w;\n      v.y = h - ry;\n      v.controls.left.clear();\n      v.controls.right.x = 0;\n      v.controls.right.y = ry;\n      v = this.vertices[4];\n      v.x = w - rx;\n      v.y = h;\n      v.controls.right.clear();\n      v.controls.left.clear();\n      v = this.vertices[5];\n      v.x = -(w - rx);\n      v.y = h;\n      v.controls.left.clear();\n      v.controls.right.x = -rx;\n      v.controls.right.y = 0;\n      v = this.vertices[6];\n      v.x = -w;\n      v.y = h - ry;\n      v.controls.left.clear();\n      v.controls.right.clear();\n      v = this.vertices[7];\n      v.x = -w;\n      v.y = -(h - ry);\n      v.controls.left.clear();\n      v.controls.right.x = 0;\n      v.controls.right.y = -ry;\n      v = this.vertices[8];\n      v.x = -(w - rx);\n      v.y = -h;\n      v.controls.left.clear();\n      v.controls.right.clear();\n      v = this.vertices[9];\n      v.copy(this.vertices[8]);\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.RoundedRectangle#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = this._flagRadius = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.RoundedRectangle#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.RoundedRectangle}\n   * @description Create a new instance of {@link Two.RoundedRectangle} with the same properties of the current path.\n   */\n  clone(parent) {\n    const width = this.width;\n    const height = this.height;\n    const radius = this.radius;\n    const clone = new _RoundedRectangle(0, 0, width, height, radius);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.RoundedRectangle#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"rounded-rectangle\";\n    for (let i = 0; i < _RoundedRectangle.Properties.length; i++) {\n      const k = _RoundedRectangle.Properties[i];\n      object[k] = this[k];\n    }\n    object.radius = typeof this.radius === \"number\" ? this.radius : this.radius.toObject();\n    return object;\n  }\n};\n/**\n * @name Two.RoundedRectangle.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.RoundedRectangle}.\n */\n__publicField(_RoundedRectangle, \"Properties\", [\"width\", \"height\", \"radius\"]);\nvar RoundedRectangle = _RoundedRectangle;\nvar proto18 = {\n  width: {\n    enumerable: true,\n    get: function() {\n      return this._width;\n    },\n    set: function(v) {\n      this._width = v;\n      this._flagWidth = true;\n    }\n  },\n  height: {\n    enumerable: true,\n    get: function() {\n      return this._height;\n    },\n    set: function(v) {\n      this._height = v;\n      this._flagHeight = true;\n    }\n  },\n  radius: {\n    enumerable: true,\n    get: function() {\n      return this._radius;\n    },\n    set: function(v) {\n      if (this._radius instanceof Vector) {\n        this._radius.unbind(Events.Types.change, this._renderer.flagRadius);\n      }\n      this._radius = v;\n      if (this._radius instanceof Vector) {\n        this._radius.bind(Events.Types.change, this._renderer.flagRadius);\n      }\n      this._flagRadius = true;\n    }\n  }\n};\nfunction FlagRadius() {\n  this._flagRadius = true;\n}\n\n// src/shapes/star.js\nvar cos5 = Math.cos;\nvar sin5 = Math.sin;\nvar _Star = class _Star extends Path {\n  constructor(x, y, innerRadius, outerRadius, sides) {\n    if (arguments.length <= 3) {\n      outerRadius = innerRadius;\n      innerRadius = outerRadius / 2;\n    }\n    if (typeof sides !== \"number\" || sides <= 0) {\n      sides = 5;\n    }\n    super();\n    /**\n     * @name Two.Star#_flagInnerRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#innerRadius} needs updating.\n     */\n    __publicField(this, \"_flagInnerRadius\", false);\n    /**\n     * @name Two.Star#_flagOuterRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#outerRadius} needs updating.\n     */\n    __publicField(this, \"_flagOuterRadius\", false);\n    /**\n     * @name Two.Star#_flagSides\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#sides} needs updating.\n     */\n    __publicField(this, \"_flagSides\", false);\n    /**\n     * @name Two.Star#_innerRadius\n     * @private\n     * @see {@link Two.Star#innerRadius}\n     */\n    __publicField(this, \"_innerRadius\", 0);\n    /**\n     * @name Two.Star#_outerRadius\n     * @private\n     * @see {@link Two.Star#outerRadius}\n     */\n    __publicField(this, \"_outerRadius\", 0);\n    /**\n     * @name Two.Star#_sides\n     * @private\n     * @see {@link Two.Star#sides}\n     */\n    __publicField(this, \"_sides\", 0);\n    this._renderer.type = \"star\";\n    for (let prop in proto19) {\n      Object.defineProperty(this, prop, proto19[prop]);\n    }\n    this.closed = true;\n    this.automatic = false;\n    if (typeof innerRadius === \"number\") {\n      this.innerRadius = innerRadius;\n    }\n    if (typeof outerRadius === \"number\") {\n      this.outerRadius = outerRadius;\n    }\n    if (typeof sides === \"number\") {\n      this.sides = sides;\n    }\n    this._update();\n    if (typeof x === \"number\") {\n      this.translation.x = x;\n    }\n    if (typeof y === \"number\") {\n      this.translation.y = y;\n    }\n  }\n  /**\n   * @name Two.Star.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Star} to create a new instance\n   * @returns {Two.Star}\n   * @description Create a new {@link Two.Star} from an object notation of a {@link Two.Star}.\n   * @nota-bene Works in conjunction with {@link Two.Star#toObject}\n   */\n  static fromObject(obj) {\n    const star = new _Star().copy(obj);\n    if (\"id\" in obj) {\n      star.id = obj.id;\n    }\n    return star;\n  }\n  /**\n   * @name Two.Star#copy\n   * @function\n   * @param {Two.Star} star - The reference {@link Two.Star}\n   * @description Copy the properties of one {@link Two.Star} onto another.\n   */\n  copy(star) {\n    super.copy.call(this, star);\n    for (let i = 0; i < _Star.Properties.length; i++) {\n      const k = _Star.Properties[i];\n      if (k in star && typeof star[k] === \"number\") {\n        this[k] = star[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Star#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagInnerRadius || this._flagOuterRadius || this._flagSides) {\n      const sides = this._sides * 2;\n      const amount = sides + 1;\n      let length = this.vertices.length;\n      if (length > sides) {\n        this.vertices.splice(sides - 1, length - sides);\n        length = sides;\n      }\n      for (let i = 0; i < amount; i++) {\n        const pct = (i + 0.5) / sides;\n        const theta = TWO_PI * pct;\n        const r = (!(i % 2) ? this._innerRadius : this._outerRadius) / 2;\n        const x = r * cos5(theta);\n        const y = r * sin5(theta);\n        if (i >= length) {\n          this.vertices.push(new Anchor(x, y));\n        } else {\n          this.vertices[i].set(x, y);\n        }\n        this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Star#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false;\n    super.flagReset.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Star#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Star}\n   * @description Create a new instance of {@link Two.Star} with the same properties of the current path.\n   */\n  clone(parent) {\n    const ir = this.innerRadius;\n    const or = this.outerRadius;\n    const sides = this.sides;\n    const clone = new _Star(0, 0, ir, or, sides);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Star#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"star\";\n    for (let i = 0; i < _Star.Properties.length; i++) {\n      const k = _Star.Properties[i];\n      object[k] = this[k];\n    }\n    return object;\n  }\n};\n/**\n * @name Two.Star.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Star}.\n */\n__publicField(_Star, \"Properties\", [\"innerRadius\", \"outerRadius\", \"sides\"]);\nvar Star = _Star;\nvar proto19 = {\n  innerRadius: {\n    enumerable: true,\n    get: function() {\n      return this._innerRadius;\n    },\n    set: function(v) {\n      this._innerRadius = v;\n      this._flagInnerRadius = true;\n    }\n  },\n  outerRadius: {\n    enumerable: true,\n    get: function() {\n      return this._outerRadius;\n    },\n    set: function(v) {\n      this._outerRadius = v;\n      this._flagOuterRadius = true;\n    }\n  },\n  sides: {\n    enumerable: true,\n    get: function() {\n      return this._sides;\n    },\n    set: function(v) {\n      this._sides = v;\n      this._flagSides = true;\n    }\n  }\n};\n\n// src/text.js\nvar canvas;\nvar min2 = Math.min;\nvar max2 = Math.max;\nif (root.document) {\n  canvas = document.createElement(\"canvas\");\n}\nvar _Text = class _Text extends Shape {\n  constructor(message, x, y, styles) {\n    super();\n    /**\n     * @name Two.Text#_flagValue\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#value} need updating.\n     */\n    __publicField(this, \"_flagValue\", true);\n    /**\n     * @name Two.Text#_flagFamily\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#family} need updating.\n     */\n    __publicField(this, \"_flagFamily\", true);\n    /**\n     * @name Two.Text#_flagSize\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#size} need updating.\n     */\n    __publicField(this, \"_flagSize\", true);\n    /**\n     * @name Two.Text#_flagLeading\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#leading} need updating.\n     */\n    __publicField(this, \"_flagLeading\", true);\n    /**\n     * @name Two.Text#_flagAlignment\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#alignment} need updating.\n     */\n    __publicField(this, \"_flagAlignment\", true);\n    /**\n     * @name Two.Text#_flagBaseline\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#baseline} need updating.\n     */\n    __publicField(this, \"_flagBaseline\", true);\n    /**\n     * @name Two.Text#_flagStyle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#style} need updating.\n     */\n    __publicField(this, \"_flagStyle\", true);\n    /**\n     * @name Two.Text#_flagWeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#weight} need updating.\n     */\n    __publicField(this, \"_flagWeight\", true);\n    /**\n     * @name Two.Text#_flagDecoration\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#decoration} need updating.\n     */\n    __publicField(this, \"_flagDecoration\", true);\n    /**\n     * @name Two.Text#_flagFill\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#fill} need updating.\n     */\n    __publicField(this, \"_flagFill\", true);\n    /**\n     * @name Two.Text#_flagStroke\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#stroke} need updating.\n     */\n    __publicField(this, \"_flagStroke\", true);\n    /**\n     * @name Two.Text#_flagLinewidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#linewidth} need updating.\n     */\n    __publicField(this, \"_flagLinewidth\", true);\n    /**\n     * @name Two.Text#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#opacity} need updating.\n     */\n    __publicField(this, \"_flagOpacity\", true);\n    /**\n     * @name Two.Text#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#visible} need updating.\n     */\n    __publicField(this, \"_flagVisible\", true);\n    /**\n     * @name Two.Text#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#mask} needs updating.\n     */\n    __publicField(this, \"_flagMask\", false);\n    /**\n     * @name Two.Text#_flagClip\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#clip} needs updating.\n     */\n    __publicField(this, \"_flagClip\", false);\n    /**\n     * @name Two.Text#_flagDirection\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#direction} needs updating.\n     */\n    __publicField(this, \"_flagDirection\", true);\n    /**\n     * @name Two.Text#_flagStrokeAttenuation\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#strokeAttenuation} needs updating.\n     */\n    __publicField(this, \"_flagStrokeAttenuation\", true);\n    // Underlying Properties\n    /**\n     * @name Two.Text#value\n     * @property {String} - The characters to be rendered to the the screen. Referred to in the documentation sometimes as the `message`.\n     */\n    __publicField(this, \"_value\", \"\");\n    /**\n     * @name Two.Text#family\n     * @property {String} - The font family Two.js should attempt to register for rendering. The default value is `'sans-serif'`. Comma separated font names can be supplied as a \"stack\", similar to the CSS implementation of `font-family`.\n     */\n    __publicField(this, \"_family\", \"sans-serif\");\n    /**\n     * @name Two.Text#size\n     * @property {Number} - The font size in Two.js point space. Defaults to `13`.\n     */\n    __publicField(this, \"_size\", 13);\n    /**\n     * @name Two.Text#leading\n     * @property {Number} - The height between lines measured from base to base in Two.js point space. Defaults to `17`.\n     */\n    __publicField(this, \"_leading\", 17);\n    /**\n     * @name Two.Text#alignment\n     * @property {String} - Alignment of text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'left'`, `'center'`, `'right'`. Defaults to `'center'`.\n     */\n    __publicField(this, \"_alignment\", \"center\");\n    /**\n     * @name Two.Text#baseline\n     * @property {String} - The vertical aligment of the text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'top'`, `'middle'`, `'bottom'`, and `'baseline'`. Defaults to `'baseline'`.\n     * @nota-bene In headless environments where the canvas is based on {@link https://github.com/Automattic/node-canvas}, `baseline` seems to be the only valid property.\n     */\n    __publicField(this, \"_baseline\", \"middle\");\n    /**\n     * @name Two.Text#style\n     * @property {String} - The font's style. Possible values include '`normal`', `'italic'`. Defaults to `'normal'`.\n     */\n    __publicField(this, \"_style\", \"normal\");\n    /**\n     * @name Two.Text#weight\n     * @property {Number} - A number at intervals of 100 to describe the font's weight. This compatibility varies with the typeface's variant weights. Larger values are bolder. Smaller values are thinner. Defaults to `'500'`.\n     */\n    __publicField(this, \"_weight\", 500);\n    /**\n     * @name Two.Text#decoration\n     * @property {String} - String to delineate whether text should be decorated with for instance an `'underline'`. Defaults to `'none'`.\n     */\n    __publicField(this, \"_decoration\", \"none\");\n    /**\n     * @name Two.Text#direction\n     * @property {String} - String to determine what direction the text should run. Possibly values are `'ltr'` for left-to-right and `'rtl'` for right-to-left. Defaults to `'ltr'`.\n     */\n    __publicField(this, \"_direction\", \"ltr\");\n    /**\n     * @name Two.Text#fill\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the text object should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    __publicField(this, \"_fill\", \"#000\");\n    /**\n     * @name Two.Text#stroke\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the text object should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    __publicField(this, \"_stroke\", \"none\");\n    /**\n     * @name Two.Text#linewidth\n     * @property {Number} - The thickness in pixels of the stroke.\n     */\n    __publicField(this, \"_linewidth\", 1);\n    /**\n     * @name Two.Text#opacity\n     * @property {Number} - The opaqueness of the text object.\n     * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n     */\n    __publicField(this, \"_opacity\", 1);\n    /**\n     * @name Two.Text#visible\n     * @property {Boolean} - Display the text object or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    __publicField(this, \"_visible\", true);\n    /**\n     * @name Two.Text#mask\n     * @property {Two.Shape} - The shape whose alpha property becomes a clipping area for the text.\n     * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n     */\n    __publicField(this, \"_mask\", null);\n    /**\n     * @name Two.Text#clip\n     * @property {Two.Shape} - Object to define clipping area.\n     * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n     */\n    __publicField(this, \"_clip\", false);\n    /**\n     * @name Two.Text#_dashes\n     * @private\n     * @see {@link Two.Text#dashes}\n     */\n    __publicField(this, \"_dashes\", null);\n    /**\n     * @name Two.Text#_strokeAttenuation\n     * @private\n     * @see {@link Two.Text#strokeAttenuation}\n     */\n    __publicField(this, \"_strokeAttenuation\", true);\n    for (let prop in proto20) {\n      Object.defineProperty(this, prop, proto20[prop]);\n    }\n    this._renderer.type = \"text\";\n    this._renderer.flagFill = FlagFill2.bind(this);\n    this._renderer.flagStroke = FlagStroke2.bind(this);\n    this.value = message;\n    if (typeof x === \"number\") {\n      this.translation.x = x;\n    }\n    if (typeof y === \"number\") {\n      this.translation.y = y;\n    }\n    this.dashes = [];\n    this.dashes.offset = 0;\n    if (!_.isObject(styles)) {\n      return this;\n    }\n    for (let i = 0; i < _Text.Properties.length; i++) {\n      const property = _Text.Properties[i];\n      if (property in styles) {\n        this[property] = styles[property];\n      }\n    }\n  }\n  /**\n   *\n   * @name Two.Measure\n   * @function\n   * @param {Two.Text} [text] - The instance of {@link Two.Text} to measure.\n   * @returns {Object} - The width and height of the {@link Two.Text} instance.\n   */\n  static Measure(text) {\n    if (canvas) {\n      const ctx = canvas.getContext(\"2d\");\n      ctx.font = [\n        text._style,\n        text._weight,\n        `${text._size}px/${text._leading}px`,\n        text._family\n      ].join(\" \");\n      const metrics = ctx.measureText(text.value, 0, 0);\n      const height = metrics.actualBoundingBoxDescent + metrics.actualBoundingBoxAscent;\n      return {\n        width: metrics.width,\n        height\n      };\n    } else {\n      const width = this.value.length * this.size * _Text.Ratio;\n      const height = this.leading;\n      console.warn(\n        \"Two.Text: unable to accurately measure text, so using an approximation.\"\n      );\n      return {\n        width,\n        height\n      };\n    }\n  }\n  /**\n   * @name Two.Text.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Text} to create a new instance\n   * @returns {Two.Text}\n   * @description Create a new {@link Two.Text} from an object notation of a {@link Two.Text}.\n   * @nota-bene Works in conjunction with {@link Two.Text#toObject}\n   */\n  static fromObject(obj) {\n    const fill = typeof obj.fill === \"string\" ? obj.fill : getEffectFromObject(obj.fill);\n    const stroke = typeof obj.stroke === \"string\" ? obj.stroke : getEffectFromObject(obj.stroke);\n    const text = new _Text().copy(__spreadProps(__spreadValues({}, obj), { fill, stroke }));\n    if (\"id\" in obj) {\n      text.id = obj.id;\n    }\n    return text;\n  }\n  /**\n   * @name Two.Text#copy\n   * @function\n   * @param {Two.Text} text\n   * @description Copy the properties of one {@link Two.Text} onto another.\n   */\n  copy(text) {\n    super.copy.call(this, text);\n    for (let i = 0; i < _Text.Properties.length; i++) {\n      const k = _Text.Properties[i];\n      if (k in text) {\n        this[k] = text[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Text#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Text}\n   * @description Create a new instance of {@link Two.Text} with the same properties of the current text object.\n   */\n  clone(parent) {\n    const clone = new _Text(this.value);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    for (let i = 0; i < _Text.Properties.length; i++) {\n      const prop = _Text.Properties[i];\n      clone[prop] = this[prop];\n    }\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone._update();\n  }\n  /**\n   * @name Two.Text#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the text object.\n   * @nota-bene Works in conjunction with {@link Two.Text.fromObject}\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = \"text\";\n    for (let i = 0; i < _Text.Properties.length; i++) {\n      const prop = _Text.Properties[i];\n      result[prop] = this[prop];\n    }\n    return result;\n  }\n  /**\n   * @name Two.Text#dispose\n   * @function\n   * @returns {Two.Text}\n   * @description Release the text's renderer resources and detach all events.\n   * This method disposes fill and stroke effects (calling dispose() on\n   * Gradients and Textures for thorough cleanup) while preserving the\n   * renderer type for potential re-attachment to a new renderer.\n   */\n  dispose() {\n    super.dispose();\n    if (typeof this.fill === \"object\" && typeof this.fill.dispose === \"function\") {\n      this.fill.dispose();\n    } else if (typeof this.fill === \"object\" && typeof this.fill.unbind === \"function\") {\n      this.fill.unbind();\n    }\n    if (typeof this.stroke === \"object\" && typeof this.stroke.dispose === \"function\") {\n      this.stroke.dispose();\n    } else if (typeof this.stroke === \"object\" && typeof this.stroke.unbind === \"function\") {\n      this.stroke.unbind();\n    }\n    return this;\n  }\n  /**\n   * @name Two.Text#noFill\n   * @function\n   * @description Short hand method to set fill to `none`.\n   */\n  noFill() {\n    this.fill = \"none\";\n    return this;\n  }\n  /**\n   * @name Two.Text#noStroke\n   * @function\n   * @description Short hand method to set stroke to `none`.\n   */\n  noStroke() {\n    this.stroke = \"none\";\n    this.linewidth = 0;\n    return this;\n  }\n  // A shim to not break `getBoundingClientRect` calls.\n  // TODO: Implement a way to calculate proper bounding\n  // boxes of `Two.Text`.\n  /**\n   * @name Two.Text#getBoundingClientRect\n   * @function\n   * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n   * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n   * @description Return an object with top, left, right, bottom, width, and height parameters of the text object.\n   */\n  getBoundingClientRect(shallow) {\n    let matrix;\n    let left, right, top, bottom;\n    this._update(true);\n    matrix = shallow ? this.matrix : this.worldMatrix;\n    const { width, height } = _Text.Measure(this);\n    const border = (this._linewidth || 0) / 2;\n    switch (this.alignment) {\n      case \"left\":\n        left = -border;\n        right = width + border;\n        break;\n      case \"right\":\n        left = -(width + border);\n        right = border;\n        break;\n      default:\n        left = -(width / 2 + border);\n        right = width / 2 + border;\n    }\n    switch (this.baseline) {\n      case \"middle\":\n        top = -(height / 2 + border);\n        bottom = height / 2 + border;\n        break;\n      default:\n        top = -(height + border);\n        bottom = border;\n    }\n    const [ax, ay] = matrix.multiply(left, top);\n    const [bx, by] = matrix.multiply(left, bottom);\n    const [cx, cy] = matrix.multiply(right, top);\n    const [dx, dy] = matrix.multiply(right, bottom);\n    top = min2(ay, by, cy, dy);\n    left = min2(ax, bx, cx, dx);\n    right = max2(ax, bx, cx, dx);\n    bottom = max2(ay, by, cy, dy);\n    return {\n      top,\n      left,\n      right,\n      bottom,\n      width: right - left,\n      height: bottom - top\n    };\n  }\n  /**\n   * @name Two.Text#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    super.flagReset.call(this);\n    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;\n    return this;\n  }\n};\n/**\n * @name Two.Text.Ratio\n * @property {Number} - Approximate aspect ratio of a typeface's character width to height.\n */\n__publicField(_Text, \"Ratio\", 0.6);\n/**\n * @name Two.Text.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Text}.\n */\n__publicField(_Text, \"Properties\", [\n  \"value\",\n  \"family\",\n  \"size\",\n  \"leading\",\n  \"alignment\",\n  \"linewidth\",\n  \"style\",\n  \"weight\",\n  \"decoration\",\n  \"direction\",\n  \"baseline\",\n  \"opacity\",\n  \"visible\",\n  \"fill\",\n  \"stroke\",\n  \"dashes\",\n  \"strokeAttenuation\"\n]);\nvar Text = _Text;\nvar proto20 = {\n  value: {\n    enumerable: true,\n    get: function() {\n      return this._value;\n    },\n    set: function(v) {\n      this._value = v;\n      this._flagValue = true;\n    }\n  },\n  family: {\n    enumerable: true,\n    get: function() {\n      return this._family;\n    },\n    set: function(v) {\n      this._family = v;\n      this._flagFamily = true;\n    }\n  },\n  size: {\n    enumerable: true,\n    get: function() {\n      return this._size;\n    },\n    set: function(v) {\n      this._size = v;\n      this._flagSize = true;\n    }\n  },\n  leading: {\n    enumerable: true,\n    get: function() {\n      return this._leading;\n    },\n    set: function(v) {\n      this._leading = v;\n      this._flagLeading = true;\n    }\n  },\n  alignment: {\n    enumerable: true,\n    get: function() {\n      return this._alignment;\n    },\n    set: function(v) {\n      this._alignment = v;\n      this._flagAlignment = true;\n    }\n  },\n  linewidth: {\n    enumerable: true,\n    get: function() {\n      return this._linewidth;\n    },\n    set: function(v) {\n      this._linewidth = v;\n      this._flagLinewidth = true;\n    }\n  },\n  style: {\n    enumerable: true,\n    get: function() {\n      return this._style;\n    },\n    set: function(v) {\n      this._style = v;\n      this._flagStyle = true;\n    }\n  },\n  weight: {\n    enumerable: true,\n    get: function() {\n      return this._weight;\n    },\n    set: function(v) {\n      this._weight = v;\n      this._flagWeight = true;\n    }\n  },\n  decoration: {\n    enumerable: true,\n    get: function() {\n      return this._decoration;\n    },\n    set: function(v) {\n      this._decoration = v;\n      this._flagDecoration = true;\n    }\n  },\n  direction: {\n    enumerable: true,\n    get: function() {\n      return this._direction;\n    },\n    set: function(v) {\n      this._direction = v;\n      this._flagDirection = true;\n    }\n  },\n  baseline: {\n    enumerable: true,\n    get: function() {\n      return this._baseline;\n    },\n    set: function(v) {\n      this._baseline = v;\n      this._flagBaseline = true;\n    }\n  },\n  opacity: {\n    enumerable: true,\n    get: function() {\n      return this._opacity;\n    },\n    set: function(v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n    }\n  },\n  visible: {\n    enumerable: true,\n    get: function() {\n      return this._visible;\n    },\n    set: function(v) {\n      this._visible = v;\n      this._flagVisible = true;\n    }\n  },\n  fill: {\n    enumerable: true,\n    get: function() {\n      return this._fill;\n    },\n    set: function(f) {\n      if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n        this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n      }\n      this._fill = f;\n      this._flagFill = true;\n      if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n        this._fill.bind(Events.Types.change, this._renderer.flagFill);\n      }\n    }\n  },\n  stroke: {\n    enumerable: true,\n    get: function() {\n      return this._stroke;\n    },\n    set: function(f) {\n      if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n        this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n      }\n      this._stroke = f;\n      this._flagStroke = true;\n      if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n        this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n      }\n    }\n  },\n  mask: {\n    enumerable: true,\n    get: function() {\n      return this._mask;\n    },\n    set: function(v) {\n      this._mask = v;\n      this._flagMask = true;\n      if (_.isObject(v) && !v.clip) {\n        v.clip = true;\n      }\n    }\n  },\n  clip: {\n    enumerable: true,\n    get: function() {\n      return this._clip;\n    },\n    set: function(v) {\n      this._clip = v;\n      this._flagClip = true;\n    }\n  },\n  dashes: {\n    enumerable: true,\n    get: function() {\n      return this._dashes;\n    },\n    set: function(v) {\n      if (typeof v.offset !== \"number\") {\n        v.offset = this.dashes && this._dashes.offset || 0;\n      }\n      this._dashes = v;\n    }\n  },\n  /**\n   * @name Two.Text#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n   * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function() {\n      return this._strokeAttenuation;\n    },\n    set: function(v) {\n      this._strokeAttenuation = !!v;\n      this._flagStrokeAttenuation = true;\n      this._flagLinewidth = true;\n    }\n  }\n};\nfunction FlagFill2() {\n  this._flagFill = true;\n}\nfunction FlagStroke2() {\n  this._flagStroke = true;\n}\n\n// src/effects/image-sequence.js\nvar _ImageSequence = class _ImageSequence extends Rectangle {\n  constructor(src, ox, oy, frameRate) {\n    super(ox, oy, 0, 0);\n    /**\n     * @name Two.ImageSequence#_flagTextures\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#textures} need updating.\n     */\n    __publicField(this, \"_flagTextures\", false);\n    /**\n     * @name Two.ImageSequence#_flagFrameRate\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#frameRate} needs updating.\n     */\n    __publicField(this, \"_flagFrameRate\", false);\n    /**\n     * @name Two.ImageSequence#_flagIndex\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#index} needs updating.\n     */\n    __publicField(this, \"_flagIndex\", false);\n    // Private variables\n    /**\n     * @name Two.ImageSequence#_amount\n     * @private\n     * @property {Number} - Number of frames for a given {@link Two.ImageSequence}.\n     */\n    __publicField(this, \"_amount\", 1);\n    /**\n     * @name Two.ImageSequence#_duration\n     * @private\n     * @property {Number} - Number of milliseconds a {@link Two.ImageSequence}.\n     */\n    __publicField(this, \"_duration\", 0);\n    /**\n     * @name Two.ImageSequence#_index\n     * @private\n     * @property {Number} - The current frame the {@link Two.ImageSequence} is currently displaying.\n     */\n    __publicField(this, \"_index\", 0);\n    /**\n     * @name Two.ImageSequence#_startTime\n     * @private\n     * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.ImageSequence} started.\n     */\n    __publicField(this, \"_startTime\", 0);\n    /**\n     * @name Two.ImageSequence#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} is animating or not.\n     */\n    __publicField(this, \"_playing\", false);\n    /**\n     * @name Two.ImageSequence#_firstFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.ImageSequence} should start with.\n     */\n    __publicField(this, \"_firstFrame\", 0);\n    /**\n     * @name Two.ImageSequence#_lastFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.ImageSequence} should end with.\n     */\n    __publicField(this, \"_lastFrame\", 0);\n    /**\n     * @name Two.ImageSequence#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} should loop or not.\n     */\n    __publicField(this, \"_loop\", true);\n    // Exposed through getter-setter\n    /**\n     * @name Two.ImageSequence#_textures\n     * @private\n     * @see {@link Two.ImageSequence#textures}\n     */\n    __publicField(this, \"_textures\", null);\n    /**\n     * @name Two.ImageSequence#_frameRate\n     * @private\n     * @see {@link Two.ImageSequence#frameRate}\n     */\n    __publicField(this, \"_frameRate\", 0);\n    /**\n     * @name Two.ImageSequence#_origin\n     * @private\n     * @see {@link Two.ImageSequence#origin}\n     */\n    __publicField(this, \"_origin\", null);\n    this._renderer.type = \"image-sequence\";\n    for (let prop in proto21) {\n      Object.defineProperty(this, prop, proto21[prop]);\n    }\n    this._renderer.flagTextures = FlagTextures.bind(this);\n    this._renderer.bindTextures = BindTextures.bind(this);\n    this._renderer.unbindTextures = UnbindTextures.bind(this);\n    this.noStroke();\n    this.noFill();\n    if (Array.isArray(src)) {\n      this.textures = src.map(GenerateTexture.bind(this));\n    } else if (typeof src === \"string\") {\n      this.textures = [GenerateTexture(src)];\n    }\n    this.origin = new Vector();\n    this._update();\n    if (typeof frameRate === \"number\") {\n      this.frameRate = frameRate;\n    } else {\n      this.frameRate = _ImageSequence.DefaultFrameRate;\n    }\n    this.index = 0;\n  }\n  /**\n   * @name Two.ImageSequence.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.ImageSequence} to create a new instance\n   * @returns {Two.ImageSequence}\n   * @description Create a new {@link Two.ImageSequence} from an object notation of a {@link Two.ImageSequence}.\n   * @nota-bene Works in conjunction with {@link Two.ImageSequence#toObject}\n   */\n  static fromObject(obj) {\n    const sequence = new _ImageSequence().copy(obj);\n    if (\"id\" in obj) {\n      sequence.id = obj.id;\n    }\n    return sequence;\n  }\n  /**\n   * @name Two.ImageSequence#copy\n   * @function\n   * @param {Two.ImageSequence} imageSequence - The reference {@link Two.ImageSequence}\n   * @description Copy the properties of one {@link Two.ImageSequence} onto another.\n   */\n  copy(imageSequence) {\n    super.copy.call(this, imageSequence);\n    for (let i = 0; i < _ImageSequence.Properties.length; i++) {\n      const k = _ImageSequence.Properties[i];\n      if (k in imageSequence) {\n        this[k] = imageSequence[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.ImageSequence#play\n   * @function\n   * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n   * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.ImageSequence#textures}.\n   * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the image sequence is looped.\n   * @description Initiate animation playback of a {@link Two.ImageSequence}.\n   */\n  play(firstFrame, lastFrame, onLastFrame) {\n    this._playing = true;\n    this._firstFrame = 0;\n    this._lastFrame = this.amount - 1;\n    this._startTime = _.performance.now();\n    if (typeof firstFrame === \"number\") {\n      this._firstFrame = firstFrame;\n    }\n    if (typeof lastFrame === \"number\") {\n      this._lastFrame = lastFrame;\n    }\n    if (typeof onLastFrame === \"function\") {\n      this._onLastFrame = onLastFrame;\n    } else {\n      delete this._onLastFrame;\n    }\n    if (this._index !== this._firstFrame) {\n      this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate;\n    }\n    return this;\n  }\n  /**\n   * @name Two.ImageSequence#pause\n   * @function\n   * @description Halt animation playback of a {@link Two.ImageSequence}.\n   */\n  pause() {\n    this._playing = false;\n    return this;\n  }\n  /**\n   * @name Two.ImageSequence#stop\n   * @function\n   * @description Halt animation playback of a {@link Two.ImageSequence} and set the current frame back to the first frame.\n   */\n  stop() {\n    this._playing = false;\n    this._index = this._firstFrame;\n    return this;\n  }\n  /**\n   * @name Two.ImageSequence#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.ImageSequence}\n   * @description Create a new instance of {@link Two.ImageSequence} with the same properties of the current image sequence.\n   */\n  clone(parent) {\n    const clone = new _ImageSequence(\n      this.textures,\n      this.translation.x,\n      this.translation.y,\n      this.frameRate\n    );\n    clone._loop = this._loop;\n    if (this._playing) {\n      clone.play();\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.ImageSequence#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"image-sequence\";\n    object.textures = this.textures.map(function(texture) {\n      return texture.toObject();\n    });\n    object.frameRate = this.frameRate;\n    object.index = this.index;\n    object.firstFrame = this.firstFrame;\n    object.lastFrame = this.lastFrame;\n    object.loop = this.loop;\n    return object;\n  }\n  /**\n   * @name Two.ImageSequence#dispose\n   * @function\n   * @returns {Two.ImageSequence}\n   * @description Release the image sequence's renderer resources and detach all events.\n   * This method stops any running animation, clears animation callbacks, unbinds\n   * textures collection events, and disposes individual textures (calling dispose()\n   * for thorough cleanup) while preserving the renderer type for potential\n   * re-attachment to a new renderer.\n   */\n  dispose() {\n    super.dispose();\n    if (this._playing) {\n      this._playing = false;\n    }\n    this._onLastFrame = null;\n    if (this.textures && typeof this.textures.unbind === \"function\") {\n      try {\n        this.textures.unbind();\n      } catch (e) {\n      }\n    }\n    if (this.textures) {\n      for (let i = 0; i < this.textures.length; i++) {\n        const texture = this.textures[i];\n        if (typeof texture.dispose === \"function\") {\n          texture.dispose();\n        } else if (typeof texture.unbind === \"function\") {\n          texture.unbind();\n        }\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.ImageSequence#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    const effect = this._textures;\n    let width, height, elapsed, amount, duration, texture;\n    let index, frames;\n    if (effect) {\n      if (this._flagTextures) {\n        this._amount = effect.length;\n      }\n      if (this._flagFrameRate) {\n        this._duration = 1e3 * this._amount / this._frameRate;\n      }\n      if (this._playing && this._frameRate > 0) {\n        amount = this._amount;\n        if (_.isNaN(this._lastFrame)) {\n          this._lastFrame = amount - 1;\n        }\n        elapsed = _.performance.now() - this._startTime;\n        frames = this._lastFrame + 1;\n        duration = 1e3 * (frames - this._firstFrame) / this._frameRate;\n        if (this._loop) {\n          elapsed = elapsed % duration;\n        } else {\n          elapsed = Math.min(elapsed, duration);\n        }\n        index = lerp(this._firstFrame, frames, elapsed / duration);\n        index = Math.floor(index);\n        if (index !== this._index) {\n          this._index = index;\n          texture = effect[this._index];\n          if (texture.loaded) {\n            width = texture.image.width;\n            height = texture.image.height;\n            if (this.width !== width) {\n              this.width = width;\n            }\n            if (this.height !== height) {\n              this.height = height;\n            }\n            this.fill = texture;\n            if (index >= this._lastFrame - 1 && this._onLastFrame) {\n              this._onLastFrame();\n            }\n          }\n        }\n      } else if (this._flagIndex || !(this.fill instanceof Texture)) {\n        texture = effect[this._index];\n        if (texture.loaded) {\n          width = texture.image.width;\n          height = texture.image.height;\n          if (this.width !== width) {\n            this.width = width;\n          }\n          if (this.height !== height) {\n            this.height = height;\n          }\n        }\n        this.fill = texture;\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.ImageSequence#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagTextures = this._flagFrameRate = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n/**\n * @name Two.ImageSequence.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.ImageSequence}.\n */\n__publicField(_ImageSequence, \"Properties\", [\n  \"textures\",\n  \"frameRate\",\n  \"index\",\n  \"firstFrame\",\n  \"lastFrame\",\n  \"loop\"\n]);\n/**\n * @name Two.ImageSequence.DefaultFrameRate\n * @property The default frame rate that {@link Two.ImageSequence#frameRate} is set to when instantiated.\n */\n__publicField(_ImageSequence, \"DefaultFrameRate\", 30);\nvar ImageSequence = _ImageSequence;\nvar proto21 = {\n  frameRate: {\n    enumerable: true,\n    get: function() {\n      return this._frameRate;\n    },\n    set: function(v) {\n      this._frameRate = v;\n      this._flagFrameRate = true;\n    }\n  },\n  index: {\n    enumerable: true,\n    get: function() {\n      return this._index;\n    },\n    set: function(v) {\n      this._index = v;\n      this._flagIndex = true;\n    }\n  },\n  textures: {\n    enumerable: true,\n    get: function() {\n      return this._textures;\n    },\n    set: function(textures) {\n      const bindTextures = this._renderer.bindTextures;\n      const unbindTextures = this._renderer.unbindTextures;\n      if (this._textures) {\n        this._textures.unbind(Events.Types.insert, bindTextures).unbind(Events.Types.remove, unbindTextures);\n      }\n      this._textures = new Collection((textures || []).slice(0));\n      this._textures.bind(Events.Types.insert, bindTextures).bind(Events.Types.remove, unbindTextures);\n      bindTextures(this._textures);\n    }\n  },\n  firstFrame: {\n    enumerable: true,\n    get: function() {\n      return this._firstFrame;\n    },\n    set: function(v) {\n      this._firstFrame = v;\n    }\n  },\n  lastFrame: {\n    enumerable: true,\n    get: function() {\n      return this._lastFrame;\n    },\n    set: function(v) {\n      this._lastFrame = v;\n    }\n  },\n  loop: {\n    enumerable: true,\n    get: function() {\n      return this._loop;\n    },\n    set: function(v) {\n      this._loop = !!v;\n    }\n  }\n};\nfunction FlagTextures() {\n  this._flagTextures = true;\n}\nfunction BindTextures(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].bind(Events.Types.change, this._renderer.flagTextures);\n  }\n  this._renderer.flagTextures();\n}\nfunction UnbindTextures(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].unbind(Events.Types.change, this._renderer.flagTextures);\n  }\n  this._renderer.flagTextures();\n}\nfunction GenerateTexture(obj) {\n  if (obj instanceof Texture) {\n    return obj;\n  } else if (typeof obj === \"string\") {\n    return new Texture(obj);\n  }\n}\n\n// src/group.js\nvar min3 = Math.min;\nvar max3 = Math.max;\nvar cache = {\n  getShapesAtPoint: {\n    results: [],\n    hitOptions: {},\n    context: {\n      x: 0,\n      y: 0,\n      visibleOnly: true,\n      results: null\n    },\n    single: [],\n    output: [],\n    empty: []\n  }\n};\nvar _Group = class _Group extends Shape {\n  constructor(children) {\n    super();\n    /**\n     * @name Two.Group#_flagAdditions\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#additions} needs updating.\n     */\n    __publicField(this, \"_flagAdditions\", false);\n    /**\n     * @name Two.Group#_flagSubtractions\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#subtractions} needs updating.\n     */\n    __publicField(this, \"_flagSubtractions\", false);\n    /**\n     * @name Two.Group#_flagOrder\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#order} needs updating.\n     */\n    __publicField(this, \"_flagOrder\", false);\n    /**\n     * @name Two.Group#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#visible} needs updating.\n     */\n    /**\n     * @name Two.Group#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#opacity} needs updating.\n     */\n    __publicField(this, \"_flagOpacity\", true);\n    /**\n     * @name Two.Group#_flagBeginning\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#beginning} needs updating.\n     */\n    __publicField(this, \"_flagBeginning\", false);\n    /**\n     * @name Two.Group#_flagEnding\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#ending} needs updating.\n     */\n    __publicField(this, \"_flagEnding\", false);\n    /**\n     * @name Two.Group#_flagLength\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#length} needs updating.\n     */\n    __publicField(this, \"_flagLength\", false);\n    /**\n     * @name Two.Group#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#mask} needs updating.\n     */\n    __publicField(this, \"_flagMask\", false);\n    // Underlying Properties\n    /**\n     * @name Two.Group#fill\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what all child shapes should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    __publicField(this, \"_fill\", \"#fff\");\n    /**\n     * @name Two.Group#stroke\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what all child shapes should be outlined in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    __publicField(this, \"_stroke\", \"#000\");\n    /**\n     * @name Two.Group#linewidth\n     * @property {Number} - The thickness in pixels of the stroke for all child shapes.\n     */\n    __publicField(this, \"_linewidth\", 1);\n    /**\n     * @name Two.Group#opacity\n     * @property {Number} - The opaqueness of all child shapes.\n     * @nota-bene Becomes multiplied by the individual child's opacity property.\n     */\n    __publicField(this, \"_opacity\", 1);\n    /**\n     * @name Two.Group#visible\n     * @property {Boolean} - Display the path or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    __publicField(this, \"_visible\", true);\n    /**\n     * @name Two.Group#cap\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty}\n     */\n    __publicField(this, \"_cap\", \"round\");\n    /**\n     * @name Two.Group#join\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty}\n     */\n    __publicField(this, \"_join\", \"round\");\n    /**\n     * @name Two.Group#miter\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty}\n     */\n    __publicField(this, \"_miter\", 4);\n    /**\n     * @name Two.Group#closed\n     * @property {Boolean} - Determines whether a final line is drawn between the final point in the `vertices` array and the first point of all child shapes.\n     */\n    __publicField(this, \"_closed\", true);\n    /**\n     * @name Two.Group#curved\n     * @property {Boolean} - When the child's path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n     */\n    __publicField(this, \"_curved\", false);\n    /**\n     * @name Two.Group#automatic\n     * @property {Boolean} - Determines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n     */\n    __publicField(this, \"_automatic\", true);\n    /**\n     * @name Two.Group#beginning\n     * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n     * @description {@link Two.Group#beginning} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#ending}.\n     */\n    __publicField(this, \"_beginning\", 0);\n    /**\n     * @name Two.Group#ending\n     * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n     * @description {@link Two.Group#ending} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#beginning}.\n     */\n    __publicField(this, \"_ending\", 1);\n    /**\n     * @name Two.Group#length\n     * @property {Number} - The sum of distances between all child lengths.\n     */\n    __publicField(this, \"_length\", 0);\n    /**\n     * @name Two.Group#mask\n     * @property {Two.Shape} - The Two.js object to clip from a group's rendering.\n     */\n    __publicField(this, \"_mask\", null);\n    /**\n     * @name Two.Group#_strokeAttenuation\n     * @private\n     * @see {@link Two.Group#strokeAttenuation}\n     */\n    __publicField(this, \"_strokeAttenuation\", true);\n    for (let prop in proto22) {\n      Object.defineProperty(this, prop, proto22[prop]);\n    }\n    this._renderer.type = \"group\";\n    this.additions = [];\n    this.subtractions = [];\n    this.children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments);\n  }\n  /**\n   * @name Two.Group.InsertChildren\n   * @function\n   * @param {Two.Shape[]} children - The objects to be inserted.\n   * @description Cached method to let renderers know children have been added to a {@link Two.Group}.\n   */\n  static InsertChildren(children) {\n    for (let i = 0; i < children.length; i++) {\n      replaceParent.call(this, children[i], this);\n    }\n  }\n  /**\n   * @name Two.Group.RemoveChildren\n   * @function\n   * @param {Two.Shape[]} children - The objects to be removed.\n   * @description Cached method to let renderers know children have been removed from a {@link Two.Group}.\n   */\n  static RemoveChildren(children) {\n    for (let i = 0; i < children.length; i++) {\n      replaceParent.call(this, children[i]);\n    }\n  }\n  /**\n   * @name Two.Group.OrderChildren\n   * @function\n   * @description Cached method to let renderers know order has been updated on a {@link Two.Group}.\n   */\n  static OrderChildren(children) {\n    this._flagOrder = true;\n  }\n  static fromObject(obj) {\n    const group = new _Group();\n    for (let i = 0; i < _Group.Properties.length; i++) {\n      const k = _Group.Properties[i];\n      if (k in obj) {\n        if (/(fill|stroke)/i.test(k)) {\n          group[k] = typeof obj[k] === \"string\" ? obj[k] : getEffectFromObject(obj[k]);\n        } else {\n          group[k] = obj[k];\n        }\n      }\n    }\n    if (\"mask\" in obj) {\n      group.mask = getShapeFromObject(obj.mask);\n    }\n    if (\"id\" in obj) {\n      group.id = obj.id;\n    }\n    group.children = obj.children.map(getShapeFromObject);\n    return group;\n    function getShapeFromObject(child) {\n      if (child && child.renderer) {\n        switch (child.renderer.type) {\n          case \"arc-segment\":\n            return ArcSegment.fromObject(child);\n          case \"circle\":\n            return Circle.fromObject(child);\n          case \"element\":\n            return Element.fromObject(child);\n          case \"ellipse\":\n            return Ellipse.fromObject(child);\n          case \"group\":\n            return _Group.fromObject(child);\n          case \"image\":\n            return Image.fromObject(child);\n          case \"image-sequence\":\n            return ImageSequence.fromObject(child);\n          case \"path\":\n            return Path.fromObject(child);\n          case \"points\":\n            return Points.fromObject(child);\n          case \"polygon\":\n            return Polygon.fromObject(child);\n          case \"rectangle\":\n            return Rectangle.fromObject(child);\n          case \"rounded-rectangle\":\n            return RoundedRectangle.fromObject(child);\n          case \"shape\":\n            return Shape.fromObject(child);\n          case \"sprite\":\n            return Sprite.fromObject(child);\n          case \"star\":\n            return Star.fromObject(child);\n          case \"text\":\n            return Text.fromObject(child);\n        }\n      }\n      return child;\n    }\n  }\n  static IsVisible(element, visibleOnly) {\n    if (!visibleOnly) {\n      return true;\n    }\n    let current = element;\n    while (current) {\n      if (typeof current.visible === \"boolean\" && !current.visible) {\n        return false;\n      }\n      if (typeof current.opacity === \"number\" && current.opacity <= 0) {\n        return false;\n      }\n      current = current.parent;\n    }\n    return true;\n  }\n  static VisitForHitTest(group, context, includeGroups, filter, hitOptions, tolerance, stopOnFirst) {\n    const children = group && group.children;\n    if (!children) {\n      return false;\n    }\n    const results = context.results;\n    for (let i = children.length - 1; i >= 0; i--) {\n      const child = children[i];\n      if (!child) {\n        continue;\n      }\n      if (!_Group.IsVisible(child, context.visibleOnly)) {\n        continue;\n      }\n      const rect = typeof child.getBoundingClientRect === \"function\" ? child.getBoundingClientRect() : null;\n      if (rect && !boundsContains(rect, context.x, context.y, tolerance)) {\n        continue;\n      }\n      if (child instanceof _Group) {\n        if (includeGroups && (!filter || filter(child)) && typeof child.contains === \"function\" && child.contains(context.x, context.y, hitOptions)) {\n          results.push(child);\n          if (stopOnFirst) {\n            return true;\n          }\n        }\n        if (_Group.VisitForHitTest(\n          child,\n          context,\n          includeGroups,\n          filter,\n          hitOptions,\n          tolerance,\n          stopOnFirst\n        )) {\n          return true;\n        }\n        continue;\n      }\n      if (!(child instanceof Shape)) {\n        continue;\n      }\n      if (filter && !filter(child)) {\n        continue;\n      }\n      if (typeof child.contains !== \"function\") {\n        continue;\n      }\n      if (child.contains(context.x, context.y, hitOptions)) {\n        results.push(child);\n        if (stopOnFirst) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n  /**\n   * @name Two.Group#copy\n   * @function\n   * @param {Two.Group} [group] - The reference {@link Two.Group}\n   * @returns {Two.Group}\n   * @description Copy the properties of one {@link Two.Group} onto another.\n   */\n  copy(group) {\n    super.copy.call(this, group);\n    console.warn(\n      \"Two.js: attempting to copy group. Two.Group.children copying not supported.\"\n    );\n    for (let i = 0; i < _Group.Properties.length; i++) {\n      const k = _Group.Properties[i];\n      if (k in group) {\n        this[k] = group[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Group#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Group}\n   * @description Create a new instance of {@link Two.Group} with the same properties of the current group.\n   */\n  clone(parent) {\n    const clone = new _Group();\n    const children = this.children.map(function(child) {\n      return child.clone();\n    });\n    clone.add(children);\n    clone.opacity = this.opacity;\n    if (this.mask) {\n      clone.mask = this.mask;\n    }\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.className = this.className;\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone._update();\n  }\n  /**\n   * @name Two.Group#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the group.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = \"group\";\n    result.children = [];\n    result.opacity = this.opacity;\n    result.className = this.className;\n    result.mask = this.mask ? this.mask.toObject() : null;\n    _.each(\n      this.children,\n      (child, i) => {\n        result.children[i] = child.toObject();\n      },\n      this\n    );\n    return result;\n  }\n  /**\n   * @name Two.Group#dispose\n   * @function\n   * @returns {Two.Group}\n   * @description Release the group's renderer resources and detach all events.\n   * This method recursively disposes all child objects, unbinds the children\n   * collection events, and preserves the renderer type for potential re-attachment\n   * to a new renderer.\n   */\n  dispose() {\n    super.dispose();\n    if (this.children) {\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (typeof child.dispose === \"function\") {\n          child.dispose();\n        }\n      }\n    }\n    if (this.children && typeof this.children.unbind === \"function\") {\n      try {\n        this.children.unbind();\n      } catch (e) {\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Group#getShapesAtPoint\n   * @function\n   * @param {Number} x - X coordinate in world space.\n   * @param {Number} y - Y coordinate in world space.\n   * @param {Object} [options] - Hit test configuration.\n   * @param {Boolean} [options.visibleOnly=true] - Limit results to visible shapes.\n   * @param {Boolean} [options.includeGroups=false] - Include groups in the hit results.\n   * @param {('all'|'deepest')} [options.mode='all'] - Whether to return all intersecting shapes or only the top-most.\n   * @param {Boolean} [options.deepest] - Alias for `mode: 'deepest'`.\n   * @param {Number} [options.precision] - Segmentation precision for curved geometry.\n   * @param {Number} [options.tolerance=0] - Pixel tolerance applied to hit testing.\n   * @param {Boolean} [options.fill] - Override fill testing behaviour.\n   * @param {Boolean} [options.stroke] - Override stroke testing behaviour.\n   * @param {Function} [options.filter] - Predicate to filter shapes from the result set.\n   * @returns {Shape[]} Ordered list of intersecting shapes, front to back.\n   * @description Traverse the group hierarchy and return shapes that contain the specified point.\n   * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n   */\n  getShapesAtPoint(x, y, options) {\n    const opts = options || {};\n    const { results, hitOptions, context, single, empty } = cache.getShapesAtPoint;\n    results.length = 0;\n    const mode = opts.mode === \"deepest\" || opts.deepest ? \"deepest\" : \"all\";\n    const visibleOnly = opts.visibleOnly !== false;\n    const includeGroups = !!opts.includeGroups;\n    const filter = typeof opts.filter === \"function\" ? opts.filter : null;\n    const tolerance = typeof opts.tolerance === \"number\" ? opts.tolerance : 0;\n    if (typeof opts.precision === \"number\") {\n      hitOptions.precision = opts.precision;\n    } else {\n      delete hitOptions.precision;\n    }\n    if (typeof opts.fill !== \"undefined\") {\n      hitOptions.fill = opts.fill;\n    } else {\n      delete hitOptions.fill;\n    }\n    if (typeof opts.stroke !== \"undefined\") {\n      hitOptions.stroke = opts.stroke;\n    } else {\n      delete hitOptions.stroke;\n    }\n    hitOptions.tolerance = tolerance;\n    hitOptions.ignoreVisibility = !visibleOnly;\n    const stopOnFirst = mode === \"deepest\";\n    context.x = x;\n    context.y = y;\n    context.visibleOnly = visibleOnly;\n    context.results = results;\n    _Group.VisitForHitTest(\n      this,\n      context,\n      includeGroups,\n      filter,\n      hitOptions,\n      tolerance,\n      stopOnFirst\n    );\n    if (stopOnFirst) {\n      if (results.length > 0) {\n        const first = results[0];\n        results.length = 0;\n        single[0] = first;\n        single.length = 1;\n        return single;\n      }\n      empty.length = 0;\n      return empty;\n    }\n    const hits = results.slice();\n    results.length = 0;\n    return hits;\n  }\n  /**\n   * @name Two.Group#corner\n   * @function\n   * @description Orient the children of the group to the upper left-hand corner of that group.\n   */\n  corner() {\n    const rect = this.getBoundingClientRect(true);\n    for (let i = 0; i < this.children.length; i++) {\n      const child = this.children[i];\n      child.translation.x -= rect.left;\n      child.translation.y -= rect.top;\n    }\n    if (this.mask) {\n      this.mask.translation.x -= rect.left;\n      this.mask.translation.y -= rect.top;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Group#center\n   * @function\n   * @description Orient the children of the group to the center of that group.\n   */\n  center() {\n    const rect = this.getBoundingClientRect(true);\n    const cx = rect.left + rect.width / 2 - this.translation.x;\n    const cy = rect.top + rect.height / 2 - this.translation.y;\n    for (let i = 0; i < this.children.length; i++) {\n      const child = this.children[i];\n      if (child.isShape) {\n        child.translation.x -= cx;\n        child.translation.y -= cy;\n      }\n    }\n    if (this.mask) {\n      this.mask.translation.x -= cx;\n      this.mask.translation.y -= cy;\n    }\n    return this;\n  }\n  /**\n   * @name Two.Group#getById\n   * @function\n   * @description Recursively search for id. Returns the first element found.\n   * @returns {Two.Shape} - Or `null` if nothing is found.\n   */\n  getById(id) {\n    let found = null;\n    function search(node) {\n      if (node.id === id) {\n        return node;\n      } else if (node.children) {\n        if (node.children.ids[id]) {\n          return node.children.ids[id];\n        }\n        for (let i = 0; i < node.children.length; i++) {\n          found = search(node.children[i]);\n          if (found) {\n            return found;\n          }\n        }\n      }\n      return null;\n    }\n    return search(this);\n  }\n  /**\n   * @name Two.Group#getByClassName\n   * @function\n   * @description Recursively search for classes. Returns an array of matching elements.\n   * @returns {Two.Shape[]} - Or empty array if nothing is found.\n   */\n  getByClassName(className) {\n    const found = [];\n    function search(node) {\n      if (Array.prototype.indexOf.call(node.classList, className) >= 0) {\n        found.push(node);\n      }\n      if (node.children) {\n        for (let i = 0; i < node.children.length; i++) {\n          const child = node.children[i];\n          search(child);\n        }\n      }\n      return found;\n    }\n    return search(this);\n  }\n  /**\n   * @name Two.Group#getByType\n   * @function\n   * @description Recursively search for children of a specific type, e.g. {@link Two.Path}. Pass a reference to this type as the param. Returns an array of matching elements.\n   * @returns {Two.Shape[]} - Empty array if nothing is found.\n   */\n  getByType(type) {\n    const found = [];\n    function search(node) {\n      if (node instanceof type) {\n        found.push(node);\n      }\n      if (node.children) {\n        for (let i = 0; i < node.children.length; i++) {\n          const child = node.children[i];\n          search(child);\n        }\n      }\n      return found;\n    }\n    return search(this);\n  }\n  /**\n   * @name Two.Group#add\n   * @function\n   * @param {Two.Shape[]|...Two.Shape} objects - An array of objects to be added. Can also be supplied as individual arguments.\n   * @description Add objects to the group.\n   */\n  add(objects) {\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    } else {\n      objects = objects.slice();\n    }\n    for (let i = 0; i < objects.length; i++) {\n      const child = objects[i];\n      if (!(child && child.id)) {\n        continue;\n      }\n      const index = Array.prototype.indexOf.call(this.children, child);\n      if (index >= 0) {\n        this.children.splice(index, 1);\n      }\n      this.children.push(child);\n    }\n    return this;\n  }\n  /**\n   * @name Two.Group#remove\n   * @function\n   * @param {Two.Shape[]|...Two.Shape} [objects=self] - An array of objects to be removed. Can be also removed as individual arguments. If no arguments are passed, then it removes itself from its parent.\n   * @description Remove objects from the group.\n   */\n  remove(objects) {\n    const l = arguments.length, grandparent = this.parent;\n    if (l <= 0 && grandparent) {\n      grandparent.remove(this);\n      return this;\n    }\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    } else {\n      objects = objects.slice();\n    }\n    for (let i = 0; i < objects.length; i++) {\n      const object = objects[i];\n      if (!object || !this.children.ids[object.id]) {\n        continue;\n      }\n      const index = this.children.indexOf(object);\n      if (index >= 0) {\n        this.children.splice(index, 1);\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Group#getBoundingClientRect\n   * @function\n   * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n   * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n   * @description Return an object with top, left, right, bottom, width, and height parameters of the group.\n   */\n  getBoundingClientRect(shallow) {\n    let rect, matrix, tc, lc, rc, bc;\n    this._update(true);\n    let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity;\n    const regex3 = /texture|gradient/i;\n    matrix = shallow ? this.matrix : this.worldMatrix;\n    for (let i = 0; i < this.children.length; i++) {\n      const child = this.children[i];\n      if (!child.visible || regex3.test(child._renderer.type)) {\n        continue;\n      }\n      rect = child.getBoundingClientRect(shallow);\n      tc = typeof rect.top !== \"number\" || _.isNaN(rect.top) || !isFinite(rect.top);\n      lc = typeof rect.left !== \"number\" || _.isNaN(rect.left) || !isFinite(rect.left);\n      rc = typeof rect.right !== \"number\" || _.isNaN(rect.right) || !isFinite(rect.right);\n      bc = typeof rect.bottom !== \"number\" || _.isNaN(rect.bottom) || !isFinite(rect.bottom);\n      if (tc || lc || rc || bc) {\n        continue;\n      }\n      if (shallow) {\n        const [ax, ay] = matrix.multiply(rect.left, rect.top);\n        const [bx, by] = matrix.multiply(rect.right, rect.top);\n        const [cx, cy] = matrix.multiply(rect.left, rect.bottom);\n        const [dx, dy] = matrix.multiply(rect.right, rect.bottom);\n        top = min3(ay, by, cy, dy, top);\n        left = min3(ax, bx, cx, dx, left);\n        right = max3(ax, bx, cx, dx, right);\n        bottom = max3(ay, by, cy, dy, bottom);\n      } else {\n        top = min3(rect.top, top);\n        left = min3(rect.left, left);\n        right = max3(rect.right, right);\n        bottom = max3(rect.bottom, bottom);\n      }\n    }\n    return {\n      top,\n      left,\n      right,\n      bottom,\n      width: right - left,\n      height: bottom - top\n    };\n  }\n  /**\n   * @name Two.Group#noFill\n   * @function\n   * @description Apply `noFill` method to all child shapes.\n   */\n  noFill() {\n    this.children.forEach(function(child) {\n      child.noFill();\n    });\n    return this;\n  }\n  /**\n   * @name Two.Group#noStroke\n   * @function\n   * @description Apply `noStroke` method to all child shapes.\n   */\n  noStroke() {\n    this.children.forEach(function(child) {\n      child.noStroke();\n    });\n    return this;\n  }\n  /**\n   * @name Two.Group#subdivide\n   * @function\n   * @description Apply `subdivide` method to all child shapes.\n   */\n  subdivide() {\n    const args = arguments;\n    this.children.forEach(function(child) {\n      child.subdivide.apply(child, args);\n    });\n    return this;\n  }\n  /**\n   * @name Two.Group#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    let i, l, child;\n    if (this._flagBeginning || this._flagEnding) {\n      const beginning = Math.min(this._beginning, this._ending);\n      const ending = Math.max(this._beginning, this._ending);\n      const length = this.length;\n      let sum = 0;\n      const bd = beginning * length;\n      const ed = ending * length;\n      for (i = 0; i < this.children.length; i++) {\n        child = this.children[i];\n        l = child.length;\n        if (bd > sum + l) {\n          child.beginning = 1;\n          child.ending = 1;\n        } else if (ed < sum) {\n          child.beginning = 0;\n          child.ending = 0;\n        } else if (bd > sum && bd < sum + l) {\n          child.beginning = (bd - sum) / l;\n          child.ending = 1;\n        } else if (ed > sum && ed < sum + l) {\n          child.beginning = 0;\n          child.ending = (ed - sum) / l;\n        } else {\n          child.beginning = 0;\n          child.ending = 1;\n        }\n        sum += l;\n      }\n    }\n    return super._update.apply(this, arguments);\n  }\n  /**\n   * @name Two.Group#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    if (this._flagAdditions) {\n      this.additions.length = 0;\n      this._flagAdditions = false;\n    }\n    if (this._flagSubtractions) {\n      this.subtractions.length = 0;\n      this._flagSubtractions = false;\n    }\n    this._flagOrder = this._flagMask = this._flagOpacity = this._flagBeginning = this._flagEnding = false;\n    super.flagReset.call(this);\n    return this;\n  }\n};\n__publicField(_Group, \"Children\", Children);\n/**\n * @name Two.Group.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Group}.\n */\n__publicField(_Group, \"Properties\", [\n  \"fill\",\n  \"stroke\",\n  \"linewidth\",\n  \"cap\",\n  \"join\",\n  \"miter\",\n  \"closed\",\n  \"curved\",\n  \"automatic\"\n]);\nvar Group = _Group;\nvar proto22 = {\n  visible: {\n    enumerable: true,\n    get: function() {\n      return this._visible;\n    },\n    set: function(v) {\n      this._flagVisible = this._visible !== v || this._flagVisible;\n      this._visible = v;\n    }\n  },\n  opacity: {\n    enumerable: true,\n    get: function() {\n      return this._opacity;\n    },\n    set: function(v) {\n      this._flagOpacity = this._opacity !== v || this._flagOpacity;\n      this._opacity = v;\n    }\n  },\n  beginning: {\n    enumerable: true,\n    get: function() {\n      return this._beginning;\n    },\n    set: function(v) {\n      this._flagBeginning = this._beginning !== v || this._flagBeginning;\n      this._beginning = v;\n    }\n  },\n  ending: {\n    enumerable: true,\n    get: function() {\n      return this._ending;\n    },\n    set: function(v) {\n      this._flagEnding = this._ending !== v || this._flagEnding;\n      this._ending = v;\n    }\n  },\n  length: {\n    enumerable: true,\n    get: function() {\n      if (this._flagLength || this._length <= 0) {\n        this._length = 0;\n        if (!this.children) {\n          return this._length;\n        }\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          this._length += child.length;\n        }\n      }\n      return this._length;\n    }\n  },\n  fill: {\n    enumerable: true,\n    get: function() {\n      return this._fill;\n    },\n    set: function(v) {\n      this._fill = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.fill = v;\n      }\n    }\n  },\n  stroke: {\n    enumerable: true,\n    get: function() {\n      return this._stroke;\n    },\n    set: function(v) {\n      this._stroke = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.stroke = v;\n      }\n    }\n  },\n  linewidth: {\n    enumerable: true,\n    get: function() {\n      return this._linewidth;\n    },\n    set: function(v) {\n      this._linewidth = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.linewidth = v;\n      }\n    }\n  },\n  join: {\n    enumerable: true,\n    get: function() {\n      return this._join;\n    },\n    set: function(v) {\n      this._join = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.join = v;\n      }\n    }\n  },\n  miter: {\n    enumerable: true,\n    get: function() {\n      return this._miter;\n    },\n    set: function(v) {\n      this._miter = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.miter = v;\n      }\n    }\n  },\n  cap: {\n    enumerable: true,\n    get: function() {\n      return this._cap;\n    },\n    set: function(v) {\n      this._cap = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.cap = v;\n      }\n    }\n  },\n  closed: {\n    enumerable: true,\n    get: function() {\n      return this._closed;\n    },\n    set: function(v) {\n      this._closed = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.closed = v;\n      }\n    }\n  },\n  curved: {\n    enumerable: true,\n    get: function() {\n      return this._curved;\n    },\n    set: function(v) {\n      this._curved = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.curved = v;\n      }\n    }\n  },\n  automatic: {\n    enumerable: true,\n    get: function() {\n      return this._automatic;\n    },\n    set: function(v) {\n      this._automatic = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.automatic = v;\n      }\n    }\n  },\n  children: {\n    enumerable: true,\n    get: function() {\n      return this._children;\n    },\n    set: function(children) {\n      const insertChildren = Group.InsertChildren.bind(this);\n      const removeChildren = Group.RemoveChildren.bind(this);\n      const orderChildren = Group.OrderChildren.bind(this);\n      if (this._children) {\n        this._children.unbind();\n        if (this._children.length > 0) {\n          removeChildren(this._children);\n        }\n      }\n      this._children = new Children(children);\n      this._children.bind(Events.Types.insert, insertChildren);\n      this._children.bind(Events.Types.remove, removeChildren);\n      this._children.bind(Events.Types.order, orderChildren);\n      if (children.length > 0) {\n        insertChildren(children);\n      }\n    }\n  },\n  mask: {\n    enumerable: true,\n    get: function() {\n      return this._mask;\n    },\n    set: function(v) {\n      this._mask = v;\n      this._flagMask = true;\n      if (_.isObject(v) && !v.clip) {\n        v.clip = true;\n      }\n    }\n  },\n  /**\n   * @name Two.Group#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space for all child shapes.\n   * @description When `strokeAttenuation` is `false`, this property is applied to all child shapes, making their stroke widths automatically adjust to compensate for the group's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke widths scale normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function() {\n      return this._strokeAttenuation;\n    },\n    set: function(v) {\n      this._strokeAttenuation = !!v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (child.strokeAttenuation !== void 0) {\n          child.strokeAttenuation = v;\n        }\n      }\n    }\n  }\n};\nfunction replaceParent(child, newParent) {\n  const parent = child.parent;\n  let index;\n  if (parent === newParent) {\n    add();\n    return;\n  }\n  if (parent && parent.children.ids[child.id]) {\n    index = Array.prototype.indexOf.call(parent.children, child);\n    parent.children.splice(index, 1);\n    splice();\n  }\n  if (newParent) {\n    add();\n    return;\n  }\n  splice();\n  if (parent._flagAdditions && parent.additions.length === 0) {\n    parent._flagAdditions = false;\n  }\n  if (parent._flagSubtractions && parent.subtractions.length === 0) {\n    parent._flagSubtractions = false;\n  }\n  delete child.parent;\n  function add() {\n    if (newParent.subtractions.length > 0) {\n      index = Array.prototype.indexOf.call(newParent.subtractions, child);\n      if (index >= 0) {\n        newParent.subtractions.splice(index, 1);\n      }\n    }\n    if (newParent.additions.length > 0) {\n      index = Array.prototype.indexOf.call(newParent.additions, child);\n      if (index >= 0) {\n        newParent.additions.splice(index, 1);\n      }\n    }\n    child.parent = newParent;\n    newParent.additions.push(child);\n    newParent._flagAdditions = true;\n  }\n  function splice() {\n    index = Array.prototype.indexOf.call(parent.additions, child);\n    if (index >= 0) {\n      parent.additions.splice(index, 1);\n    }\n    index = Array.prototype.indexOf.call(parent.subtractions, child);\n    if (index < 0) {\n      parent.subtractions.push(child);\n      parent._flagSubtractions = true;\n    }\n  }\n}\n\n// src/shapes/line.js\nvar Line = class extends Path {\n  constructor(x1, y1, x2, y2) {\n    const points = [new Anchor(x1, y1), new Anchor(x2, y2)];\n    super(points);\n    for (let prop in proto23) {\n      Object.defineProperty(this, prop, proto23[prop]);\n    }\n    this.vertices[0].command = Commands.move;\n    this.vertices[1].command = Commands.line;\n    this.automatic = false;\n  }\n};\n__publicField(Line, \"Properties\", [\"left\", \"right\"]);\nvar proto23 = {\n  left: {\n    enumerable: true,\n    get: function() {\n      return this.vertices[0];\n    },\n    set: function(v) {\n      if (_.isObject(v)) {\n        this.vertices.splice(0, 1, v);\n        this.vertices[0].command = Commands.move;\n      } else {\n        const error = new TwoError(\"Two.Line.left argument is not an object.\");\n        console.warn(error.name, error.message);\n      }\n    }\n  },\n  right: {\n    enumerable: true,\n    get: function() {\n      return this.vertices[1];\n    },\n    set: function(v) {\n      if (_.isObject(v)) {\n        this.vertices.splice(1, 1, v);\n        this.vertices[1].command = Commands.line;\n      } else {\n        const error = new TwoError(\"Two.Line.right argument is not an object.\");\n        console.warn(error.name, error.message);\n      }\n    }\n  }\n};\n\n// src/utils/interpret-svg.js\nvar regex2 = {\n  path: /[+-]?(?:\\d*\\.\\d+|\\d+)(?:[eE][+-]\\d+)?/g,\n  cssBackgroundImage: /url\\(['\"]?#([\\w\\d-_]*)['\"]?\\)/i,\n  unitSuffix: /[a-zA-Z%]*/i\n};\nvar alignments = {\n  start: \"left\",\n  middle: \"center\",\n  end: \"right\"\n};\nvar reservedAttributesToRemove = [\n  \"id\",\n  \"class\",\n  \"transform\",\n  \"xmlns\",\n  \"viewBox\"\n];\nvar overwriteAttrs = [\"x\", \"y\", \"width\", \"height\", \"href\", \"xlink:href\"];\nfunction getAlignment(anchor2) {\n  return alignments[anchor2];\n}\nfunction getBaseline(node) {\n  const a = node.getAttribute(\"dominant-baseline\");\n  const b = node.getAttribute(\"alignment-baseline\");\n  return a || b;\n}\nfunction getTagName(tag) {\n  return tag.replace(/svg:/gi, \"\").toLowerCase();\n}\nfunction applyTransformsToVector(transforms, vector3) {\n  vector3.x += transforms.translateX;\n  vector3.y += transforms.translateY;\n  vector3.x *= transforms.scaleX;\n  vector3.y *= transforms.scaleY;\n  if (transforms.rotation !== 0) {\n    const l = vector3.length();\n    vector3.x = l * Math.cos(transforms.rotation);\n    vector3.y = l * Math.sin(transforms.rotation);\n  }\n}\nfunction extractCSSText(text, styles) {\n  if (!styles) {\n    styles = {};\n  }\n  const commands = text.split(\";\");\n  for (let i = 0; i < commands.length; i++) {\n    const command = commands[i].split(\":\");\n    const name = command[0];\n    const value = command[1];\n    if (typeof name === \"undefined\" || typeof value === \"undefined\") {\n      continue;\n    }\n    const trimmedName = name.replace(/\\s/g, \"\");\n    const trimmedValue = value.replace(/\\s/g, \"\");\n    styles[trimmedName] = trimmedValue;\n  }\n  return styles;\n}\nfunction getSvgStyles(node) {\n  const styles = {};\n  const attributes = getSvgAttributes(node);\n  const length = Math.max(attributes.length, node.style.length);\n  for (let i = 0; i < length; i++) {\n    const command = node.style[i];\n    const attribute = attributes[i];\n    if (command) {\n      styles[command] = node.style[command];\n    }\n    if (attribute) {\n      styles[attribute] = node.getAttribute(attribute);\n    }\n  }\n  return styles;\n}\nfunction getSvgAttributes(node) {\n  const attributes = node.getAttributeNames();\n  for (let i = 0; i < reservedAttributesToRemove.length; i++) {\n    const keyword = reservedAttributesToRemove[i];\n    const index = Array.prototype.indexOf.call(attributes, keyword);\n    if (index >= 0) {\n      attributes.splice(index, 1);\n    }\n  }\n  return attributes;\n}\nfunction applySvgViewBox(node, value) {\n  const elements = value.split(/[\\s,]/);\n  const x = -parseFloat(elements[0]);\n  const y = -parseFloat(elements[1]);\n  const width = parseFloat(elements[2]);\n  const height = parseFloat(elements[3]);\n  if (x && y) {\n    for (let i = 0; i < node.children.length; i++) {\n      const child = node.children[i];\n      if (\"translation\" in child) {\n        child.translation.add(x, y);\n      } else if (\"x\" in child) {\n        child.x = x;\n      } else if (\"y\" in child) {\n        child.y = y;\n      }\n    }\n  }\n  const xExists = typeof node.x === \"number\";\n  const yExists = typeof node.y === \"number\";\n  const widthExists = typeof node.width === \"number\";\n  const heightExists = typeof node.height === \"number\";\n  if (xExists) {\n    node.translation.x += node.x;\n  }\n  if (yExists) {\n    node.translation.y += node.y;\n  }\n  if (widthExists || heightExists) {\n    node.scale = new Vector(1, 1);\n  }\n  if (widthExists) {\n    node.scale.x = node.width / width;\n  }\n  if (heightExists) {\n    node.scale.y = node.height / height;\n  }\n  node.mask = new Rectangle(0, 0, width, height);\n  node.mask.origin.set(-width / 2, -height / 2);\n  return node;\n}\nfunction applySvgAttributes(node, elem, parentStyles) {\n  const styles = {}, attributes = {}, extracted = {};\n  let i, m, key, value, prop, attr;\n  let transforms, x, y;\n  let id, scene, ref, tagName;\n  let ca, cb, cc, error;\n  if (node === null) {\n    return styles;\n  }\n  if (root.getComputedStyle) {\n    const computedStyles = root.getComputedStyle(node);\n    i = computedStyles.length;\n    while (i--) {\n      key = computedStyles[i];\n      value = computedStyles[key];\n      if (typeof value !== \"undefined\") {\n        styles[key] = value;\n      }\n    }\n  }\n  for (i = 0; i < node.attributes.length; i++) {\n    attr = node.attributes[i];\n    if (/style/i.test(attr.nodeName)) {\n      extractCSSText(attr.value, extracted);\n    } else {\n      attributes[attr.nodeName] = attr.value;\n    }\n  }\n  if (typeof styles.opacity !== \"undefined\") {\n    styles[\"stroke-opacity\"] = styles.opacity;\n    styles[\"fill-opacity\"] = styles.opacity;\n    delete styles.opacity;\n  }\n  if (parentStyles) {\n    _.defaults(styles, parentStyles);\n  }\n  _.extend(styles, extracted, attributes);\n  styles.visible = !(typeof styles.display === \"undefined\" && /none/i.test(styles.display)) || typeof styles.visibility === \"undefined\" && /hidden/i.test(styles.visibility);\n  for (key in styles) {\n    value = styles[key];\n    switch (key) {\n      case \"gradientTransform\":\n        if (/none/i.test(value)) break;\n        m = node.gradientTransform && node.gradientTransform.baseVal && node.gradientTransform.baseVal.length > 0 ? node.gradientTransform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null;\n        if (m === null) break;\n        transforms = decomposeMatrix(m);\n        switch (elem._renderer.type) {\n          case \"linear-gradient\":\n            applyTransformsToVector(transforms, elem.left);\n            applyTransformsToVector(transforms, elem.right);\n            break;\n          case \"radial-gradient\":\n            elem.center.x += transforms.translateX;\n            elem.center.y += transforms.translateY;\n            elem.focal.x += transforms.translateX;\n            elem.focal.y += transforms.translateY;\n            elem.radius *= Math.max(transforms.scaleX, transforms.scaleY);\n            break;\n        }\n        break;\n      case \"transform\":\n        if (/none/i.test(value)) break;\n        m = node.transform && node.transform.baseVal && node.transform.baseVal.length > 0 ? node.transform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null;\n        if (m === null) break;\n        if (Constants.AutoCalculateImportedMatrices) {\n          transforms = decomposeMatrix(m);\n          elem.translation.set(transforms.translateX, transforms.translateY);\n          elem.rotation = Math.PI * (transforms.rotation / 180);\n          elem.scale = new Vector(transforms.scaleX, transforms.scaleY);\n          x = parseFloat((styles.x + \"\").replace(\"px\"));\n          y = parseFloat((styles.y + \"\").replace(\"px\"));\n          if (x) {\n            elem.translation.x = x;\n          }\n          if (y) {\n            elem.translation.y = y;\n          }\n        } else {\n          m = node.getCTM();\n          elem._matrix.manual = true;\n          elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f);\n        }\n        break;\n      case \"visible\":\n        if (elem instanceof Group) {\n          elem._visible = value;\n          break;\n        }\n        elem.visible = value;\n        break;\n      case \"stroke-linecap\":\n        if (elem instanceof Group) {\n          elem._cap = value;\n          break;\n        }\n        elem.cap = value;\n        break;\n      case \"stroke-linejoin\":\n        if (elem instanceof Group) {\n          elem._join = value;\n          break;\n        }\n        elem.join = value;\n        break;\n      case \"stroke-miterlimit\":\n        if (elem instanceof Group) {\n          elem._miter = value;\n          break;\n        }\n        elem.miter = value;\n        break;\n      case \"stroke-width\":\n        if (elem instanceof Group) {\n          elem._linewidth = parseFloat(value);\n          break;\n        }\n        elem.linewidth = parseFloat(value);\n        break;\n      case \"opacity\":\n      case \"stroke-opacity\":\n      case \"fill-opacity\":\n        if (elem instanceof Group) {\n          elem._opacity = parseFloat(value);\n          break;\n        }\n        elem.opacity = parseFloat(value);\n        break;\n      case \"clip-path\":\n        if (regex2.cssBackgroundImage.test(value)) {\n          id = value.replace(regex2.cssBackgroundImage, \"$1\");\n          if (read.defs.current && read.defs.current.contains(id)) {\n            ref = read.defs.current.get(id);\n            if (ref && ref.childNodes.length > 0) {\n              ref = ref.childNodes[0];\n              tagName = getTagName(ref.nodeName);\n              elem.mask = read[tagName].call(this, ref, {});\n              switch (elem._renderer.type) {\n                case \"text\":\n                case \"path\":\n                  elem.position.add(elem.mask.position);\n                  elem.mask.position.clear();\n                  break;\n              }\n            }\n          }\n        }\n        break;\n      case \"fill\":\n      case \"stroke\":\n        prop = (elem instanceof Group ? \"_\" : \"\") + key;\n        if (regex2.cssBackgroundImage.test(value)) {\n          id = value.replace(regex2.cssBackgroundImage, \"$1\");\n          if (read.defs.current && read.defs.current.contains(id)) {\n            ref = read.defs.current.get(id);\n            if (!ref.object) {\n              tagName = getTagName(ref.nodeName);\n              ref.object = read[tagName].call(this, ref, {});\n            }\n            ref = ref.object;\n          } else {\n            scene = getScene(this);\n            ref = scene.getById(id);\n          }\n          elem[prop] = ref;\n        } else {\n          elem[prop] = value;\n        }\n        break;\n      case \"id\":\n        elem.id = value;\n        break;\n      case \"class\":\n      case \"className\":\n        elem.classList = value.split(\" \");\n        elem._flagClassName = true;\n        break;\n      case \"x\":\n      case \"y\":\n        ca = elem instanceof Gradient;\n        cb = elem instanceof LinearGradient;\n        cc = elem instanceof RadialGradient;\n        if (ca || cb || cc) {\n          break;\n        }\n        if (value.match(\"[a-z%]$\") && !value.endsWith(\"px\")) {\n          error = new TwoError(\n            \"only pixel values are supported with the \" + key + \" attribute.\"\n          );\n          console.warn(error.name, error.message);\n        }\n        elem.translation[key] = parseFloat(value);\n        break;\n      case \"font-family\":\n        if (elem instanceof Text) {\n          elem.family = value;\n        }\n        break;\n      case \"font-size\":\n        if (elem instanceof Text) {\n          if (value.match(\"[a-z%]$\") && !value.endsWith(\"px\")) {\n            error = new TwoError(\n              \"only pixel values are supported with the \" + key + \" attribute.\"\n            );\n            console.warn(error.name, error.message);\n          }\n          elem.size = parseFloat(value);\n        }\n        break;\n      case \"font-weight\":\n        if (elem instanceof Text) {\n          elem.weight = value;\n        }\n        break;\n      case \"font-style\":\n        if (elem instanceof Text) {\n          elem.style = value;\n        }\n        break;\n      case \"text-decoration\":\n        if (elem instanceof Text) {\n          elem.decoration = value;\n        }\n        break;\n      case \"line-height\":\n        if (elem instanceof Text) {\n          elem.leading = value;\n        }\n        break;\n    }\n  }\n  if (Object.keys(node.dataset).length) elem.dataset = node.dataset;\n  return styles;\n}\nfunction updateDefsCache(node, defsCache) {\n  for (let i = 0, l = node.childNodes.length; i < l; i++) {\n    const n = node.childNodes[i];\n    if (!n.id) continue;\n    const tagName = getTagName(node.nodeName);\n    if (tagName === \"#text\") continue;\n    defsCache.add(n.id, n);\n  }\n}\nfunction getScene(node) {\n  while (node.parent) {\n    node = node.parent;\n  }\n  return node.scene;\n}\nvar read = {\n  svg: function(node) {\n    const defs = read.defs.current = new Registry();\n    const elements = node.getElementsByTagName(\"defs\");\n    for (let i = 0; i < elements.length; i++) {\n      updateDefsCache(elements[i], defs);\n    }\n    const svg2 = read.g.call(this, node);\n    const viewBox = node.getAttribute(\"viewBox\");\n    const x = node.getAttribute(\"x\");\n    const y = node.getAttribute(\"y\");\n    const width = node.getAttribute(\"width\");\n    const height = node.getAttribute(\"height\");\n    svg2.defs = defs;\n    const viewBoxExists = viewBox !== null;\n    const xExists = x !== null;\n    const yExists = y !== null;\n    const widthExists = width !== null;\n    const heightExists = height !== null;\n    if (xExists) {\n      svg2.x = parseFloat(x.replace(regex2.unitSuffix, \"\"));\n    }\n    if (yExists) {\n      svg2.y = parseFloat(y.replace(regex2.unitSuffix, \"\"));\n    }\n    if (widthExists) {\n      svg2.width = parseFloat(width.replace(regex2.unitSuffix, \"\"));\n    }\n    if (heightExists) {\n      svg2.height = parseFloat(height.replace(regex2.unitSuffix, \"\"));\n    }\n    if (viewBoxExists) {\n      applySvgViewBox(svg2, viewBox);\n    }\n    delete read.defs.current;\n    return svg2;\n  },\n  defs: function(node) {\n    return null;\n  },\n  use: function(node, styles) {\n    let error;\n    const href = node.getAttribute(\"href\") || node.getAttribute(\"xlink:href\");\n    if (!href) {\n      error = new TwoError(\"encountered <use /> with no href.\");\n      console.warn(error.name, error.message);\n      return null;\n    }\n    const id = href.slice(1);\n    if (!read.defs.current.contains(id)) {\n      error = new TwoError(\n        \"unable to find element for reference \" + href + \".\"\n      );\n      console.warn(error.name, error.message);\n      return null;\n    }\n    const template = read.defs.current.get(id);\n    const fullNode = template.cloneNode(true);\n    for (let i = 0; i < node.attributes.length; i++) {\n      const attr = node.attributes[i];\n      const ca = overwriteAttrs.includes(attr.nodeName);\n      const cb = !fullNode.hasAttribute(attr.nodeName);\n      if (ca || cb) {\n        fullNode.setAttribute(attr.nodeName, attr.value);\n      }\n    }\n    const tagName = getTagName(fullNode.nodeName);\n    return read[tagName].call(this, fullNode, styles);\n  },\n  g: function(node, parentStyles) {\n    const group = new Group();\n    applySvgAttributes.call(this, node, group, parentStyles);\n    this.add(group);\n    const styles = getSvgStyles.call(this, node);\n    for (let i = 0, l = node.childNodes.length; i < l; i++) {\n      const n = node.childNodes[i];\n      const tag = n.nodeName;\n      if (!tag) return;\n      const tagName = getTagName(tag);\n      if (tagName in read) {\n        const o = read[tagName].call(group, n, styles);\n        if (!!o && !o.parent) {\n          group.add(o);\n        }\n      }\n    }\n    return group;\n  },\n  polygon: function(node, parentStyles) {\n    let points;\n    if (typeof node === \"string\") {\n      points = node;\n    } else {\n      points = node.getAttribute(\"points\");\n    }\n    const verts = [];\n    points.replace(\n      /(-?[\\d.eE-]+)[,|\\s](-?[\\d.eE-]+)/g,\n      function(match, p1, p2) {\n        verts.push(new Anchor(parseFloat(p1), parseFloat(p2)));\n      }\n    );\n    const poly = new Path(verts, true);\n    poly.stroke = \"none\";\n    poly.fill = \"black\";\n    applySvgAttributes.call(this, node, poly, parentStyles);\n    return poly;\n  },\n  polyline: function(node, parentStyles) {\n    const poly = read.polygon.call(this, node, parentStyles);\n    poly.closed = false;\n    return poly;\n  },\n  path: function(node, parentStyles) {\n    let path;\n    if (typeof node === \"string\") {\n      path = node;\n      node = null;\n    } else {\n      path = node.getAttribute(\"d\");\n    }\n    let points = [];\n    let closed2 = false, relative = false;\n    if (path) {\n      let coord = new Anchor();\n      let control, coords;\n      let commands = path.match(/[a-df-z][^a-df-z]*/gi);\n      const last = commands.length - 1;\n      _.each(commands.slice(0), function(command, i) {\n        const items = command.slice(1).trim().match(regex2.path);\n        const type = command[0];\n        const lower = type.toLowerCase();\n        let bin, j, l, ct, times;\n        const result = [];\n        if (i === 0) {\n          commands = [];\n        }\n        switch (lower) {\n          case \"h\":\n          case \"v\":\n            if (items.length > 1) {\n              bin = 1;\n            }\n            break;\n          case \"m\":\n          case \"l\":\n          case \"t\":\n            if (items.length > 2) {\n              bin = 2;\n            }\n            break;\n          case \"s\":\n          case \"q\":\n            if (items.length > 4) {\n              bin = 4;\n            }\n            break;\n          case \"c\":\n            if (items.length > 6) {\n              bin = 6;\n            }\n            break;\n          case \"a\":\n            if (items.length > 7) {\n              bin = 7;\n            }\n            break;\n        }\n        if (bin) {\n          for (j = 0, l = items.length, times = 0; j < l; j += bin) {\n            ct = type;\n            if (times > 0) {\n              switch (type) {\n                case \"m\":\n                  ct = \"l\";\n                  break;\n                case \"M\":\n                  ct = \"L\";\n                  break;\n              }\n            }\n            result.push(ct + items.slice(j, j + bin).join(\" \"));\n            times++;\n          }\n          commands = Array.prototype.concat.apply(commands, result);\n        } else {\n          commands.push(command);\n        }\n      });\n      _.each(commands, function(command, i) {\n        let result, x, y;\n        const type = command[0];\n        const lower = type.toLowerCase();\n        coords = command.slice(1).trim().match(regex2.path);\n        relative = type === lower;\n        let x1, y1, x2, y2, x3, y3, x4, y4, reflection;\n        let a, b;\n        let anchor2, rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n        switch (lower) {\n          case \"z\":\n            if (i >= last) {\n              closed2 = true;\n            } else {\n              x = coord.x;\n              y = coord.y;\n              result = new Anchor(\n                x,\n                y,\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                Commands.close\n              );\n              for (let j = points.length - 1; j >= 0; j--) {\n                const point = points[j];\n                if (/m/i.test(point.command)) {\n                  coord = point;\n                  break;\n                }\n              }\n            }\n            break;\n          case \"m\":\n          case \"l\":\n            control = void 0;\n            x = parseFloat(coords[0]);\n            y = parseFloat(coords[1]);\n            result = new Anchor(\n              x,\n              y,\n              void 0,\n              void 0,\n              void 0,\n              void 0,\n              /m/i.test(lower) ? Commands.move : Commands.line\n            );\n            if (relative) {\n              result.addSelf(coord);\n            }\n            coord = result;\n            break;\n          case \"h\":\n          case \"v\":\n            a = /h/i.test(lower) ? \"x\" : \"y\";\n            b = /x/i.test(a) ? \"y\" : \"x\";\n            result = new Anchor(\n              void 0,\n              void 0,\n              void 0,\n              void 0,\n              void 0,\n              void 0,\n              Commands.line\n            );\n            result[a] = parseFloat(coords[0]);\n            result[b] = coord[b];\n            if (relative) {\n              result[a] += coord[a];\n            }\n            coord = result;\n            break;\n          case \"c\":\n          case \"s\":\n            x1 = coord.x;\n            y1 = coord.y;\n            if (!control) {\n              control = new Vector();\n            }\n            if (/c/i.test(lower)) {\n              x2 = parseFloat(coords[0]);\n              y2 = parseFloat(coords[1]);\n              x3 = parseFloat(coords[2]);\n              y3 = parseFloat(coords[3]);\n              x4 = parseFloat(coords[4]);\n              y4 = parseFloat(coords[5]);\n            } else {\n              reflection = getReflection(coord, control, relative);\n              x2 = reflection.x;\n              y2 = reflection.y;\n              x3 = parseFloat(coords[0]);\n              y3 = parseFloat(coords[1]);\n              x4 = parseFloat(coords[2]);\n              y4 = parseFloat(coords[3]);\n            }\n            if (relative) {\n              x2 += x1;\n              y2 += y1;\n              x3 += x1;\n              y3 += y1;\n              x4 += x1;\n              y4 += y1;\n            }\n            coord.controls.right.set(x2 - coord.x, y2 - coord.y);\n            result = new Anchor(\n              x4,\n              y4,\n              x3 - x4,\n              y3 - y4,\n              void 0,\n              void 0,\n              Commands.curve\n            );\n            coord = result;\n            control = result.controls.left;\n            break;\n          case \"t\":\n          case \"q\":\n            x1 = coord.x;\n            y1 = coord.y;\n            if (!control) {\n              control = new Vector();\n            }\n            if (/q/i.test(lower)) {\n              x2 = parseFloat(coords[0]);\n              y2 = parseFloat(coords[1]);\n              x3 = parseFloat(coords[0]);\n              y3 = parseFloat(coords[1]);\n              x4 = parseFloat(coords[2]);\n              y4 = parseFloat(coords[3]);\n            } else {\n              reflection = getReflection(coord, control, relative);\n              x2 = reflection.x;\n              y2 = reflection.y;\n              x3 = reflection.x;\n              y3 = reflection.y;\n              x4 = parseFloat(coords[0]);\n              y4 = parseFloat(coords[1]);\n            }\n            if (relative) {\n              x2 += x1;\n              y2 += y1;\n              x3 += x1;\n              y3 += y1;\n              x4 += x1;\n              y4 += y1;\n            }\n            coord.controls.right.set(\n              (x2 - coord.x) * 0.33,\n              (y2 - coord.y) * 0.33\n            );\n            result = new Anchor(\n              x4,\n              y4,\n              x3 - x4,\n              y3 - y4,\n              void 0,\n              void 0,\n              Commands.curve\n            );\n            coord = result;\n            control = result.controls.left;\n            break;\n          case \"a\":\n            x1 = coord.x;\n            y1 = coord.y;\n            rx = parseFloat(coords[0]);\n            ry = parseFloat(coords[1]);\n            xAxisRotation = parseFloat(coords[2]);\n            largeArcFlag = parseFloat(coords[3]);\n            sweepFlag = parseFloat(coords[4]);\n            x4 = parseFloat(coords[5]);\n            y4 = parseFloat(coords[6]);\n            if (relative) {\n              x4 += x1;\n              y4 += y1;\n            }\n            anchor2 = new Anchor(x4, y4);\n            anchor2.command = Commands.arc;\n            anchor2.rx = rx;\n            anchor2.ry = ry;\n            anchor2.xAxisRotation = xAxisRotation;\n            anchor2.largeArcFlag = largeArcFlag;\n            anchor2.sweepFlag = sweepFlag;\n            result = anchor2;\n            coord = anchor2;\n            control = void 0;\n            break;\n        }\n        if (result) {\n          if (Array.isArray(result)) {\n            points = points.concat(result);\n          } else {\n            points.push(result);\n          }\n        }\n      });\n    }\n    path = new Path(points, closed2, void 0, true);\n    path.stroke = \"none\";\n    path.fill = \"black\";\n    const rect = path.getBoundingClientRect(true);\n    rect.centroid = {\n      x: rect.left + rect.width / 2,\n      y: rect.top + rect.height / 2\n    };\n    _.each(path.vertices, function(v) {\n      v.subSelf(rect.centroid);\n    });\n    applySvgAttributes.call(this, node, path, parentStyles);\n    path.translation.addSelf(rect.centroid);\n    return path;\n  },\n  circle: function(node, parentStyles) {\n    const x = parseFloat(node.getAttribute(\"cx\"));\n    const y = parseFloat(node.getAttribute(\"cy\"));\n    const r = parseFloat(node.getAttribute(\"r\"));\n    const circle = new Circle(0, 0, r);\n    circle.stroke = \"none\";\n    circle.fill = \"black\";\n    applySvgAttributes.call(this, node, circle, parentStyles);\n    circle.translation.x = x;\n    circle.translation.y = y;\n    return circle;\n  },\n  ellipse: function(node, parentStyles) {\n    const x = parseFloat(node.getAttribute(\"cx\"));\n    const y = parseFloat(node.getAttribute(\"cy\"));\n    const width = parseFloat(node.getAttribute(\"rx\"));\n    const height = parseFloat(node.getAttribute(\"ry\"));\n    const ellipse = new Ellipse(0, 0, width, height);\n    ellipse.stroke = \"none\";\n    ellipse.fill = \"black\";\n    applySvgAttributes.call(this, node, ellipse, parentStyles);\n    ellipse.translation.x = x;\n    ellipse.translation.y = y;\n    return ellipse;\n  },\n  rect: function(node, parentStyles) {\n    const rx = parseFloat(node.getAttribute(\"rx\"));\n    const ry = parseFloat(node.getAttribute(\"ry\"));\n    if (!_.isNaN(rx) || !_.isNaN(ry)) {\n      return read[\"rounded-rect\"](node);\n    }\n    const width = parseFloat(node.getAttribute(\"width\"));\n    const height = parseFloat(node.getAttribute(\"height\"));\n    const w2 = width / 2;\n    const h2 = height / 2;\n    const rect = new Rectangle(0, 0, width, height);\n    rect.stroke = \"none\";\n    rect.fill = \"black\";\n    applySvgAttributes.call(this, node, rect, parentStyles);\n    rect.translation.x += w2;\n    rect.translation.y += h2;\n    return rect;\n  },\n  \"rounded-rect\": function(node, parentStyles) {\n    const rx = parseFloat(node.getAttribute(\"rx\")) || 0;\n    const ry = parseFloat(node.getAttribute(\"ry\")) || 0;\n    const width = parseFloat(node.getAttribute(\"width\"));\n    const height = parseFloat(node.getAttribute(\"height\"));\n    const w2 = width / 2;\n    const h2 = height / 2;\n    const radius = new Vector(rx, ry);\n    const rect = new RoundedRectangle(0, 0, width, height, radius);\n    rect.stroke = \"none\";\n    rect.fill = \"black\";\n    applySvgAttributes.call(this, node, rect, parentStyles);\n    rect.translation.x += w2;\n    rect.translation.y += h2;\n    return rect;\n  },\n  line: function(node, parentStyles) {\n    const x1 = parseFloat(node.getAttribute(\"x1\"));\n    const y1 = parseFloat(node.getAttribute(\"y1\"));\n    const x2 = parseFloat(node.getAttribute(\"x2\"));\n    const y2 = parseFloat(node.getAttribute(\"y2\"));\n    const line = new Line(x1, y1, x2, y2).noFill();\n    applySvgAttributes.call(this, node, line, parentStyles);\n    return line;\n  },\n  lineargradient: function(node, parentStyles) {\n    let units = node.getAttribute(\"gradientUnits\");\n    let spread = node.getAttribute(\"spreadMethod\");\n    if (!units) {\n      units = \"objectBoundingBox\";\n    }\n    if (!spread) {\n      spread = \"pad\";\n    }\n    let x1 = parseFloat(node.getAttribute(\"x1\") || 0);\n    let y1 = parseFloat(node.getAttribute(\"y1\") || 0);\n    let x2 = parseFloat(node.getAttribute(\"x2\") || 0);\n    let y2 = parseFloat(node.getAttribute(\"y2\") || 0);\n    const ox = (x2 + x1) / 2;\n    const oy = (y2 + y1) / 2;\n    if (/userSpaceOnUse/i.test(units)) {\n      x1 -= ox;\n      y1 -= oy;\n      x2 -= ox;\n      y2 -= oy;\n    }\n    const stops = [];\n    for (let i = 0; i < node.children.length; i++) {\n      const child = node.children[i];\n      let offset = child.getAttribute(\"offset\");\n      if (/%/gi.test(offset)) {\n        offset = parseFloat(offset.replace(/%/gi, \"\")) / 100;\n      }\n      offset = parseFloat(offset);\n      let color = child.getAttribute(\"stop-color\");\n      let opacity = child.getAttribute(\"stop-opacity\");\n      let style = child.getAttribute(\"style\");\n      let matches;\n      if (color === null) {\n        matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n        color = matches && matches.length > 1 ? matches[1] : void 0;\n      }\n      if (opacity === null) {\n        matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n        opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n      } else {\n        opacity = parseFloat(opacity);\n      }\n      stops.push(new Stop(offset, color, opacity));\n    }\n    const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n    gradient.spread = spread;\n    gradient.units = units;\n    applySvgAttributes.call(this, node, gradient, parentStyles);\n    return gradient;\n  },\n  radialgradient: function(node, parentStyles) {\n    let units = node.getAttribute(\"gradientUnits\");\n    let spread = node.getAttribute(\"spreadMethod\");\n    if (!units) {\n      units = \"objectBoundingBox\";\n    }\n    if (!spread) {\n      spread = \"pad\";\n    }\n    let cx = parseFloat(node.getAttribute(\"cx\")) || 0;\n    let cy = parseFloat(node.getAttribute(\"cy\")) || 0;\n    let r = parseFloat(node.getAttribute(\"r\"));\n    let fx = parseFloat(node.getAttribute(\"fx\"));\n    let fy = parseFloat(node.getAttribute(\"fy\"));\n    if (_.isNaN(fx)) {\n      fx = cx;\n    }\n    if (_.isNaN(fy)) {\n      fy = cy;\n    }\n    const ox = Math.abs(cx + fx) / 2;\n    const oy = Math.abs(cy + fy) / 2;\n    if (/userSpaceOnUse/i.test(units)) {\n      cx -= ox;\n      cy -= oy;\n      fx -= ox;\n      fy -= oy;\n    }\n    const stops = [];\n    for (let i = 0; i < node.children.length; i++) {\n      const child = node.children[i];\n      let offset = child.getAttribute(\"offset\");\n      if (/%/gi.test(offset)) {\n        offset = parseFloat(offset.replace(/%/gi, \"\")) / 100;\n      }\n      offset = parseFloat(offset);\n      let color = child.getAttribute(\"stop-color\");\n      let opacity = child.getAttribute(\"stop-opacity\");\n      let style = child.getAttribute(\"style\");\n      let matches;\n      if (color === null) {\n        matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n        color = matches && matches.length > 1 ? matches[1] : void 0;\n      }\n      if (opacity === null) {\n        matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n        opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n      } else {\n        opacity = parseFloat(opacity);\n      }\n      stops.push(new Stop(offset, color, opacity));\n    }\n    const gradient = new RadialGradient(cx, cy, r, stops, fx, fy);\n    gradient.spread = spread;\n    gradient.units = units;\n    applySvgAttributes.call(this, node, gradient, parentStyles);\n    return gradient;\n  },\n  text: function(node, parentStyles) {\n    const alignment = getAlignment(node.getAttribute(\"text-anchor\")) || \"left\";\n    const baseline = getBaseline(node) || \"baseline\";\n    let message = \"\";\n    if (node.childNodes.length > 0 && node.childNodes[0].tagName === \"TSPAN\") {\n      message = node.childNodes[0].textContent;\n    } else {\n      message = node.textContent;\n    }\n    const text = new Text(message);\n    applySvgAttributes.call(this, node, text, parentStyles);\n    text.alignment = alignment;\n    text.baseline = baseline;\n    return text;\n  },\n  clippath: function(node, parentStyles) {\n    if (read.defs.current && !read.defs.current.contains(node.id)) {\n      read.defs.current.add(node.id, node);\n    }\n    return null;\n  },\n  image: function(node, parentStyles) {\n    let error;\n    const href = node.getAttribute(\"href\") || node.getAttribute(\"xlink:href\");\n    if (!href) {\n      error = new TwoError(\"encountered <image /> with no href.\");\n      console.warn(error.name, error.message);\n      return null;\n    }\n    const x = parseFloat(node.getAttribute(\"x\")) || 0;\n    const y = parseFloat(node.getAttribute(\"y\")) || 0;\n    const width = parseFloat(node.getAttribute(\"width\"));\n    const height = parseFloat(node.getAttribute(\"height\"));\n    const sprite = new Sprite(href, x, y);\n    if (!_.isNaN(width)) {\n      sprite.width = width;\n    }\n    if (!_.isNaN(height)) {\n      sprite.height = height;\n    }\n    applySvgAttributes.call(this, node, sprite, parentStyles);\n    return sprite;\n  }\n};\n\n// src/utils/xhr.js\nfunction xhr(path, callback) {\n  const xhr2 = new XMLHttpRequest();\n  xhr2.open(\"GET\", path);\n  xhr2.onreadystatechange = function() {\n    if (xhr2.readyState === 4 && xhr2.status === 200) {\n      callback(xhr2.responseText);\n    }\n  };\n  xhr2.send();\n  return xhr2;\n}\n\n// src/effects/image.js\nvar _Image = class _Image extends Rectangle {\n  constructor(src, ox, oy, width, height, mode) {\n    super(ox, oy, width || 1, height || 1);\n    /**\n     * @name Two.Image#_flagTexture\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Image#texture} needs updating.\n     */\n    __publicField(this, \"_flagTexture\", false);\n    /**\n     * @name Two.Image#_flagMode\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Image#mode} needs updating.\n     */\n    __publicField(this, \"_flagMode\", false);\n    /**\n     * @name Two.Image#_texture\n     * @private\n     * @see {@link Two.Image#texture}\n     */\n    __publicField(this, \"_texture\", null);\n    /**\n     * @name Two.Image#_mode\n     * @private\n     * @see {@link Two.Image#mode}\n     */\n    __publicField(this, \"_mode\", \"fill\");\n    this._renderer.type = \"image\";\n    for (let prop in proto24) {\n      Object.defineProperty(this, prop, proto24[prop]);\n    }\n    this.noStroke();\n    this.noFill();\n    if (src instanceof Texture) {\n      this.texture = src;\n    } else if (typeof src === \"string\") {\n      this.texture = new Texture(src);\n    }\n    if (typeof mode === \"string\") {\n      this.mode = mode;\n    }\n    this._update();\n  }\n  /**\n   * @name Two.Image.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Image} to create a new instance\n   * @returns {Two.Image}\n   * @description Create a new {@link Two.Image} from an object notation of a {@link Two.Image}.\n   * @nota-bene Works in conjunction with {@link Two.Image#toObject}\n   */\n  static fromObject(obj) {\n    const image = new _Image().copy(obj);\n    if (\"id\" in obj) {\n      image.id = obj.id;\n    }\n    return image;\n  }\n  /**\n   * @name Two.Image#copy\n   * @function\n   * @param {Two.Image} image - The reference {@link Two.Image}\n   * @description Copy the properties of one {@link Two.Image} onto another.\n   */\n  copy(image) {\n    super.copy.call(this, image);\n    for (let i = 0; i < _Image.Properties.length; i++) {\n      const k = _Image.Properties[i];\n      if (k in image) {\n        this[k] = image[k];\n      }\n    }\n    return this;\n  }\n  /**\n   * @name Two.Image#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Image}\n   * @description Create a new instance of {@link Two.Image} with the same properties of the current image.\n   */\n  clone(parent) {\n    const clone = new _Image(\n      this.texture,\n      this.translation.x,\n      this.translation.y,\n      this.width,\n      this.height\n    );\n    if (parent) {\n      parent.add(clone);\n    }\n    return clone;\n  }\n  /**\n   * @name Two.Image#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the image.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = \"image\";\n    object.texture = this.texture.toObject();\n    object.mode = this.mode;\n    return object;\n  }\n  /**\n   * @name Two.Image#dispose\n   * @function\n   * @returns {Two.Image}\n   * @description Release the image's renderer resources and detach all events.\n   * This method disposes the texture (calling dispose() for thorough cleanup) and inherits comprehensive\n   * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n   * for potential re-attachment.\n   */\n  dispose() {\n    super.dispose();\n    if (this._texture && typeof this._texture.dispose === \"function\") {\n      this._texture.dispose();\n    } else if (this._texture && typeof this._texture.unbind === \"function\") {\n      this._texture.unbind();\n    }\n    return this;\n  }\n  /**\n   * @name Two.Image#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    const effect = this._texture;\n    if (effect) {\n      if (this._flagTexture) {\n        this.fill = effect;\n      }\n      if (effect.loaded) {\n        const iw = effect.image.width;\n        const ih = effect.image.height;\n        const rw = this.width;\n        const rh = this.height;\n        const scaleX = rw / iw;\n        const scaleY = rh / ih;\n        switch (this._mode) {\n          case _Image.Modes.fill: {\n            const scale = Math.max(scaleX, scaleY);\n            effect.scale = scale;\n            effect.offset.x = 0;\n            effect.offset.y = 0;\n            effect.repeat = \"repeat\";\n            break;\n          }\n          case _Image.Modes.fit: {\n            const scale = Math.min(scaleX, scaleY);\n            effect.scale = scale;\n            effect.offset.x = 0;\n            effect.offset.y = 0;\n            effect.repeat = \"no-repeat\";\n            break;\n          }\n          case _Image.Modes.crop: {\n            break;\n          }\n          case _Image.Modes.tile: {\n            effect.offset.x = (iw - rw) / 2;\n            effect.offset.y = (ih - rh) / 2;\n            effect.repeat = \"repeat\";\n            break;\n          }\n          case _Image.Modes.stretch:\n          default: {\n            effect.scale = new Vector(scaleX, scaleY);\n            effect.offset.x = 0;\n            effect.offset.y = 0;\n            effect.repeat = \"repeat\";\n          }\n        }\n      }\n    }\n    super._update.call(this);\n    return this;\n  }\n  /**\n   * @name Two.Image#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    super.flagReset.call(this);\n    this._flagTexture = this._flagMode = false;\n    return this;\n  }\n};\n/**\n * @name Two.Image.Modes\n * @property {Object} Modes - Different mode types to render an image inspired by Figma.\n * @property {String} Modes.fill - Scale image to fill the bounds while preserving aspect ratio.\n * @property {String} Modes.fit - Scale image to fit within bounds while preserving aspect ratio.\n * @property {String} Modes.crop - Scale image to fill bounds while preserving aspect ratio, cropping excess.\n * @property {String} Modes.tile - Repeat image at original size to fill the bounds.\n * @property {String} Modes.stretch - Stretch image to fill dimensions, ignoring aspect ratio.\n */\n__publicField(_Image, \"Modes\", {\n  fill: \"fill\",\n  fit: \"fit\",\n  crop: \"crop\",\n  tile: \"tile\",\n  stretch: \"stretch\"\n});\n/**\n * @name Two.Image.Properties\n * @property {String[]} - A list of properties that are on every {@link Two.Image}.\n */\n__publicField(_Image, \"Properties\", [\"texture\", \"mode\"]);\nvar Image2 = _Image;\nvar proto24 = {\n  texture: {\n    enumerable: true,\n    get: function() {\n      return this._texture;\n    },\n    set: function(v) {\n      this._texture = v;\n      this._flagTexture = true;\n    }\n  },\n  mode: {\n    enumerable: true,\n    get: function() {\n      return this._mode;\n    },\n    set: function(v) {\n      this._mode = v;\n      this._flagMode = true;\n    }\n  }\n};\n\n// src/renderers/canvas.js\nvar emptyArray = [];\nvar max4 = Math.max;\nvar min4 = Math.min;\nvar abs = Math.abs;\nvar sin6 = Math.sin;\nvar cos6 = Math.cos;\nvar acos = Math.acos;\nvar sqrt = Math.sqrt;\nvar canvas2 = {\n  isHidden: /(undefined|none|transparent)/i,\n  alignments: {\n    left: \"start\",\n    middle: \"center\",\n    right: \"end\"\n  },\n  baselines: {\n    top: \"top\",\n    middle: \"middle\",\n    bottom: \"bottom\",\n    baseline: \"alphabetic\"\n  },\n  getRendererType: function(type) {\n    return type in canvas2 ? type : \"path\";\n  },\n  group: {\n    renderChild: function(child) {\n      const prop = canvas2.getRendererType(child._renderer.type);\n      canvas2[prop].render.call(child, this.ctx, true, this.clip);\n    },\n    render: function(ctx) {\n      if (!this._visible) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const matrix = this._matrix.elements;\n      const parent = this.parent;\n      this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1);\n      const mask = this._mask;\n      const defaultMatrix = isDefaultMatrix(matrix);\n      const shouldIsolate = !defaultMatrix || !!mask;\n      if (!this._renderer.context) {\n        this._renderer.context = {};\n      }\n      this._renderer.context.ctx = ctx;\n      if (shouldIsolate) {\n        ctx.save();\n        if (!defaultMatrix) {\n          ctx.transform(\n            matrix[0],\n            matrix[3],\n            matrix[1],\n            matrix[4],\n            matrix[2],\n            matrix[5]\n          );\n        }\n      }\n      if (mask) {\n        const prop = canvas2.getRendererType(mask._renderer.type);\n        canvas2[prop].render.call(mask, ctx, true);\n      }\n      if (this._opacity > 0 && this._scale !== 0) {\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          const prop = canvas2.getRendererType(child._renderer.type);\n          canvas2[prop].render.call(child, ctx);\n        }\n      }\n      if (shouldIsolate) {\n        ctx.restore();\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  path: {\n    render: function(ctx, forced, parentClipped) {\n      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;\n      po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n      mask = this._mask;\n      clip = this._clip;\n      opacity = this._opacity * (po || 1);\n      visible = this._visible;\n      if (!forced && (!visible || clip || opacity === 0)) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      matrix = this._matrix.elements;\n      stroke = this._stroke;\n      linewidth = this._linewidth;\n      fill = this._fill;\n      cap = this._cap;\n      join = this._join;\n      miter = this._miter;\n      closed2 = this._closed;\n      commands = this._renderer.vertices;\n      length = commands.length;\n      last = length - 1;\n      defaultMatrix = isDefaultMatrix(matrix);\n      dashes = this.dashes;\n      if (!defaultMatrix) {\n        ctx.save();\n        ctx.transform(\n          matrix[0],\n          matrix[3],\n          matrix[1],\n          matrix[4],\n          matrix[2],\n          matrix[5]\n        );\n      }\n      if (mask) {\n        const prop = canvas2.getRendererType(mask._renderer.type);\n        canvas2[prop].render.call(mask, ctx, true);\n      }\n      if (fill) {\n        if (typeof fill === \"string\") {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = canvas2.getRendererType(fill._renderer.type);\n          canvas2[prop].render.call(fill, ctx, this);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === \"string\") {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = canvas2.getRendererType(stroke._renderer.type);\n          canvas2[prop].render.call(stroke, ctx, this);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(this);\n        }\n        if (miter) {\n          ctx.miterLimit = miter;\n        }\n        if (join) {\n          ctx.lineJoin = join;\n        }\n        if (!closed2 && cap) {\n          ctx.lineCap = cap;\n        }\n      }\n      if (typeof opacity === \"number\") {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n      ctx.beginPath();\n      let rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n      for (let i = 0; i < length; i++) {\n        b = commands[i];\n        x = b.x;\n        y = b.y;\n        switch (b.command) {\n          case Commands.close:\n            ctx.closePath();\n            break;\n          case Commands.arc:\n            rx = b.rx;\n            ry = b.ry;\n            xAxisRotation = b.xAxisRotation;\n            largeArcFlag = b.largeArcFlag;\n            sweepFlag = b.sweepFlag;\n            prev = closed2 ? mod(i - 1, length) : max4(i - 1, 0);\n            a = commands[prev];\n            ax = a.x;\n            ay = a.y;\n            canvas2.renderSvgArcCommand(\n              ctx,\n              ax,\n              ay,\n              rx,\n              ry,\n              largeArcFlag,\n              sweepFlag,\n              xAxisRotation,\n              x,\n              y\n            );\n            break;\n          case Commands.curve:\n            prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n            a = commands[prev];\n            ar = a.controls && a.controls.right || Vector.zero;\n            bl = b.controls && b.controls.left || Vector.zero;\n            if (a._relative) {\n              vx = ar.x + a.x;\n              vy = ar.y + a.y;\n            } else {\n              vx = ar.x;\n              vy = ar.y;\n            }\n            if (b._relative) {\n              ux = bl.x + b.x;\n              uy = bl.y + b.y;\n            } else {\n              ux = bl.x;\n              uy = bl.y;\n            }\n            ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n            if (i >= last && closed2) {\n              c = d;\n              br = b.controls && b.controls.right || Vector.zero;\n              cl = c.controls && c.controls.left || Vector.zero;\n              if (b._relative) {\n                vx = br.x + b.x;\n                vy = br.y + b.y;\n              } else {\n                vx = br.x;\n                vy = br.y;\n              }\n              if (c._relative) {\n                ux = cl.x + c.x;\n                uy = cl.y + c.y;\n              } else {\n                ux = cl.x;\n                uy = cl.y;\n              }\n              x = c.x;\n              y = c.y;\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n            }\n            break;\n          case Commands.line:\n            ctx.lineTo(x, y);\n            break;\n          case Commands.move:\n            d = b;\n            ctx.moveTo(x, y);\n            break;\n        }\n      }\n      if (closed2) {\n        ctx.closePath();\n      }\n      if (!clip && !parentClipped) {\n        if (!canvas2.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!canvas2.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n      }\n      if (!defaultMatrix) {\n        ctx.restore();\n      }\n      if (clip && !parentClipped) {\n        ctx.clip();\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.setLineDash(emptyArray);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  points: {\n    render: function(ctx, forced, parentClipped) {\n      let me, stroke, linewidth, fill, opacity, visible, size, commands, length, b, x, y, defaultMatrix, isOffset, dashes, po;\n      po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n      opacity = this._opacity * (po || 1);\n      visible = this._visible;\n      if (!forced && (!visible || opacity === 0)) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      me = this._matrix.elements;\n      stroke = this._stroke;\n      linewidth = this._linewidth;\n      fill = this._fill;\n      commands = this._renderer.collection;\n      length = commands.length;\n      defaultMatrix = isDefaultMatrix(me);\n      dashes = this.dashes;\n      size = this._size;\n      if (!defaultMatrix) {\n        ctx.save();\n        ctx.transform(me[0], me[3], me[1], me[4], me[2], me[5]);\n      }\n      if (fill) {\n        if (typeof fill === \"string\") {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = canvas2.getRendererType(fill._renderer.type);\n          canvas2[prop].render.call(fill, ctx, this);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === \"string\") {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = canvas2.getRendererType(stroke._renderer.type);\n          canvas2[prop].render.call(stroke, ctx, this);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(this);\n        }\n      }\n      if (typeof opacity === \"number\") {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n      ctx.beginPath();\n      let radius = size * 0.5, m;\n      if (!this._sizeAttenuation) {\n        m = this.worldMatrix.elements;\n        m = decomposeMatrix(m[0], m[3], m[1], m[4], m[2], m[5]);\n        radius /= Math.max(m.scaleX, m.scaleY);\n      }\n      for (let i = 0; i < length; i++) {\n        b = commands[i];\n        x = b.x;\n        y = b.y;\n        ctx.moveTo(x + radius, y);\n        ctx.arc(x, y, radius, 0, TWO_PI);\n      }\n      if (!parentClipped) {\n        if (!canvas2.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!canvas2.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n      }\n      if (!defaultMatrix) {\n        ctx.restore();\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.setLineDash(emptyArray);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  text: {\n    render: function(ctx, forced, parentClipped) {\n      const po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n      const opacity = this._opacity * po;\n      const visible = this._visible;\n      const mask = this._mask;\n      const clip = this._clip;\n      if (!forced && (!visible || clip || opacity === 0)) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const matrix = this._matrix.elements;\n      const stroke = this._stroke;\n      const linewidth = this._linewidth;\n      const fill = this._fill;\n      const decoration = this._decoration;\n      const direction = this._direction;\n      const defaultMatrix = isDefaultMatrix(matrix);\n      const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;\n      const dashes = this.dashes;\n      const alignment = canvas2.alignments[this._alignment] || this._alignment;\n      const baseline = canvas2.baselines[this._baseline] || this._baseline;\n      let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n      if (!defaultMatrix) {\n        ctx.save();\n        ctx.transform(\n          matrix[0],\n          matrix[3],\n          matrix[1],\n          matrix[4],\n          matrix[2],\n          matrix[5]\n        );\n      }\n      if (mask) {\n        const prop = canvas2.getRendererType(mask._renderer.type);\n        canvas2[prop].render.call(mask, ctx, true);\n      }\n      if (!isOffset) {\n        ctx.font = [\n          this._style,\n          this._weight,\n          this._size + \"px/\" + this._leading + \"px\",\n          this._family\n        ].join(\" \");\n      }\n      ctx.textAlign = alignment;\n      ctx.textBaseline = baseline;\n      ctx.direction = direction;\n      if (fill) {\n        if (typeof fill === \"string\") {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = canvas2.getRendererType(fill._renderer.type);\n          canvas2[prop].render.call(fill, ctx, this);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === \"string\") {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = canvas2.getRendererType(stroke._renderer.type);\n          canvas2[prop].render.call(stroke, ctx, this);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(this);\n        }\n      }\n      if (typeof opacity === \"number\") {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n      if (!clip && !parentClipped) {\n        if (!canvas2.isHidden.test(fill)) {\n          if (fill._renderer && fill._renderer.offset) {\n            sx = fill._renderer.scale.x;\n            sy = fill._renderer.scale.y;\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(sx, sy);\n            a = this._size / fill._renderer.scale.y;\n            b = this._leading / fill._renderer.scale.y;\n            ctx.font = [\n              this._style,\n              this._weight,\n              a + \"px/\",\n              b + \"px\",\n              this._family\n            ].join(\" \");\n            c = fill._renderer.offset.x / fill._renderer.scale.x;\n            d = fill._renderer.offset.y / fill._renderer.scale.y;\n            ctx.fillText(this.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.fillText(this.value, 0, 0);\n          }\n        }\n        if (!canvas2.isHidden.test(stroke)) {\n          if (stroke._renderer && stroke._renderer.offset) {\n            sx = stroke._renderer.scale.x;\n            sy = stroke._renderer.scale.y;\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(sx, sy);\n            a = this._size / stroke._renderer.scale.y;\n            b = this._leading / stroke._renderer.scale.y;\n            ctx.font = [\n              this._style,\n              this._weight,\n              a + \"px/\",\n              b + \"px\",\n              this._family\n            ].join(\" \");\n            c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n            d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n            e = linewidth / stroke._renderer.scale.x;\n            ctx.lineWidth = e;\n            ctx.strokeText(this.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.strokeText(this.value, 0, 0);\n          }\n        }\n      }\n      if (/(underline|strikethrough)/i.test(decoration)) {\n        const metrics = ctx.measureText(this.value);\n        let scalar = 1;\n        switch (decoration) {\n          case \"underline\":\n            y1 = metrics.actualBoundingBoxDescent;\n            y2 = metrics.actualBoundingBoxDescent;\n            break;\n          case \"strikethrough\":\n            y1 = 0;\n            y2 = 0;\n            scalar = 0.5;\n            break;\n        }\n        switch (baseline) {\n          case \"top\":\n            y1 += this._size * scalar;\n            y2 += this._size * scalar;\n            break;\n          case \"baseline\":\n          case \"bottom\":\n            y1 -= this._size * scalar;\n            y2 -= this._size * scalar;\n            break;\n        }\n        switch (alignment) {\n          case \"left\":\n          case \"start\":\n            x1 = 0;\n            x2 = metrics.width;\n            break;\n          case \"right\":\n          case \"end\":\n            x1 = -metrics.width;\n            x2 = 0;\n            break;\n          default:\n            x1 = -metrics.width / 2;\n            x2 = metrics.width / 2;\n        }\n        ctx.lineWidth = Math.max(Math.floor(this._size / 15), 1);\n        ctx.strokeStyle = ctx.fillStyle;\n        ctx.beginPath();\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n        ctx.stroke();\n      }\n      if (!defaultMatrix) {\n        ctx.restore();\n      }\n      if (clip && !parentClipped) {\n        ctx.clip();\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.setLineDash(emptyArray);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  \"linear-gradient\": {\n    render: function(ctx, parent) {\n      if (!parent) {\n        return;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) {\n        let rect;\n        let lx = this.left._x;\n        let ly = this.left._y;\n        let rx = this.right._x;\n        let ry = this.right._y;\n        if (/objectBoundingBox/i.test(this._units)) {\n          rect = parent.getBoundingClientRect(true);\n          lx = (lx - 0.5) * rect.width;\n          ly = (ly - 0.5) * rect.height;\n          rx = (rx - 0.5) * rect.width;\n          ry = (ry - 0.5) * rect.height;\n        }\n        this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  \"radial-gradient\": {\n    render: function(ctx, parent) {\n      if (!parent) {\n        return;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) {\n        let rect;\n        let cx = this.center._x;\n        let cy = this.center._y;\n        let fx = this.focal._x;\n        let fy = this.focal._y;\n        let radius = this._radius;\n        if (/objectBoundingBox/i.test(this._units)) {\n          rect = parent.getBoundingClientRect(true);\n          cx = (cx - 0.5) * rect.width * 0.5;\n          cy = (cy - 0.5) * rect.height * 0.5;\n          fx = (fx - 0.5) * rect.width * 0.5;\n          fy = (fy - 0.5) * rect.height * 0.5;\n          radius *= Math.min(rect.width, rect.height);\n        }\n        this._renderer.effect = ctx.createRadialGradient(\n          cx,\n          cy,\n          0,\n          fx,\n          fy,\n          radius\n        );\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  texture: {\n    render: function(ctx) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const image = this.image;\n      if (!this._renderer.effect || (this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) {\n        this._renderer.effect = ctx.createPattern(this.image, this._repeat);\n      }\n      if (this._flagOffset || this._flagLoaded || this._flagScale) {\n        if (!(this._renderer.offset instanceof Vector)) {\n          this._renderer.offset = new Vector();\n        }\n        this._renderer.offset.x = -this._offset.x;\n        this._renderer.offset.y = -this._offset.y;\n        if (image) {\n          this._renderer.offset.x += image.width / 2;\n          this._renderer.offset.y += image.height / 2;\n          if (this._scale instanceof Vector) {\n            this._renderer.offset.x *= this._scale.x;\n            this._renderer.offset.y *= this._scale.y;\n          } else {\n            this._renderer.offset.x *= this._scale;\n            this._renderer.offset.y *= this._scale;\n          }\n        }\n      }\n      if (this._flagScale || this._flagLoaded) {\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        if (this._scale instanceof Vector) {\n          this._renderer.scale.copy(this._scale);\n        } else {\n          this._renderer.scale.set(this._scale, this._scale);\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  renderSvgArcCommand: function(ctx, ax, ay, rx, ry, largeArcFlag, sweepFlag, xAxisRotation, x, y) {\n    xAxisRotation = xAxisRotation * Math.PI / 180;\n    rx = abs(rx);\n    ry = abs(ry);\n    const dx2 = (ax - x) / 2;\n    const dy2 = (ay - y) / 2;\n    const x1p = cos6(xAxisRotation) * dx2 + sin6(xAxisRotation) * dy2;\n    const y1p = -sin6(xAxisRotation) * dx2 + cos6(xAxisRotation) * dy2;\n    const x1ps = x1p * x1p;\n    const y1ps = y1p * y1p;\n    let rxs = rx * rx;\n    let rys = ry * ry;\n    const cr = x1ps / rxs + y1ps / rys;\n    if (cr > 1) {\n      const s = sqrt(cr);\n      rx = s * rx;\n      ry = s * ry;\n      rxs = rx * rx;\n      rys = ry * ry;\n    }\n    const dq = rxs * y1ps + rys * x1ps;\n    const pq = (rxs * rys - dq) / dq;\n    let q = sqrt(max4(0, pq));\n    if (largeArcFlag === sweepFlag) q = -q;\n    const cxp = q * rx * y1p / ry;\n    const cyp = -q * ry * x1p / rx;\n    const cx = cos6(xAxisRotation) * cxp - sin6(xAxisRotation) * cyp + (ax + x) / 2;\n    const cy = sin6(xAxisRotation) * cxp + cos6(xAxisRotation) * cyp + (ay + y) / 2;\n    const startAngle = svgAngle2(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n    const delta = svgAngle2(\n      (x1p - cxp) / rx,\n      (y1p - cyp) / ry,\n      (-x1p - cxp) / rx,\n      (-y1p - cyp) / ry\n    ) % TWO_PI;\n    const endAngle = startAngle + delta;\n    const clockwise = sweepFlag === 0;\n    renderArcEstimate(\n      ctx,\n      cx,\n      cy,\n      rx,\n      ry,\n      startAngle,\n      endAngle,\n      clockwise,\n      xAxisRotation\n    );\n  }\n};\nvar Renderer = class extends Events {\n  constructor(params) {\n    super();\n    const smoothing = params.smoothing !== false;\n    this.domElement = params.domElement || document.createElement(\"canvas\");\n    this.ctx = this.domElement.getContext(\"2d\");\n    this.overdraw = params.overdraw || false;\n    if (typeof this.ctx.imageSmoothingEnabled !== \"undefined\") {\n      this.ctx.imageSmoothingEnabled = smoothing;\n    }\n    this.scene = new Group();\n    this.scene.parent = this;\n  }\n  /**\n   * @name Two.CanvasRenderer#setSize\n   * @function\n   * @fires resize\n   * @param {Number} width - The new width of the renderer.\n   * @param {Number} height - The new height of the renderer.\n   * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n   * @description Change the size of the renderer.\n   */\n  setSize(width, height, ratio) {\n    this.width = width;\n    this.height = height;\n    this.ratio = typeof ratio === \"undefined\" ? getRatio(this.ctx) : ratio;\n    this.domElement.width = width * this.ratio;\n    this.domElement.height = height * this.ratio;\n    if (this.domElement.style) {\n      _.extend(this.domElement.style, {\n        width: width + \"px\",\n        height: height + \"px\"\n      });\n    }\n    return this.trigger(Events.Types.resize, width, height, ratio);\n  }\n  /**\n   * @name Two.CanvasRenderer#render\n   * @function\n   * @description Render the current scene to the `<canvas />`.\n   */\n  render() {\n    const isOne = this.ratio === 1;\n    if (!isOne) {\n      this.ctx.save();\n      this.ctx.scale(this.ratio, this.ratio);\n    }\n    if (!this.overdraw) {\n      this.ctx.clearRect(0, 0, this.width, this.height);\n    }\n    canvas2.group.render.call(this.scene, this.ctx);\n    if (!isOne) {\n      this.ctx.restore();\n    }\n    return this;\n  }\n};\n/**\n * @name Two.CanvasRenderer.Utils\n * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />`.\n */\n__publicField(Renderer, \"Utils\", canvas2);\nfunction renderArcEstimate(ctx, ox, oy, rx, ry, startAngle, endAngle, clockwise, xAxisRotation) {\n  const delta = endAngle - startAngle;\n  const epsilon = Curve.Tolerance.epsilon;\n  const samePoints = Math.abs(delta) < epsilon;\n  let deltaAngle = mod(delta, TWO_PI);\n  if (deltaAngle < epsilon) {\n    if (samePoints) {\n      deltaAngle = 0;\n    } else {\n      deltaAngle = TWO_PI;\n    }\n  }\n  if (clockwise === true && !samePoints) {\n    if (deltaAngle === TWO_PI) {\n      deltaAngle = -TWO_PI;\n    } else {\n      deltaAngle = deltaAngle - TWO_PI;\n    }\n  }\n  for (let i = 0; i < Constants.Resolution; i++) {\n    const t = i / (Constants.Resolution - 1);\n    const angle = startAngle + t * deltaAngle;\n    let x = ox + rx * Math.cos(angle);\n    let y = oy + ry * Math.sin(angle);\n    if (xAxisRotation !== 0) {\n      const cos7 = Math.cos(xAxisRotation);\n      const sin7 = Math.sin(xAxisRotation);\n      const tx = x - ox;\n      const ty = y - oy;\n      x = tx * cos7 - ty * sin7 + ox;\n      y = tx * sin7 + ty * cos7 + oy;\n    }\n    ctx.lineTo(x, y);\n  }\n}\nfunction svgAngle2(ux, uy, vx, vy) {\n  const dot = ux * vx + uy * vy;\n  const len = sqrt(ux * ux + uy * uy) * sqrt(vx * vx + vy * vy);\n  let ang = acos(max4(-1, min4(1, dot / len)));\n  if (ux * vy - uy * vx < 0) {\n    ang = -ang;\n  }\n  return ang;\n}\nfunction isDefaultMatrix(m) {\n  return m[0] === 1 && m[3] === 0 && m[1] === 0 && m[4] === 1 && m[2] === 0 && m[5] === 0;\n}\n\n// src/renderers/svg.js\nvar svg = {\n  version: 1.1,\n  ns: \"http://www.w3.org/2000/svg\",\n  xlink: \"http://www.w3.org/1999/xlink\",\n  alignments: {\n    left: \"start\",\n    center: \"middle\",\n    right: \"end\"\n  },\n  baselines: {\n    top: \"hanging\",\n    middle: \"middle\",\n    bottom: \"ideographic\",\n    baseline: \"alphabetic\"\n  },\n  // Create an svg namespaced element.\n  createElement: function(name, attrs) {\n    const tag = name;\n    const elem = document.createElementNS(svg.ns, tag);\n    if (tag === \"svg\") {\n      attrs = _.defaults(attrs || {}, {\n        version: svg.version\n      });\n    }\n    if (attrs && Object.keys(attrs).length > 0) {\n      svg.setAttributes(elem, attrs);\n    }\n    return elem;\n  },\n  // Add attributes from an svg element.\n  setAttributes: function(elem, attrs) {\n    const keys = Object.keys(attrs);\n    for (let i = 0; i < keys.length; i++) {\n      if (/href/.test(keys[i])) {\n        elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]);\n      } else {\n        elem.setAttribute(keys[i], attrs[keys[i]]);\n      }\n    }\n    return this;\n  },\n  // Remove attributes from an svg element.\n  removeAttributes: function(elem, attrs) {\n    for (let key in attrs) {\n      elem.removeAttribute(key);\n    }\n    return this;\n  },\n  // Turn a set of vertices into a string for the d property of a path\n  // element. It is imperative that the string collation is as fast as\n  // possible, because this call will be happening multiple times a\n  // second.\n  toString: function(points, closed2) {\n    let l = points.length, last = l - 1, d, string = \"\";\n    for (let i = 0; i < l; i++) {\n      const b = points[i];\n      const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0);\n      const a = points[prev];\n      let command, c;\n      let vx, vy, ux, uy, ar, bl, br, cl;\n      let rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n      let x = toFixed(b.x);\n      let y = toFixed(b.y);\n      switch (b.command) {\n        case Commands.close:\n          command = Commands.close;\n          break;\n        case Commands.arc:\n          rx = b.rx;\n          ry = b.ry;\n          xAxisRotation = b.xAxisRotation;\n          largeArcFlag = b.largeArcFlag;\n          sweepFlag = b.sweepFlag;\n          command = Commands.arc + \" \" + rx + \" \" + ry + \" \" + xAxisRotation + \" \" + largeArcFlag + \" \" + sweepFlag + \" \" + x + \" \" + y;\n          break;\n        case Commands.curve:\n          ar = a.controls && a.controls.right || Vector.zero;\n          bl = b.controls && b.controls.left || Vector.zero;\n          if (a.relative) {\n            vx = toFixed(ar.x + a.x);\n            vy = toFixed(ar.y + a.y);\n          } else {\n            vx = toFixed(ar.x);\n            vy = toFixed(ar.y);\n          }\n          if (b.relative) {\n            ux = toFixed(bl.x + b.x);\n            uy = toFixed(bl.y + b.y);\n          } else {\n            ux = toFixed(bl.x);\n            uy = toFixed(bl.y);\n          }\n          command = (i === 0 ? Commands.move : Commands.curve) + \" \" + vx + \" \" + vy + \" \" + ux + \" \" + uy + \" \" + x + \" \" + y;\n          break;\n        case Commands.move:\n          d = b;\n          command = Commands.move + \" \" + x + \" \" + y;\n          break;\n        default:\n          command = b.command + \" \" + x + \" \" + y;\n      }\n      if (i >= last && closed2) {\n        if (b.command === Commands.curve) {\n          c = d;\n          br = b.controls && b.controls.right || b;\n          cl = c.controls && c.controls.left || c;\n          if (b.relative) {\n            vx = toFixed(br.x + b.x);\n            vy = toFixed(br.y + b.y);\n          } else {\n            vx = toFixed(br.x);\n            vy = toFixed(br.y);\n          }\n          if (c.relative) {\n            ux = toFixed(cl.x + c.x);\n            uy = toFixed(cl.y + c.y);\n          } else {\n            ux = toFixed(cl.x);\n            uy = toFixed(cl.y);\n          }\n          x = toFixed(c.x);\n          y = toFixed(c.y);\n          command += \" C \" + vx + \" \" + vy + \" \" + ux + \" \" + uy + \" \" + x + \" \" + y;\n        }\n        if (b.command !== Commands.close) {\n          command += \" Z\";\n        }\n      }\n      string += command + \" \";\n    }\n    return string;\n  },\n  pointsToString: function(points, size) {\n    let string = \"\";\n    const r = size * 0.5;\n    for (let i = 0; i < points.length; i++) {\n      const x = points[i].x;\n      const y = points[i].y - r;\n      string += Commands.move + \" \" + x + \" \" + y + \" \";\n      string += \"a \" + r + \" \" + r + \" 0 1 0 0.001 0 Z\";\n    }\n    return string;\n  },\n  getClip: function(shape, domElement) {\n    let clip = shape._renderer.clip;\n    if (!clip) {\n      clip = shape._renderer.clip = svg.createElement(\"clipPath\", {\n        \"clip-rule\": \"nonzero\"\n      });\n    }\n    if (clip.parentNode === null) {\n      domElement.defs.appendChild(clip);\n    }\n    return clip;\n  },\n  getRendererType: function(type) {\n    return type in svg ? type : \"path\";\n  },\n  defs: {\n    update: function(domElement) {\n      const { defs } = domElement;\n      if (defs._flagUpdate) {\n        const children = Array.prototype.slice.call(defs.children, 0);\n        for (let i = 0; i < children.length; i++) {\n          const child = children[i];\n          const id = child.id;\n          const selector = `[fill=\"url(#${id})\"],[stroke=\"url(#${id})\"],[clip-path=\"url(#${id})\"]`;\n          const exists = domElement.querySelector(selector);\n          if (!exists) {\n            defs.removeChild(child);\n          }\n        }\n        defs._flagUpdate = false;\n      }\n    }\n  },\n  group: {\n    // TODO: Can speed up.\n    // TODO: How does this effect a f\n    appendChild: function(object) {\n      const elem = object._renderer.elem;\n      if (!elem) {\n        return;\n      }\n      const tag = elem.nodeName;\n      if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) {\n        return;\n      }\n      this.elem.appendChild(elem);\n    },\n    removeChild: function(object) {\n      const elem = object._renderer.elem;\n      if (!elem || elem.parentNode != this.elem) {\n        return;\n      }\n      const tag = elem.nodeName;\n      if (!tag) {\n        return;\n      }\n      if (object._clip) {\n        return;\n      }\n      this.elem.removeChild(elem);\n    },\n    orderChild: function(object) {\n      this.elem.appendChild(object._renderer.elem);\n    },\n    renderChild: function(child) {\n      const prop = svg.getRendererType(child._renderer.type);\n      svg[prop].render.call(child, this);\n    },\n    render: function(domElement) {\n      if (!this._visible && !this._flagVisible || this._opacity === 0 && !this._flagOpacity) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      if (!this._renderer.elem) {\n        this._renderer.elem = svg.createElement(\"g\", {\n          id: this.id\n        });\n        domElement.appendChild(this._renderer.elem);\n      }\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const context = {\n        domElement,\n        elem: this._renderer.elem\n      };\n      if (flagMatrix) {\n        this._renderer.elem.setAttribute(\n          \"transform\",\n          \"matrix(\" + this._matrix.toString() + \")\"\n        );\n      }\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        const prop = svg.getRendererType(child._renderer.type);\n        svg[prop].render.call(child, domElement);\n      }\n      if (this._flagId) {\n        this._renderer.elem.setAttribute(\"id\", this._id);\n      }\n      if (this._flagOpacity) {\n        this._renderer.elem.setAttribute(\"opacity\", this._opacity);\n      }\n      if (this._flagVisible) {\n        this._renderer.elem.setAttribute(\n          \"display\",\n          this._visible ? \"inline\" : \"none\"\n        );\n      }\n      if (this._flagClassName) {\n        this._renderer.elem.setAttribute(\"class\", this.classList.join(\" \"));\n      }\n      if (this._flagAdditions) {\n        this.additions.forEach(svg.group.appendChild, context);\n      }\n      if (this._flagSubtractions) {\n        this.subtractions.forEach(svg.group.removeChild, context);\n      }\n      if (this._flagOrder) {\n        this.children.forEach(svg.group.orderChild, context);\n      }\n      if (this._flagMask) {\n        if (this._mask) {\n          const prop = svg.getRendererType(this._mask._renderer.type);\n          svg[prop].render.call(this._mask, domElement);\n          this._renderer.elem.setAttribute(\n            \"clip-path\",\n            \"url(#\" + this._mask.id + \")\"\n          );\n        } else {\n          this._renderer.elem.removeAttribute(\"clip-path\");\n        }\n      }\n      if (this.dataset) {\n        Object.assign(this._renderer.elem.dataset, this.dataset);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  path: {\n    render: function(domElement) {\n      if (this._opacity === 0 && !this._flagOpacity) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const changed = {};\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      if (flagMatrix) {\n        changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n      }\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n      if (this._flagVertices) {\n        const vertices = svg.toString(this._renderer.vertices, this._closed);\n        changed.d = vertices;\n      }\n      if (this._fill && this._fill._renderer) {\n        this._renderer.hasFillEffect = true;\n        this._fill._update();\n        const prop = svg.getRendererType(this._fill._renderer.type);\n        svg[prop].render.call(this._fill, domElement, true);\n      }\n      if (this._flagFill) {\n        changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n        if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasFillEffect;\n        }\n      }\n      if (this._stroke && this._stroke._renderer) {\n        this._renderer.hasStrokeEffect = true;\n        this._stroke._update();\n        const prop = svg.getRendererType(this._stroke._renderer.type);\n        svg[prop].render.call(this._stroke, domElement, true);\n      }\n      if (this._flagStroke) {\n        changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n        if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasStrokeEffect;\n        }\n      }\n      if (this._flagLinewidth) {\n        changed[\"stroke-width\"] = getEffectiveStrokeWidth(this);\n      }\n      if (this._flagOpacity) {\n        changed[\"stroke-opacity\"] = this._opacity;\n        changed[\"fill-opacity\"] = this._opacity;\n      }\n      if (this._flagClassName) {\n        changed[\"class\"] = this.classList.join(\" \");\n      }\n      if (this._flagVisible) {\n        changed.visibility = this._visible ? \"visible\" : \"hidden\";\n      }\n      if (this._flagCap) {\n        changed[\"stroke-linecap\"] = this._cap;\n      }\n      if (this._flagJoin) {\n        changed[\"stroke-linejoin\"] = this._join;\n      }\n      if (this._flagMiter) {\n        changed[\"stroke-miterlimit\"] = this._miter;\n      }\n      if (this.dashes && this.dashes.length > 0) {\n        changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n        changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n      }\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement(\"path\", changed);\n        domElement.appendChild(this._renderer.elem);\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n      if (this._flagClip) {\n        const clip = svg.getClip(this, domElement);\n        const elem = this._renderer.elem;\n        if (this._clip) {\n          elem.removeAttribute(\"id\");\n          clip.setAttribute(\"id\", this.id);\n          clip.appendChild(elem);\n        } else {\n          clip.removeAttribute(\"id\");\n          elem.setAttribute(\"id\", this.id);\n          this.parent._renderer.elem.appendChild(elem);\n        }\n      }\n      if (this._flagMask) {\n        if (this._mask) {\n          const prop = svg.getRendererType(this._mask._renderer.type);\n          svg[prop].render.call(this._mask, domElement);\n          this._renderer.elem.setAttribute(\n            \"clip-path\",\n            \"url(#\" + this._mask.id + \")\"\n          );\n        } else {\n          this._renderer.elem.removeAttribute(\"clip-path\");\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  points: {\n    render: function(domElement) {\n      if (this._opacity === 0 && !this._flagOpacity) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const changed = {};\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      if (flagMatrix) {\n        changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n      }\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n      if (this._flagVertices || this._flagSize || this._flagSizeAttenuation) {\n        let size = this._size;\n        if (!this._sizeAttenuation) {\n          const me = this.worldMatrix.elements;\n          const m = decomposeMatrix(me[0], me[3], me[1], me[4], me[2], me[5]);\n          size /= Math.max(m.scaleX, m.scaleY);\n        }\n        const vertices = svg.pointsToString(this._renderer.collection, size);\n        changed.d = vertices;\n      }\n      if (this._fill && this._fill._renderer) {\n        this._renderer.hasFillEffect = true;\n        this._fill._update();\n        const prop = svg.getRendererType(this._fill._renderer.type);\n        svg[prop].render.call(this._fill, domElement, true);\n      }\n      if (this._flagFill) {\n        changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n        if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasFillEffect;\n        }\n      }\n      if (this._stroke && this._stroke._renderer) {\n        this._renderer.hasStrokeEffect = true;\n        this._stroke._update();\n        const prop = svg.getRendererType(this._stroke._renderer.type);\n        svg[prop].render.call(this._stroke, domElement, true);\n      }\n      if (this._flagStroke) {\n        changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n        if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasStrokeEffect;\n        }\n      }\n      if (this._flagLinewidth) {\n        changed[\"stroke-width\"] = getEffectiveStrokeWidth(this);\n      }\n      if (this._flagOpacity) {\n        changed[\"stroke-opacity\"] = this._opacity;\n        changed[\"fill-opacity\"] = this._opacity;\n      }\n      if (this._flagClassName) {\n        changed[\"class\"] = this.classList.join(\" \");\n      }\n      if (this._flagVisible) {\n        changed.visibility = this._visible ? \"visible\" : \"hidden\";\n      }\n      if (this.dashes && this.dashes.length > 0) {\n        changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n        changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n      }\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement(\"path\", changed);\n        domElement.appendChild(this._renderer.elem);\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  text: {\n    render: function(domElement) {\n      this._update();\n      const changed = {};\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      if (flagMatrix) {\n        changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n      }\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n      if (this._flagFamily) {\n        changed[\"font-family\"] = this._family;\n      }\n      if (this._flagSize) {\n        changed[\"font-size\"] = this._size;\n      }\n      if (this._flagLeading) {\n        changed[\"line-height\"] = this._leading;\n      }\n      if (this._flagAlignment) {\n        changed[\"text-anchor\"] = svg.alignments[this._alignment] || this._alignment;\n      }\n      if (this._flagBaseline) {\n        changed[\"dominant-baseline\"] = svg.baselines[this._baseline] || this._baseline;\n      }\n      if (this._flagStyle) {\n        changed[\"font-style\"] = this._style;\n      }\n      if (this._flagWeight) {\n        changed[\"font-weight\"] = this._weight;\n      }\n      if (this._flagDecoration) {\n        changed[\"text-decoration\"] = this._decoration;\n      }\n      if (this._flagDirection) {\n        changed[\"direction\"] = this._direction;\n      }\n      if (this._fill && this._fill._renderer) {\n        this._renderer.hasFillEffect = true;\n        this._fill._update();\n        const prop = svg.getRendererType(this._fill._renderer.type);\n        svg[prop].render.call(this._fill, domElement, true);\n      }\n      if (this._flagFill) {\n        changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n        if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasFillEffect;\n        }\n      }\n      if (this._stroke && this._stroke._renderer) {\n        this._renderer.hasStrokeEffect = true;\n        this._stroke._update();\n        const prop = svg.getRendererType(this._stroke._renderer.type);\n        svg[prop].render.call(this._stroke, domElement, true);\n      }\n      if (this._flagStroke) {\n        changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n        if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasStrokeEffect;\n        }\n      }\n      if (this._flagLinewidth) {\n        changed[\"stroke-width\"] = getEffectiveStrokeWidth(this);\n      }\n      if (this._flagOpacity) {\n        changed.opacity = this._opacity;\n      }\n      if (this._flagClassName) {\n        changed[\"class\"] = this.classList.join(\" \");\n      }\n      if (this._flagVisible) {\n        changed.visibility = this._visible ? \"visible\" : \"hidden\";\n      }\n      if (this.dashes && this.dashes.length > 0) {\n        changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n        changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n      }\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement(\"text\", changed);\n        domElement.appendChild(this._renderer.elem);\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n      if (this._flagClip) {\n        const clip = svg.getClip(this, domElement);\n        const elem = this._renderer.elem;\n        if (this._clip) {\n          elem.removeAttribute(\"id\");\n          clip.setAttribute(\"id\", this.id);\n          clip.appendChild(elem);\n        } else {\n          clip.removeAttribute(\"id\");\n          elem.setAttribute(\"id\", this.id);\n          this.parent._renderer.elem.appendChild(elem);\n        }\n      }\n      if (this._flagMask) {\n        if (this._mask) {\n          const prop = svg.getRendererType(this._mask._renderer.type);\n          svg[prop].render.call(this._mask, domElement);\n          this._renderer.elem.setAttribute(\n            \"clip-path\",\n            \"url(#\" + this._mask.id + \")\"\n          );\n        } else {\n          this._renderer.elem.removeAttribute(\"clip-path\");\n        }\n      }\n      if (this._flagValue) {\n        this._renderer.elem.textContent = this._value;\n      }\n      return this.flagReset();\n    }\n  },\n  \"linear-gradient\": {\n    render: function(domElement, silent) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      if (!silent) {\n        this._update();\n      }\n      const changed = {};\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n      if (this._flagEndPoints) {\n        changed.x1 = this.left._x;\n        changed.y1 = this.left._y;\n        changed.x2 = this.right._x;\n        changed.y2 = this.right._y;\n      }\n      if (this._flagSpread) {\n        changed.spreadMethod = this._spread;\n      }\n      if (this._flagUnits) {\n        changed.gradientUnits = this._units;\n      }\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement(\"linearGradient\", changed);\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n      if (this._renderer.elem.parentNode === null) {\n        domElement.defs.appendChild(this._renderer.elem);\n      }\n      if (this._flagStops) {\n        const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length;\n        if (lengthChanged) {\n          while (this._renderer.elem.lastChild) {\n            this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n          }\n        }\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          const attrs = {};\n          if (stop._flagOffset) {\n            attrs.offset = 100 * stop._offset + \"%\";\n          }\n          if (stop._flagColor) {\n            attrs[\"stop-color\"] = stop._color;\n          }\n          if (stop._flagOpacity) {\n            attrs[\"stop-opacity\"] = stop._opacity;\n          }\n          if (!stop._renderer.elem) {\n            stop._renderer.elem = svg.createElement(\"stop\", attrs);\n          } else {\n            svg.setAttributes(stop._renderer.elem, attrs);\n          }\n          if (lengthChanged) {\n            this._renderer.elem.appendChild(stop._renderer.elem);\n          }\n          stop.flagReset();\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  \"radial-gradient\": {\n    render: function(domElement, silent) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      if (!silent) {\n        this._update();\n      }\n      const changed = {};\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n      if (this._flagCenter) {\n        changed.cx = this.center._x;\n        changed.cy = this.center._y;\n      }\n      if (this._flagFocal) {\n        changed.fx = this.focal._x;\n        changed.fy = this.focal._y;\n      }\n      if (this._flagRadius) {\n        changed.r = this._radius;\n      }\n      if (this._flagSpread) {\n        changed.spreadMethod = this._spread;\n      }\n      if (this._flagUnits) {\n        changed.gradientUnits = this._units;\n      }\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement(\"radialGradient\", changed);\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n      if (this._renderer.elem.parentNode === null) {\n        domElement.defs.appendChild(this._renderer.elem);\n      }\n      if (this._flagStops) {\n        const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length;\n        if (lengthChanged) {\n          while (this._renderer.elem.lastChild) {\n            this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n          }\n        }\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          const attrs = {};\n          if (stop._flagOffset) {\n            attrs.offset = 100 * stop._offset + \"%\";\n          }\n          if (stop._flagColor) {\n            attrs[\"stop-color\"] = stop._color;\n          }\n          if (stop._flagOpacity) {\n            attrs[\"stop-opacity\"] = stop._opacity;\n          }\n          if (!stop._renderer.elem) {\n            stop._renderer.elem = svg.createElement(\"stop\", attrs);\n          } else {\n            svg.setAttributes(stop._renderer.elem, attrs);\n          }\n          if (lengthChanged) {\n            this._renderer.elem.appendChild(stop._renderer.elem);\n          }\n          stop.flagReset();\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  texture: {\n    render: function(domElement, silent) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      if (!silent) {\n        this._update();\n      }\n      const changed = {};\n      const styles = { x: 0, y: 0 };\n      const image = this.image;\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n      if (this._flagLoaded && this.loaded) {\n        switch (image.nodeName.toLowerCase()) {\n          case \"canvas\":\n            styles.href = styles[\"xlink:href\"] = image.toDataURL(\"image/png\");\n            break;\n          case \"img\":\n          case \"image\":\n            styles.href = styles[\"xlink:href\"] = this.src;\n            break;\n        }\n      }\n      if (this._flagOffset || this._flagLoaded || this._flagScale) {\n        changed.x = this._offset.x;\n        changed.y = this._offset.y;\n        if (image) {\n          changed.x -= image.width / 2;\n          changed.y -= image.height / 2;\n          if (this._scale instanceof Vector) {\n            changed.x *= this._scale.x;\n            changed.y *= this._scale.y;\n          } else {\n            changed.x *= this._scale;\n            changed.y *= this._scale;\n          }\n        }\n        if (changed.x > 0) {\n          changed.x *= -1;\n        }\n        if (changed.y > 0) {\n          changed.y *= -1;\n        }\n      }\n      if (this._flagScale || this._flagLoaded || this._flagRepeat) {\n        changed.width = 0;\n        changed.height = 0;\n        if (image) {\n          changed.width = image.width;\n          changed.height = image.height;\n          switch (this._repeat) {\n            case \"no-repeat\":\n              changed.width += 1;\n              changed.height += 1;\n              break;\n          }\n          if (this._scale instanceof Vector) {\n            changed.width *= this._scale.x;\n            changed.height *= this._scale.y;\n          } else {\n            changed.width *= this._scale;\n            changed.height *= this._scale;\n          }\n          if (/no-repeat/i.test(this._repeat)) {\n            styles.preserveAspectRatio = \"xMidYMid\";\n          } else {\n            styles.preserveAspectRatio = \"none\";\n          }\n          styles.width = changed.width;\n          styles.height = changed.height;\n        }\n      }\n      if (this._flagScale || this._flagLoaded) {\n        if (!this._renderer.image) {\n          this._renderer.image = svg.createElement(\"image\", styles);\n        } else {\n          svg.setAttributes(this._renderer.image, styles);\n        }\n      }\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        changed.patternUnits = \"userSpaceOnUse\";\n        this._renderer.elem = svg.createElement(\"pattern\", changed);\n      } else if (Object.keys(changed).length !== 0) {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n      if (this._renderer.elem.parentNode === null) {\n        domElement.defs.appendChild(this._renderer.elem);\n      }\n      if (this._renderer.elem && this._renderer.image && !this._renderer.appended) {\n        this._renderer.elem.appendChild(this._renderer.image);\n        this._renderer.appended = true;\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  }\n};\nvar Renderer2 = class extends Events {\n  constructor(params) {\n    super();\n    this.domElement = params.domElement || svg.createElement(\"svg\");\n    this.scene = new Group();\n    this.scene.parent = this;\n    this.defs = svg.createElement(\"defs\");\n    this.defs._flagUpdate = false;\n    this.domElement.appendChild(this.defs);\n    this.domElement.defs = this.defs;\n    this.domElement.style.overflow = \"hidden\";\n  }\n  /**\n   * @name Two.SVGRenderer#setSize\n   * @function\n   * @param {Number} width - The new width of the renderer.\n   * @param {Number} height - The new height of the renderer.\n   * @description Change the size of the renderer.\n   * @nota-bene Triggers a `Two.Events.resize`.\n   */\n  setSize(width, height) {\n    this.width = width;\n    this.height = height;\n    svg.setAttributes(this.domElement, {\n      width,\n      height\n    });\n    return this.trigger(Events.Types.resize, width, height);\n  }\n  /**\n   * @name Two.SVGRenderer#render\n   * @function\n   * @description Render the current scene to the `<svg />`.\n   */\n  render() {\n    svg.group.render.call(this.scene, this.domElement);\n    svg.defs.update(this.domElement);\n    return this;\n  }\n};\n/**\n * @name Two.SVGRenderer.Utils\n * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<svg />`.\n */\n__publicField(Renderer2, \"Utils\", svg);\n\n// src/utils/shaders.js\nvar shaders = {\n  create: function(gl, source, type) {\n    const shader = gl.createShader(gl[type]);\n    gl.shaderSource(shader, source);\n    gl.compileShader(shader);\n    const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);\n    if (!compiled) {\n      const error = gl.getShaderInfoLog(shader);\n      gl.deleteShader(shader);\n      throw new TwoError(\"unable to compile shader \" + shader + \": \" + error);\n    }\n    return shader;\n  },\n  types: {\n    vertex: \"VERTEX_SHADER\",\n    fragment: \"FRAGMENT_SHADER\"\n  },\n  path: {\n    vertex: `\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n      uniform vec4 u_rect;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 rectCoords = (a_position * (u_rect.zw - u_rect.xy)) + u_rect.xy;\n        vec2 projected = (u_matrix * vec3(rectCoords, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n    fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec4 texel = texture2D(u_image, v_textureCoords);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `\n  },\n  points: {\n    vertex: `\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform float u_size;\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_PointSize = u_size;\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n    fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n\n      void main() {\n        vec4 texel = texture2D(u_image, gl_PointCoord);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `\n  }\n};\n\n// src/renderers/webgl.js\nvar multiplyMatrix = Matrix2.Multiply;\nvar identity = [1, 0, 0, 0, 1, 0, 0, 0, 1];\nvar transformation = new NumArray(9);\nvar CanvasUtils = Renderer.Utils;\nvar vector2 = new Vector();\nvar quad = new NumArray([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);\nvar webgl = {\n  precision: 0.9,\n  isHidden: /(undefined|none|transparent)/i,\n  canvas: root.document ? root.document.createElement(\"canvas\") : { getContext: function() {\n  } },\n  alignments: {\n    left: \"start\",\n    middle: \"center\",\n    right: \"end\"\n  },\n  matrix: new Matrix2(),\n  group: {\n    removeChild: function(child, gl) {\n      if (child.children) {\n        for (let i = 0; i < child.children.length; i++) {\n          webgl.group.removeChild(child.children[i], gl);\n        }\n      }\n      if (child._renderer.texture) {\n        gl.deleteTexture(child._renderer.texture);\n        delete child._renderer.texture;\n      }\n      if (child._renderer.positionBuffer) {\n        gl.deleteBuffer(child._renderer.positionBuffer);\n        delete child._renderer.positionBuffer;\n      }\n    },\n    /**\n     * @function\n     // * @type {(gl: any, programs: any) => any}\n     * @param {WebGLContext} gl\n     * @param {Object} programs\n     */\n    render: function(gl, programs) {\n      if (!this._visible) {\n        return;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const parent = this.parent;\n      const flagParentMatrix = parent._matrix && parent._matrix.manual || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      if (flagParentMatrix || flagMatrix) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n        this._matrix.toTransformArray(true, transformation);\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix\n        );\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        if (this._scale instanceof Vector) {\n          this._renderer.scale.x = this._scale.x;\n          this._renderer.scale.y = this._scale.y;\n        } else {\n          this._renderer.scale.x = this._scale;\n          this._renderer.scale.y = this._scale;\n        }\n        if (!/renderer/i.test(parent._renderer.type)) {\n          this._renderer.scale.x *= parent._renderer.scale.x;\n          this._renderer.scale.y *= parent._renderer.scale.y;\n        }\n        if (flagParentMatrix) {\n          this._flagMatrix = true;\n        }\n      }\n      if (this._mask) {\n        gl.clear(gl.STENCIL_BUFFER_BIT);\n        gl.enable(gl.STENCIL_TEST);\n        gl.stencilFunc(gl.ALWAYS, 1, 0);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n        gl.colorMask(false, false, false, false);\n        const prop = Renderer.Utils.getRendererType(\n          this._mask._renderer.type\n        );\n        webgl[prop].render.call(this._mask, gl, programs, this);\n        gl.stencilFunc(gl.EQUAL, 1, 255);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n        gl.colorMask(true, true, true, true);\n      }\n      this._flagOpacity = parent._flagOpacity || this._flagOpacity;\n      this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1);\n      let i;\n      if (this._flagSubtractions) {\n        for (i = 0; i < this.subtractions.length; i++) {\n          webgl.group.removeChild(this.subtractions[i], gl);\n        }\n      }\n      for (i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        const prop = Renderer.Utils.getRendererType(child._renderer.type);\n        webgl[prop].render.call(child, gl, programs);\n      }\n      if (this._mask) {\n        gl.disable(gl.STENCIL_TEST);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  path: {\n    updateCanvas: function(gl, elem) {\n      let prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y;\n      let isOffset;\n      const commands = elem._renderer.vertices;\n      const canvas3 = this.canvas;\n      const ctx = this.ctx;\n      const ratio = gl.renderer.ratio;\n      const scale = vector2.copy(elem._renderer.scale).multiply(ratio);\n      const stroke = elem._stroke;\n      const linewidth = elem._linewidth;\n      const fill = elem._fill;\n      const opacity = elem._renderer.opacity || elem._opacity;\n      const cap = elem._cap;\n      const join = elem._join;\n      const miter = elem._miter;\n      const closed2 = elem._closed;\n      const dashes = elem.dashes;\n      const length = commands.length;\n      const last = length - 1;\n      canvas3.width = Math.max(\n        Math.ceil(elem._renderer.rect.width * scale.x),\n        1\n      );\n      canvas3.height = Math.max(\n        Math.ceil(elem._renderer.rect.height * scale.y),\n        1\n      );\n      const centroid = elem._renderer.rect.centroid;\n      const cx = centroid.x;\n      const cy = centroid.y;\n      ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n      if (fill) {\n        if (typeof fill === \"string\") {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = Renderer.Utils.getRendererType(\n            fill._renderer.type\n          );\n          webgl[prop].render.call(fill, ctx, elem);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === \"string\") {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = Renderer.Utils.getRendererType(\n            stroke._renderer.type\n          );\n          webgl[prop].render.call(stroke, ctx, elem);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(elem);\n        }\n        if (miter) {\n          ctx.miterLimit = miter;\n        }\n        if (join) {\n          ctx.lineJoin = join;\n        }\n        if (!closed2 && cap) {\n          ctx.lineCap = cap;\n        }\n      }\n      if (typeof opacity === \"number\") {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n      let d, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n      ctx.save();\n      ctx.scale(scale.x, scale.y);\n      ctx.translate(cx, cy);\n      ctx.beginPath();\n      for (let i = 0; i < commands.length; i++) {\n        const b = commands[i];\n        x = b.x;\n        y = b.y;\n        switch (b.command) {\n          case Commands.close:\n            ctx.closePath();\n            break;\n          case Commands.arc:\n            rx = b.rx;\n            ry = b.ry;\n            xAxisRotation = b.xAxisRotation;\n            largeArcFlag = b.largeArcFlag;\n            sweepFlag = b.sweepFlag;\n            prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n            a = commands[prev];\n            ax = a.x;\n            ay = a.y;\n            CanvasUtils.renderSvgArcCommand(\n              ctx,\n              ax,\n              ay,\n              rx,\n              ry,\n              largeArcFlag,\n              sweepFlag,\n              xAxisRotation,\n              x,\n              y\n            );\n            break;\n          case Commands.curve:\n            prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n            a = commands[prev];\n            ar = a.controls && a.controls.right || Vector.zero;\n            bl = b.controls && b.controls.left || Vector.zero;\n            if (a._relative) {\n              vx = ar.x + a.x;\n              vy = ar.y + a.y;\n            } else {\n              vx = ar.x;\n              vy = ar.y;\n            }\n            if (b._relative) {\n              ux = bl.x + b.x;\n              uy = bl.y + b.y;\n            } else {\n              ux = bl.x;\n              uy = bl.y;\n            }\n            ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n            if (i >= last && closed2) {\n              c = d;\n              br = b.controls && b.controls.right || Vector.zero;\n              cl = c.controls && c.controls.left || Vector.zero;\n              if (b._relative) {\n                vx = br.x + b.x;\n                vy = br.y + b.y;\n              } else {\n                vx = br.x;\n                vy = br.y;\n              }\n              if (c._relative) {\n                ux = cl.x + c.x;\n                uy = cl.y + c.y;\n              } else {\n                ux = cl.x;\n                uy = cl.y;\n              }\n              x = c.x;\n              y = c.y;\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n            }\n            break;\n          case Commands.line:\n            ctx.lineTo(x, y);\n            break;\n          case Commands.move:\n            d = b;\n            ctx.moveTo(x, y);\n            break;\n        }\n      }\n      if (closed2) {\n        ctx.closePath();\n      }\n      if (!webgl.isHidden.test(fill)) {\n        isOffset = fill._renderer && fill._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n          ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n        }\n        ctx.fill();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n      if (!webgl.isHidden.test(stroke)) {\n        isOffset = stroke._renderer && stroke._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n          ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n          ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n        }\n        ctx.stroke();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n      ctx.restore();\n    },\n    // Returns the rect of a set of verts. Typically takes vertices that are\n    // \"centered\" around 0 and returns them to be anchored upper-left.\n    getBoundingClientRect: function(vertices, border, rect) {\n      let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity, width, height;\n      vertices.forEach(function(v) {\n        const x = v.x, y = v.y, controls = v.controls;\n        let a, b, c, d, cl, cr;\n        top = Math.min(y, top);\n        left = Math.min(x, left);\n        right = Math.max(x, right);\n        bottom = Math.max(y, bottom);\n        if (!v.controls) {\n          return;\n        }\n        cl = controls.left;\n        cr = controls.right;\n        if (!cl || !cr) {\n          return;\n        }\n        a = v._relative ? cl.x + x : cl.x;\n        b = v._relative ? cl.y + y : cl.y;\n        c = v._relative ? cr.x + x : cr.x;\n        d = v._relative ? cr.y + y : cr.y;\n        if (!a || !b || !c || !d) {\n          return;\n        }\n        top = Math.min(b, d, top);\n        left = Math.min(a, c, left);\n        right = Math.max(a, c, right);\n        bottom = Math.max(b, d, bottom);\n      });\n      if (typeof border === \"number\") {\n        top -= border;\n        left -= border;\n        right += border;\n        bottom += border;\n      }\n      width = right - left;\n      height = bottom - top;\n      rect.top = top;\n      rect.left = left;\n      rect.right = right;\n      rect.bottom = bottom;\n      rect.width = width;\n      rect.height = height;\n      if (!rect.centroid) {\n        rect.centroid = {};\n      }\n      rect.centroid.x = -left;\n      rect.centroid.y = -top;\n    },\n    render: function(gl, programs, forcedParent) {\n      if (!this._visible || !this._opacity) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const parent = forcedParent || this.parent;\n      const prop = Renderer.Utils.getRendererType(this._renderer.type);\n      const program = programs[prop];\n      const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const parentChanged = this._renderer.parent !== parent;\n      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;\n      if (flagParentMatrix || flagMatrix || parentChanged) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n        this._matrix.toTransformArray(true, transformation);\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix\n        );\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x * parent._renderer.scale.x;\n          sy = this._scale.y * parent._renderer.scale.y;\n        } else {\n          sx = this._scale * parent._renderer.scale.x;\n          sy = this._scale * parent._renderer.scale.y;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n        if (parentChanged) {\n          this._renderer.parent = parent;\n        }\n      }\n      if (this._mask) {\n        gl.clear(gl.STENCIL_BUFFER_BIT);\n        gl.enable(gl.STENCIL_TEST);\n        gl.stencilFunc(gl.ALWAYS, 1, 0);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n        gl.colorMask(false, false, false, false);\n        const prop2 = Renderer.Utils.getRendererType(\n          this._mask._renderer.type\n        );\n        webgl[prop2].render.call(this._mask, gl, programs, this);\n        gl.stencilFunc(gl.EQUAL, 1, 255);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n        gl.colorMask(true, true, true, true);\n      }\n      if (flagTexture) {\n        if (!this._renderer.rect) {\n          this._renderer.rect = {};\n        }\n        this._renderer.opacity = this._opacity * parent._renderer.opacity;\n        webgl.path.getBoundingClientRect(\n          this._renderer.vertices,\n          this._linewidth,\n          this._renderer.rect\n        );\n        webgl.updateTexture.call(webgl, gl, this);\n      } else {\n        if (this._fill && this._fill._update) {\n          this._fill._update();\n        }\n        if (this._stroke && this._stroke._update) {\n          this._stroke._update();\n        }\n      }\n      if (this._clip && !forcedParent || !this._renderer.texture) {\n        return this;\n      }\n      if (programs.current !== program) {\n        gl.useProgram(program);\n        gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n        gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n        gl.enableVertexAttribArray(program.position);\n        gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n        if (!programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        programs.current = program;\n      }\n      if (programs.resolution.flagged) {\n        gl.uniform2f(\n          gl.getUniformLocation(program, \"u_resolution\"),\n          programs.resolution.width,\n          programs.resolution.height\n        );\n      }\n      gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n      const rect = this._renderer.rect;\n      gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n      gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n      gl.drawArrays(gl.TRIANGLES, 0, 6);\n      if (this._mask) {\n        gl.disable(gl.STENCIL_TEST);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  points: {\n    // The canvas is a texture that is a rendering of one vertex\n    updateCanvas: function(gl, elem) {\n      let isOffset;\n      const canvas3 = this.canvas;\n      const ctx = this.ctx;\n      const ratio = gl.renderer.ratio;\n      const stroke = elem._stroke;\n      const linewidth = elem._linewidth;\n      const fill = elem._fill;\n      const opacity = elem._renderer.opacity || elem._opacity;\n      const dashes = elem.dashes;\n      const size = elem._size * ratio;\n      let dimension = size;\n      if (!webgl.isHidden.test(stroke)) {\n        dimension += linewidth;\n      }\n      canvas3.width = getPoT(dimension);\n      canvas3.height = canvas3.width;\n      const aspect = dimension / canvas3.width;\n      const cx = canvas3.width / 2;\n      const cy = canvas3.height / 2;\n      ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n      if (fill) {\n        if (typeof fill === \"string\") {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = Renderer.Utils.getRendererType(\n            fill._renderer.type\n          );\n          webgl[prop].render.call(fill, ctx, elem);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === \"string\") {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = Renderer.Utils.getRendererType(\n            stroke._renderer.type\n          );\n          webgl[prop].render.call(stroke, ctx, elem);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(elem) / aspect;\n        }\n      }\n      if (typeof opacity === \"number\") {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n      ctx.save();\n      ctx.translate(cx, cy);\n      ctx.scale(webgl.precision, webgl.precision);\n      ctx.beginPath();\n      ctx.arc(0, 0, size / aspect * 0.5, 0, TWO_PI);\n      ctx.restore();\n      if (closed) {\n        ctx.closePath();\n      }\n      if (!webgl.isHidden.test(fill)) {\n        isOffset = fill._renderer && fill._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n          ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n        }\n        ctx.fill();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n      if (!webgl.isHidden.test(stroke)) {\n        isOffset = stroke._renderer && stroke._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n          ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n          ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n        }\n        ctx.stroke();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n    },\n    render: function(gl, programs, forcedParent) {\n      if (!this._visible || !this._opacity) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      let size = this._size;\n      const parent = forcedParent || this.parent;\n      const program = programs[this._renderer.type];\n      const sizeAttenuation = this._sizeAttenuation;\n      const stroke = this._stroke;\n      const linewidth = this._linewidth;\n      const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const parentChanged = this._renderer.parent !== parent;\n      const commands = this._renderer.vertices;\n      const length = this._renderer.collection.length;\n      const flagVertices = this._flagVertices;\n      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;\n      if (flagParentMatrix || flagMatrix || parentChanged) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n        this._matrix.toTransformArray(true, transformation);\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix\n        );\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x * parent._renderer.scale.x;\n          sy = this._scale.y * parent._renderer.scale.y;\n        } else {\n          sx = this._scale * parent._renderer.scale.x;\n          sy = this._scale * parent._renderer.scale.y;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n        if (parentChanged) {\n          this._renderer.parent = parent;\n        }\n      }\n      if (flagVertices) {\n        const positionBuffer = this._renderer.positionBuffer;\n        if (positionBuffer) {\n          gl.deleteBuffer(positionBuffer);\n        }\n        this._renderer.positionBuffer = gl.createBuffer();\n        gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.positionBuffer);\n        gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n        gl.enableVertexAttribArray(program.position);\n        gl.bufferData(gl.ARRAY_BUFFER, commands, gl.STATIC_DRAW);\n      }\n      if (flagTexture) {\n        this._renderer.opacity = this._opacity * parent._renderer.opacity;\n        webgl.updateTexture.call(webgl, gl, this);\n      } else {\n        if (this._fill && this._fill._update) {\n          this._fill._update();\n        }\n        if (this._stroke && this._stroke._update) {\n          this._stroke._update();\n        }\n      }\n      if (this._clip && !forcedParent || !this._renderer.texture) {\n        return this;\n      }\n      if (!webgl.isHidden.test(stroke)) {\n        size += linewidth;\n      }\n      size /= webgl.precision;\n      if (sizeAttenuation) {\n        size *= Math.max(this._renderer.scale.x, this._renderer.scale.y);\n      }\n      if (programs.current !== program) {\n        gl.useProgram(program);\n        if (!programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        programs.current = program;\n      }\n      if (programs.resolution.flagged) {\n        gl.uniform2f(\n          gl.getUniformLocation(program, \"u_resolution\"),\n          programs.resolution.width,\n          programs.resolution.height\n        );\n      }\n      gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n      gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n      gl.uniform1f(program.size, size * programs.resolution.ratio);\n      gl.drawArrays(gl.POINTS, 0, length);\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  text: {\n    updateCanvas: function(gl, elem) {\n      const canvas3 = this.canvas;\n      const ctx = this.ctx;\n      const ratio = gl.renderer.ratio;\n      const scale = vector2.copy(elem._renderer.scale).multiply(ratio);\n      const stroke = elem._stroke;\n      const linewidth = elem._linewidth;\n      const fill = elem._fill;\n      const opacity = elem._renderer.opacity || elem._opacity;\n      const dashes = elem.dashes;\n      const decoration = elem._decoration;\n      const direction = elem._direction;\n      canvas3.width = Math.max(\n        Math.ceil(elem._renderer.rect.width * scale.x),\n        1\n      );\n      canvas3.height = Math.max(\n        Math.ceil(elem._renderer.rect.height * scale.y),\n        1\n      );\n      const centroid = elem._renderer.rect.centroid;\n      const cx = centroid.x;\n      const cy = centroid.y;\n      let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n      const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;\n      ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n      if (!isOffset) {\n        ctx.font = [\n          elem._style,\n          elem._weight,\n          elem._size + \"px/\" + elem._leading + \"px\",\n          elem._family\n        ].join(\" \");\n      }\n      ctx.textAlign = \"center\";\n      ctx.textBaseline = \"middle\";\n      ctx.textDirection = direction;\n      if (fill) {\n        if (typeof fill === \"string\") {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = Renderer.Utils.getRendererType(\n            fill._renderer.type\n          );\n          webgl[prop].render.call(fill, ctx, elem);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === \"string\") {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = Renderer.Utils.getRendererType(\n            stroke._renderer.type\n          );\n          webgl[prop].render.call(stroke, ctx, elem);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(elem);\n        }\n      }\n      if (typeof opacity === \"number\") {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n      ctx.save();\n      ctx.scale(scale.x, scale.y);\n      ctx.translate(cx, cy);\n      if (!webgl.isHidden.test(fill)) {\n        if (fill._renderer && fill._renderer.offset) {\n          sx = fill._renderer.scale.x;\n          sy = fill._renderer.scale.y;\n          ctx.save();\n          ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n          ctx.scale(sx, sy);\n          a = elem._size / fill._renderer.scale.y;\n          b = elem._leading / fill._renderer.scale.y;\n          ctx.font = [\n            elem._style,\n            elem._weight,\n            a + \"px/\",\n            b + \"px\",\n            elem._family\n          ].join(\" \");\n          c = fill._renderer.offset.x / fill._renderer.scale.x;\n          d = fill._renderer.offset.y / fill._renderer.scale.y;\n          ctx.fillText(elem.value, c, d);\n          ctx.restore();\n        } else {\n          ctx.fillText(elem.value, 0, 0);\n        }\n      }\n      if (!webgl.isHidden.test(stroke)) {\n        if (stroke._renderer && stroke._renderer.offset) {\n          sx = stroke._renderer.scale.x;\n          sy = stroke._renderer.scale.y;\n          ctx.save();\n          ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n          ctx.scale(sx, sy);\n          a = elem._size / stroke._renderer.scale.y;\n          b = elem._leading / stroke._renderer.scale.y;\n          ctx.font = [\n            elem._style,\n            elem._weight,\n            a + \"px/\",\n            b + \"px\",\n            elem._family\n          ].join(\" \");\n          c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n          d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n          e = linewidth / stroke._renderer.scale.x;\n          ctx.lineWidth = e;\n          ctx.strokeText(elem.value, c, d);\n          ctx.restore();\n        } else {\n          ctx.strokeText(elem.value, 0, 0);\n        }\n      }\n      if (/(underline|strikethrough)/i.test(decoration)) {\n        const metrics = ctx.measureText(elem.value);\n        switch (decoration) {\n          case \"underline\":\n            y1 = metrics.actualBoundingBoxDescent;\n            y2 = metrics.actualBoundingBoxDescent;\n            break;\n          case \"strikethrough\":\n            y1 = 0;\n            y2 = 0;\n            break;\n        }\n        x1 = -metrics.width / 2;\n        x2 = metrics.width / 2;\n        ctx.lineWidth = Math.max(Math.floor(elem._size / 15), 1);\n        ctx.strokeStyle = ctx.fillStyle;\n        ctx.beginPath();\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n        ctx.stroke();\n      }\n      ctx.restore();\n    },\n    getBoundingClientRect: function(elem, rect) {\n      const ctx = webgl.ctx;\n      ctx.font = [\n        elem._style,\n        elem._weight,\n        elem._size + \"px/\" + elem._leading + \"px\",\n        elem._family\n      ].join(\" \");\n      ctx.textAlign = \"center\";\n      ctx.textBaseline = Renderer.Utils.baselines[elem._baseline] || elem._baseline;\n      const metrics = ctx.measureText(elem._value);\n      let width = metrics.width;\n      let height = 1.15 * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);\n      if (this._linewidth && !webgl.isHidden.test(this._stroke)) {\n        width += this._linewidth * 2;\n        height += this._linewidth * 2;\n      }\n      const w = width / 2;\n      const h = height / 2;\n      switch (webgl.alignments[elem._alignment] || elem._alignment) {\n        case webgl.alignments.left:\n          if (elem.direction === \"ltr\") {\n            rect.left = 0;\n            rect.right = width;\n          } else {\n            rect.left = -width;\n            rect.right = 0;\n          }\n          break;\n        case webgl.alignments.right:\n          if (elem.direction === \"ltr\") {\n            rect.left = -width;\n            rect.right = 0;\n          } else {\n            rect.left = 0;\n            rect.right = width;\n          }\n          break;\n        default:\n          rect.left = -w;\n          rect.right = w;\n      }\n      switch (elem._baseline) {\n        case \"bottom\":\n          rect.top = -height;\n          rect.bottom = 0;\n          break;\n        case \"top\":\n          rect.top = 0;\n          rect.bottom = height;\n          break;\n        case \"baseline\":\n          rect.top = -h * 1.5;\n          rect.bottom = h * 0.5;\n          break;\n        default:\n          rect.top = -h;\n          rect.bottom = h;\n      }\n      rect.width = width;\n      rect.height = height;\n      if (!rect.centroid) {\n        rect.centroid = {};\n      }\n      rect.centroid.x = w;\n      rect.centroid.y = h;\n    },\n    render: function(gl, programs, forcedParent) {\n      if (!this._visible || !this._opacity) {\n        return this;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const parent = forcedParent || this.parent;\n      const program = programs[this._renderer.type];\n      const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const parentChanged = this._renderer.parent !== parent;\n      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;\n      if (flagParentMatrix || flagMatrix || parentChanged) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n        this._matrix.toTransformArray(true, transformation);\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix\n        );\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x * parent._renderer.scale.x;\n          sy = this._scale.y * parent._renderer.scale.y;\n        } else {\n          sx = this._scale * parent._renderer.scale.x;\n          sy = this._scale * parent._renderer.scale.y;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n        if (parentChanged) {\n          this._renderer.parent = parent;\n        }\n      }\n      if (this._mask) {\n        gl.clear(gl.STENCIL_BUFFER_BIT);\n        gl.enable(gl.STENCIL_TEST);\n        gl.stencilFunc(gl.ALWAYS, 1, 0);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n        gl.colorMask(false, false, false, false);\n        const prop = Renderer.Utils.getRendererType(\n          this._mask._renderer.type\n        );\n        webgl[prop].render.call(this._mask, gl, programs, this);\n        gl.stencilFunc(gl.EQUAL, 1, 255);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n        gl.colorMask(true, true, true, true);\n      }\n      if (flagTexture) {\n        if (!this._renderer.rect) {\n          this._renderer.rect = {};\n        }\n        this._renderer.opacity = this._opacity * parent._renderer.opacity;\n        webgl.text.getBoundingClientRect(this, this._renderer.rect);\n        webgl.updateTexture.call(webgl, gl, this);\n      } else {\n        if (this._fill && this._fill._update) {\n          this._fill._update();\n        }\n        if (this._stroke && this._stroke._update) {\n          this._stroke._update();\n        }\n      }\n      if (this._clip && !forcedParent || !this._renderer.texture) {\n        return this;\n      }\n      if (programs.current !== program) {\n        gl.useProgram(program);\n        gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n        gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n        gl.enableVertexAttribArray(program.position);\n        gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n        if (!programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        programs.current = program;\n      }\n      if (programs.resolution.flagged) {\n        gl.uniform2f(\n          gl.getUniformLocation(program, \"u_resolution\"),\n          programs.resolution.width,\n          programs.resolution.height\n        );\n      }\n      gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n      const rect = this._renderer.rect;\n      gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n      gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n      gl.drawArrays(gl.TRIANGLES, 0, 6);\n      if (this._mask) {\n        gl.disable(gl.STENCIL_TEST);\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  \"linear-gradient\": {\n    render: function(ctx, parent) {\n      if (!ctx.canvas.getContext(\"2d\") || !parent) {\n        return;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) {\n        let rect;\n        let lx = this.left._x;\n        let ly = this.left._y;\n        let rx = this.right._x;\n        let ry = this.right._y;\n        if (/objectBoundingBox/i.test(this._units)) {\n          rect = parent.getBoundingClientRect(true);\n          lx = (lx - 0.5) * rect.width;\n          ly = (ly - 0.5) * rect.height;\n          rx = (rx - 0.5) * rect.width;\n          ry = (ry - 0.5) * rect.height;\n        }\n        this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  \"radial-gradient\": {\n    render: function(ctx, parent) {\n      if (!ctx.canvas.getContext(\"2d\") || !parent) {\n        return;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) {\n        let rect;\n        let cx = this.center._x;\n        let cy = this.center._y;\n        let fx = this.focal._x;\n        let fy = this.focal._y;\n        let radius = this._radius;\n        if (/objectBoundingBox/i.test(this._units)) {\n          rect = parent.getBoundingClientRect(true);\n          cx = (cx - 0.5) * rect.width * 0.5;\n          cy = (cy - 0.5) * rect.height * 0.5;\n          fx = (fx - 0.5) * rect.width * 0.5;\n          fy = (fy - 0.5) * rect.height * 0.5;\n          radius *= Math.min(rect.width, rect.height);\n        }\n        this._renderer.effect = ctx.createRadialGradient(\n          cx,\n          cy,\n          0,\n          fx,\n          fy,\n          radius\n        );\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  texture: {\n    render: function(ctx, elem) {\n      if (!ctx.canvas.getContext(\"2d\")) {\n        return;\n      }\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n      this._update();\n      const image = this.image;\n      if ((this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) {\n        this._renderer.effect = ctx.createPattern(image, this._repeat);\n      } else if (!this._renderer.effect) {\n        return this.flagReset();\n      }\n      if (this._flagOffset || this._flagLoaded || this._flagScale) {\n        if (!(this._renderer.offset instanceof Vector)) {\n          this._renderer.offset = new Vector();\n        }\n        this._renderer.offset.x = -this._offset.x;\n        this._renderer.offset.y = -this._offset.y;\n        if (image) {\n          this._renderer.offset.x += image.width / 2;\n          this._renderer.offset.y += image.height / 2;\n          if (this._scale instanceof Vector) {\n            this._renderer.offset.x *= this._scale.x;\n            this._renderer.offset.y *= this._scale.y;\n          } else {\n            this._renderer.offset.x *= this._scale;\n            this._renderer.offset.y *= this._scale;\n          }\n        }\n      }\n      if (this._flagScale || this._flagLoaded) {\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x;\n          sy = this._scale.y;\n        } else {\n          sx = this._scale;\n          sy = this._scale;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n      }\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n      return this.flagReset();\n    }\n  },\n  updateTexture: function(gl, elem) {\n    const prop = Renderer.Utils.getRendererType(elem._renderer.type);\n    this[prop].updateCanvas.call(webgl, gl, elem);\n    if (this.canvas.width <= 0 || this.canvas.height <= 0) {\n      if (elem._renderer.texture) {\n        gl.deleteTexture(elem._renderer.texture);\n      }\n      delete elem._renderer.texture;\n      return;\n    }\n    if (!elem._renderer.texture) {\n      elem._renderer.texture = gl.createTexture();\n    }\n    gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture);\n    gl.texImage2D(\n      gl.TEXTURE_2D,\n      0,\n      gl.RGBA,\n      gl.RGBA,\n      gl.UNSIGNED_BYTE,\n      this.canvas\n    );\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n  },\n  program: {\n    create: function(gl, shaders2) {\n      let program, linked, error;\n      program = gl.createProgram();\n      _.each(shaders2, function(s) {\n        gl.attachShader(program, s);\n      });\n      gl.linkProgram(program);\n      linked = gl.getProgramParameter(program, gl.LINK_STATUS);\n      if (!linked) {\n        error = gl.getProgramInfoLog(program);\n        gl.deleteProgram(program);\n        throw new TwoError(\"unable to link program: \" + error);\n      }\n      return program;\n    }\n  },\n  extensions: {\n    init: function(gl) {\n      const extensions = {};\n      const names = [\n        \"EXT_texture_filter_anisotropic\",\n        \"WEBGL_compressed_texture_s3tc\",\n        \"OES_texture_float_linear\",\n        \"WEBGL_multisampled_render_to_texture\"\n      ];\n      for (let i = 0; i < names.length; i++) {\n        const name = names[i];\n        extensions[name] = webgl.extensions.get(gl, name);\n      }\n      return extensions;\n    },\n    get: function(gl, name) {\n      return gl.getExtension(name) || gl.getExtension(`MOZ_${name}`) || gl.getExtension(`WEBKIT_${name}`);\n    }\n  },\n  TextureRegistry: new Registry()\n};\nwebgl.ctx = webgl.canvas.getContext(\"2d\");\nvar Renderer3 = class extends Events {\n  constructor(params) {\n    super();\n    let gl, program, vs, fs;\n    this.domElement = params.domElement || document.createElement(\"canvas\");\n    if (typeof params.offscreenElement !== \"undefined\") {\n      webgl.canvas = params.offscreenElement;\n      webgl.ctx = webgl.canvas.getContext(\"2d\");\n    }\n    this.scene = new Group();\n    this.scene.parent = this;\n    this._renderer = {\n      type: \"renderer\",\n      matrix: new NumArray(identity),\n      scale: 1,\n      opacity: 1\n    };\n    this._flagMatrix = true;\n    params = _.defaults(params || {}, {\n      antialias: false,\n      alpha: true,\n      premultipliedAlpha: true,\n      stencil: true,\n      preserveDrawingBuffer: true,\n      overdraw: false\n    });\n    this.overdraw = params.overdraw;\n    gl = this.ctx = this.domElement.getContext(\"webgl\", params) || this.domElement.getContext(\"experimental-webgl\", params);\n    if (!this.ctx) {\n      throw new TwoError(\n        \"unable to create a webgl context. Try using another renderer.\"\n      );\n    }\n    vs = shaders.create(gl, shaders.path.vertex, shaders.types.vertex);\n    fs = shaders.create(gl, shaders.path.fragment, shaders.types.fragment);\n    this.programs = {\n      current: null,\n      buffers: {\n        position: gl.createBuffer()\n      },\n      resolution: {\n        width: 0,\n        height: 0,\n        ratio: 1,\n        flagged: false\n      }\n    };\n    program = this.programs.path = webgl.program.create(gl, [vs, fs]);\n    this.programs.text = this.programs.path;\n    gl.extensions = webgl.extensions.init(gl);\n    gl.renderer = this;\n    program.position = gl.getAttribLocation(program, \"a_position\");\n    program.matrix = gl.getUniformLocation(program, \"u_matrix\");\n    program.rect = gl.getUniformLocation(program, \"u_rect\");\n    const positionBuffer = gl.createBuffer();\n    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n    gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n    gl.enableVertexAttribArray(program.position);\n    gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n    vs = shaders.create(gl, shaders.points.vertex, shaders.types.vertex);\n    fs = shaders.create(gl, shaders.points.fragment, shaders.types.fragment);\n    program = this.programs.points = webgl.program.create(gl, [vs, fs]);\n    program.position = gl.getAttribLocation(program, \"a_position\");\n    program.matrix = gl.getUniformLocation(program, \"u_matrix\");\n    program.size = gl.getUniformLocation(program, \"u_size\");\n    gl.enable(gl.BLEND);\n    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n    gl.blendEquation(gl.FUNC_ADD);\n    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n  }\n  /**\n   * @name Two.WebGLRenderer#setSize\n   * @function\n   * @fires resize\n   * @param {Number} width - The new width of the renderer.\n   * @param {Number} height - The new height of the renderer.\n   * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n   * @description Change the size of the renderer.\n   */\n  setSize(width, height, ratio) {\n    let w, h;\n    const ctx = this.ctx;\n    this.width = width;\n    this.height = height;\n    this.ratio = typeof ratio === \"undefined\" ? getRatio(ctx) : ratio;\n    this.domElement.width = width * this.ratio;\n    this.domElement.height = height * this.ratio;\n    if (_.isObject(this.domElement.style)) {\n      _.extend(this.domElement.style, {\n        width: width + \"px\",\n        height: height + \"px\"\n      });\n    }\n    this._renderer.matrix[0] = this._renderer.matrix[4] = this._renderer.scale = this.ratio;\n    this._flagMatrix = true;\n    w = width * this.ratio;\n    h = height * this.ratio;\n    ctx.viewport(0, 0, w, h);\n    this.programs.resolution.width = w;\n    this.programs.resolution.height = h;\n    this.programs.resolution.ratio = this.ratio;\n    this.programs.resolution.flagged = true;\n    return this.trigger(Events.Types.resize, width, height, ratio);\n  }\n  /**\n   * @name Two.WebGLRenderer#render\n   * @function\n   * @description Render the current scene to the `<canvas />`.\n   */\n  render() {\n    const gl = this.ctx;\n    if (!this.overdraw) {\n      gl.clear(gl.COLOR_BUFFER_BIT);\n    }\n    webgl.group.render.call(this.scene, gl, this.programs);\n    this._flagMatrix = false;\n    this.programs.resolution.flagged = true;\n    return this;\n  }\n};\n/**\n * @name Two.WebGLRenderer.Utils\n * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />` through the WebGL API.\n */\n__publicField(Renderer3, \"Utils\", webgl);\n\n// src/two.js\nvar Utils = _.extend(\n  {\n    Error: TwoError,\n    getRatio,\n    read,\n    xhr\n  },\n  _,\n  CanvasPolyfill,\n  curves_exports,\n  math_exports\n);\nvar _Two = class _Two {\n  constructor(options) {\n    // Warning: inherit events while overriding static properties\n    /**\n     * @private\n     */\n    __publicField(this, \"_events\", new Events());\n    /**\n     * @name Two#type\n     * @property {String} - A string representing which type of renderer the instance has instantiated.\n     */\n    __publicField(this, \"type\", \"\");\n    /**\n     * @name Two#renderer\n     * @property {(Two.SVGRenderer|Two.CanvasRenderer|Two.WebGLRenderer)} - The instantiated rendering class for the instance. For a list of possible rendering types check out Two.Types.\n     */\n    __publicField(this, \"renderer\", null);\n    /**\n     * @name Two#scene\n     * @property {Two.Group} - The base level {@link Two.Group} which houses all objects for the instance. Because it is a {@link Two.Group} transformations can be applied to it that will affect all objects in the instance. This is handy as a makeshift inverted camera.\n     */\n    __publicField(this, \"scene\", null);\n    /**\n     * @name Two#width\n     * @property {Number} - The width of the instance's dom element.\n     */\n    __publicField(this, \"width\", 0);\n    /**\n     * @name Two#height\n     * @property {Number} - The height of the instance's dom element.\n     */\n    __publicField(this, \"height\", 0);\n    /**\n     * @name Two#frameCount\n     * @property {Number} - An integer representing how many frames have elapsed.\n     */\n    __publicField(this, \"frameCount\", 0);\n    /**\n     * @name Two#timeDelta\n     * @property {Number} - A number representing how much time has elapsed since the last frame in milliseconds.\n     */\n    __publicField(this, \"timeDelta\", 0);\n    /**\n     * @name Two#playing\n     * @property {Boolean} - A boolean representing whether or not the instance is being updated through the automatic `requestAnimationFrame`.\n     */\n    __publicField(this, \"playing\", false);\n    const params = _.defaults(options || {}, {\n      fullscreen: false,\n      fitted: false,\n      width: 640,\n      height: 480,\n      type: _Two.Types.svg,\n      autostart: false\n    });\n    _.each(\n      params,\n      function(v, k) {\n        if (/fullscreen/i.test(k) || /autostart/i.test(k)) {\n          return;\n        }\n        this[k] = v;\n      },\n      this\n    );\n    if (_.isElement(params.domElement)) {\n      const tagName = params.domElement.tagName.toLowerCase();\n      if (!/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test(\n        this.type + \"-\" + tagName\n      )) {\n        this.type = _Two.Types[tagName];\n      }\n    }\n    this.renderer = new _Two[this.type](this);\n    this.setPlaying(params.autostart);\n    this.frameCount = 0;\n    if (params.fullscreen) {\n      this.fit = fitToWindow.bind(this);\n      this.fit.domElement = window;\n      this.fit.attached = true;\n      _.extend(document.body.style, {\n        overflow: \"hidden\",\n        margin: 0,\n        padding: 0,\n        top: 0,\n        left: 0,\n        right: 0,\n        bottom: 0,\n        position: \"fixed\"\n      });\n      _.extend(this.renderer.domElement.style, {\n        display: \"block\",\n        top: 0,\n        left: 0,\n        right: 0,\n        bottom: 0,\n        position: \"fixed\"\n      });\n      dom.bind(this.fit.domElement, \"resize\", this.fit);\n      this.fit();\n    } else if (params.fitted) {\n      this.fit = fitToParent.bind(this);\n      _.extend(this.renderer.domElement.style, {\n        display: \"block\"\n      });\n    } else if (typeof params.width === \"number\" && typeof params.height === \"number\") {\n      this.renderer.setSize(params.width, params.height, this.ratio);\n      this.width = params.width;\n      this.height = params.height;\n    }\n    this.renderer.bind(Events.Types.resize, updateDimensions.bind(this));\n    this.scene = this.renderer.scene;\n    _Two.Instances.push(this);\n    if (params.autostart) {\n      raf.init();\n    }\n  }\n  // Getters and setters aren't enumerable\n  get _bound() {\n    return this._events._bound;\n  }\n  set _bound(v) {\n    this._events._bound = v;\n  }\n  addEventListener() {\n    var _a;\n    return (_a = this._events.addEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  on() {\n    var _a;\n    return (_a = this._events.addEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  bind() {\n    var _a;\n    return (_a = this._events.addEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  removeEventListener() {\n    var _a;\n    return (_a = this._events.removeEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  off() {\n    var _a;\n    return (_a = this._events.removeEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  unbind() {\n    var _a;\n    return (_a = this._events.removeEventListener) == null ? void 0 : _a.apply(this, arguments);\n  }\n  dispatchEvent() {\n    var _a;\n    return (_a = this._events.dispatchEvent) == null ? void 0 : _a.apply(this, arguments);\n  }\n  trigger() {\n    var _a;\n    return (_a = this._events.dispatchEvent) == null ? void 0 : _a.apply(this, arguments);\n  }\n  listen() {\n    var _a;\n    return (_a = this._events.listen) == null ? void 0 : _a.apply(this, arguments);\n  }\n  ignore() {\n    var _a;\n    return (_a = this._events.ignore) == null ? void 0 : _a.apply(this, arguments);\n  }\n  /**\n   * @name Two#appendTo\n   * @function\n   * @param {Element} elem - The DOM element to append the Two.js stage to.\n   * @description Shorthand method to append your instance of Two.js to the `document`.\n   */\n  appendTo(elem) {\n    elem.appendChild(this.renderer.domElement);\n    if (this.fit) {\n      if (this.fit.domElement !== window) {\n        this.fit.domElement = elem;\n        this.fit.attached = false;\n      }\n      this.update();\n    }\n    return this;\n  }\n  /**\n   * @name Two#play\n   * @function\n   * @fires play\n   * @description Call to start an internal animation loop.\n   * @nota-bene This function initiates a `requestAnimationFrame` loop.\n   */\n  play() {\n    this.playing = true;\n    raf.init();\n    return this.trigger(Events.Types.play);\n  }\n  /**\n   * @name Two#pause\n   * @function\n   * @fires pause\n   * @description Call to stop the internal animation loop for a specific instance of Two.js.\n   */\n  pause() {\n    this.playing = false;\n    return this.trigger(Events.Types.pause);\n  }\n  setPlaying(p) {\n    this.playing = p;\n  }\n  /**\n   * @name Two#release\n   * @function\n   * @param {Two.Element} [obj] - Object to release from event listening. If none provided then the root {@link Two.Group} will be used.\n   * @returns {Two.Element} The object passed for event deallocation.\n   * @description Release a {@link Two.Element}’s events from memory and recurse through its children, effects, and/or vertices.\n   */\n  release(obj) {\n    let i, v, child;\n    if (typeof obj === \"undefined\") {\n      return this.release(this.scene);\n    }\n    if (typeof obj.unbind === \"function\") {\n      obj.unbind();\n    }\n    if (typeof obj.fill === \"object\" && typeof obj.fill.unbind === \"function\") {\n      obj.fill.unbind();\n    }\n    if (typeof obj.stroke === \"object\" && typeof obj.stroke.unbind === \"function\") {\n      obj.stroke.unbind();\n    }\n    if (obj.vertices) {\n      if (typeof obj.vertices.unbind === \"function\") {\n        try {\n          obj.vertices.unbind();\n        } catch (e) {\n        }\n      }\n      for (i = 0; i < obj.vertices.length; i++) {\n        v = obj.vertices[i];\n        if (typeof v.unbind === \"function\") {\n          v.unbind();\n        }\n        if (v.controls) {\n          if (v.controls.left && typeof v.controls.left.unbind === \"function\") {\n            v.controls.left.unbind();\n          }\n          if (v.controls.right && typeof v.controls.right.unbind === \"function\") {\n            v.controls.right.unbind();\n          }\n        }\n      }\n    }\n    if (obj.children) {\n      for (i = 0; i < obj.children.length; i++) {\n        child = obj.children[i];\n        this.release(child);\n      }\n      if (typeof obj.children.unbind === \"function\") {\n        try {\n          obj.children.unbind();\n        } catch (e) {\n        }\n      }\n    }\n    if (obj._renderer) {\n      if (obj._renderer.elem && obj._renderer.elem.parentNode) {\n        obj._renderer.elem.parentNode.removeChild(obj._renderer.elem);\n        delete obj._renderer.elem;\n      }\n      if (this.type === \"WebGLRenderer\" && this.renderer.ctx) {\n        const gl = this.renderer.ctx;\n        if (obj._renderer.texture) {\n          gl.deleteTexture(obj._renderer.texture);\n          delete obj._renderer.texture;\n        }\n        if (obj._renderer.positionBuffer) {\n          gl.deleteBuffer(obj._renderer.positionBuffer);\n          delete obj._renderer.positionBuffer;\n        }\n        if (obj._renderer.effect) {\n          obj._renderer.effect = null;\n        }\n      }\n      if (this.type === \"CanvasRenderer\" && obj._renderer.context) {\n        delete obj._renderer.context;\n      }\n    }\n    return obj;\n  }\n  /**\n   * @name Two#getShapesAtPoint\n   * @function\n   * @param {Number} x - X coordinate in world space.\n   * @param {Number} y - Y coordinate in world space.\n   * @param {Object} [options] - Hit test configuration.\n   * @param {Boolean} [options.visibleOnly=true] - Limit results to visible shapes.\n   * @param {Boolean} [options.includeGroups=false] - Include groups in the hit results.\n   * @param {('all'|'deepest')} [options.mode='all'] - Whether to return all intersecting shapes or only the top-most.\n   * @param {Boolean} [options.deepest] - Alias for `mode: 'deepest'`.\n   * @param {Number} [options.precision] - Segmentation precision for curved geometry.\n   * @param {Number} [options.tolerance=0] - Pixel tolerance applied to hit testing.\n   * @param {Boolean} [options.fill] - Override fill testing behaviour.\n   * @param {Boolean} [options.stroke] - Override stroke testing behaviour.\n   * @param {Function} [options.filter] - Predicate to filter shapes from the result set.\n   * @returns {Two.Shape[]} Ordered list of shapes under the specified point, front to back.\n   * @description Returns shapes underneath the provided coordinates. Coordinates are expected in world space (matching the renderer output).\n   * @nota-bene Delegates to {@link Two.Group#getShapesAtPoint} on the root scene.\n   */\n  getShapesAtPoint(x, y, options) {\n    if (this.scene && typeof this.scene.getShapesAtPoint === \"function\") {\n      return this.scene.getShapesAtPoint(x, y, options);\n    }\n    return [];\n  }\n  /**\n   * @name Two#update\n   * @function\n   * @fires update\n   * @description Update positions and calculations in one pass before rendering. Then render to the canvas.\n   * @nota-bene This function is called automatically if using {@link Two#play} or the `autostart` parameter in construction.\n   */\n  update() {\n    const animated = !!this._lastFrame;\n    const now = _.performance.now();\n    if (animated) {\n      this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3));\n    }\n    this._lastFrame = now;\n    if (this.fit && this.fit.domElement && !this.fit.attached) {\n      dom.bind(this.fit.domElement, \"resize\", this.fit);\n      this.fit.attached = true;\n      this.fit();\n    }\n    const width = this.width;\n    const height = this.height;\n    const renderer = this.renderer;\n    if (width !== renderer.width || height !== renderer.height) {\n      renderer.setSize(width, height, this.ratio);\n    }\n    this.trigger(Events.Types.update, this.frameCount, this.timeDelta);\n    return this.render();\n  }\n  /**\n   * @name Two#render\n   * @function\n   * @fires render\n   * @description Render all drawable and visible objects of the scene.\n   */\n  render() {\n    this.renderer.render();\n    return this.trigger(Events.Types.render, this.frameCount++);\n  }\n  // Convenience Methods\n  /**\n   * @name Two#add\n   * @function\n   * @param {(Two.Shape[]|...Two.Shape)} [objects] - An array of Two.js objects. Alternatively can add objects as individual arguments.\n   * @description A shorthand method to add specific Two.js objects to the scene.\n   */\n  add(objects) {\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    }\n    this.scene.add(objects);\n    return this;\n  }\n  /**\n   * @name Two#remove\n   * @function\n   * @param {(Two.Shape[]|...Two.Shape)} [objects] - An array of Two.js objects.\n   * @description A shorthand method to remove specific Two.js objects from the scene.\n   */\n  remove(objects) {\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    }\n    this.scene.remove(objects);\n    return this;\n  }\n  /**\n   * @name Two#clear\n   * @function\n   * @description Removes all objects from the instance's scene. If you intend to have the browser garbage collect this, don't forget to delete the references in your application as well.\n   */\n  clear() {\n    this.scene.remove(this.scene.children);\n    return this;\n  }\n  /**\n   * @name Two#makeLine\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} x2\n   * @param {Number} y2\n   * @returns {Two.Line}\n   * @description Creates a Two.js line and adds it to the scene.\n   */\n  makeLine(x1, y1, x2, y2) {\n    const line = new Line(x1, y1, x2, y2);\n    this.scene.add(line);\n    return line;\n  }\n  /**\n   * @name Two#makeArrow\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} x2\n   * @param {Number} y2\n   * @returns {Two.Path}\n   * @description Creates a Two.js arrow and adds it to the scene.\n   */\n  makeArrow(x1, y1, x2, y2, size) {\n    const headlen = typeof size === \"number\" ? size : 10;\n    const angle = Math.atan2(y2 - y1, x2 - x1);\n    const vertices = [\n      new Anchor(\n        x1,\n        y1,\n        void 0,\n        void 0,\n        void 0,\n        void 0,\n        Commands.move\n      ),\n      new Anchor(\n        x2,\n        y2,\n        void 0,\n        void 0,\n        void 0,\n        void 0,\n        Commands.line\n      ),\n      new Anchor(\n        x2 - headlen * Math.cos(angle - Math.PI / 4),\n        y2 - headlen * Math.sin(angle - Math.PI / 4),\n        void 0,\n        void 0,\n        void 0,\n        void 0,\n        Commands.line\n      ),\n      new Anchor(\n        x2,\n        y2,\n        void 0,\n        void 0,\n        void 0,\n        void 0,\n        Commands.move\n      ),\n      new Anchor(\n        x2 - headlen * Math.cos(angle + Math.PI / 4),\n        y2 - headlen * Math.sin(angle + Math.PI / 4),\n        void 0,\n        void 0,\n        void 0,\n        void 0,\n        Commands.line\n      )\n    ];\n    const path = new Path(vertices, false, false, true);\n    path.noFill();\n    path.cap = \"round\";\n    path.join = \"round\";\n    this.scene.add(path);\n    return path;\n  }\n  /**\n   * @name Two#makeRectangle\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} width\n   * @param {Number} height\n   * @returns {Two.Rectangle}\n   * @description Creates a Two.js rectangle and adds it to the scene.\n   */\n  makeRectangle(x, y, width, height) {\n    const rect = new Rectangle(x, y, width, height);\n    this.scene.add(rect);\n    return rect;\n  }\n  /**\n   * @name Two#makeRoundedRectangle\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} width\n   * @param {Number} height\n   * @param {Number} sides\n   * @returns {Two.RoundedRectangle}\n   * @description Creates a Two.js rounded rectangle and adds it to the scene.\n   */\n  makeRoundedRectangle(x, y, width, height, sides) {\n    const rect = new RoundedRectangle(x, y, width, height, sides);\n    this.scene.add(rect);\n    return rect;\n  }\n  /**\n   * @name Two#makeCircle\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} radius\n   * @param {Number} [resolution=4]\n   * @returns {Two.Circle}\n   * @description Creates a Two.js circle and adds it to the scene.\n   */\n  makeCircle(x, y, radius, resolution) {\n    const circle = new Circle(x, y, radius, resolution);\n    this.scene.add(circle);\n    return circle;\n  }\n  /**\n   * @name Two#makeEllipse\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} rx\n   * @param {Number} ry\n   * @param {Number} [resolution=4]\n   * @returns {Two.Ellipse}\n   * @description Creates a Two.js ellipse and adds it to the scene.\n   */\n  makeEllipse(x, y, rx, ry, resolution) {\n    const ellipse = new Ellipse(x, y, rx, ry, resolution);\n    this.scene.add(ellipse);\n    return ellipse;\n  }\n  /**\n   * @name Two#makeStar\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} outerRadius\n   * @param {Number} innerRadius\n   * @param {Number} sides\n   * @returns {Two.Star}\n   * @description Creates a Two.js star and adds it to the scene.\n   */\n  makeStar(x, y, outerRadius, innerRadius, sides) {\n    const star = new Star(x, y, outerRadius, innerRadius, sides);\n    this.scene.add(star);\n    return star;\n  }\n  /**\n   * @name Two#makeCurve\n   * @function\n   * @param {Two.Anchor[]} [points] - An array of {@link Two.Anchor} points.\n   * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n   * @returns {Two.Path} - Where `path.curved` is set to `true`.\n   * @description Creates a Two.js path that is curved and adds it to the scene.\n   * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n   */\n  makeCurve(points) {\n    const l = arguments.length;\n    if (!Array.isArray(points)) {\n      points = [];\n      for (let i = 0; i < l; i += 2) {\n        const x = arguments[i];\n        if (typeof x !== \"number\") {\n          break;\n        }\n        const y = arguments[i + 1];\n        points.push(new Anchor(x, y));\n      }\n    }\n    const last = arguments[l - 1];\n    const curve = new Path(\n      points,\n      !(typeof last === \"boolean\" ? last : void 0),\n      true\n    );\n    const rect = curve.getBoundingClientRect();\n    curve.center().translation.set(rect.left + rect.width / 2, rect.top + rect.height / 2);\n    this.scene.add(curve);\n    return curve;\n  }\n  /**\n   * @name Two#makePolygon\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} radius\n   * @param {Number} sides\n   * @returns {Two.Polygon}\n   * @description Creates a Two.js polygon and adds it to the scene.\n   */\n  makePolygon(x, y, radius, sides) {\n    const poly = new Polygon(x, y, radius, sides);\n    this.scene.add(poly);\n    return poly;\n  }\n  /**\n   * @name Two#makeArcSegment\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} innerRadius\n   * @param {Number} outerRadius\n   * @param {Number} startAngle\n   * @param {Number} endAngle\n   * @param {Number} [resolution=Two.Resolution] - The number of vertices that should comprise the arc segment.\n   * @returns {Two.ArcSegment}\n   */\n  makeArcSegment(x, y, innerRadius, outerRadius, startAngle, endAngle, resolution) {\n    const arcSegment = new ArcSegment(\n      x,\n      y,\n      innerRadius,\n      outerRadius,\n      startAngle,\n      endAngle,\n      resolution\n    );\n    this.scene.add(arcSegment);\n    return arcSegment;\n  }\n  /**\n   * @name Two#makePoints\n   * @function\n   * @param {Two.Vector[]} [points] - An array of {@link Two.Vector} points\n   * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual agrguments. These will be combined into {@link Two.Vector}s for use in the points object.\n   * @returns {Two.Points}\n   * @description Creates a Two.js points object and adds it to the current scene.\n   */\n  makePoints(p) {\n    const l = arguments.length;\n    let vertices = p;\n    if (!Array.isArray(p)) {\n      vertices = [];\n      for (let i = 0; i < l; i += 2) {\n        const x = arguments[i];\n        if (typeof x !== \"number\") {\n          break;\n        }\n        const y = arguments[i + 1];\n        vertices.push(new Vector(x, y));\n      }\n    }\n    const points = new Points(vertices);\n    this.scene.add(points);\n    return points;\n  }\n  /**\n   * @name Two#makePath\n   * @function\n   * @param {Two.Anchor[]} [points] - An array of {@link Two.Anchor} points\n   * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n   * @returns {Two.Path}\n   * @description Creates a Two.js path and adds it to the scene.\n   * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n   */\n  makePath(p) {\n    const l = arguments.length;\n    let points = p;\n    if (!Array.isArray(p)) {\n      points = [];\n      for (let i = 0; i < l; i += 2) {\n        const x = arguments[i];\n        if (typeof x !== \"number\") {\n          break;\n        }\n        const y = arguments[i + 1];\n        points.push(new Anchor(x, y));\n      }\n    }\n    const last = arguments[l - 1];\n    const path = new Path(\n      points,\n      !(typeof last === \"boolean\" ? last : void 0)\n    );\n    const rect = path.getBoundingClientRect();\n    if (typeof rect.top === \"number\" && typeof rect.left === \"number\" && typeof rect.right === \"number\" && typeof rect.bottom === \"number\") {\n      path.center().translation.set(\n        rect.left + rect.width / 2,\n        rect.top + rect.height / 2\n      );\n    }\n    this.scene.add(path);\n    return path;\n  }\n  /**\n   * @name Two#makeText\n   * @function\n   * @param {String} message\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Object} [styles] - An object to describe any of the {@link Two.Text.Properties} including `fill`, `stroke`, `linewidth`, `family`, `alignment`, `leading`, `opacity`, etc..\n   * @returns {Two.Text}\n   * @description Creates a Two.js text object and adds it to the scene.\n   */\n  makeText(message, x, y, styles) {\n    const text = new Text(message, x, y, styles);\n    this.add(text);\n    return text;\n  }\n  /**\n   * @name Two#makeLinearGradient\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} x2\n   * @param {Number} y2\n   * @param {...Two.Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n   * @returns {Two.LinearGradient}\n   * @description Creates a Two.js linear gradient and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n   */\n  makeLinearGradient(x1, y1, x2, y2) {\n    const stops = Array.prototype.slice.call(arguments, 4);\n    const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n    this.add(gradient);\n    return gradient;\n  }\n  /**\n   * @name Two#makeRadialGradient\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} radius\n   * @param {...Two.Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n   * @returns {Two.RadialGradient}\n   * @description Creates a Two.js linear-gradient object and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n   */\n  makeRadialGradient(x1, y1, radius) {\n    const stops = Array.prototype.slice.call(arguments, 3);\n    const gradient = new RadialGradient(x1, y1, radius, stops);\n    this.add(gradient);\n    return gradient;\n  }\n  /**\n   * @name Two#makeSprite\n   * @function\n   * @param {(String|Two.Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} [columns=1]\n   * @param {Number} [rows=1]\n   * @param {Number} [frameRate=0]\n   * @param {Boolean} [autostart=false]\n   * @returns {Two.Sprite}\n   * @description Creates a Two.js sprite object and adds it to the scene. Sprites can be used for still images as well as animations.\n   */\n  makeSprite(src, x, y, columns, rows, frameRate, autostart) {\n    const sprite = new Sprite(src, x, y, columns, rows, frameRate);\n    if (autostart) {\n      sprite.play();\n    }\n    this.add(sprite);\n    return sprite;\n  }\n  /**\n   * @name Two#makeImage\n   * @function\n   * @param {(String|Two.Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} width\n   * @param {Number} height\n   * @param {String} [mode=\"fill\"]\n   * @returns {Two.Image}\n   * @description Creates a Two.js image object and adds it to the scene. Images are scaled to fit the provided width and height.\n   */\n  makeImage(src, x, y, width, height, mode) {\n    const image = new Image2(src, x, y, width, height, mode);\n    this.add(image);\n    return image;\n  }\n  /**\n   * @name Two#makeImageSequence\n   * @function\n   * @param {(String[]|Two.Texture[])} src - An array of paths or of {@link Two.Textures}.\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} [frameRate=0]\n   * @param {Boolean} [autostart=false]\n   * @returns {Two.ImageSequence}\n   * @description Creates a Two.js image sequence object and adds it to the scene.\n   */\n  makeImageSequence(src, x, y, frameRate, autostart) {\n    const imageSequence = new ImageSequence(src, x, y, frameRate);\n    if (autostart) {\n      imageSequence.play();\n    }\n    this.add(imageSequence);\n    return imageSequence;\n  }\n  /**\n   * @name Two#makeTexture\n   * @function\n   * @param {(String|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement)} [src] - The URL path to an image or a DOM image-like element.\n   * @param {Function} [callback] - Function to be invoked when the image is loaded.\n   * @returns {Two.Texture}\n   * @description Creates a Two.js texture object.\n   */\n  makeTexture(src, callback) {\n    const texture = new Texture(src, callback);\n    return texture;\n  }\n  /**\n   * @name Two#makeGroup\n   * @function\n   * @param {(Two.Shape[]|...Two.Shape)} [objects] - Two.js objects to be added to the group in the form of an array or as individual arguments.\n   * @returns {Two.Group}\n   * @description Creates a Two.js group object and adds it to the scene.\n   */\n  makeGroup(objects) {\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    }\n    const group = new Group();\n    this.scene.add(group);\n    group.add(objects);\n    return group;\n  }\n  /**\n   * @name Two#interpret\n   * @function\n   * @param {SVGElement} svg - The SVG node to be parsed.\n   * @param {Boolean} shallow - Don't create a top-most group but append all content directly.\n   * @param {Boolean} [add=true] – Automatically add the reconstructed SVG node to scene.\n   * @returns {Two.Group}\n   * @description Interpret an SVG Node and add it to this instance's scene. The distinction should be made that this doesn't `import` svg's, it solely interprets them into something compatible for Two.js - this is slightly different than a direct transcription.\n   */\n  interpret(svg2, shallow, add) {\n    const tag = svg2.tagName.toLowerCase();\n    add = typeof add !== \"undefined\" ? add : true;\n    if (!(tag in read)) {\n      return null;\n    }\n    const node = read[tag].call(this, svg2);\n    if (add) {\n      this.add(shallow && node instanceof Group ? node.children : node);\n    } else if (node.parent) {\n      node.remove();\n    }\n    return node;\n  }\n  /**\n   * @name Two#load\n   * @function\n   * @param {String|SVGElement} pathOrSVGContent - The URL path of an SVG file or an SVG document as text.\n   * @param {Function} [callback] - Function to call once loading has completed.\n   * @returns {Two.Group}\n   * @description Load an SVG file or SVG text and interpret it into Two.js legible objects.\n   */\n  load(pathOrSVGContent, callback) {\n    const group = new Group();\n    let elem, i, child;\n    const attach = function(data) {\n      dom.temp.innerHTML = data;\n      for (i = 0; i < dom.temp.children.length; i++) {\n        elem = dom.temp.children[i];\n        child = this.interpret(elem, false, false);\n        if (child !== null) {\n          group.add(child);\n        }\n      }\n      if (typeof callback === \"function\") {\n        const svg2 = dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children;\n        callback(group, svg2);\n      }\n    }.bind(this);\n    if (/\\.svg$/i.test(pathOrSVGContent)) {\n      xhr(pathOrSVGContent, attach);\n      return group;\n    }\n    attach(pathOrSVGContent);\n    return group;\n  }\n};\n__publicField(_Two, \"NextFrameId\", Constants.NextFrameId);\n// Primitive\n/**\n * @name Two.Types\n * @property {Object} - The different rendering types available in the library.\n */\n__publicField(_Two, \"Types\", Constants.Types);\n/**\n * @name Two.Version\n * @property {String} - The current working version of the library, `$version`.\n */\n__publicField(_Two, \"Version\", Constants.Version);\n/**\n * @name Two.PublishDate\n * @property {String} - The automatically generated publish date in the build process to verify version release candidates.\n */\n__publicField(_Two, \"PublishDate\", Constants.PublishDate);\n/**\n * @name Two.Identifier\n * @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.\n */\n__publicField(_Two, \"Identifier\", Constants.Identifier);\n/**\n * @name Two.Resolution\n * @property {Number} - Default amount of vertices to be used for interpreting Arcs and ArcSegments.\n */\n__publicField(_Two, \"Resolution\", Constants.Resolution);\n/**\n * @name Two.AutoCalculateImportedMatrices\n * @property {Boolean} - When importing SVGs through the {@link Two#interpret} and {@link Two#load}, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n * @nota-bene `false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n */\n__publicField(_Two, \"AutoCalculateImportedMatrices\", Constants.AutoCalculateImportedMatrices);\n/**\n * @name Two.Instances\n * @property {Two[]} - Registered list of all Two.js instances in the current session.\n */\n__publicField(_Two, \"Instances\", Constants.Instances);\n/**\n * @function Two.uniqueId\n * @description Simple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n * @returns {Number} Ever increasing Number.\n */\n__publicField(_Two, \"uniqueId\", Constants.uniqueId);\n__publicField(_Two, \"Anchor\", Anchor);\n__publicField(_Two, \"Collection\", Collection);\n__publicField(_Two, \"Events\", Events);\n__publicField(_Two, \"Group\", Group);\n__publicField(_Two, \"Matrix\", Matrix2);\n__publicField(_Two, \"Path\", Path);\n__publicField(_Two, \"Registry\", Registry);\n__publicField(_Two, \"Element\", Element);\n__publicField(_Two, \"Shape\", Shape);\n__publicField(_Two, \"Text\", Text);\n__publicField(_Two, \"Vector\", Vector);\n__publicField(_Two, \"Gradient\", Gradient);\n__publicField(_Two, \"Image\", Image2);\n__publicField(_Two, \"ImageSequence\", ImageSequence);\n__publicField(_Two, \"LinearGradient\", LinearGradient);\n__publicField(_Two, \"RadialGradient\", RadialGradient);\n__publicField(_Two, \"Sprite\", Sprite);\n__publicField(_Two, \"Stop\", Stop);\n__publicField(_Two, \"Texture\", Texture);\n__publicField(_Two, \"ArcSegment\", ArcSegment);\n__publicField(_Two, \"Circle\", Circle);\n__publicField(_Two, \"Ellipse\", Ellipse);\n__publicField(_Two, \"Line\", Line);\n__publicField(_Two, \"Points\", Points);\n__publicField(_Two, \"Polygon\", Polygon);\n__publicField(_Two, \"Rectangle\", Rectangle);\n__publicField(_Two, \"RoundedRectangle\", RoundedRectangle);\n__publicField(_Two, \"Star\", Star);\n__publicField(_Two, \"CanvasRenderer\", Renderer);\n__publicField(_Two, \"SVGRenderer\", Renderer2);\n__publicField(_Two, \"WebGLRenderer\", Renderer3);\n/**\n * @name Two.Commands\n * @property {Object} - Map of possible path commands. Taken from the SVG specification. Commands include: `move`, `line`, `curve`, `arc`, and `close`.\n */\n__publicField(_Two, \"Commands\", Commands);\n/**\n * @name Two.Utils\n * @property {Object} Utils - A massive object filled with utility functions and properties.\n * @property {Object} Two.Utils.read - A collection of SVG parsing functions indexed by element name.\n * @property {Function} Two.Utils.read.path - Parse SVG path element or `d` attribute string.\n */\n__publicField(_Two, \"Utils\", Utils);\nvar Two = _Two;\nfunction fitToWindow() {\n  const wr = document.body.getBoundingClientRect();\n  const width = this.width = wr.width;\n  const height = this.height = wr.height;\n  this.renderer.setSize(width, height, this.ratio);\n}\nfunction fitToParent() {\n  const parent = this.renderer.domElement.parentElement;\n  if (!parent) {\n    console.warn(\"Two.js: Attempting to fit to parent, but no parent found.\");\n    return;\n  }\n  const wr = parent.getBoundingClientRect();\n  const width = this.width = wr.width;\n  const height = this.height = wr.height;\n  this.renderer.setSize(width, height, this.ratio);\n}\nfunction updateDimensions(width, height) {\n  this.width = width;\n  this.height = height;\n  this.trigger(Events.Types.resize, width, height);\n}\nvar raf = dom.getRequestAnimationFrame();\nfunction loop() {\n  for (let i = 0; i < Two.Instances.length; i++) {\n    const t = Two.Instances[i];\n    if (t.playing) {\n      t.update();\n    }\n  }\n  Two.NextFrameId = raf(loop);\n}\nraf.init = function() {\n  loop();\n  raf.init = function() {\n  };\n};\nexport {\n  Two as default\n};\n"
  },
  {
    "path": "deploy.sh",
    "content": "#!/usr/bin/env sh\n\n# abort on errors\nset -e\n\n# build\nnpm run docs:build\n\n# navigate into the build output directory\ncd wiki/.vuepress/dist\n\n# if you are deploying to a custom domain\necho 'two.js.org' > CNAME\n\ngit init\ngit add -A\ngit commit -m 'deploy'\n\ngit push -f git@github.com:jonobr1/two.js.git main:gh-pages\n\ncd -\n"
  },
  {
    "path": "extras/js/arc.js",
    "content": "(function () {\n  const TWO_PI = Math.PI * 2;\n  const cos = Math.cos,\n    sin = Math.sin;\n\n  /**\n   * @name Two.Arc\n   * @class\n   * @extends Two.Path\n   * @param {Number} [x=0] - The x position of the arc.\n   * @param {Number} [y=0] - The y position of the arc.\n   * @param {Number} [width=0] - The width, horizontal diameter, of the arc.\n   * @param {Number} [height=0] - The height, vertical diameter, of the arc.\n   * @param {Number} [startAngle=0] - The starting angle of the arc in radians.\n   * @param {Number} [endAngle=6.283] - The ending angle of the arc in radians.\n   * @param {Number} [resolution=4] - The number of vertices used to construct the circle.\n   */\n  class Arc extends Two.Path {\n    _flagWidth = false;\n    _flagHeight = false;\n    _flagStartAngle = false;\n    _flagEndAngle = false;\n\n    _width = 0;\n    _height = 0;\n    _startAngle = 0;\n    _endAngle = TWO_PI;\n\n    constructor(x, y, width, height, startAngle, endAngle, resolution) {\n      if (typeof resolution !== 'number') {\n        resolution = Two.Resolution;\n      }\n\n      const points = [];\n      for (let i = 0; i < resolution; i++) {\n        points.push(new Two.Anchor());\n      }\n\n      super(points);\n\n      for (let j = 0; j < Arc.Properties.length; j++) {\n        const prop = Arc.Properties[j];\n        Object.defineProperty(this, prop, protos[prop]);\n      }\n\n      this.curved = true;\n\n      /**\n       * @name Two.Arc#width\n       * @property {Number} - The horizontal size of the arc.\n       */\n      if (typeof width === 'number') {\n        this.width = width;\n      }\n\n      /**\n       * @name Two.Arc#height\n       * @property {Number} - The vertical size of the arc.\n       */\n      if (typeof height === 'number') {\n        this.height = height;\n      }\n\n      /**\n       * @name Two.ArcSegment#startAngle\n       * @property {Number} - The angle of one side for the arc segment.\n       */\n      if (typeof startAngle === 'number') {\n        this.startAngle = startAngle;\n      }\n\n      /**\n       * @name Two.ArcSegment#endAngle\n       * @property {Number} - The angle of the other side for the arc segment.\n       */\n      if (typeof endAngle === 'number') {\n        this.endAngle = endAngle;\n      }\n\n      this._update();\n\n      if (typeof x === 'number') {\n        this.position.x = x;\n      }\n      if (typeof y === 'number') {\n        this.position.y = y;\n      }\n    }\n\n    /**\n     * @name Two.Arc.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Arc}.\n     */\n    static Properties = ['width', 'height', 'startAngle', 'endAngle'];\n\n    /**\n     * @name Two.Arc#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update() {\n      if (\n        this._flagVertices ||\n        this._flagRadius ||\n        this._flagStartAngle ||\n        this._flagWidth ||\n        this._flagHeight ||\n        this._flagEndAngle\n      ) {\n        const { width, height, startAngle, endAngle, vertices } = this;\n        const rx = width / 2;\n        const ry = height / 2;\n\n        for (let i = 0; i < vertices.length; i++) {\n          const v = vertices[i];\n          const pct = i / (vertices.length - 1);\n          const theta = pct * (endAngle - startAngle) + startAngle;\n\n          v.x = rx * cos(theta);\n          v.y = ry * sin(theta);\n        }\n      }\n\n      super._update.call(this);\n\n      return this;\n    }\n\n    /**\n     * @name Two.Arc#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset() {\n      super.flagReset.call(this);\n\n      this._flagWidth =\n        this._flagHeight =\n        this._flagStartAngle =\n        this._flagEndAngle =\n          false;\n\n      return this;\n    }\n\n    /**\n     * @name Two.Arc#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.ArcSegment}\n     * @description Create a new instance of {@link Two.ArcSegment} with the same properties of the current path.\n     */\n    clone() {\n      const { width, height, startAngle, endAngle } = this;\n      const resolution = this.vertices.length;\n\n      const clone = new Arc(\n        0,\n        0,\n        width,\n        height,\n        startAngle,\n        endAngle,\n        resolution\n      );\n\n      clone.position.copy(this.position);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n\n      for (let i = 0; i < Two.Path.Properties.length; i++) {\n        const k = Two.Path.Properties[i];\n        clone[k] = this[k];\n      }\n\n      if (parent) {\n        parent.add(clone);\n      }\n\n      return clone;\n    }\n  }\n\n  const protos = {\n    width: {\n      enumerable: true,\n      get: function () {\n        return this._width;\n      },\n      set: function (v) {\n        if (v !== this._width) {\n          this._width = v;\n          this._flagWidth = true;\n        }\n      },\n    },\n    height: {\n      enumerable: true,\n      get: function () {\n        return this._height;\n      },\n      set: function (v) {\n        if (v !== this._height) {\n          this._height = v;\n          this._flagHeight = true;\n        }\n      },\n    },\n    startAngle: {\n      enumerable: true,\n      get: function () {\n        return this._startAngle;\n      },\n      set: function (v) {\n        if (v !== this._startAngle) {\n          this._startAngle = v;\n          this._flagStartAngle = true;\n        }\n      },\n    },\n    endAngle: {\n      enumerable: true,\n      get: function () {\n        return this._endAngle;\n      },\n      set: function (v) {\n        if (v !== this._endAngle) {\n          this._endAngle = v;\n          this._flagEndAngle = true;\n        }\n      },\n    },\n  };\n\n  Two.Arc = Arc;\n})();\n"
  },
  {
    "path": "extras/js/zui.js",
    "content": "(function () {\n  class Surface {\n    constructor(object) {\n      this.object = object;\n    }\n\n    limits(min, max) {\n      const min_exists = typeof min !== 'undefined';\n      const max_exists = typeof max !== 'undefined';\n\n      if (!max_exists && !min_exists) {\n        return { min: this.min, max: this.max };\n      }\n\n      this.min = min_exists ? min : this.min;\n      this.max = max_exists ? max : this.max;\n\n      return this;\n    }\n\n    apply(px, py, s) {\n      this.object.translation.set(px, py);\n      this.object.scale = s;\n      return this;\n    }\n  }\n\n  /**\n   * @name Two.ZUI\n   * @class\n   * @param {Two.Group} group - The scene or group to\n   * @param {HTMLElement} [domElement=document.body] - The HTML Element to attach event listeners to.\n   */\n  class ZUI {\n    constructor(group, domElement) {\n      this.limits = {\n        scale: ZUI.Limit.clone(),\n        x: ZUI.Limit.clone(),\n        y: ZUI.Limit.clone(),\n      };\n\n      this.viewport = domElement || document.body;\n      this.viewportOffset = {\n        top: 0,\n        left: 0,\n        matrix: new Two.Matrix(),\n      };\n\n      this.surfaceMatrix = new Two.Matrix();\n\n      this.surfaces = [];\n      this.reset();\n      this.updateSurface();\n\n      this.add(new Surface(group));\n    }\n\n    static Surface = Surface;\n\n    static Clamp(v, min, max) {\n      return Math.min(Math.max(v, min), max);\n    }\n\n    static Limit = {\n      min: -Infinity,\n      max: Infinity,\n      clone: function () {\n        const result = {};\n        for (let k in this) {\n          result[k] = this[k];\n        }\n        return result;\n      },\n    };\n\n    static TranslateMatrix(m, x, y) {\n      m.elements[2] += x;\n      m.elements[5] += y;\n      return m;\n    }\n\n    static PositionToScale(pos) {\n      return Math.exp(pos);\n    }\n\n    static ScaleToPosition(scale) {\n      return Math.log(scale);\n    }\n\n    //\n\n    add(surface) {\n      this.surfaces.push(surface);\n      const limits = surface.limits();\n      this.addLimits(limits.min, limits.max);\n      return this;\n    }\n\n    addLimits(min, max) {\n      if (typeof min !== 'undefined') {\n        if (this.limits.scale.min) {\n          this.limits.scale.min = Math.max(min, this.limits.scale.min);\n        } else {\n          this.limits.scale.min = min;\n        }\n      }\n\n      if (typeof max === 'undefined') {\n        return this;\n      }\n\n      if (this.limits.scale.max) {\n        this.limits.scale.max = Math.min(max, this.limits.scale.max);\n      } else {\n        this.limits.scale.max = max;\n      }\n\n      return this;\n    }\n\n    clientToSurface(a, b, c) {\n      this.updateOffset();\n      const m = this.surfaceMatrix.inverse();\n      let x, y, z;\n      if (arguments.length === 1) {\n        const v = a;\n        x = typeof v.x === 'number' ? v.x : 0;\n        y = typeof v.y === 'number' ? v.y : 0;\n        z = typeof v.z === 'number' ? v.z : 1;\n      } else {\n        x = typeof a === 'number' ? a : 0;\n        y = typeof b === 'number' ? b : 0;\n        z = typeof c === 'number' ? c : 1;\n      }\n      const n = this.viewportOffset.matrix.inverse().multiply(x, y, z);\n      const r = m.multiply(n[0], n[1], n[2]);\n      return { x: r[0], y: r[1], z: r[2] };\n    }\n\n    surfaceToClient(a, b, c) {\n      this.updateOffset();\n      const vo = this.viewportOffset.matrix.clone();\n      let x, y, z;\n      if (arguments.length === 1) {\n        const v = a;\n        x = typeof v.x === 'number' ? v.x : 0;\n        y = typeof v.y === 'number' ? v.y : 0;\n        z = typeof v.z === 'number' ? v.z : 1;\n      } else {\n        x = typeof a === 'number' ? a : 0;\n        y = typeof b === 'number' ? b : 0;\n        z = typeof c === 'number' ? c : 1;\n      }\n      const sm = this.surfaceMatrix.multiply(x, y, z);\n      const r = vo.multiply(sm[0], sm[1], sm[2]);\n      return { x: r[0], y: r[1], z: r[2] };\n    }\n\n    zoomBy(byF, clientX, clientY) {\n      const s = ZUI.PositionToScale(this.zoom + byF);\n      this.zoomSet(s, clientX, clientY);\n      return this;\n    }\n\n    zoomSet(zoom, clientX, clientY) {\n      const newScale = this.fitToLimits(zoom);\n      this.zoom = ZUI.ScaleToPosition(newScale);\n\n      if (newScale === this.scale) {\n        return this;\n      }\n\n      const sf = this.clientToSurface(clientX, clientY);\n      const scaleBy = newScale / this.scale;\n\n      this.surfaceMatrix.scale(scaleBy);\n      this.scale = newScale;\n\n      const c = this.surfaceToClient(sf);\n      const dx = clientX - c.x;\n      const dy = clientY - c.y;\n      this.translateSurface(dx, dy);\n\n      return this;\n    }\n\n    translateSurface(x, y) {\n      ZUI.TranslateMatrix(this.surfaceMatrix, x, y);\n      this.updateSurface();\n      return this;\n    }\n\n    updateOffset() {\n      const rect = this.viewport.getBoundingClientRect();\n\n      this.viewportOffset.left = rect.left - document.body.scrollLeft;\n      this.viewportOffset.top = rect.top - document.body.scrollTop;\n\n      this.viewportOffset.matrix\n        .identity()\n        .translate(this.viewportOffset.left, this.viewportOffset.top);\n\n      return this;\n    }\n\n    updateSurface() {\n      const e = this.surfaceMatrix.elements;\n      for (let i = 0; i < this.surfaces.length; i++) {\n        this.surfaces[i].apply(e[2], e[5], e[0]);\n      }\n\n      return this;\n    }\n\n    reset() {\n      this.zoom = 0;\n      this.scale = 1.0;\n      this.surfaceMatrix.identity();\n      this.updateSurface();\n      return this;\n    }\n\n    fitToLimits(s) {\n      return ZUI.Clamp(s, this.limits.scale.min, this.limits.scale.max);\n    }\n  }\n\n  Two.ZUI = ZUI;\n})();\n"
  },
  {
    "path": "extras/jsm/arc.js",
    "content": "import { TWO_PI } from '../../src/utils/math.js';\nimport { Constants } from '../../src/constants.js';\n\nimport { Anchor } from '../../src/anchor.js';\nimport { Path } from '../../src/path.js';\n\nconst cos = Math.cos,\n  sin = Math.sin;\n\n/**\n * @name Two.Arc\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the arc.\n * @param {Number} [y=0] - The y position of the arc.\n * @param {Number} [width=0] - The width, horizontal diameter, of the arc.\n * @param {Number} [height=0] - The height, vertical diameter, of the arc.\n * @param {Number} [startAngle=0] - The starting angle of the arc in radians.\n * @param {Number} [endAngle=6.283] - The ending angle of the arc in radians.\n * @param {Number} [resolution=4] - The number of vertices used to construct the circle.\n */\nexport class Arc extends Path {\n  _flagWidth = false;\n  _flagHeight = false;\n  _flagStartAngle = false;\n  _flagEndAngle = false;\n\n  _width = 0;\n  _height = 0;\n  _startAngle = 0;\n  _endAngle = TWO_PI;\n\n  constructor(x, y, width, height, startAngle, endAngle, resolution) {\n    if (typeof resolution !== 'number') {\n      resolution = Constants.Resolution;\n    }\n\n    const points = [];\n    for (let i = 0; i < resolution; i++) {\n      points.push(new Anchor());\n    }\n\n    super(points);\n\n    for (let j = 0; j < Arc.Properties.length; j++) {\n      const prop = Arc.Properties[j];\n      Object.defineProperty(this, prop, protos[prop]);\n    }\n\n    this.curved = true;\n\n    /**\n     * @name Two.Arc#width\n     * @property {Number} - The horizontal size of the arc.\n     */\n    if (typeof width === 'number') {\n      this.width = width;\n    }\n\n    /**\n     * @name Two.Arc#height\n     * @property {Number} - The vertical size of the arc.\n     */\n    if (typeof height === 'number') {\n      this.height = height;\n    }\n\n    /**\n     * @name Two.ArcSegment#startAngle\n     * @property {Number} - The angle of one side for the arc segment.\n     */\n    if (typeof startAngle === 'number') {\n      this.startAngle = startAngle;\n    }\n\n    /**\n     * @name Two.ArcSegment#endAngle\n     * @property {Number} - The angle of the other side for the arc segment.\n     */\n    if (typeof endAngle === 'number') {\n      this.endAngle = endAngle;\n    }\n\n    this._update();\n\n    if (typeof x === 'number') {\n      this.position.x = x;\n    }\n    if (typeof y === 'number') {\n      this.position.y = y;\n    }\n  }\n\n  /**\n   * @name Two.Arc.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Arc}.\n   */\n  static Properties = ['width', 'height', 'startAngle', 'endAngle'];\n\n  /**\n   * @name Two.Arc#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (\n      this._flagVertices ||\n      this._flagRadius ||\n      this._flagWidth ||\n      this._flagHeight ||\n      this._flagStartAngle ||\n      this._flagEndAngle\n    ) {\n      const { width, height, startAngle, endAngle, vertices } = this;\n      const rx = width / 2;\n      const ry = height / 2;\n\n      for (let i = 0; i < vertices.length; i++) {\n        const v = vertices[i];\n        const pct = i / (vertices.length - 1);\n        const theta = pct * (endAngle - startAngle) + startAngle;\n\n        v.x = rx * cos(theta);\n        v.y = ry * sin(theta);\n      }\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Arc#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    super.flagReset.call(this);\n\n    this._flagWidth =\n      this._flagHeight =\n      this._flagStartAngle =\n      this._flagEndAngle =\n        false;\n\n    return this;\n  }\n\n  /**\n   * @name Two.Arc#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.ArcSegment}\n   * @description Create a new instance of {@link Two.ArcSegment} with the same properties of the current path.\n   */\n  clone() {\n    const { width, height, startAngle, endAngle } = this;\n    const resolution = this.vertices.length;\n\n    const clone = new Arc(\n      0,\n      0,\n      width,\n      height,\n      startAngle,\n      endAngle,\n      resolution\n    );\n\n    clone.position.copy(this.position);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n}\n\nconst protos = {\n  width: {\n    enumerable: true,\n    get: function () {\n      return this._width;\n    },\n    set: function (v) {\n      if (v !== this._width) {\n        this._width = v;\n        this._flagWidth = true;\n      }\n    },\n  },\n  height: {\n    enumerable: true,\n    get: function () {\n      return this._height;\n    },\n    set: function (v) {\n      if (v !== this._height) {\n        this._height = v;\n        this._flagHeight = true;\n      }\n    },\n  },\n  startAngle: {\n    enumerable: true,\n    get: function () {\n      return this._startAngle;\n    },\n    set: function (v) {\n      if (v !== this._startAngle) {\n        this._startAngle = v;\n        this._flagStartAngle = true;\n      }\n    },\n  },\n  endAngle: {\n    enumerable: true,\n    get: function () {\n      return this._endAngle;\n    },\n    set: function (v) {\n      if (v !== this._endAngle) {\n        this._endAngle = v;\n        this._flagEndAngle = true;\n      }\n    },\n  },\n};\n"
  },
  {
    "path": "extras/jsm/zui.d.ts",
    "content": "declare module 'two.js/extras/jsm/zui' {\n  /**\n   * @name Two.ZUI\n   * @class\n   * @param {Group} group - The scene or group to\n   * @param {HTMLElement} [domElement=document.body] - The HTML Element to attach event listeners to.\n   */\n  export class ZUI {\n    static Surface: Surface;\n    static Clamp(v: any, min: any, max: any): number;\n    static Limit: {\n      min: number;\n      max: number;\n      clone: () => {};\n    };\n    static TranslateMatrix(m: any, x: any, y: any): any;\n    static PositionToScale(pos: any): number;\n    static ScaleToPosition(scale: any): number;\n    constructor(group?: Group, domElement?: HTMLElement);\n    limits: {\n      scale: {};\n      x: {};\n      y: {};\n    };\n    viewport: any;\n    viewportOffset: {\n      top: number;\n      left: number;\n      matrix: Matrix;\n    };\n    surfaceMatrix: Matrix;\n    surfaces: any[];\n    add(surface: any): ZUI;\n    addLimits(min: number, max: number, type?: number): ZUI;\n    clientToSurface(v?: { x?: number; y?: number; z?: number }): {\n      x: number;\n      y: number;\n      z: number;\n    };\n    surfaceToClient(v?: { x?: number; y?: number; z?: number }): {\n      x: number;\n      y: number;\n      z: number;\n    };\n    zoomBy(byF: any, clientX: any, clientY: any): ZUI;\n    zoomSet(zoom: any, clientX: any, clientY: any): ZUI;\n    zoom: number;\n    scale: any;\n    translateSurface(x: any, y: any): ZUI;\n    updateOffset(): ZUI;\n    updateSurface(): ZUI;\n    reset(): ZUI;\n    fitToLimits(s: any): number;\n  }\n  import { Matrix } from 'two.js/src/matrix';\n  import { Group } from 'two.js/src/group';\n  class Surface {\n    constructor(object: any);\n    object: any;\n    limits(\n      min: any,\n      max: any\n    ):\n      | Surface\n      | {\n          min: any;\n          max: any;\n        };\n    min: any;\n    max: any;\n    apply(px: any, py: any, s: any): Surface;\n  }\n}\n"
  },
  {
    "path": "extras/jsm/zui.js",
    "content": "import { Matrix } from '../../src/matrix.js';\n\nclass Surface {\n  constructor(object) {\n    this.object = object;\n  }\n\n  limits(min, max) {\n    const min_exists = typeof min !== 'undefined';\n    const max_exists = typeof max !== 'undefined';\n\n    if (!max_exists && !min_exists) {\n      return { min: this.min, max: this.max };\n    }\n\n    this.min = min_exists ? min : this.min;\n    this.max = max_exists ? max : this.max;\n\n    return this;\n  }\n\n  apply(px, py, s) {\n    this.object.translation.set(px, py);\n    this.object.scale = s;\n    return this;\n  }\n}\n\n/**\n * @name Two.ZUI\n * @class\n * @param {Two.Group} group - The scene or group to\n * @param {HTMLElement} [domElement=document.body] - The HTML Element to attach event listeners to.\n * @description {@link Two.ZUI} is an extra class to turn your Two.js scene into a Google Maps or Adobe Illustrator style interface. See {@link https://codepen.io/jonobr1/pen/PobMKwb} for example usage.\n */\nexport class ZUI {\n  constructor(group, domElement) {\n    this.limits = {\n      scale: ZUI.Limit.clone(),\n      x: ZUI.Limit.clone(),\n      y: ZUI.Limit.clone(),\n    };\n\n    this.viewport = domElement || document.body;\n    this.viewportOffset = {\n      top: 0,\n      left: 0,\n      matrix: new Matrix(),\n    };\n\n    this.surfaceMatrix = new Matrix();\n\n    this.surfaces = [];\n    this.reset();\n    this.updateSurface();\n\n    this.add(new Surface(group));\n  }\n\n  static Surface = Surface;\n\n  static Clamp(v, min, max) {\n    return Math.min(Math.max(v, min), max);\n  }\n\n  static Limit = {\n    min: -Infinity,\n    max: Infinity,\n    clone: function () {\n      const result = {};\n      for (let k in this) {\n        result[k] = this[k];\n      }\n      return result;\n    },\n  };\n\n  static TranslateMatrix(m, x, y) {\n    m.elements[2] += x;\n    m.elements[5] += y;\n    return m;\n  }\n\n  static PositionToScale(pos) {\n    return Math.exp(pos);\n  }\n\n  static ScaleToPosition(scale) {\n    return Math.log(scale);\n  }\n\n  //\n\n  add(surface) {\n    this.surfaces.push(surface);\n    const limits = surface.limits();\n    this.addLimits(limits.min, limits.max);\n    return this;\n  }\n\n  /**\n   * @name Two.ZUI#addLimits\n   * @function\n   * @param {Number} [min=-Infinity] - The minimum scale the ZUI can zoom out to.\n   * @param {Number} [max=Infinity] - The maximum scale the ZUI can zoom in to.\n   */\n  addLimits(min, max) {\n    if (typeof min !== 'undefined') {\n      if (this.limits.scale.min) {\n        this.limits.scale.min = Math.max(min, this.limits.scale.min);\n      } else {\n        this.limits.scale.min = min;\n      }\n    }\n\n    if (typeof max === 'undefined') {\n      return this;\n    }\n\n    if (this.limits.scale.max) {\n      this.limits.scale.max = Math.min(max, this.limits.scale.max);\n    } else {\n      this.limits.scale.max = max;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.ZUI#clientToSurface\n   * @function\n   * @param {Two.Vector} a\n   * @description Convert an x, y coordinate in the user’s space to the object's projected space. Optionally pass a z property on the object to apply depth.\n   * @returns {Object} - An object with x, y, and z components\n   * @overloaded\n   */\n\n  /**\n   * @name Two.ZUI#clientToSurface\n   * @param {Number} [a=0] - The x component of position to be transformed.\n   * @param {Number} [b=0] - The y component of position to be transformed.\n   * @param {Number} [c=1] - The optional z component of position to be transformed.\n   * @description Convert an x, y coordinate in the user’s space to the object's projected space. Optionally pass a z property on the object to apply depth.\n   * @returns {Object} - An object with x, y, and z components\n   * @overloaded\n   */\n  clientToSurface(a, b, c) {\n    this.updateOffset();\n    const m = this.surfaceMatrix.inverse();\n    let x, y, z;\n    if (arguments.length === 1) {\n      const v = a;\n      x = typeof v.x === 'number' ? v.x : 0;\n      y = typeof v.y === 'number' ? v.y : 0;\n      z = typeof v.z === 'number' ? v.z : 1;\n    } else {\n      x = typeof a === 'number' ? a : 0;\n      y = typeof b === 'number' ? b : 0;\n      z = typeof c === 'number' ? c : 1;\n    }\n    const n = this.viewportOffset.matrix.inverse().multiply(x, y, z);\n    const r = m.multiply(n[0], n[1], n[2]);\n    return { x: r[0], y: r[1], z: r[2] };\n  }\n\n  /**\n   * @name Two.ZUI#surfaceToClient\n   * @function\n   * @param {Two.Vector} a\n   * @description Convert an x, y coordinate in projected space to the user’s space. Optionally pass a z property on the object to apply depth.\n   * @returns {Object} - An object with x, y, and z components\n   * @overloaded\n   */\n\n  /**\n   * @name Two.ZUI#surfaceToClient\n   * @param {Number} [a=0] - The x component of position to be transformed.\n   * @param {Number} [b=0] - The y component of position to be transformed.\n   * @param {Number} [c=1] - The optional z component of position to be transformed.\n   * @description Convert an x, y coordinate in projected space to the user’s space. Optionally pass a z property on the object to apply depth.\n   * @returns {Object} - An object with x, y, and z components\n   * @overloaded\n   */\n  surfaceToClient(a, b, c) {\n    this.updateOffset();\n    const vo = this.viewportOffset.matrix.clone();\n    let x, y, z;\n    if (arguments.length === 1) {\n      const v = a;\n      x = typeof v.x === 'number' ? v.x : 0;\n      y = typeof v.y === 'number' ? v.y : 0;\n      z = typeof v.z === 'number' ? v.z : 1;\n    } else {\n      x = typeof a === 'number' ? a : 0;\n      y = typeof b === 'number' ? b : 0;\n      z = typeof c === 'number' ? c : 1;\n    }\n    const sm = this.surfaceMatrix.multiply(x, y, z);\n    const r = vo.multiply(sm[0], sm[1], sm[2]);\n    return { x: r[0], y: r[1], z: r[2] };\n  }\n\n  /**\n   * @name Two.ZUI#zoomBy\n   * @function\n   * @param {Number} byF - The factor to scale by.\n   * @param {Number} clientX - The x position of the user's input.\n   * @param {Number} clientY - The y position of the user's input.\n   * @description A function to zoom by an incremental amount and a position. Typically used for pinch-and-zoom or mousewheel effects.\n   */\n  zoomBy(byF, clientX, clientY) {\n    const s = ZUI.PositionToScale(this.zoom + byF);\n    this.zoomSet(s, clientX, clientY);\n    return this;\n  }\n\n  /**\n   * @name Two.ZUI#zoomSet\n   * @function\n   * @param {Number} zoom - The level of the zoom.\n   * @param {Number} clientX - The x position of the user's input.\n   * @param {Number} clientY - The y position of the user's input.\n   * @description A function to set the zoom amount and the origin position. This is used internally by {@Two.ZUI#zoomBy}.\n   */\n  zoomSet(zoom, clientX, clientY) {\n    const newScale = this.fitToLimits(zoom);\n    this.zoom = ZUI.ScaleToPosition(newScale);\n\n    if (newScale === this.scale) {\n      return this;\n    }\n\n    const sf = this.clientToSurface(clientX, clientY);\n    const scaleBy = newScale / this.scale;\n\n    this.surfaceMatrix.scale(scaleBy);\n    this.scale = newScale;\n\n    const c = this.surfaceToClient(sf);\n    const dx = clientX - c.x;\n    const dy = clientY - c.y;\n    this.translateSurface(dx, dy);\n\n    return this;\n  }\n\n  /**\n   * @name Two.ZUI#translateSurface\n   * @function\n   * @param {Number} x - The x amount to pan.\n   * @param {Number} y - The y amount to pan.\n   * @description Set the position of the ZUI by an incremental translation amount.\n   */\n  translateSurface(x, y) {\n    ZUI.TranslateMatrix(this.surfaceMatrix, x, y);\n    this.updateSurface();\n    return this;\n  }\n\n  updateOffset() {\n    const rect = this.viewport.getBoundingClientRect();\n\n    this.viewportOffset.left = rect.left - document.body.scrollLeft;\n    this.viewportOffset.top = rect.top - document.body.scrollTop;\n\n    this.viewportOffset.matrix\n      .identity()\n      .translate(this.viewportOffset.left, this.viewportOffset.top);\n\n    return this;\n  }\n\n  updateSurface() {\n    const e = this.surfaceMatrix.elements;\n    for (let i = 0; i < this.surfaces.length; i++) {\n      this.surfaces[i].apply(e[2], e[5], e[0]);\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.ZUI#reset\n   * @function\n   * @description Reset the zoom and scale factors to their original instantiated state.\n   */\n  reset() {\n    this.zoom = 0;\n    this.scale = 1.0;\n    this.surfaceMatrix.identity();\n    this.updateSurface();\n    return this;\n  }\n\n  fitToLimits(s) {\n    return ZUI.Clamp(s, this.limits.scale.min, this.limits.scale.max);\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"two.js\",\n  \"version\": \"v0.8.23\",\n  \"description\": \"A renderer agnostic two-dimensional drawing api for the web.\",\n  \"module\": \"build/two.module.js\",\n  \"types\": \"src/two.d.ts\",\n  \"files\": [\n    \"package.json\",\n    \"LICENSE\",\n    \"README.md\",\n    \"build\",\n    \"extras\",\n    \"src\"\n  ],\n  \"scripts\": {\n    \"build\": \"node ./utils/build\",\n    \"dev\": \"esbuild --servedir=. --serve=8080\",\n    \"docs:generate\": \"node ./utils/document\",\n    \"docs:dev\": \"vuepress dev wiki\",\n    \"docs:build\": \"vuepress build wiki\",\n    \"docs:publish\": \"./deploy.sh\",\n    \"lint\": \"eslint . --fix\"\n  },\n  \"directories\": {\n    \"docs\": \"wiki/docs\",\n    \"test\": \"tests\",\n    \"example\": \"wiki/examples\"\n  },\n  \"keywords\": [\n    \"svg\",\n    \"canvas2d\",\n    \"webgl\",\n    \"animation\",\n    \"rendering\",\n    \"scenegraph\",\n    \"motiongraphics\",\n    \"visualization\",\n    \"dom\",\n    \"w3c\",\n    \"bitmap\",\n    \"vector\"\n  ],\n  \"homepage\": \"https://two.js.org/\",\n  \"author\": {\n    \"name\": \"jonobr1\",\n    \"url\": \"http://jono.fyi/\"\n  },\n  \"main\": \"build/two.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/jonobr1/two.js\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/jonobr1/two.js/issues\"\n  },\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"@types/minimatch\": \"^6.0.0\",\n    \"@vuepress/plugin-google-analytics\": \"^1.8.2\",\n    \"@vuepress/plugin-nprogress\": \"^1.5.4\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"chalk\": \"^2.4.2\",\n    \"commander\": \"^2.20.3\",\n    \"esbuild\": \"^0.25.9\",\n    \"eslint\": \"^7.8.1\",\n    \"esm\": \"^3.2.25\",\n    \"gzip-size\": \"^5.1.1\",\n    \"jsdoc\": \"^3.6.6\",\n    \"jsdoc-api\": \"^6.0.0\",\n    \"typescript\": \"^4.5.4\",\n    \"vuepress\": \"^1.9.5\",\n    \"vuepress-plugin-sitemap\": \"^2.3.1\"\n  },\n  \"autoupdate\": {\n    \"source\": \"git\",\n    \"target\": \"git://github.com/jonobr1/two.js.git\",\n    \"fileMap\": [\n      {\n        \"basePath\": \"build\",\n        \"files\": [\n          \"two*.js\"\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "src/anchor.d.ts",
    "content": "declare module 'two.js/src/anchor' {\n  /**\n     * @class\n     * @name Two.Anchor\n     * @param {Number} [x=0] - The x position of the root anchor point.\n     * @param {Number} [y=0] - The y position of the root anchor point.\n     * @param {Number} [ax=0] - The x position of the left handle point.\n     * @param {Number} [ay=0] - The y position of the left handle point.\n     * @param {Number} [bx=0] - The x position of the right handle point.\n     * @param {Number} [by=0] - The y position of the right handle point.\n     * @param {String} [command=Two.Commands.move] - The command to describe how to render. Applicable commands are {@link Two.Commands}\n\n     * @description An object that holds 3 {@link Two.Vector}s, the anchor point and its corresponding handles: `left` and `right`. In order to properly describe the bezier curve about the point there is also a command property to describe what type of drawing should occur when Two.js renders the anchors.\n     */\n  export class Anchor extends Vector {\n    static makeBroadcast(scope: Anchor): () => void;\n    /**\n     * @name Two.Anchor.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Anchor} to create a new instance\n     * @returns {Two.Anchor}\n     * @description Create a new {@link Two.Anchor} from an object notation of a {@link Two.Anchor}.\n     * @nota-bene Works in conjunction with {@link Two.Anchor#toObject}\n     */\n    static fromObject(\n      obj:\n        | object\n        | {\n            x?: number;\n            y?: number;\n            command?: Commands[keyof Commands];\n            relative?: boolean;\n            controls?: {\n              left: { x: number; y: number } | Vector;\n              right: { x: number; y: number } | Vector;\n            };\n            rx?: number;\n            ry?: number;\n            xAxisRotation?: number;\n            largeArcFlag?: number;\n          }\n    ): Anchor;\n    constructor(\n      x?: number,\n      y?: number,\n      ax?: number,\n      ay?: number,\n      bx?: number,\n      by?: number,\n      command?: Commands[keyof Commands]\n    );\n    controls: {\n      left: Vector;\n      right: Vector;\n    };\n    command: Commands[keyof Commands];\n    relative: boolean;\n    rx?: number;\n    ry?: number;\n    xAxisRotation?: number;\n    largeArcFlag?: number;\n    sweepFlag?: number;\n    /**\n     * @name Two.Anchor#copy\n     * @function\n     * @param {Two.Anchor} v - The anchor to apply values to.\n     * @description Copy the properties of one {@link Two.Anchor} onto another.\n     */\n    copy(anchor: Anchor): Anchor;\n    /**\n     * @name Two.Anchor#clone\n     * @function\n     * @returns {Two.Anchor}\n     * @description Create a new {@link Two.Anchor}, set all its values to the current instance and return it for use.\n     */\n    clone(): Anchor;\n    /**\n     * @name Two.Anchor#toObject\n     * @function\n     * @returns {Object} - An object with properties filled out to mirror {@link Two.Anchor}.\n     * @description Create a JSON compatible plain object of the current instance. Intended for use with storing values in a database.\n     * @nota-bene Works in conjunction with {@link Two.Anchor.fromObject}\n     */\n    toObject(): object;\n    /**\n     * @name Two.Anchor#toString\n     * @function\n     * @returns {String} - A String with comma-separated values reflecting the various values on the current instance.\n     * @description Create a string form of the current instance. Intended for use with storing values in a database. This is lighter to store than the JSON compatible {@link Two.Anchor#toObject}.\n     */\n    toString(): string;\n  }\n  import { Vector } from 'two.js/src/vector';\n  import { Commands } from 'two.js/src/utils/path-commands';\n}\n"
  },
  {
    "path": "src/anchor.js",
    "content": "import { Commands } from './utils/path-commands.js';\nimport { Events } from './events.js';\nimport { Vector } from './vector.js';\nimport { toFixed } from './utils/math.js';\n\n/**\n * @class\n * @name Two.Anchor\n * @param {Number} [x=0] - The x position of the root anchor point.\n * @param {Number} [y=0] - The y position of the root anchor point.\n * @param {Number} [ax=0] - The x position of the left handle point.\n * @param {Number} [ay=0] - The y position of the left handle point.\n * @param {Number} [bx=0] - The x position of the right handle point.\n * @param {Number} [by=0] - The y position of the right handle point.\n * @param {String} [command=Two.Commands.move] - The command to describe how to render. Applicable commands are {@link Two.Commands}\n * @extends Two.Vector\n * @description An object that holds 3 {@link Two.Vector}s, the anchor point and its corresponding handles: `left` and `right`. In order to properly describe the bezier curve about the point there is also a command property to describe what type of drawing should occur when Two.js renders the anchors.\n */\nexport class Anchor extends Vector {\n  controls = {\n    left: new Vector(),\n    right: new Vector(),\n  };\n  _command = Commands.move;\n  _relative = true;\n\n  _rx = 0;\n  _ry = 0;\n  _xAxisRotation = 0;\n  _largeArcFlag = 0;\n  _sweepFlag = 1;\n\n  constructor(\n    x = 0,\n    y = 0,\n    ax = 0,\n    ay = 0,\n    bx = 0,\n    by = 0,\n    command = Commands.move\n  ) {\n    super(x, y);\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this.command = command;\n    this.relative = true;\n\n    const broadcast = Anchor.makeBroadcast(this);\n\n    this.controls.left\n      .set(ax, ay)\n      .addEventListener(Events.Types.change, broadcast);\n    this.controls.right\n      .set(bx, by)\n      .addEventListener(Events.Types.change, broadcast);\n  }\n\n  static makeBroadcast(scope) {\n    return broadcast;\n    function broadcast() {\n      if (scope._bound) {\n        scope.dispatchEvent(Events.Types.change);\n      }\n    }\n  }\n\n  /**\n   * @name Two.Anchor.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Anchor} to create a new instance\n   * @returns {Two.Anchor}\n   * @description Create a new {@link Two.Anchor} from an object notation of a {@link Two.Anchor}.\n   * @nota-bene Works in conjunction with {@link Two.Anchor#toObject}\n   */\n  static fromObject(obj) {\n    return new Anchor().copy(obj);\n  }\n\n  /**\n   * @name Two.Anchor#copy\n   * @function\n   * @param {Two.Anchor} v - The anchor to apply values to.\n   * @description Copy the properties of one {@link Two.Anchor} onto another.\n   */\n  copy(v) {\n    this.x = v.x;\n    this.y = v.y;\n\n    if (typeof v.command === 'string') {\n      this.command = v.command;\n    }\n\n    if (v.controls) {\n      if (v.controls.left) {\n        this.controls.left.copy(v.controls.left);\n      }\n      if (v.controls.right) {\n        this.controls.right.copy(v.controls.right);\n      }\n    }\n\n    if (typeof v.relative === 'boolean') {\n      this.relative = v.relative;\n    }\n\n    if (typeof v.rx === 'number') {\n      this.rx = v.rx;\n    }\n    if (typeof v.ry === 'number') {\n      this.ry = v.ry;\n    }\n    if (typeof v.xAxisRotation === 'number') {\n      this.xAxisRotation = v.xAxisRotation;\n    }\n    if (typeof v.largeArcFlag === 'number') {\n      this.largeArcFlag = v.largeArcFlag;\n    }\n    if (typeof v.sweepFlag === 'number') {\n      this.sweepFlag = v.sweepFlag;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Anchor#clone\n   * @function\n   * @returns {Two.Anchor}\n   * @description Create a new {@link Two.Anchor}, set all its values to the current instance and return it for use.\n   */\n  clone() {\n    return new Anchor().copy(this);\n  }\n\n  /**\n   * @name Two.Anchor#toObject\n   * @function\n   * @returns {Object} - An object with properties filled out to mirror {@link Two.Anchor}.\n   * @description Create a JSON compatible plain object of the current instance. Intended for use with storing values in a database.\n   * @nota-bene Works in conjunction with {@link Two.Anchor.fromObject}\n   */\n  toObject() {\n    return {\n      x: toFixed(this.x),\n      y: toFixed(this.y),\n      command: this.command,\n      relative: this.relative,\n      controls: {\n        left: this.controls.left.toObject(),\n        right: this.controls.right.toObject(),\n      },\n      rx: toFixed(this.rx),\n      ry: toFixed(this.ry),\n      xAxisRotation: toFixed(this.xAxisRotation),\n      largeArcFlag: toFixed(this.largeArcFlag),\n      sweepFlag: toFixed(this.sweepFlag),\n    };\n  }\n\n  /**\n   * @name Two.Anchor#toString\n   * @function\n   * @returns {String} - A String with comma-separated values reflecting the various values on the current instance.\n   * @description Create a string form of the current instance. Intended for use with storing values in a database. This is lighter to store than the JSON compatible {@link Two.Anchor#toObject}.\n   */\n  toString() {\n    return JSON.stringify(this.toObject());\n  }\n}\n\nconst proto = {\n  command: {\n    enumerable: true,\n    get: function () {\n      return this._command;\n    },\n    set: function (command) {\n      if (this._command !== command) {\n        this._command = command;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n  relative: {\n    enumerable: true,\n    get: function () {\n      return this._relative;\n    },\n    set: function (relative) {\n      if (this._relative !== !!relative) {\n        this._relative = !!relative;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n  rx: {\n    enumerable: true,\n    get: function () {\n      return this._rx;\n    },\n    set: function (rx) {\n      if (this._rx !== rx) {\n        this._rx = rx;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n  ry: {\n    enumerable: true,\n    get: function () {\n      return this._ry;\n    },\n    set: function (ry) {\n      if (this._ry !== ry) {\n        this._ry = ry;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n  xAxisRotation: {\n    enumerable: true,\n    get: function () {\n      return this._xAxisRotation;\n    },\n    set: function (xAxisRotation) {\n      if (this._xAxisRotation !== xAxisRotation) {\n        this._xAxisRotation = xAxisRotation;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n  largeArcFlag: {\n    enumerable: true,\n    get: function () {\n      return this._largeArcFlag;\n    },\n    set: function (largeArcFlag) {\n      if (this._largeArcFlag !== largeArcFlag) {\n        this._largeArcFlag = largeArcFlag;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n  sweepFlag: {\n    get: function () {\n      return this._sweepFlag;\n    },\n    set: function (sweepFlag) {\n      if (this._sweepFlag !== sweepFlag) {\n        this._sweepFlag = sweepFlag;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n};\n"
  },
  {
    "path": "src/children.d.ts",
    "content": "declare module 'two.js/src/children' {\n  /**\n     * @class\n     * @name Two.Group.Children\n\n     * @description A children collection which is accesible both by index and by object `id`.\n     */\n  export class Children extends Collection<Shape> {\n    constructor(children?: Shape[]);\n    constructor(...args: Shape[]);\n    /**\n     * @name Two.Group.Children#ids\n     * @property {Object} - Map of all elements in the list keyed by `id`s.\n     */\n    ids: { [id: string]: Shape };\n    /**\n     * @function\n     * @name Two.Group.Children#attach\n     * @param {Shape[]} children - The objects which extend {@link Two.Shape} to be added.\n     * @description Adds elements to the `ids` map.\n     */\n    attach(children: Shape[]): Children;\n    /**\n     * @function\n     * @name Two.Group.Children#detach\n     * @param {Shape[]} children - The objects which extend {@link Two.Shape} to be removed.\n     * @description Removes elements to the `ids` map.\n     */\n    detach(children: Shape[]): Children;\n  }\n  import { Collection } from 'two.js/src/collection';\n  import { Shape } from 'two.js/src/shape';\n}\n"
  },
  {
    "path": "src/children.js",
    "content": "import { Events } from './events.js';\nimport { Collection } from './collection.js';\n\n/**\n * @class\n * @name Two.Group.Children\n * @extends Two.Collection\n * @description A children collection which is accesible both by index and by object `id`.\n */\nexport class Children extends Collection {\n  /**\n   * @name Two.Group.Children#ids\n   * @property {Object} - Map of all elements in the list keyed by `id`s.\n   */\n  // N.B: Technique to disable enumeration on object\n  #ids = {};\n  get ids() {\n    return this.#ids;\n  }\n\n  constructor(children) {\n    children = Array.isArray(children)\n      ? children\n      : Array.prototype.slice.call(arguments);\n\n    super(children);\n\n    this.attach(children);\n\n    this.on(Events.Types.insert, this.attach);\n    this.on(Events.Types.remove, this.detach);\n  }\n\n  /**\n   * @function\n   * @name Two.Group.Children#attach\n   * @param {Two.Shape[]} children - The objects which extend {@link Two.Shape} to be added.\n   * @description Adds elements to the `ids` map.\n   */\n  attach(children) {\n    for (let i = 0; i < children.length; i++) {\n      const child = children[i];\n      if (child && child.id) {\n        this.ids[child.id] = child;\n      }\n    }\n    return this;\n  }\n\n  /**\n   * @function\n   * @name Two.Group.Children#detach\n   * @param {Two.Shape[]} children - The objects which extend {@link Two.Shape} to be removed.\n   * @description Removes elements to the `ids` map.\n   */\n  detach(children) {\n    for (let i = 0; i < children.length; i++) {\n      delete this.ids[children[i].id];\n    }\n    return this;\n  }\n}\n"
  },
  {
    "path": "src/collection.d.ts",
    "content": "declare module 'two.js/src/collection' {\n  /**\n     * @name Two.Collection\n     * @class\n\n     * @description An `Array` like object with additional event propagation on actions. `pop`, `shift`, and `splice` trigger `removed` events. `push`, `unshift`, and `splice` with more than 2 arguments trigger 'inserted'. Finally, `sort` and `reverse` trigger `order` events.\n     */\n  export class Collection<T = any> extends Array<T> {\n    constructor(...args: any[]);\n    /**\n     * @private\n     */\n    private _events;\n    private set _bound(arg: boolean);\n    private get _bound(): boolean;\n    addEventListener(...args: any[]): any;\n    on(...args: any[]): any;\n    bind(...args: any[]): any;\n    removeEventListener(...args: any[]): any;\n    off(...args: any[]): any;\n    unbind(...args: any[]): any;\n    dispatchEvent(...args: any[]): any;\n    trigger(...args: any[]): any;\n    listen(...args: any[]): any;\n    ignore(...args: any[]): any;\n  }\n}\n"
  },
  {
    "path": "src/collection.js",
    "content": "import { Events } from './events.js';\n\n/**\n * @name Two.Collection\n * @class\n * @extends Two.Events\n * @description An `Array` like object with additional event propagation on actions. `pop`, `shift`, and `splice` trigger `removed` events. `push`, `unshift`, and `splice` with more than 2 arguments trigger 'inserted'. Finally, `sort` and `reverse` trigger `order` events.\n */\n\nexport class Collection extends Array {\n  // Warning: Multiple inheritance hack\n  /**\n   * @private\n   */\n  #events = new Events();\n  // N.B: Technique to disable enumeration on object\n  get _events() {\n    return this.#events;\n  }\n  set _events(e) {\n    this.#events = e;\n  }\n\n  // Getters and setters aren't enumerable\n  get _bound() {\n    return this.#events._bound;\n  }\n  set _bound(v) {\n    this.#events._bound = v;\n  }\n\n  addEventListener() {\n    return this.#events.addEventListener?.apply(this, arguments);\n  }\n  on() {\n    return this.#events.on?.apply(this, arguments);\n  }\n  bind() {\n    return this.#events.bind?.apply(this, arguments);\n  }\n  removeEventListener() {\n    return this.#events.removeEventListener?.apply(this, arguments);\n  }\n  off() {\n    return this.#events.off?.apply(this, arguments);\n  }\n  unbind() {\n    return this.#events.unbind?.apply(this, arguments);\n  }\n  dispatchEvent() {\n    return this.#events.dispatchEvent?.apply(this, arguments);\n  }\n  trigger() {\n    return this.#events.trigger?.apply(this, arguments);\n  }\n  listen() {\n    return this.#events.listen?.apply(this, arguments);\n  }\n  ignore() {\n    return this.#events.ignore?.apply(this, arguments);\n  }\n\n  constructor() {\n    super();\n\n    if (arguments[0] && Array.isArray(arguments[0])) {\n      if (arguments[0].length > 0) {\n        this.push.apply(this, arguments[0]);\n      }\n    } else if (arguments.length > 0) {\n      this.push.apply(this, arguments);\n    }\n  }\n\n  pop() {\n    const popped = super.pop.apply(this, arguments);\n    this.trigger(Events.Types.remove, [popped]);\n    return popped;\n  }\n\n  shift() {\n    const shifted = super.shift.apply(this, arguments);\n    this.trigger(Events.Types.remove, [shifted]);\n    return shifted;\n  }\n\n  push() {\n    const pushed = super.push.apply(this, arguments);\n    this.trigger(Events.Types.insert, arguments);\n    return pushed;\n  }\n\n  unshift() {\n    const unshifted = super.unshift.apply(this, arguments);\n    this.trigger(Events.Types.insert, arguments);\n    return unshifted;\n  }\n\n  splice() {\n    const spliced = super.splice.apply(this, arguments);\n    this.trigger(Events.Types.remove, spliced);\n    if (arguments.length > 2) {\n      const inserted = this.slice(\n        arguments[0],\n        arguments[0] + arguments.length - 2\n      );\n      this.trigger(Events.Types.insert, inserted);\n      this.trigger(Events.Types.order);\n    }\n    return spliced;\n  }\n\n  sort() {\n    super.sort.apply(this, arguments);\n    this.trigger(Events.Types.order);\n    return this;\n  }\n\n  reverse() {\n    super.reverse.apply(this, arguments);\n    this.trigger(Events.Types.order);\n    return this;\n  }\n\n  indexOf() {\n    return super.indexOf.apply(this, arguments);\n  }\n\n  map(func, scope) {\n    const results = [];\n    for (let key = 0; key < this.length; key++) {\n      const value = this[key];\n      let result;\n      if (scope) {\n        result = func.call(scope, value, key);\n      } else {\n        result = func(value, key);\n      }\n      results.push(result);\n    }\n    return results;\n  }\n}\n"
  },
  {
    "path": "src/constants.d.ts",
    "content": "declare module 'two.js/src/constants' {\n  export interface Constants {\n    NextFrameId: number;\n    Types: {\n      webgl: 'WebGLRenderer';\n      svg: 'SVGRenderer';\n      canvas: 'CanvasRenderer';\n    };\n    Version: string;\n    PublishDate: string;\n    Identifier: string;\n    Resolution: number;\n    AutoCalculateImportedMatrices: boolean;\n    Instances: Two[];\n    uniqueId(): number;\n  }\n  import Two from 'two.js';\n}\n"
  },
  {
    "path": "src/constants.js",
    "content": "let count = 0;\n\nexport const Constants = {\n  /**\n   * @name Two.NextFrameId\n   * @property {Number}\n   * @description The id of the next `requestAnimationFrame` function. Used to control the (or cancel) the default behavior of Two.js animation loops.\n   */\n  NextFrameId: null,\n\n  // Primitive\n\n  /**\n   * @name Two.Types\n   * @property {Object} - The different rendering types available in the library.\n   */\n  Types: {\n    webgl: 'WebGLRenderer',\n    svg: 'SVGRenderer',\n    canvas: 'CanvasRenderer',\n  },\n\n  /**\n   * @name Two.Version\n   * @property {String} - The current working version of the library.\n   */\n  Version: '<%= version %>',\n\n  /**\n   * @name Two.PublishDate\n   * @property {String} - The automatically generated publish date in the build process to verify version release candidates.\n   */\n  PublishDate: '<%= publishDate %>',\n\n  /**\n   * @name Two.Identifier\n   * @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.\n   */\n  Identifier: 'two-',\n\n  /**\n   * @name Two.Resolution\n   * @property {Number} - Default amount of vertices to be used for interpreting Arcs and ArcSegments.\n   */\n  Resolution: 12,\n\n  /**\n   * @name Two.AutoCalculateImportedMatrices\n   * @property {Boolean} - When importing SVGs through the {@link Two#interpret} and {@link Two#load}, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n   * @nota-bene `false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n   */\n  AutoCalculateImportedMatrices: true,\n\n  /**\n   * @name Two.Instances\n   * @property {Two[]} - Registered list of all Two.js instances in the current session.\n   */\n  Instances: [],\n\n  /**\n   * @function Two.uniqueId\n   * @description Simple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n   * @returns {Number} Ever increasing Number.\n   */\n  uniqueId: function () {\n    return count++;\n  },\n};\n"
  },
  {
    "path": "src/effects/gradient.d.ts",
    "content": "declare module 'two.js/src/effects/gradient' {\n  type SpreadProperties = 'pad' | 'reflect' | 'repeat';\n  type UnitsProperties = 'userSpaceOnUse' | 'objectBoundingBox';\n  /**\n   * @name Two.Gradient\n   * @class\n   * @param {Stop[]} [stops] - A list of {@link Two.Stop}s that contain the gradient fill pattern for the gradient.\n   * @description This is the base class for constructing different types of gradients with Two.js. The two common gradients are {@link Two.LinearGradient} and {@link Two.RadialGradient}.\n   */\n  export class Gradient extends TwoElement {\n    /**\n     * @name Two.Gradient.Stop\n     * @see {@link Two.Stop}\n     */\n    static Stop: Stop;\n    /**\n     * @name Two.Gradient.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Gradient}.\n     */\n    static Properties: ('spread' | 'stops' | 'units' | string)[];\n    /**\n     * @name Two.Gradient.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Gradient} to create a new instance\n     * @returns {Two.Gradient}\n     * @description Create a new {@link Two.Gradient} from an object notation of a {@link Two.Gradient}.\n     * @nota-bene Works in conjunction with {@link Two.Gradient#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof TwoElement.fromObject>[0] & {\n        stops?: number[];\n        spread?: SpreadProperties;\n        units?: UnitsProperties;\n      }\n    ): Gradient;\n    constructor(stops?: Stop[]);\n    private _flagStops: boolean;\n    private _flagSpread: boolean;\n    private _flagUnits: boolean;\n    private _spread: string;\n    private _units: string;\n    /**\n     * @name Two.Gradient#renderer\n     * @property {Object}\n     * @description Object access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n     * @nota-bene With the {@link Two.SVGRenderer} you can access the underlying SVG element created via `shape.renderer.elem`.\n     */\n    /**\n     * @name Two.Gradient#id\n     * @property {String} - Session specific unique identifier.\n     * @nota-bene In the {@link Two.SVGRenderer} change this to change the underlying SVG element's id too.\n     */\n    id: string;\n    /**\n     * @name Two.Gradient#spread\n     * @property {String} - Indicates what happens if the gradient starts or ends inside the bounds of the target rectangle. Possible values are `'pad'`, `'reflect'`, and `'repeat'`.\n     * @see {@link https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute} for more information\n     */\n    spread: SpreadProperties;\n    /**\n     * @name Two.Gradient#units\n     * @property {String} [units='objectBoundingBox'] - Indicates how coordinate values are interpreted by the renderer. Possible values are `'userSpaceOnUse'` and `'objectBoundingBox'`.\n     * @see {@link https://www.w3.org/TR/SVG11/pservers.html#RadialGradientElementGradientUnitsAttribute} for more information\n     */\n    units: UnitsProperties;\n    /**\n     * @name Two.Gradient#stops\n     * @property {Two.Stop[]} - An ordered list of {@link Two.Stop}s for rendering the gradient.\n     * @nota-bene Actually a {@link Two.Collection} polyfilled to act like an observable Array.\n     */\n    stops: Stop[];\n    /**\n     * @name Two.Gradient#copy\n     * @function\n     * @param {Two.Gradient} gradient - The reference {@link Two.Gradient}\n     * @description Copy the properties of one {@link Two.Gradient} onto another.\n     */\n    copy(gradient: Gradient): Gradient;\n    /**\n     * @name Two.Gradient#clone\n     * @function\n     * @param {Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Gradient}\n     * @description Create a new instance of {@link Two.Gradient} with the same properties of the current path.\n     */\n    clone(parent?: Group): Gradient;\n    /**\n     * @name Two.Gradient#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject(): object;\n    /**\n     * @name Two.Gradient#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    protected _update(bubbles?: boolean): Gradient;\n    /**\n     * @name Two.Gradient#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Gradient;\n    /**\n     * @name Two.Gradient#dispose\n     * @function\n     * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n     */\n    dispose(): Gradient;\n  }\n  import { Element as TwoElement } from 'two.js/src/element';\n  import { Stop } from 'two.js/src/effects/stop';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/effects/gradient.js",
    "content": "import { Collection } from '../collection.js';\nimport { Events } from '../events.js';\nimport { Element } from '../element.js';\nimport { _ } from '../utils/underscore.js';\nimport { Stop } from './stop.js';\n\n/**\n * @name Two.Gradient\n * @class\n * @extends Two.Element\n * @param {Two.Stop[]} [stops] - A list of {@link Two.Stop}s that contain the gradient fill pattern for the gradient.\n * @description This is the base class for constructing different types of gradients with Two.js. The two common gradients are {@link Two.LinearGradient} and {@link Two.RadialGradient}.\n */\nexport class Gradient extends Element {\n  _flagStops = false;\n  _flagSpread = false;\n  _flagUnits = false;\n\n  _spread = '';\n  _units = '';\n\n  constructor(stops) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'gradient';\n    this._renderer.flagStops = FlagStops.bind(this);\n    this._renderer.bindStops = BindStops.bind(this);\n    this._renderer.unbindStops = UnbindStops.bind(this);\n\n    /**\n     * @name Two.Gradient#spread\n     * @property {String} - Indicates what happens if the gradient starts or ends inside the bounds of the target rectangle. Possible values are `'pad'`, `'reflect'`, and `'repeat'`.\n     * @see {@link https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute} for more information\n     */\n    this.spread = 'pad';\n\n    /**\n     * @name Two.Gradient#units\n     * @property {String} [units='objectBoundingBox'] - Indicates how coordinate values are interpreted by the renderer. Possible values are `'userSpaceOnUse'` and `'objectBoundingBox'`.\n     * @see {@link https://www.w3.org/TR/SVG11/pservers.html#RadialGradientElementGradientUnitsAttribute} for more information\n     */\n    this.units = 'objectBoundingBox';\n\n    /**\n     * @name Two.Gradient#stops\n     * @property {Two.Stop[]} - An ordered list of {@link Two.Stop}s for rendering the gradient.\n     */\n    if (stops) {\n      this.stops = stops;\n    }\n  }\n\n  /**\n   * @name Two.Gradient.Stop\n   * @see {@link Two.Stop}\n   */\n  static Stop = Stop;\n\n  /**\n   * @name Two.Gradient.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Gradient}.\n   */\n  static Properties = ['spread', 'stops', 'units'];\n\n  /**\n   * @name Two.Gradient.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Gradient} to create a new instance\n   * @returns {Two.Gradient}\n   * @description Create a new {@link Two.Gradient} from an object notation of a {@link Two.Gradient}.\n   * @nota-bene Works in conjunction with {@link Two.Gradient#toObject}\n   */\n  static fromObject(obj) {\n    let stops = obj.stops;\n    if (stops && stops.length > 0) {\n      stops = stops.map((o) => (o instanceof Stop ? o : new Stop().copy(o)));\n    }\n    const gradient = new Gradient(stops).copy(obj);\n\n    if ('id' in obj) {\n      gradient.id = obj.id;\n    }\n\n    return gradient;\n  }\n\n  /**\n   * @name Two.Gradient#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Gradient}\n   * @description Create a new instance of {@link Two.Gradient} with the same properties of the current path.\n   */\n  clone(parent) {\n    const stops = this.stops.map((s) => {\n      return s.clone();\n    });\n\n    const clone = new Gradient(stops);\n\n    _.each(\n      Gradient.Properties,\n      (k) => {\n        clone[k] = this[k];\n      },\n      this\n    );\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Gradient#copy\n   * @function\n   * @param {Two.Gradient} gradient - The reference {@link Two.Gradient}\n   * @description Copy the properties of one {@link Two.Gradient} onto another.\n   */\n  copy(gradient) {\n    super.copy.call(this, gradient);\n\n    for (let i = 0; i < Gradient.Properties.length; i++) {\n      const k = Gradient.Properties[i];\n      if (k in gradient) {\n        this[k] = gradient[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Gradient#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = {\n      stops: this.stops.map((s) => {\n        return s.toObject();\n      }),\n    };\n\n    _.each(\n      Gradient.Properties,\n      (k) => {\n        result[k] = this[k];\n      },\n      this\n    );\n\n    return result;\n  }\n\n  /**\n   * @name Two.Gradient#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagSpread || this._flagStops) {\n      this.trigger(Events.Types.change);\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Gradient#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagSpread = this._flagUnits = this._flagStops = false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Gradient#dispose\n   * @function\n   * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n   */\n  dispose() {\n    // Remove gradient from SVG document\n    if ('elem' in this._renderer) {\n      const elem = this._renderer.elem;\n      elem.parentNode.removeChild(elem);\n    }\n    // Deallocate textures from the graphics card\n    if ('effect' in this._renderer) {\n      this._renderer.effect = null;\n    }\n    return this;\n  }\n}\n\nconst proto = {\n  spread: {\n    enumerable: true,\n    get: function () {\n      return this._spread;\n    },\n    set: function (v) {\n      this._spread = v;\n      this._flagSpread = true;\n    },\n  },\n  units: {\n    enumerable: true,\n    get: function () {\n      return this._units;\n    },\n    set: function (v) {\n      this._units = v;\n      this._flagUnits = true;\n    },\n  },\n  stops: {\n    enumerable: true,\n\n    get: function () {\n      return this._stops;\n    },\n\n    set: function (stops) {\n      const bindStops = this._renderer.bindStops;\n      const unbindStops = this._renderer.unbindStops;\n\n      // Remove previous listeners\n      if (this._stops) {\n        this._stops\n          .unbind(Events.Types.insert, bindStops)\n          .unbind(Events.Types.remove, unbindStops);\n      }\n\n      // Create new Collection with copy of Stops\n      this._stops = new Collection((stops || []).slice(0));\n\n      // Listen for Collection changes and bind / unbind\n      this._stops\n        .bind(Events.Types.insert, bindStops)\n        .bind(Events.Types.remove, unbindStops);\n\n      // Bind Initial Stops\n      bindStops(this._stops);\n    },\n  },\n};\n\n/**\n * @name FlagStops\n * @private\n * @function\n * @description Cached method to let renderers know stops have been updated on a {@link Two.Gradient}.\n */\nfunction FlagStops() {\n  this._flagStops = true;\n}\n\n/**\n * @name BindVertices\n * @private\n * @function\n * @description Cached method to let {@link Two.Gradient} know vertices have been added to the instance.\n */\nfunction BindStops(items) {\n  // This function is called a lot\n  // when importing a large SVG\n  let i = items.length;\n  while (i--) {\n    items[i].bind(Events.Types.change, this._renderer.flagStops);\n    items[i].parent = this;\n  }\n\n  this._renderer.flagStops();\n}\n\n/**\n * @name UnbindStops\n * @private\n * @function\n * @description Cached method to let {@link Two.Gradient} know vertices have been removed from the instance.\n */\nfunction UnbindStops(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].unbind(Events.Types.change, this._renderer.flagStops);\n    delete items[i].parent;\n  }\n\n  this._renderer.flagStops();\n}\n"
  },
  {
    "path": "src/effects/image-sequence.d.ts",
    "content": "declare module 'two.js/src/effects/image-sequence' {\n  /**\n     * @name Two.ImageSequence\n     * @class\n\n     * @param {String|String[]|Texture|Texture[]} [src] - A list of URLs or {@link Two.Texture}s.\n     * @param {Number} [ox=0] - The initial `x` position of the Two.ImageSequence.\n     * @param {Number} [oy=0] - The initial `y` position of the Two.ImageSequence.\n     * @param {Number} [frameRate=30] - The frame rate at which the images should playback at.\n     * @description A convenient package to display still or animated images organized as a series of still images.\n     */\n  export class ImageSequence extends Rectangle {\n    /**\n     * @name Two.ImageSequence.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.ImageSequence}.\n     */\n    Properties: (\n      | 'textures'\n      | 'frameRate'\n      | 'index'\n      | 'firstFrame'\n      | 'lastFrame'\n      | 'loop'\n      | string\n    )[];\n    /**\n     * @name Two.ImageSequence.DefaultFrameRate\n     * @property The default frame rate that {@link Two.ImageSequence#frameRate} is set to when instantiated.\n     */\n    static DefaultFrameRate: 30;\n    /**\n     * @name Two.ImageSequence.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.ImageSequence} to create a new instance\n     * @returns {Two.ImageSequence}\n     * @description Create a new {@link Two.ImageSequence} from an object notation of a {@link Two.ImageSequence}.\n     * @nota-bene Works in conjunction with {@link Two.ImageSequence#toObject}\n     */\n    fromObject(\n      obj: Parameters<typeof Rectangle.fromObject>[0] & {\n        textures?: Parameters<typeof Texture.fromObject>[0][];\n        frameRate?: number;\n        index?: number;\n        firstFrame?: number;\n        lastFrame?: number;\n        loop?: boolean;\n      }\n    ): ImageSequence;\n\n    constructor(\n      src?: string | string[] | Texture | Texture[],\n      ox?: number,\n      oy?: number,\n      frameRate?: number\n    );\n    /**\n     * @name Two.ImageSequence#_flagTextures\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#textures} need updating.\n     */\n    private _flagTextures;\n    /**\n     * @name Two.ImageSequence#_flagFrameRate\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#frameRate} needs updating.\n     */\n    private _flagFrameRate;\n    /**\n     * @name Two.ImageSequence#_flagIndex\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ImageSequence#index} needs updating.\n     */\n    private _flagIndex;\n    /**\n     * @name Two.ImageSequence#_amount\n     * @private\n     * @property {Number} - Number of frames for a given {@link Two.ImageSequence}.\n     */\n    private _amount;\n    /**\n     * @name Two.ImageSequence#_duration\n     * @private\n     * @property {Number} - Number of milliseconds a {@link Two.ImageSequence}.\n     */\n    private _duration;\n    /**\n     * @name Two.ImageSequence#_index\n     * @private\n     * @property {Number} - The current frame the {@link Two.ImageSequence} is currently displaying.\n     */\n    private _index;\n    /**\n     * @name Two.ImageSequence#_startTime\n     * @private\n     * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.ImageSequence} started.\n     */\n    private _startTime;\n    /**\n     * @name Two.ImageSequence#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} is animating or not.\n     */\n    private _playing;\n    /**\n     * @name Two.ImageSequence#_firstFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.ImageSequence} should start with.\n     */\n    private _firstFrame;\n    /**\n     * @name Two.ImageSequence#_lastFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.ImageSequence} should end with.\n     */\n    private _lastFrame;\n    /**\n     * @name Two.ImageSequence#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} should loop or not.\n     */\n    private _loop;\n    /**\n     * @name Two.ImageSequence#_textures\n     * @private\n     * @see {@link Two.ImageSequence#textures}\n     */\n    private _textures;\n    /**\n     * @name Two.ImageSequence#_frameRate\n     * @private\n     * @see {@link Two.ImageSequence#frameRate}\n     */\n    private _frameRate;\n    textures: Texture[];\n    frameRate: number;\n    /**\n     * @name Two.ImageSequence#index\n     * @property {Number} - The index of the current tile of the sprite to display. Defaults to `0`.\n     */\n    index: number;\n    /**\n     * @name Two.ImageSequence#copy\n     * @function\n     * @param {Two.ImageSequence} imageSequence - The reference {@link Two.ImageSequence}\n     * @description Copy the properties of one {@link Two.ImageSequence} onto another.\n     */\n    copy(imageSeqence: ImageSequence): ImageSequence;\n    /**\n     * @name Two.ImageSequence#play\n     * @function\n     * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n     * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.ImageSequence#textures}.\n     * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the image sequence is looped.\n     * @description Initiate animation playback of a {@link Two.ImageSequence}.\n     */\n    play(\n      firstFrame?: number,\n      lastFrame?: number,\n      onLastFrame?: () => void\n    ): ImageSequence;\n    /**\n     * @name Two.ImageSequence#pause\n     * @function\n     * @description Halt animation playback of a {@link Two.ImageSequence}.\n     */\n    pause(): ImageSequence;\n    /**\n     * @name Two.ImageSequence#stop\n     * @function\n     * @description Halt animation playback of a {@link Two.ImageSequence} and set the current frame back to the first frame.\n     */\n    stop(): ImageSequence;\n    /**\n     * @name Two.ImageSequence#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.ImageSequence}\n     * @description Create a new instance of {@link Two.ImageSequence} with the same properties of the current image sequence.\n     */\n    clone(parent: Group): ImageSequence;\n    /**\n     * @name Two.ImageSequence#dispose\n     * @function\n     * @description Release the image sequence's renderer resources and detach all events.\n     * This method stops any running animation, clears animation callbacks, unbinds\n     * textures collection events, and disposes individual textures (calling dispose()\n     * for thorough cleanup) while preserving the renderer type for potential\n     * re-attachment to a new renderer.\n     */\n    dispose(): ImageSequence;\n  }\n  import { Rectangle } from 'two.js/src/shapes/rectangle';\n  import { Texture } from 'two.js/src/effects/texture';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/effects/image-sequence.js",
    "content": "import { Collection } from '../collection.js';\nimport { Events } from '../events.js';\nimport { lerp } from '../utils/math.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Vector } from '../vector.js';\nimport { Rectangle } from '../shapes/rectangle.js';\nimport { Texture } from './texture.js';\n\n/**\n * @name Two.ImageSequence\n * @class\n * @extends Two.Rectangle\n * @param {String|String[]|Two.Texture|Two.Texture[]} [src] - A list of URLs or {@link Two.Texture}s.\n * @param {Number} [ox=0] - The initial `x` position of the Two.ImageSequence.\n * @param {Number} [oy=0] - The initial `y` position of the Two.ImageSequence.\n * @param {Number} [frameRate=30] - The frame rate at which the images should playback at.\n * @description A convenient package to display still or animated images organized as a series of still images.\n */\nexport class ImageSequence extends Rectangle {\n  /**\n   * @name Two.ImageSequence#_flagTextures\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#textures} need updating.\n   */\n  _flagTextures = false;\n\n  /**\n   * @name Two.ImageSequence#_flagFrameRate\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#frameRate} needs updating.\n   */\n  _flagFrameRate = false;\n\n  /**\n   * @name Two.ImageSequence#_flagIndex\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#index} needs updating.\n   */\n  _flagIndex = false;\n\n  // Private variables\n\n  /**\n   * @name Two.ImageSequence#_amount\n   * @private\n   * @property {Number} - Number of frames for a given {@link Two.ImageSequence}.\n   */\n  _amount = 1;\n\n  /**\n   * @name Two.ImageSequence#_duration\n   * @private\n   * @property {Number} - Number of milliseconds a {@link Two.ImageSequence}.\n   */\n  _duration = 0;\n\n  /**\n   * @name Two.ImageSequence#_index\n   * @private\n   * @property {Number} - The current frame the {@link Two.ImageSequence} is currently displaying.\n   */\n  _index = 0;\n\n  /**\n   * @name Two.ImageSequence#_startTime\n   * @private\n   * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.ImageSequence} started.\n   */\n  _startTime = 0;\n\n  /**\n   * @name Two.ImageSequence#_playing\n   * @private\n   * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} is animating or not.\n   */\n  _playing = false;\n\n  /**\n   * @name Two.ImageSequence#_firstFrame\n   * @private\n   * @property {Number} - The frame the {@link Two.ImageSequence} should start with.\n   */\n  _firstFrame = 0;\n\n  /**\n   * @name Two.ImageSequence#_lastFrame\n   * @private\n   * @property {Number} - The frame the {@link Two.ImageSequence} should end with.\n   */\n  _lastFrame = 0;\n\n  /**\n   * @name Two.ImageSequence#_playing\n   * @private\n   * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} should loop or not.\n   */\n  _loop = true;\n\n  // Exposed through getter-setter\n\n  /**\n   * @name Two.ImageSequence#_textures\n   * @private\n   * @see {@link Two.ImageSequence#textures}\n   */\n  _textures = null;\n\n  /**\n   * @name Two.ImageSequence#_frameRate\n   * @private\n   * @see {@link Two.ImageSequence#frameRate}\n   */\n  _frameRate = 0;\n\n  /**\n   * @name Two.ImageSequence#_origin\n   * @private\n   * @see {@link Two.ImageSequence#origin}\n   */\n  _origin = null;\n\n  constructor(src, ox, oy, frameRate) {\n    super(ox, oy, 0, 0);\n\n    this._renderer.type = 'image-sequence';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.flagTextures = FlagTextures.bind(this);\n    this._renderer.bindTextures = BindTextures.bind(this);\n    this._renderer.unbindTextures = UnbindTextures.bind(this);\n\n    this.noStroke();\n    this.noFill();\n\n    /**\n     * @name Two.ImageSequence#textures\n     * @property {Two.Texture[]} - A list of textures to be used as frames for animating the {@link Two.ImageSequence}.\n     */\n    if (Array.isArray(src)) {\n      this.textures = src.map(GenerateTexture.bind(this));\n    } else if (typeof src === 'string') {\n      // If just a single src convert into a single Two.Texture\n      this.textures = [GenerateTexture(src)];\n    }\n\n    this.origin = new Vector();\n\n    this._update();\n\n    /**\n     * @name Two.ImageSequence#frameRate\n     * @property {Number} - The number of frames to animate against per second.\n     */\n    if (typeof frameRate === 'number') {\n      this.frameRate = frameRate;\n    } else {\n      this.frameRate = ImageSequence.DefaultFrameRate;\n    }\n\n    /**\n     * @name Two.ImageSequence#index\n     * @property {Number} - The index of the current tile of the sprite to display. Defaults to `0`.\n     */\n    this.index = 0;\n  }\n\n  /**\n   * @name Two.ImageSequence.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.ImageSequence}.\n   */\n  static Properties = [\n    'textures',\n    'frameRate',\n    'index',\n    'firstFrame',\n    'lastFrame',\n    'loop',\n  ];\n\n  /**\n   * @name Two.ImageSequence.DefaultFrameRate\n   * @property The default frame rate that {@link Two.ImageSequence#frameRate} is set to when instantiated.\n   */\n  static DefaultFrameRate = 30;\n\n  /**\n   * @name Two.ImageSequence.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.ImageSequence} to create a new instance\n   * @returns {Two.ImageSequence}\n   * @description Create a new {@link Two.ImageSequence} from an object notation of a {@link Two.ImageSequence}.\n   * @nota-bene Works in conjunction with {@link Two.ImageSequence#toObject}\n   */\n  static fromObject(obj) {\n    const sequence = new ImageSequence().copy(obj);\n\n    if ('id' in obj) {\n      sequence.id = obj.id;\n    }\n\n    return sequence;\n  }\n\n  /**\n   * @name Two.ImageSequence#copy\n   * @function\n   * @param {Two.ImageSequence} imageSequence - The reference {@link Two.ImageSequence}\n   * @description Copy the properties of one {@link Two.ImageSequence} onto another.\n   */\n  copy(imageSequence) {\n    super.copy.call(this, imageSequence);\n\n    for (let i = 0; i < ImageSequence.Properties.length; i++) {\n      const k = ImageSequence.Properties[i];\n      if (k in imageSequence) {\n        this[k] = imageSequence[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.ImageSequence#play\n   * @function\n   * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n   * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.ImageSequence#textures}.\n   * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the image sequence is looped.\n   * @description Initiate animation playback of a {@link Two.ImageSequence}.\n   */\n  play(firstFrame, lastFrame, onLastFrame) {\n    this._playing = true;\n    this._firstFrame = 0;\n    this._lastFrame = this.amount - 1;\n    this._startTime = _.performance.now();\n\n    if (typeof firstFrame === 'number') {\n      this._firstFrame = firstFrame;\n    }\n    if (typeof lastFrame === 'number') {\n      this._lastFrame = lastFrame;\n    }\n    if (typeof onLastFrame === 'function') {\n      this._onLastFrame = onLastFrame;\n    } else {\n      delete this._onLastFrame;\n    }\n\n    if (this._index !== this._firstFrame) {\n      this._startTime -=\n        (1000 * Math.abs(this._index - this._firstFrame)) / this._frameRate;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.ImageSequence#pause\n   * @function\n   * @description Halt animation playback of a {@link Two.ImageSequence}.\n   */\n  pause() {\n    this._playing = false;\n    return this;\n  }\n\n  /**\n   * @name Two.ImageSequence#stop\n   * @function\n   * @description Halt animation playback of a {@link Two.ImageSequence} and set the current frame back to the first frame.\n   */\n  stop() {\n    this._playing = false;\n    this._index = this._firstFrame;\n\n    return this;\n  }\n\n  /**\n   * @name Two.ImageSequence#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.ImageSequence}\n   * @description Create a new instance of {@link Two.ImageSequence} with the same properties of the current image sequence.\n   */\n  clone(parent) {\n    const clone = new ImageSequence(\n      this.textures,\n      this.translation.x,\n      this.translation.y,\n      this.frameRate\n    );\n\n    clone._loop = this._loop;\n\n    if (this._playing) {\n      clone.play();\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.ImageSequence#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = 'image-sequence';\n    object.textures = this.textures.map(function (texture) {\n      return texture.toObject();\n    });\n    object.frameRate = this.frameRate;\n    object.index = this.index;\n    object.firstFrame = this.firstFrame;\n    object.lastFrame = this.lastFrame;\n    object.loop = this.loop;\n    return object;\n  }\n\n  /**\n   * @name Two.ImageSequence#dispose\n   * @function\n   * @returns {Two.ImageSequence}\n   * @description Release the image sequence's renderer resources and detach all events.\n   * This method stops any running animation, clears animation callbacks, unbinds\n   * textures collection events, and disposes individual textures (calling dispose()\n   * for thorough cleanup) while preserving the renderer type for potential\n   * re-attachment to a new renderer.\n   */\n  dispose() {\n    // Call parent dispose to preserve renderer type and unbind events\n    super.dispose();\n\n    // Stop animation if playing\n    if (this._playing) {\n      this._playing = false;\n    }\n\n    // Clear animation callbacks\n    this._onLastFrame = null;\n\n    // Unbind textures collection events\n    if (this.textures && typeof this.textures.unbind === 'function') {\n      try {\n        this.textures.unbind();\n      } catch (e) {\n        // Ignore unbind errors for incomplete Collection objects\n      }\n    }\n\n    // Dispose individual textures (more thorough than unbind)\n    if (this.textures) {\n      for (let i = 0; i < this.textures.length; i++) {\n        const texture = this.textures[i];\n        if (typeof texture.dispose === 'function') {\n          texture.dispose();\n        } else if (typeof texture.unbind === 'function') {\n          texture.unbind();\n        }\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.ImageSequence#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    const effect = this._textures;\n    let width, height, elapsed, amount, duration, texture;\n    let index, frames;\n\n    if (effect) {\n      if (this._flagTextures) {\n        this._amount = effect.length;\n      }\n\n      if (this._flagFrameRate) {\n        this._duration = (1000 * this._amount) / this._frameRate;\n      }\n\n      if (this._playing && this._frameRate > 0) {\n        amount = this._amount;\n\n        if (_.isNaN(this._lastFrame)) {\n          this._lastFrame = amount - 1;\n        }\n\n        // TODO: Offload perf logic to instance of `Two`.\n        elapsed = _.performance.now() - this._startTime;\n        frames = this._lastFrame + 1;\n        duration = (1000 * (frames - this._firstFrame)) / this._frameRate;\n\n        if (this._loop) {\n          elapsed = elapsed % duration;\n        } else {\n          elapsed = Math.min(elapsed, duration);\n        }\n\n        index = lerp(this._firstFrame, frames, elapsed / duration);\n        index = Math.floor(index);\n\n        if (index !== this._index) {\n          this._index = index;\n          texture = effect[this._index];\n\n          if (texture.loaded) {\n            width = texture.image.width;\n            height = texture.image.height;\n\n            if (this.width !== width) {\n              this.width = width;\n            }\n            if (this.height !== height) {\n              this.height = height;\n            }\n\n            this.fill = texture;\n\n            if (index >= this._lastFrame - 1 && this._onLastFrame) {\n              this._onLastFrame(); // Shortcut for chainable sprite animations\n            }\n          }\n        }\n      } else if (this._flagIndex || !(this.fill instanceof Texture)) {\n        texture = effect[this._index];\n\n        if (texture.loaded) {\n          width = texture.image.width;\n          height = texture.image.height;\n\n          if (this.width !== width) {\n            this.width = width;\n          }\n          if (this.height !== height) {\n            this.height = height;\n          }\n        }\n\n        this.fill = texture;\n      }\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.ImageSequence#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagTextures = this._flagFrameRate = false;\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  frameRate: {\n    enumerable: true,\n    get: function () {\n      return this._frameRate;\n    },\n    set: function (v) {\n      this._frameRate = v;\n      this._flagFrameRate = true;\n    },\n  },\n  index: {\n    enumerable: true,\n    get: function () {\n      return this._index;\n    },\n    set: function (v) {\n      this._index = v;\n      this._flagIndex = true;\n    },\n  },\n  textures: {\n    enumerable: true,\n    get: function () {\n      return this._textures;\n    },\n    set: function (textures) {\n      const bindTextures = this._renderer.bindTextures;\n      const unbindTextures = this._renderer.unbindTextures;\n\n      // Remove previous listeners\n      if (this._textures) {\n        this._textures\n          .unbind(Events.Types.insert, bindTextures)\n          .unbind(Events.Types.remove, unbindTextures);\n      }\n\n      // Create new Collection with copy of vertices\n      this._textures = new Collection((textures || []).slice(0));\n\n      // Listen for Collection changes and bind / unbind\n      this._textures\n        .bind(Events.Types.insert, bindTextures)\n        .bind(Events.Types.remove, unbindTextures);\n\n      // Bind Initial Textures\n      bindTextures(this._textures);\n    },\n  },\n  firstFrame: {\n    enumerable: true,\n    get: function () {\n      return this._firstFrame;\n    },\n    set: function (v) {\n      this._firstFrame = v;\n    },\n  },\n  lastFrame: {\n    enumerable: true,\n    get: function () {\n      return this._lastFrame;\n    },\n    set: function (v) {\n      this._lastFrame = v;\n    },\n  },\n  loop: {\n    enumerable: true,\n    get: function () {\n      return this._loop;\n    },\n    set: function (v) {\n      this._loop = !!v;\n    },\n  },\n};\n\n/**\n * @name FlagTextures\n * @private\n * @function\n * @description Cached method to let renderers know textures have been updated on a {@link Two.ImageSequence}.\n */\nfunction FlagTextures() {\n  this._flagTextures = true;\n}\n\n/**\n * @name BindTextures\n * @private\n * @function\n * @description Cached method to let {@link Two.ImageSequence} know textures have been added to the instance.\n */\nfunction BindTextures(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].bind(Events.Types.change, this._renderer.flagTextures);\n  }\n\n  this._renderer.flagTextures();\n}\n\n/**\n * @name UnbindTextures\n * @private\n * @function\n * @description Cached method to let {@link Two.ImageSequence} know textures have been removed from the instance.\n */\nfunction UnbindTextures(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].unbind(Events.Types.change, this._renderer.flagTextures);\n  }\n\n  this._renderer.flagTextures();\n}\n\n/**\n * @name GenerateTexture\n * @private\n * @property {Function} - Shorthand function to prepare source image material into readable format by {@link Two.ImageSequence}.\n * @param {String|Two.Texture} textureOrString - The texture or string to create a {@link Two.Texture} from.\n * @description Function used internally by {@link Two.ImageSequence} to parse arguments and return {@link Two.Texture}s.\n * @returns {Two.Texture}\n */\nfunction GenerateTexture(obj) {\n  if (obj instanceof Texture) {\n    return obj;\n  } else if (typeof obj === 'string') {\n    return new Texture(obj);\n  }\n}\n"
  },
  {
    "path": "src/effects/image.d.ts",
    "content": "declare module 'two.js/src/effects/image' {\n  export type ModeProperties = 'fill' | 'fit' | 'crop' | 'tile' | 'stretch';\n  /**\n   * @name Two.Image\n   * @class\n   * @extends Two.Rectangle\n   * @param {String|Two.Texture} [src] - The URL path or {@link Two.Texture} to be used as the bitmap data displayed on the image.\n   * @param {Number} [ox=0] - The initial `x` position of the Two.Image.\n   * @param {Number} [oy=0] - The initial `y` position of the Two.Image.\n   * @param {Number} [width=1] - The width to display the image at.\n   * @param {Number} [height=1] - The height to display the image at.\n   * @description A convenient package to display images scaled to fit specific dimensions. Unlike {@link Two.Sprite}, this class scales the image to the provided width and height rather than using the image's native dimensions. By default, images are scaled to 'fill' within the bounds while preserving aspect ratio.\n   */\n  export class Image extends Rectangle {\n    /**\n     * @name Two.Image.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Image}.\n     */\n    static Properties: ('texture' | 'mode' | string)[];\n    /**\n     * @name Two.Image.Modes\n     * @property {Object} mode - Different mode types to render an image inspired by Figma.\n     * @property {String} mode.fill - Scale image to fill the bounds while preserving aspect ratio.\n     * @property {String} mode.fit - Scale image to fit within bounds while preserving aspect ratio.\n     * @property {String} mode.crop - Scale image to fill bounds while preserving aspect ratio, cropping excess.\n     * @property {String} mode.tile - Repeat image at original size to fill the bounds.\n     * @property {String} mode.stretch - Stretch image to fill dimensions, ignoring aspect ratio.\n     */\n    static Modes: {\n      fill: 'fill';\n      fit: 'fit';\n      crop: 'crop';\n      tile: 'tile';\n      stretch: 'stretch';\n    };\n\n    /**\n     * @name Two.Image.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Image} to create a new instance\n     * @returns {Two.Image}\n     * @description Create a new {@link Two.Image} from an object notation of a {@link Two.Image}.\n     * @nota-bene Works in conjunction with {@link Two.Image#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Rectangle.fromObject>[0] & {\n        texture?: Parameters<typeof Texture.fromObject>[0];\n        mode?: ModeProperties;\n      }\n    ): Image;\n    constructor(\n      src?: string | Texture,\n      ox?: number,\n      oy?: number,\n      width?: number,\n      height?: number,\n      mode?: ModeProperties\n    );\n    /**\n     * @name Two.Image#_flagTexture\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Image#texture} needs updating.\n     */\n    private _flagTexture;\n    /**\n     * @name Two.Image#_flagMode\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Image#mode} needs updating.\n     */\n    private _flagMode;\n    /**\n     * @name Two.Image#_texture\n     * @private\n     * @see {@link Two.Image#texture}\n     */\n    private _texture;\n    /**\n     * @name Two.Image#_mode\n     * @private\n     * @see {@link Two.Image#mode}\n     */\n    private _mode;\n    texture: Texture;\n    /**\n     * @name Two.Image#mode\n     * @property {String} - The scaling mode for the image. Can be 'fill', 'fit', 'crop', 'tile', or 'stretch'. Defaults to 'fill'.\n     */\n    mode: ModeProperties;\n    /**\n     * @name Two.Image#dispose\n     * @function\n     * @description Release the image's renderer resources and detach all events.\n     * This method disposes the texture (calling dispose() for thorough cleanup) and inherits comprehensive\n     * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n     * for potential re-attachment.\n     */\n    dispose(): Image;\n  }\n  import { Rectangle } from 'two.js/src/shapes/rectangle';\n  import { Texture } from 'two.js/src/effects/texture';\n}\n"
  },
  {
    "path": "src/effects/image.js",
    "content": "import { Vector } from '../vector.js';\nimport { Rectangle } from '../shapes/rectangle.js';\nimport { Texture } from './texture.js';\n\n/**\n * @name Two.Image\n * @class\n * @extends Two.Rectangle\n * @param {String|Two.Texture} [src] - The URL path or {@link Two.Texture} to be used as the bitmap data displayed on the image.\n * @param {Number} [ox=0] - The initial `x` position of the Two.Image.\n * @param {Number} [oy=0] - The initial `y` position of the Two.Image.\n * @param {Number} [width=1] - The width to display the image at.\n * @param {Number} [height=1] - The height to display the image at.\n * @param {String} [mode=\"fill\"] - The fill mode\n * @description A convenient package to display images scaled to fit specific dimensions. Unlike {@link Two.Sprite}, this class scales the image to the provided width and height rather than using the image's native dimensions. By default, images are scaled to 'fill' within the bounds while preserving aspect ratio.\n * @nota-bene Two.Image.fit mode in all renderers is not complete\n */\nexport class Image extends Rectangle {\n  /**\n   * @name Two.Image#_flagTexture\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Image#texture} needs updating.\n   */\n  _flagTexture = false;\n\n  /**\n   * @name Two.Image#_flagMode\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Image#mode} needs updating.\n   */\n  _flagMode = false;\n\n  /**\n   * @name Two.Image#_texture\n   * @private\n   * @see {@link Two.Image#texture}\n   */\n  _texture = null;\n\n  /**\n   * @name Two.Image#_mode\n   * @private\n   * @see {@link Two.Image#mode}\n   */\n  _mode = 'fill';\n\n  constructor(src, ox, oy, width, height, mode) {\n    super(ox, oy, width || 1, height || 1);\n\n    this._renderer.type = 'image';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this.noStroke();\n    this.noFill();\n\n    /**\n     * @name Two.Image#texture\n     * @property {Two.Texture} - The texture to be used as bitmap data to display image in the scene.\n     */\n    if (src instanceof Texture) {\n      this.texture = src;\n    } else if (typeof src === 'string') {\n      this.texture = new Texture(src);\n    }\n\n    if (typeof mode === 'string') {\n      this.mode = mode;\n    }\n\n    this._update();\n  }\n\n  /**\n   * @name Two.Image.Modes\n   * @property {Object} Modes - Different mode types to render an image inspired by Figma.\n   * @property {String} Modes.fill - Scale image to fill the bounds while preserving aspect ratio.\n   * @property {String} Modes.fit - Scale image to fit within bounds while preserving aspect ratio.\n   * @property {String} Modes.crop - Scale image to fill bounds while preserving aspect ratio, cropping excess.\n   * @property {String} Modes.tile - Repeat image at original size to fill the bounds.\n   * @property {String} Modes.stretch - Stretch image to fill dimensions, ignoring aspect ratio.\n   */\n  static Modes = {\n    fill: 'fill',\n    fit: 'fit',\n    crop: 'crop',\n    tile: 'tile',\n    stretch: 'stretch',\n  };\n\n  /**\n   * @name Two.Image.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Image}.\n   */\n  static Properties = ['texture', 'mode'];\n\n  /**\n   * @name Two.Image.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Image} to create a new instance\n   * @returns {Two.Image}\n   * @description Create a new {@link Two.Image} from an object notation of a {@link Two.Image}.\n   * @nota-bene Works in conjunction with {@link Two.Image#toObject}\n   */\n  static fromObject(obj) {\n    const image = new Image().copy(obj);\n\n    if ('id' in obj) {\n      image.id = obj.id;\n    }\n\n    return image;\n  }\n\n  /**\n   * @name Two.Image#copy\n   * @function\n   * @param {Two.Image} image - The reference {@link Two.Image}\n   * @description Copy the properties of one {@link Two.Image} onto another.\n   */\n  copy(image) {\n    super.copy.call(this, image);\n\n    for (let i = 0; i < Image.Properties.length; i++) {\n      const k = Image.Properties[i];\n      if (k in image) {\n        this[k] = image[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Image#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Image}\n   * @description Create a new instance of {@link Two.Image} with the same properties of the current image.\n   */\n  clone(parent) {\n    const clone = new Image(\n      this.texture,\n      this.translation.x,\n      this.translation.y,\n      this.width,\n      this.height\n    );\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Image#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the image.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = 'image';\n    object.texture = this.texture.toObject();\n    object.mode = this.mode;\n    return object;\n  }\n\n  /**\n   * @name Two.Image#dispose\n   * @function\n   * @returns {Two.Image}\n   * @description Release the image's renderer resources and detach all events.\n   * This method disposes the texture (calling dispose() for thorough cleanup) and inherits comprehensive\n   * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n   * for potential re-attachment.\n   */\n  dispose() {\n    // Call parent dispose for inherited cleanup (vertices, fill/stroke effects)\n    super.dispose();\n\n    // Dispose texture (more thorough than unbind)\n    if (this._texture && typeof this._texture.dispose === 'function') {\n      this._texture.dispose();\n    } else if (this._texture && typeof this._texture.unbind === 'function') {\n      this._texture.unbind();\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Image#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    const effect = this._texture;\n\n    if (effect) {\n      if (this._flagTexture) {\n        this.fill = effect;\n      }\n\n      if (effect.loaded) {\n        const iw = effect.image.width;\n        const ih = effect.image.height;\n        const rw = this.width;\n        const rh = this.height;\n\n        // Calculate base scale ratios\n        const scaleX = rw / iw;\n        const scaleY = rh / ih;\n\n        // Apply scaling based on mode\n        switch (this._mode) {\n          case Image.Modes.fill: {\n            // Fill within bounds while preserving aspect ratio\n            const scale = Math.max(scaleX, scaleY);\n            effect.scale = scale;\n            effect.offset.x = 0;\n            effect.offset.y = 0;\n            effect.repeat = 'repeat';\n            break;\n          }\n\n          case Image.Modes.fit: {\n            // Fit within bounds while preserving aspect ratio\n            const scale = Math.min(scaleX, scaleY);\n            effect.scale = scale; // TODO: For SVG this works `new Vector(scaleX, scaleY);`\n            effect.offset.x = 0;\n            effect.offset.y = 0;\n            effect.repeat = 'no-repeat';\n            break;\n          }\n\n          case Image.Modes.crop: {\n            // Intentionally left blank to allow\n            // external developer to control\n            break;\n          }\n\n          case Image.Modes.tile: {\n            // Repeat image and align it correctly\n            effect.offset.x = (iw - rw) / 2;\n            effect.offset.y = (ih - rh) / 2;\n            effect.repeat = 'repeat';\n            break;\n          }\n\n          case Image.Modes.stretch:\n          default: {\n            // Stretch the image texture to whatever the dimensions of the rect are\n            effect.scale = new Vector(scaleX, scaleY);\n            effect.offset.x = 0;\n            effect.offset.y = 0;\n            effect.repeat = 'repeat';\n          }\n        }\n      }\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Image#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    super.flagReset.call(this);\n    this._flagTexture = this._flagMode = false;\n\n    return this;\n  }\n}\n\nconst proto = {\n  texture: {\n    enumerable: true,\n    get: function () {\n      return this._texture;\n    },\n    set: function (v) {\n      this._texture = v;\n      this._flagTexture = true;\n    },\n  },\n  mode: {\n    enumerable: true,\n    get: function () {\n      return this._mode;\n    },\n    set: function (v) {\n      this._mode = v;\n      this._flagMode = true;\n    },\n  },\n};\n"
  },
  {
    "path": "src/effects/linear-gradient.d.ts",
    "content": "declare module 'two.js/src/effects/linear-gradient' {\n  /**\n   * @name Two.LinearGradient\n   * @class\n   * @param {Number} [x1=0] - The x position of the first end point of the linear gradient.\n   * @param {Number} [y1=0] - The y position of the first end point of the linear gradient.\n   * @param {Number} [x2=0] - The x position of the second end point of the linear gradient.\n   * @param {Number} [y2=0] - The y position of the second end point of the linear gradient.\n   * @param {Stop[]} [stops] - A list of {@link Two.Stop}s that contain the gradient fill pattern for the gradient.\n   * @nota-bene The linear gradient lives within the space of the parent object's matrix space.\n   */\n  export class LinearGradient extends Gradient {\n    /**\n     * @name Two.LinearGradient.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.LinearGradient} to create a new instance\n     * @returns {Two.LinearGradient}\n     * @description Create a new {@link Two.LinearGradient} from an object notation of a {@link Two.LinearGradient}.\n     * @nota-bene Works in conjunction with {@link Two.LinearGradient#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Gradient.fromObject>[0] & {\n        left?: { x: number; y: number } | Vector;\n        right?: { x: number; y: number } | Vector;\n      }\n    ): LinearGradient;\n    constructor(\n      x1?: number,\n      y1?: number,\n      x2?: number,\n      y2?: number,\n      stops?: Stop[]\n    );\n    /**\n     * @name Two.LinearGradient#_flagEndPoints\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.LinearGradient#left} or {@link Two.LinearGradient#right} changed and needs to update.\n     */\n    private _flagEndPoints;\n    private _left: Vector;\n    private _right: Vector;\n    /**\n     * @name Two.LinearGradient#left\n     * @property {Vector} - The x and y value for where the first end point is placed on the canvas.\n     */\n    left: Vector;\n    /**\n     * @name Two.LinearGradient#right\n     * @property {Vector} - The x and y value for where the second end point is placed on the canvas.\n     */\n    right: Vector;\n    /**\n     * @name Two.LinearGradient#copy\n     * @function\n     * @param {Two.LinearGradient} gradient - The reference {@link Two.LinearGradient}\n     * @description Copy the properties of one {@link Two.LinearGradient} onto another.\n     */\n    copy(gradient: LinearGradient): LinearGradient;\n    /**\n     * @name Two.LinearGradient#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Gradient}\n     * @description Create a new instance of {@link Two.LinearGradient} with the same properties of the current path.\n     */\n    clone(parent?: Group): LinearGradient;\n    /**\n     * @name Two.LinearGradient#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject(): object;\n    /**\n     * @name Two.LinearGradient#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    protected _update(bubbles?: boolean): LinearGradient;\n    /**\n     * @name Two.LinearGradient#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): LinearGradient;\n  }\n  import { Gradient } from 'two.js/src/effects/gradient';\n  import { Group } from 'two.js/src/group';\n  import { Stop } from 'two.js/src/effects/stop';\n  import { Vector } from 'two.js/src/vector';\n}\n"
  },
  {
    "path": "src/effects/linear-gradient.js",
    "content": "import { Events } from '../events.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Stop } from './stop.js';\nimport { Gradient } from './gradient.js';\nimport { Vector } from '../vector.js';\n\n/**\n * @name Two.LinearGradient\n * @class\n * @extends Two.Gradient\n * @param {Number} [x1=0] - The x position of the first end point of the linear gradient.\n * @param {Number} [y1=0] - The y position of the first end point of the linear gradient.\n * @param {Number} [x2=0] - The x position of the second end point of the linear gradient.\n * @param {Number} [y2=0] - The y position of the second end point of the linear gradient.\n * @param {Two.Stop[]} [stops] - A list of {@link Two.Stop}s that contain the gradient fill pattern for the gradient.\n * @nota-bene The linear gradient lives within the space of the parent object's matrix space.\n */\nexport class LinearGradient extends Gradient {\n  /**\n   * @name Two.LinearGradient#_flagEndPoints\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.LinearGradient#left} or {@link Two.LinearGradient#right} changed and needs to update.\n   */\n  _flagEndPoints = false;\n  _left = null;\n  _right = null;\n\n  constructor(x1, y1, x2, y2, stops) {\n    super(stops);\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'linear-gradient';\n    this._renderer.flagEndPoints = FlagEndPoints.bind(this);\n\n    /**\n     * @name Two.LinearGradient#left\n     * @property {Two.Vector} - The x and y value for where the first end point is placed on the canvas.\n     */\n    this.left = new Vector();\n    /**\n     * @name Two.LinearGradient#right\n     * @property {Two.Vector} - The x and y value for where the second end point is placed on the canvas.\n     */\n    this.right = new Vector();\n\n    if (typeof x1 === 'number') {\n      this.left.x = x1;\n    }\n    if (typeof y1 === 'number') {\n      this.left.y = y1;\n    }\n    if (typeof x2 === 'number') {\n      this.right.x = x2;\n    }\n    if (typeof y2 === 'number') {\n      this.right.y = y2;\n    }\n  }\n\n  /**\n   * @name Two.LinearGradient.Stop\n   * @see {@link Two.Stop}\n   */\n  static Stop = Stop;\n\n  static Properties = ['left', 'right'];\n\n  /**\n   * @name Two.LinearGradient.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.LinearGradient} to create a new instance\n   * @returns {Two.LinearGradient}\n   * @description Create a new {@link Two.LinearGradient} from an object notation of a {@link Two.LinearGradient}.\n   * @nota-bene Works in conjunction with {@link Two.LinearGradient#toObject}\n   */\n  static fromObject(obj) {\n    const gradient = new LinearGradient().copy(obj);\n\n    if ('id' in obj) {\n      gradient.id = obj.id;\n    }\n\n    return gradient;\n  }\n\n  /**\n   * @name Two.LinearGradient#copy\n   * @function\n   * @param {Two.LinearGradient} gradient - The reference {@link Two.LinearGradient}\n   * @description Copy the properties of one {@link Two.LinearGradient} onto another.\n   */\n  copy(gradient) {\n    super.copy.call(this, gradient);\n\n    for (let i = 0; i < LinearGradient.Properties.length; i++) {\n      const k = LinearGradient.Properties[i];\n      if (k in gradient) {\n        this[k] =\n          gradient[k] instanceof Vector\n            ? gradient[k]\n            : new Vector().copy(gradient[k]);\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.LinearGradient#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Gradient}\n   * @description Create a new instance of {@link Two.LinearGradient} with the same properties of the current path.\n   */\n  clone(parent) {\n    const stops = this.stops.map(function (stop) {\n      return stop.clone();\n    });\n\n    const clone = new LinearGradient(\n      this.left._x,\n      this.left._y,\n      this.right._x,\n      this.right._y,\n      stops\n    );\n\n    _.each(\n      Gradient.Properties,\n      function (k) {\n        clone[k] = this[k];\n      },\n      this\n    );\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.LinearGradient#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n\n    result.left = this.left.toObject();\n    result.right = this.right.toObject();\n\n    return result;\n  }\n\n  /**\n   * @name Two.LinearGradient#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagEndPoints || this._flagSpread || this._flagStops) {\n      this.trigger(Events.Types.change);\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.LinearGradient#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagEndPoints = false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  left: {\n    enumerable: true,\n    get: function () {\n      return this._left;\n    },\n    set: function (v) {\n      if (this._left instanceof Vector) {\n        this._left.unbind(Events.Types.change, this._renderer.flagEndPoints);\n      }\n      this._left = v;\n      this._left.bind(Events.Types.change, this._renderer.flagEndPoints);\n      this._flagEndPoints = true;\n    },\n  },\n  right: {\n    enumerable: true,\n    get: function () {\n      return this._right;\n    },\n    set: function (v) {\n      if (this._right instanceof Vector) {\n        this._right.unbind(Events.Types.change, this._renderer.flagEndPoints);\n      }\n      this._right = v;\n      this._right.bind(Events.Types.change, this._renderer.flagEndPoints);\n      this._flagEndPoints = true;\n    },\n  },\n};\n\n/**\n * @name FlagEndPoints\n * @private\n * @function\n * @description Cached method to let renderers know end points have been updated on a {@link Two.LinearGradient}.\n */\nfunction FlagEndPoints() {\n  this._flagEndPoints = true;\n}\n"
  },
  {
    "path": "src/effects/radial-gradient.d.ts",
    "content": "declare module 'two.js/src/effects/radial-gradient' {\n  /**\n   * @name Two.RadialGradient\n   * @class\n   * @param {Number} [x=0] - The x position of the origin of the radial gradient.\n   * @param {Number} [y=0] - The y position of the origin of the radial gradient.\n   * @param {Number} [radius=0] - The radius of the radial gradient.\n   * @param {Stop[]} [stops] - A list of {@link Two.Stop}s that contain the gradient fill pattern for the gradient.\n   * @param {Number} [focalX=0] - The x position of the focal point on the radial gradient.\n   * @param {Number} [focalY=0] - The y position of the focal point on the radial gradient.\n   * @nota-bene The radial gradient lives within the space of the parent object's matrix space.\n   */\n  export class RadialGradient extends Gradient {\n    /**\n     * @name Two.RadialGradient.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.RadialGradient} to create a new instance\n     * @returns {Two.RadialGradient}\n     * @description Create a new {@link Two.RadialGradient} from an object notation of a {@link Two.RadialGradient}.\n     * @nota-bene Works in conjunction with {@link Two.RadialGradient#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Gradient.fromObject>[0] & {\n        radius?: number;\n        center?: { x: number; y: number } | Vector;\n        focal?: { x: number; y: number } | Vector;\n      }\n    ): RadialGradient;\n    constructor(\n      cx?: number,\n      cy?: number,\n      r?: number,\n      stops?: Stop[],\n      fx?: number,\n      fy?: number\n    );\n    /**\n     * @name Two.RadialGradient#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#radius} changed and needs to update.\n     */\n    private _flagRadius;\n    /**\n     * @name Two.RadialGradient#_flagCenter\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#center} changed and needs to update.\n     */\n    private _flagCenter;\n    /**\n     * @name Two.RadialGradient#_flagFocal\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RadialGradient#focal} changed and needs to update.\n     */\n    private _flagFocal;\n    private _radius: number;\n    private _center: Vector;\n    private _focal: Vector;\n    /**\n     * @name Two.RadialGradient#center\n     * @property {Vector} - The x and y value for where the origin of the radial gradient is.\n     */\n    center: Vector;\n    radius: number;\n    /**\n     * @name Two.RadialGradient#focal\n     * @property {Vector} - The x and y value for where the focal point of the radial gradient is.\n     * @nota-bene This effects the spray or spread of the radial gradient.\n     */\n    focal: Vector;\n    /**\n     * @name Two.RadialGradient#copy\n     * @function\n     * @param {Two.RadialGradient} gradient - The reference {@link Two.RadialGradient}\n     * @description Copy the properties of one {@link Two.RadialGradient} onto another.\n     */\n    copy(gradient: RadialGradient): RadialGradient;\n    /**\n     * @name Two.RadialGradient#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.RadialGradient}\n     * @description Create a new instance of {@link Two.RadialGradient} with the same properties of the current path.\n     */\n    clone(parent?: Group): RadialGradient;\n    /**\n     * @name Two.RadialGradient#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject(): object;\n    /**\n     * @name Two.RadialGradient#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    _update(): RadialGradient;\n    /**\n     * @name Two.RadialGradient#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): RadialGradient;\n  }\n  import { Gradient } from 'two.js/src/effects/gradient';\n  import { Group } from 'two.js/src/group';\n  import { Stop } from 'two.js/src/effects/stop';\n  import { Vector } from 'two.js/src/vector';\n}\n"
  },
  {
    "path": "src/effects/radial-gradient.js",
    "content": "import { Events } from '../events.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Stop } from './stop.js';\nimport { Gradient } from './gradient.js';\nimport { Vector } from '../vector.js';\n\n/**\n * @name Two.RadialGradient\n * @class\n * @extends Two.Gradient\n * @param {Number} [x=0] - The x position of the origin of the radial gradient.\n * @param {Number} [y=0] - The y position of the origin of the radial gradient.\n * @param {Number} [radius=0] - The radius of the radial gradient.\n * @param {Two.Stop[]} [stops] - A list of {@link Two.Stop}s that contain the gradient fill pattern for the gradient.\n * @param {Number} [focalX=0] - The x position of the focal point on the radial gradient.\n * @param {Number} [focalY=0] - The y position of the focal point on the radial gradient.\n * @nota-bene The radial gradient lives within the space of the parent object's matrix space.\n */\nexport class RadialGradient extends Gradient {\n  /**\n   * @name Two.RadialGradient#_flagRadius\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.RadialGradient#radius} changed and needs to update.\n   */\n  _flagRadius = false;\n  /**\n   * @name Two.RadialGradient#_flagCenter\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.RadialGradient#center} changed and needs to update.\n   */\n  _flagCenter = false;\n  /**\n   * @name Two.RadialGradient#_flagFocal\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.RadialGradient#focal} changed and needs to update.\n   */\n  _flagFocal = false;\n\n  _radius = 0;\n  _center = null;\n  _focal = null;\n\n  constructor(cx, cy, r, stops, fx, fy) {\n    super(stops);\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'radial-gradient';\n    this._renderer.flagCenter = FlagCenter.bind(this);\n    this._renderer.flagFocal = FlagFocal.bind(this);\n\n    /**\n     * @name Two.RadialGradient#center\n     * @property {Two.Vector} - The x and y value for where the origin of the radial gradient is.\n     */\n    this.center = new Vector();\n\n    this.radius = typeof r === 'number' ? r : 1;\n\n    /**\n     * @name Two.RadialGradient#focal\n     * @property {Two.Vector} - The x and y value for where the focal point of the radial gradient is.\n     * @nota-bene This effects the spray or spread of the radial gradient.\n     */\n    this.focal = new Vector();\n\n    if (typeof cx === 'number') {\n      this.center.x = cx;\n    }\n    if (typeof cy === 'number') {\n      this.center.y = cy;\n    }\n\n    this.focal.copy(this.center);\n\n    if (typeof fx === 'number') {\n      this.focal.x = fx;\n    }\n    if (typeof fy === 'number') {\n      this.focal.y = fy;\n    }\n  }\n\n  /**\n   * @name Two.RadialGradient.Stop\n   * @see {@link Two.Stop}\n   */\n  static Stop = Stop;\n\n  /**\n   * @name Two.RadialGradient.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.RadialGradient}.\n   */\n  static Properties = ['center', 'radius', 'focal'];\n\n  /**\n   * @name Two.RadialGradient.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.RadialGradient} to create a new instance\n   * @returns {Two.RadialGradient}\n   * @description Create a new {@link Two.RadialGradient} from an object notation of a {@link Two.RadialGradient}.\n   * @nota-bene Works in conjunction with {@link Two.RadialGradient#toObject}\n   */\n  static fromObject(obj) {\n    const gradient = new RadialGradient().copy(obj);\n\n    if ('id' in obj) {\n      gradient.id = obj.id;\n    }\n\n    return gradient;\n  }\n\n  /**\n   * @name Two.RadialGradient#copy\n   * @function\n   * @param {Two.RadialGradient} gradient - The reference {@link Two.RadialGradient}\n   * @description Copy the properties of one {@link Two.RadialGradient} onto another.\n   */\n  copy(gradient) {\n    super.copy.call(this, gradient);\n\n    for (let i = 0; i < RadialGradient.Properties.length; i++) {\n      const k = RadialGradient.Properties[i];\n      if (k in gradient) {\n        if (/(center|focal)i/.test(k)) {\n          this[k] =\n            gradient[k] instanceof Vector\n              ? gradient[k]\n              : new Vector().copy(gradient[k]);\n        } else if (typeof gradient[k] === 'number') {\n          this[k] = gradient[MediaKeySystemAccess];\n        }\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.RadialGradient#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.RadialGradient}\n   * @description Create a new instance of {@link Two.RadialGradient} with the same properties of the current path.\n   */\n  clone(parent) {\n    const stops = this.stops.map(function (stop) {\n      return stop.clone();\n    });\n\n    const clone = new RadialGradient(\n      this.center._x,\n      this.center._y,\n      this._radius,\n      stops,\n      this.focal._x,\n      this.focal._y\n    );\n\n    _.each(\n      Gradient.Properties.concat(RadialGradient.Properties),\n      function (k) {\n        clone[k] = this[k];\n      },\n      this\n    );\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.RadialGradient#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n\n    _.each(\n      RadialGradient.Properties,\n      function (k) {\n        result[k] = this[k];\n      },\n      this\n    );\n\n    result.center = this.center.toObject();\n    result.focal = this.focal.toObject();\n\n    return result;\n  }\n\n  /**\n   * @name Two.RadialGradient#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (\n      this._flagRadius ||\n      this._flatCenter ||\n      this._flagFocal ||\n      this._flagSpread ||\n      this._flagStops\n    ) {\n      this.trigger(Events.Types.change);\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.RadialGradient#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagRadius = this._flagCenter = this._flagFocal = false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  radius: {\n    enumerable: true,\n    get: function () {\n      return this._radius;\n    },\n    set: function (v) {\n      this._radius = v;\n      this._flagRadius = true;\n    },\n  },\n  center: {\n    enumerable: true,\n    get: function () {\n      return this._center;\n    },\n    set: function (v) {\n      if (this._center) {\n        this._center.unbind(Events.Types.change, this._renderer.flagCenter);\n      }\n      this._center = v;\n      this._center.bind(Events.Types.change, this._renderer.flagCenter);\n      this._flagCenter = true;\n    },\n  },\n  focal: {\n    enumerable: true,\n    get: function () {\n      return this._focal;\n    },\n    set: function (v) {\n      if (this._focal) {\n        this._focal.unbind(Events.Types.change, this._renderer.flagFocal);\n      }\n      this._focal = v;\n      this._focal.bind(Events.Types.change, this._renderer.flagFocal);\n      this._flagFocal = true;\n    },\n  },\n};\n\nfunction FlagCenter() {\n  this._flagCenter = true;\n}\n\nfunction FlagFocal() {\n  this._flagFocal = true;\n}\n"
  },
  {
    "path": "src/effects/sprite.d.ts",
    "content": "declare module 'two.js/src/effects/sprite' {\n  /**\n     * @name Two.Sprite\n     * @class\n\n     * @param {String|Texture} [src] - The URL path or {@link Two.Texture} to be used as the bitmap data displayed on the sprite.\n     * @param {Number} [ox=0] - The initial `x` position of the Two.Sprite.\n     * @param {Number} [oy=0] - The initial `y` position of the Two.Sprite.\n     * @param {Number} [cols=1] - The number of columns the sprite contains.\n     * @param {Number} [rows=1] - The number of rows the sprite contains.\n     * @param {Number} [frameRate=0] - The frame rate at which the partitions of the image should playback at.\n     * @description A convenient package to display still or animated images through a tiled image source. For more information on the principals of animated imagery through tiling see [Texture Atlas](https://en.wikipedia.org/wiki/Texture_atlas) on Wikipedia.\n     */\n  export class Sprite extends Rectangle {\n    /**\n     * @name Two.Sprite.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Sprite}.\n     */\n    static Properties: (\n      | 'texture'\n      | 'columns'\n      | 'rows'\n      | 'frameRate'\n      | 'index'\n      | 'firstFrame'\n      | 'lastFrame'\n      | 'loop'\n      | string\n    )[];\n    /**\n     * @name Two.Sprite.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Sprite} to create a new instance\n     * @returns {Two.Sprite}\n     * @description Create a new {@link Two.Sprite} from an object notation of a {@link Two.Sprite}.\n     * @nota-bene Works in conjunction with {@link Two.Sprite#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Rectangle.fromObject>[0] & {\n        texture?: Parameters<typeof Texture.fromObject>[0];\n        columns?: number;\n        rows?: number;\n        frameRate?: number;\n        index?: number;\n        firstFrame?: number;\n        lastFrame?: number;\n        loop?: boolean;\n      }\n    ): Sprite;\n    constructor(\n      src?: string | Texture,\n      ox?: number,\n      oy?: number,\n      cols?: number,\n      rows?: number,\n      frameRate?: number\n    );\n    /**\n     * @name Two.Sprite#_flagTexture\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#texture} needs updating.\n     */\n    private _flagTexture;\n    /**\n     * @name Two.Sprite#_flagColumns\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#columns} need updating.\n     */\n    private _flagColumns;\n    /**\n     * @name Two.Sprite#_flagRows\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#rows} need updating.\n     */\n    private _flagRows;\n    /**\n     * @name Two.Sprite#_flagFrameRate\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#flagFrameRate} needs updating.\n     */\n    private _flagFrameRate;\n    /**\n     * @name Two.Sprite#_flagIndex\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Sprite#index} needs updating.\n     */\n    private _flagIndex;\n    /**\n     * @name Two.Sprite#_amount\n     * @private\n     * @property {Number} - Number of frames for a given {@link Two.Sprite}.\n     */\n    private _amount;\n    /**\n     * @name Two.Sprite#_duration\n     * @private\n     * @property {Number} - Number of milliseconds a {@link Two.Sprite}.\n     */\n    private _duration;\n    /**\n     * @name Two.Sprite#_startTime\n     * @private\n     * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.Sprite} started.\n     */\n    private _startTime;\n    /**\n     * @name Two.Sprite#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.Sprite} is animating or not.\n     */\n    private _playing;\n    /**\n     * @name Two.Sprite#_firstFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.Sprite} should start with.\n     */\n    private _firstFrame;\n    /**\n     * @name Two.Sprite#_lastFrame\n     * @private\n     * @property {Number} - The frame the {@link Two.Sprite} should end with.\n     */\n    private _lastFrame;\n    /**\n     * @name Two.Sprite#_playing\n     * @private\n     * @property {Boolean} - Dictates whether the {@link Two.Sprite} should loop or not.\n     */\n    private _loop;\n    /**\n     * @name Two.Sprite#_texture\n     * @private\n     * @see {@link Two.Sprite#texture}\n     */\n    private _texture;\n    /**\n     * @name Two.Sprite#_columns\n     * @private\n     * @see {@link Two.Sprite#columns}\n     */\n    private _columns;\n    /**\n     * @name Two.Sprite#_rows\n     * @private\n     * @see {@link Two.Sprite#rows}\n     */\n    private _rows;\n    /**\n     * @name Two.Sprite#_frameRate\n     * @private\n     * @see {@link Two.Sprite#frameRate}\n     */\n    private _frameRate;\n    /**\n     * @name Two.Sprite#_index\n     * @private\n     * @property {Number} - The current frame the {@link Two.Sprite} is currently displaying.\n     */\n    private _index;\n    texture: Texture;\n    columns: number;\n    rows: number;\n    frameRate: number;\n    /**\n     * @name Two.Sprite#index\n     * @property {Number} - The index of the current tile of the sprite to display. Defaults to `0`.\n     */\n    index: number;\n    /**\n     * @name Two.Sprite#copy\n     * @function\n     * @param {Two.Sprite} sprite - The reference {@link Two.Sprite}\n     * @description Copy the properties of one {@link Two.Sprite} onto another.\n     */\n    copy(sprite: Sprite): Sprite;\n    /**\n     * @name Two.Sprite#play\n     * @function\n     * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n     * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.Sprite#textures}.\n     * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the sprite is looped.\n     * @description Initiate animation playback of a {@link Two.Sprite}.\n     */\n    play(\n      firstFrame?: number,\n      lastFrame?: number,\n      onLastFrame?: () => void\n    ): Sprite;\n    /**\n     * @name Two.Sprite#pause\n     * @function\n     * @description Halt animation playback of a {@link Two.Sprite}.\n     */\n    pause(): Sprite;\n    /**\n     * @name Two.Sprite#stop\n     * @function\n     * @description Halt animation playback of a {@link Two.Sprite} and set the current frame back to the first frame.\n     */\n    stop(): Sprite;\n    /**\n     * @name Two.Sprite#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Sprite}\n     * @description Create a new instance of {@link Two.Sprite} with the same properties of the current sprite.\n     */\n    clone(parent?: Group): Sprite;\n    /**\n     * @name Two.Sprite#dispose\n     * @function\n     * @description Release the sprite's renderer resources and detach all events.\n     * This method stops any running animation, clears animation callbacks, disposes\n     * the texture (calling dispose() for thorough cleanup), and inherits comprehensive\n     * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n     * for potential re-attachment.\n     */\n    dispose(): Sprite;\n  }\n  import { Rectangle } from 'two.js/src/shapes/rectangle';\n  import { Texture } from 'two.js/src/effects/texture';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/effects/sprite.js",
    "content": "import { lerp } from '../utils/math.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Vector } from '../vector.js';\nimport { Rectangle } from '../shapes/rectangle.js';\nimport { Texture } from './texture.js';\n\n/**\n * @name Two.Sprite\n * @class\n * @extends Two.Rectangle\n * @param {String|Two.Texture} [src] - The URL path or {@link Two.Texture} to be used as the bitmap data displayed on the sprite.\n * @param {Number} [ox=0] - The initial `x` position of the Two.Sprite.\n * @param {Number} [oy=0] - The initial `y` position of the Two.Sprite.\n * @param {Number} [cols=1] - The number of columns the sprite contains.\n * @param {Number} [rows=1] - The number of rows the sprite contains.\n * @param {Number} [frameRate=0] - The frame rate at which the partitions of the image should playback at.\n * @description A convenient package to display still or animated images through a tiled image source. For more information on the principals of animated imagery through tiling see [Texture Atlas](https://en.wikipedia.org/wiki/Texture_atlas) on Wikipedia.\n */\nexport class Sprite extends Rectangle {\n  /**\n   * @name Two.Sprite#_flagTexture\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Sprite#texture} needs updating.\n   */\n  _flagTexture = false;\n\n  /**\n   * @name Two.Sprite#_flagColumns\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Sprite#columns} need updating.\n   */\n  _flagColumns = false;\n\n  /**\n   * @name Two.Sprite#_flagRows\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Sprite#rows} need updating.\n   */\n  _flagRows = false;\n\n  /**\n   * @name Two.Sprite#_flagFrameRate\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Sprite#flagFrameRate} needs updating.\n   */\n  _flagFrameRate = false;\n\n  /**\n   * @name Two.Sprite#_flagIndex\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Sprite#index} needs updating.\n   */\n  _flagIndex = false;\n\n  // Private variables\n\n  /**\n   * @name Two.Sprite#_amount\n   * @private\n   * @property {Number} - Number of frames for a given {@link Two.Sprite}.\n   */\n  _amount = 1;\n\n  /**\n   * @name Two.Sprite#_duration\n   * @private\n   * @property {Number} - Number of milliseconds a {@link Two.Sprite}.\n   */\n  _duration = 0;\n\n  /**\n   * @name Two.Sprite#_startTime\n   * @private\n   * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.Sprite} started.\n   */\n  _startTime = 0;\n\n  /**\n   * @name Two.Sprite#_playing\n   * @private\n   * @property {Boolean} - Dictates whether the {@link Two.Sprite} is animating or not.\n   */\n  _playing = false;\n\n  /**\n   * @name Two.Sprite#_firstFrame\n   * @private\n   * @property {Number} - The frame the {@link Two.Sprite} should start with.\n   */\n  _firstFrame = 0;\n\n  /**\n   * @name Two.Sprite#_lastFrame\n   * @private\n   * @property {Number} - The frame the {@link Two.Sprite} should end with.\n   */\n  _lastFrame = 0;\n\n  /**\n   * @name Two.Sprite#_loop\n   * @private\n   * @property {Boolean} - Dictates whether the {@link Two.Sprite} should loop or not.\n   */\n  _loop = true;\n\n  // Exposed through getter-setter\n\n  /**\n   * @name Two.Sprite#_texture\n   * @private\n   * @see {@link Two.Sprite#texture}\n   */\n  _texture = null;\n\n  /**\n   * @name Two.Sprite#_columns\n   * @private\n   * @see {@link Two.Sprite#columns}\n   */\n  _columns = 1;\n\n  /**\n   * @name Two.Sprite#_rows\n   * @private\n   * @see {@link Two.Sprite#rows}\n   */\n  _rows = 1;\n\n  /**\n   * @name Two.Sprite#_frameRate\n   * @private\n   * @see {@link Two.Sprite#frameRate}\n   */\n  _frameRate = 0;\n\n  /**\n   * @name Two.Sprite#_index\n   * @private\n   * @property {Number} - The current frame the {@link Two.Sprite} is currently displaying.\n   */\n  _index = 0;\n\n  /**\n   * @name Two.Sprite#_origin\n   * @private\n   * @see {@link Two.Sprite#origin}\n   */\n  _origin = null;\n\n  constructor(src, ox, oy, cols, rows, frameRate) {\n    super(ox, oy, 0, 0);\n\n    this._renderer.type = 'sprite';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this.noStroke();\n    this.noFill();\n\n    /**\n     * @name Two.Sprite#texture\n     * @property {Two.Texture} - The texture to be used as bitmap data to display image in the scene.\n     */\n    if (src instanceof Texture) {\n      this.texture = src;\n    } else if (typeof src === 'string') {\n      this.texture = new Texture(src);\n    }\n\n    this.origin = new Vector();\n\n    this._update();\n\n    /**\n     * @name Two.Sprite#columns\n     * @property {Number} - The number of columns to split the texture into. Defaults to `1`.\n     */\n    if (typeof cols === 'number') {\n      this.columns = cols;\n    }\n\n    /**\n     * @name Two.Sprite#rows\n     * @property {Number} - The number of rows to split the texture into. Defaults to `1`.\n     */\n    if (typeof rows === 'number') {\n      this.rows = rows;\n    }\n\n    /**\n     * @name Two.Sprite#frameRate\n     * @property {Number} - The number of frames to animate against per second. Defaults to `0` for non-animated sprites.\n     */\n    if (typeof frameRate === 'number') {\n      this.frameRate = frameRate;\n    }\n\n    /**\n     * @name Two.Sprite#index\n     * @property {Number} - The index of the current tile of the sprite to display. Defaults to `0`.\n     */\n    this.index = 0;\n  }\n\n  /**\n   * @name Two.Sprite.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Sprite}.\n   */\n  static Properties = [\n    'texture',\n    'columns',\n    'rows',\n    'frameRate',\n    'index',\n    'firstFrame',\n    'lastFrame',\n    'loop',\n  ];\n\n  /**\n   * @name Two.Sprite.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Sprite} to create a new instance\n   * @returns {Two.Sprite}\n   * @description Create a new {@link Two.Sprite} from an object notation of a {@link Two.Sprite}.\n   * @nota-bene Works in conjunction with {@link Two.Sprite#toObject}\n   */\n  static fromObject(obj) {\n    const sprite = new Sprite().copy(obj);\n\n    if ('id' in obj) {\n      sprite.id = obj.id;\n    }\n\n    return sprite;\n  }\n\n  /**\n   * @name Two.Sprite#copy\n   * @function\n   * @param {Two.Sprite} sprite - The reference {@link Two.Sprite}\n   * @description Copy the properties of one {@link Two.Sprite} onto another.\n   */\n  copy(sprite) {\n    super.copy.call(this, sprite);\n\n    for (let i = 0; i < Sprite.Properties.length; i++) {\n      const k = Sprite.Properties[i];\n      if (k in sprite) {\n        this[k] = sprite[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Sprite#play\n   * @function\n   * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.\n   * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.Sprite#textures}.\n   * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the sprite is looped.\n   * @description Initiate animation playback of a {@link Two.Sprite}.\n   */\n  play(firstFrame, lastFrame, onLastFrame) {\n    this._playing = true;\n    this._firstFrame = 0;\n    this._lastFrame = this.amount - 1;\n    this._startTime = _.performance.now();\n\n    if (typeof firstFrame === 'number') {\n      this._firstFrame = firstFrame;\n    }\n    if (typeof lastFrame === 'number') {\n      this._lastFrame = lastFrame;\n    }\n    if (typeof onLastFrame === 'function') {\n      this._onLastFrame = onLastFrame;\n    } else {\n      delete this._onLastFrame;\n    }\n\n    if (this._index !== this._firstFrame) {\n      this._startTime -=\n        (1000 * Math.abs(this._index - this._firstFrame)) / this._frameRate;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Sprite#pause\n   * @function\n   * @description Halt animation playback of a {@link Two.Sprite}.\n   */\n  pause() {\n    this._playing = false;\n    return this;\n  }\n\n  /**\n   * @name Two.Sprite#stop\n   * @function\n   * @description Halt animation playback of a {@link Two.Sprite} and set the current frame back to the first frame.\n   */\n  stop() {\n    this._playing = false;\n    this._index = 0;\n\n    return this;\n  }\n\n  /**\n   * @name Two.Sprite#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Sprite}\n   * @description Create a new instance of {@link Two.Sprite} with the same properties of the current sprite.\n   */\n  clone(parent) {\n    const clone = new Sprite(\n      this.texture,\n      this.translation.x,\n      this.translation.y,\n      this.columns,\n      this.rows,\n      this.frameRate\n    );\n\n    if (this.playing) {\n      clone.play(this._firstFrame, this._lastFrame);\n    }\n\n    clone.loop = this.loop;\n    clone.firstFrame = this.firstFrame;\n    clone.lastFrame = this.lastFrame;\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Sprite#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = 'sprite';\n    object.texture = this.texture.toObject();\n    object.columns = this.columns;\n    object.rows = this.rows;\n    object.frameRate = this.frameRate;\n    object.index = this.index;\n    object.firstFrame = this.firstFrame;\n    object.lastFrame = this.lastFrame;\n    object.loop = this.loop;\n    return object;\n  }\n\n  /**\n   * @name Two.Sprite#dispose\n   * @function\n   * @returns {Two.Sprite}\n   * @description Release the sprite's renderer resources and detach all events.\n   * This method stops any running animation, clears animation callbacks, disposes\n   * the texture (calling dispose() for thorough cleanup), and inherits comprehensive\n   * cleanup from the Rectangle/Path hierarchy while preserving the renderer type\n   * for potential re-attachment.\n   */\n  dispose() {\n    // Call parent dispose for inherited cleanup (vertices, fill/stroke effects)\n    super.dispose();\n\n    // Stop animation if playing\n    if (this._playing) {\n      this._playing = false;\n    }\n\n    // Clear animation callbacks\n    this._onLastFrame = null;\n\n    // Reset timing properties\n    this._startTime = 0;\n\n    // Dispose texture (more thorough than unbind)\n    if (this._texture && typeof this._texture.dispose === 'function') {\n      this._texture.dispose();\n    } else if (this._texture && typeof this._texture.unbind === 'function') {\n      this._texture.unbind();\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Sprite#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    const effect = this._texture;\n    const cols = this._columns;\n    const rows = this._rows;\n\n    let width, height, elapsed, amount, duration;\n    let index, iw, ih, frames;\n\n    if (effect) {\n      if (this._flagColumns || this._flagRows) {\n        this._amount = this._columns * this._rows;\n      }\n\n      if (this._flagFrameRate) {\n        this._duration = (1000 * this._amount) / this._frameRate;\n      }\n\n      if (this._flagTexture) {\n        this.fill = effect;\n      }\n\n      if (effect.loaded) {\n        iw = effect.image.width;\n        ih = effect.image.height;\n\n        width = iw / cols;\n        height = ih / rows;\n        amount = this._amount;\n\n        if (this.width !== width) {\n          this.width = width;\n        }\n        if (this.height !== height) {\n          this.height = height;\n        }\n\n        if (this._playing && this._frameRate > 0) {\n          if (_.isNaN(this._lastFrame)) {\n            this._lastFrame = amount - 1;\n          }\n\n          // TODO: Offload perf logic to instance of `Two`.\n          elapsed = _.performance.now() - this._startTime;\n          frames = this._lastFrame + 1;\n          duration = (1000 * (frames - this._firstFrame)) / this._frameRate;\n\n          if (this._loop) {\n            elapsed = elapsed % duration;\n          } else {\n            elapsed = Math.min(elapsed, duration);\n          }\n\n          index = lerp(this._firstFrame, frames, elapsed / duration);\n          index = Math.floor(index);\n\n          if (index !== this._index) {\n            this._index = index;\n            if (index >= this._lastFrame - 1 && this._onLastFrame) {\n              this._onLastFrame(); // Shortcut for chainable sprite animations\n            }\n          }\n        }\n\n        const col = this._index % cols;\n        const row = Math.floor(this._index / cols);\n\n        const ox = -width * col + (iw - width) / 2;\n        const oy = -height * row + (ih - height) / 2;\n\n        // TODO: Improve performance\n        if (ox !== effect.offset.x) {\n          effect.offset.x = ox;\n        }\n        if (oy !== effect.offset.y) {\n          effect.offset.y = oy;\n        }\n      }\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Sprite#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagTexture =\n      this._flagColumns =\n      this._flagRows =\n      this._flagFrameRate =\n        false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  texture: {\n    enumerable: true,\n    get: function () {\n      return this._texture;\n    },\n    set: function (v) {\n      this._texture = v;\n      this._flagTexture = true;\n    },\n  },\n  columns: {\n    enumerable: true,\n    get: function () {\n      return this._columns;\n    },\n    set: function (v) {\n      this._columns = v;\n      this._flagColumns = true;\n    },\n  },\n  rows: {\n    enumerable: true,\n    get: function () {\n      return this._rows;\n    },\n    set: function (v) {\n      this._rows = v;\n      this._flagRows = true;\n    },\n  },\n  frameRate: {\n    enumerable: true,\n    get: function () {\n      return this._frameRate;\n    },\n    set: function (v) {\n      this._frameRate = v;\n      this._flagFrameRate = true;\n    },\n  },\n  index: {\n    enumerable: true,\n    get: function () {\n      return this._index;\n    },\n    set: function (v) {\n      this._index = v;\n      this._flagIndex = true;\n    },\n  },\n  firstFrame: {\n    enumerable: true,\n    get: function () {\n      return this._firstFrame;\n    },\n    set: function (v) {\n      this._firstFrame = v;\n    },\n  },\n  lastFrame: {\n    enumerable: true,\n    get: function () {\n      return this._lastFrame;\n    },\n    set: function (v) {\n      this._lastFrame = v;\n    },\n  },\n  loop: {\n    enumerable: true,\n    get: function () {\n      return this._loop;\n    },\n    set: function (v) {\n      this._loop = !!v;\n    },\n  },\n};\n"
  },
  {
    "path": "src/effects/stop.d.ts",
    "content": "declare module 'two.js/src/effects/stop' {\n  /**\n   * @name Two.Stop\n   * @class\n   * @param {Number} [offset] - The offset percentage of the stop represented as a zero-to-one value. Default value flip flops from zero-to-one as new stops are created.\n   * @param {String} [color] - The color of the stop. Default value flip flops from white to black as new stops are created.\n   * @param {Number} [opacity] - The opacity value. Default value is 1, cannot be lower than 0.\n   * @nota-bene Used specifically in conjunction with {@link Two.Gradient}s to control color graduation.\n   */\n  export class Stop extends TwoElement {\n    /**\n     * @name Two.Stop.Index\n     * @property {Number} - The current index being referenced for calculating a stop's default offset value.\n     */\n    static Index: number;\n    /**\n     * @name Two.Stop.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Stop}.\n     */\n    static override Properties: ('offset' | 'color' | 'opacity' | string)[];\n    /**\n     * @name Two.Stop.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Stop} to create a new instance\n     * @returns {Two.Stop}\n     * @description Create a new {@link Two.Stop} from an object notation of a {@link Two.Stop}.\n     * @nota-bene Works in conjunction with {@link Two.Stop#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof TwoElement.fromObject>[0] & {\n        offset?: number;\n        color?: string;\n        opacity?: number;\n      }\n    ): Stop;\n    constructor(offset?: number, color?: string, opacity?: number);\n    /**\n     * @name Two.Stop#_flagOffset\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#offset} needs updating.\n     */\n    private _flagOffset;\n    /**\n     * @name Two.Stop#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#opacity} needs updating.\n     */\n    private _flagOpacity;\n    /**\n     * @name Two.Stop#_flagColor\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Stop#color} needs updating.\n     */\n    private _flagColor;\n    /**\n     * @name Two.Stop#_offset\n     * @private\n     * @see {@link Two.Stop#offset}\n     */\n    private _offset;\n    /**\n     * @name Two.Stop#_opacity\n     * @private\n     * @see {@link Two.Stop#opacity}\n     */\n    private _opacity;\n    /**\n     * @name Two.Stop#_color\n     * @private\n     * @see {@link Two.Stop#color}\n     */\n    private _color;\n    /**\n     * @name Two.Stop#offset\n     * @property {Number} - The offset percentage of the stop represented as a zero-to-one value.\n     */\n    offset: number;\n    /**\n     * @name Two.Stop#opacity\n     * @property {Number} - The alpha percentage of the stop represented as a zero-to-one value.\n     */\n    opacity: number;\n    /**\n     * @name Two.Stop#color\n     * @property {String} - The color of the stop.\n     */\n    color: string;\n    /**\n     * @name Two.Stop#copy\n     * @function\n     * @param {Two.Stop} stop - The reference {@link Two.Stop}\n     * @description Copy the properties of one {@link Two.Stop} onto another.\n     */\n    copy(stop: Stop): Stop;\n    /**\n     * @name Two.Stop#clone\n     * @function\n     * @param {Gradient} [parent] - The parent group or scene to add the clone to.\n     * @returns {Stop}\n     * @description Create a new instance of {@link Two.Stop} with the same properties of the current path.\n     */\n    clone(parent?: Gradient): Stop;\n    /**\n     * @name Two.Stop#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject(): object;\n    /**\n     * @name Two.Stop#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Stop;\n  }\n  import { Element as TwoElement } from 'two.js/src/element';\n  import { Gradient } from 'two.js/src/effects/gradient';\n}\n"
  },
  {
    "path": "src/effects/stop.js",
    "content": "import { _ } from '../utils/underscore.js';\nimport { Element } from '../element.js';\n\n/**\n * @name Two.Stop\n * @class\n * @extends Two.Element\n * @param {Number} [offset] - The offset percentage of the stop represented as a zero-to-one value. Default value flip flops from zero-to-one as new stops are created.\n * @param {String} [color] - The color of the stop. Default value flip flops from white to black as new stops are created.\n * @param {Number} [opacity] - The opacity value. Default value is 1, cannot be lower than 0.\n * @nota-bene Used specifically in conjunction with {@link Two.Gradient}s to control color graduation.\n */\nexport class Stop extends Element {\n  /**\n   * @name Two.Stop#_flagOffset\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Stop#offset} needs updating.\n   */\n  _flagOffset = true;\n\n  /**\n   * @name Two.Stop#_flagOpacity\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Stop#opacity} needs updating.\n   */\n  _flagOpacity = true;\n\n  /**\n   * @name Two.Stop#_flagColor\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Stop#color} needs updating.\n   */\n  _flagColor = true;\n\n  /**\n   * @name Two.Stop#_offset\n   * @private\n   * @see {@link Two.Stop#offset}\n   */\n  _offset = 0;\n\n  /**\n   * @name Two.Stop#_opacity\n   * @private\n   * @see {@link Two.Stop#opacity}\n   */\n  _opacity = 1;\n\n  /**\n   * @name Two.Stop#_color\n   * @private\n   * @see {@link Two.Stop#color}\n   */\n  _color = '#fff';\n\n  constructor(offset, color, opacity) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'stop';\n\n    /**\n     * @name Two.Stop#offset\n     * @property {Number} - The offset percentage of the stop represented as a zero-to-one value.\n     */\n    this.offset = typeof offset === 'number' ? offset : Stop.Index <= 0 ? 0 : 1;\n\n    /**\n     * @name Two.Stop#opacity\n     * @property {Number} - The alpha percentage of the stop represented as a zero-to-one value.\n     * @nota-bene This is only supported on the {@link Two.SVGRenderer}. You can get the same effect by encoding opacity into `rgba` strings in the color.\n     */\n    this.opacity = typeof opacity === 'number' ? opacity : 1;\n\n    /**\n     * @name Two.Stop#color\n     * @property {String} - The color of the stop.\n     */\n    this.color =\n      typeof color === 'string' ? color : Stop.Index <= 0 ? '#fff' : '#000';\n\n    Stop.Index = (Stop.Index + 1) % 2;\n  }\n\n  /**\n   * @name Two.Stop.Index\n   * @property {Number} - The current index being referenced for calculating a stop's default offset value.\n   */\n  static Index = 0;\n\n  /**\n   * @name Two.Stop.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Stop}.\n   */\n  static Properties = ['offset', 'opacity', 'color'];\n\n  /**\n   * @name Two.Stop.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Stop} to create a new instance\n   * @returns {Two.Stop}\n   * @description Create a new {@link Two.Stop} from an object notation of a {@link Two.Stop}.\n   * @nota-bene Works in conjunction with {@link Two.Stop#toObject}\n   */\n  static fromObject(obj) {\n    const stop = new Stop().copy(obj);\n\n    if ('id' in obj) {\n      stop.id = obj.id;\n    }\n\n    return stop;\n  }\n\n  /**\n   * @name Two.Stop#copy\n   * @function\n   * @param {Two.Stop} stop - The reference {@link Two.Stop}\n   * @description Copy the properties of one {@link Two.Stop} onto another.\n   */\n  copy(stop) {\n    super.copy.call(this, stop);\n\n    for (let i = 0; i < Stop.Properties.length; i++) {\n      const k = Stop.Properties[i];\n      if (k in stop) {\n        this[k] = stop[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Stop#clone\n   * @function\n   * @param {Two.Gradient} [parent] - The parent gradient to add the clone to.\n   * @returns {Two.Stop}\n   * @description Create a new instance of {@link Two.Stop} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new Stop();\n\n    _.each(\n      Stop.Properties,\n      function (property) {\n        clone[property] = this[property];\n      },\n      this\n    );\n\n    if (parent && parent.stops) {\n      parent.stops.push(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Stop#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = 'stop';\n\n    _.each(\n      Stop.Properties,\n      (k) => {\n        result[k] = this[k];\n      },\n      this\n    );\n\n    return result;\n  }\n\n  /**\n   * @name Two.Stop#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagOffset = this._flagColor = this._flagOpacity = false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  offset: {\n    enumerable: true,\n    get: function () {\n      return this._offset;\n    },\n    set: function (v) {\n      this._offset = v;\n      this._flagOffset = true;\n      if (this.parent) {\n        this.parent._flagStops = true;\n      }\n    },\n  },\n  opacity: {\n    enumerable: true,\n    get: function () {\n      return this._opacity;\n    },\n    set: function (v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n      if (this.parent) {\n        this.parent._flagStops = true;\n      }\n    },\n  },\n  color: {\n    enumerable: true,\n    get: function () {\n      return this._color;\n    },\n    set: function (v) {\n      this._color = v;\n      this._flagColor = true;\n      if (this.parent) {\n        this.parent._flagStops = true;\n      }\n    },\n  },\n};\n"
  },
  {
    "path": "src/effects/texture.d.ts",
    "content": "declare module 'two.js/src/effects/texture' {\n  type RepeatProperties = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';\n  /**\n   * @name Two.Texture\n   * @class\n   * @extends Two.Element\n   * @param {String|HTMLImageElement} [src] - The URL path to an image file or an `<img />` element.\n   * @param {Function} [callback] - An optional callback function once the image has been loaded.\n   * @description Fundamental to work with bitmap data, a.k.a. pregenerated imagery, in Two.js. Supported formats include jpg, png, gif, and tiff. See {@link Two.Texture.RegularExpressions} for a full list of supported formats.\n   */\n  export class Texture extends TwoElement {\n    /**\n     * @name Two.Texture.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Texture}.\n     */\n    static Properties: string[];\n    /**\n     * @name Two.Texture.RegularExpressions\n     * @property {Object} - A map of compatible DOM Elements categorized by media format.\n     */\n    static RegularExpressions: {\n      video: RegExp;\n      image: RegExp;\n      effect: RegExp;\n    };\n    /**\n     * @name Two.Texture.ImageRegistry\n     * @property {Registry} - A canonical listing of image data used in a single session of Two.js.\n     * @nota-bene This object is used to cache image data between different textures.\n     */\n    static ImageRegistry: Registry;\n    /**\n     * @name Two.Texture.getAbsoluteURL\n     * @property {Function} - Serializes a URL as an absolute path for canonical attribution in {@link Two.ImageRegistry}.\n     * @param {String} path\n     * @returns {String} - The serialized absolute path.\n     */\n    static getAbsoluteURL(path: string): string;\n    /**\n     * @name Two.Texture.loadHeadlessBuffer\n     * @property {Function} - Loads an image as a buffer in headless environments.\n     * @param {Texture} texture - The {@link Two.Texture} to be loaded.\n     * @param {Function} onLoad - The callback function to be triggered once the image is loaded.\n     * @nota-bene - This function uses node's `fs.readFileSync` to spoof the `<img />` loading process in the browser.\n     */\n    static loadHeadlessBuffer(texture: Texture, onLoad: Function): void;\n    /**\n     * @name Two.Texture.getTag\n     * @property {Function} - Retrieves the tag name of an image, video, or canvas node.\n     * @param {HTMLImageElement} image - The image to infer the tag name from.\n     * @returns {String} - Returns the tag name of an image, video, or canvas node.\n     */\n    static getTag(image: HTMLImageElement): string;\n    /**\n     * @name Two.Texture.getImage\n     * @property {Function} - Convenience function to set {@link Two.Texture#image} properties with canonincal versions set in {@link Two.Texture.ImageRegistry}.\n     * @param {String} src - The URL path of the image.\n     * @returns {HTMLImageElement} - Returns either a cached version of the image or a new one that is registered in {@link Two.Texture.ImageRegistry}.\n     */\n    static getImage(src: string): HTMLImageElement;\n    /**\n     * @name Two.Texture.Register\n     * @interface\n     * @description A collection of functions to register different types of textures. Used internally by a {@link Two.Texture}.\n     */\n    static Register: {\n      canvas: (texture: Texture, callback: Function) => void;\n      img: (texture: Texture, callback: Function) => void;\n      video: (texture: Texture, callback: Function) => void;\n    };\n    /**\n     * @name Two.Texture.load\n     * @function\n     * @param {Texture} texture - The texture to load.\n     * @param {Function} callback - The function to be called once the texture is loaded.\n     */\n    static load(texture: Texture, callback: Function): void;\n    /**\n     * @name Two.Texture.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Texture} to create a new instance\n     * @returns {Two.Texture}\n     * @description Create a new {@link Two.Texture} from an object notation of a {@link Two.Texture}.\n     * @nota-bene Works in conjunction with {@link Two.Texture#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof TwoElement.fromObject>[0] & {\n        src?: string;\n        repeat?: RepeatProperties;\n        offset?: { x: number; y: number } | Vector;\n        scale?: { x: number; y: number } | Vector;\n      }\n    ): Texture;\n    constructor(\n      src?: string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement,\n      callback?: Function\n    );\n    /**\n     * @name Two.Texture#_flagSrc\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#src} needs updating.\n     */\n    private _flagSrc;\n    /**\n     * @name Two.Texture#_flagImage\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#image} needs updating.\n     */\n    private _flagImage;\n    /**\n     * @name Two.Texture#_flagVideo\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#video} needs updating.\n     */\n    private _flagVideo;\n    /**\n     * @name Two.Texture#_flagLoaded\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#loaded} needs updating.\n     */\n    private _flagLoaded;\n    /**\n     * @name Two.Texture#_flagRepeat\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#repeat} needs updating.\n     */\n    private _flagRepeat;\n    /**\n     * @name Two.Texture#_flagOffset\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#offset} needs updating.\n     */\n    private _flagOffset;\n    /**\n     * @name Two.Texture#_flagScale\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Texture#scale} needs updating.\n     */\n    private _flagScale;\n    /**\n     * @name Two.Texture#_src\n     * @private\n     * @see {@link Two.Texture#src}\n     */\n    private _src;\n    /**\n     * @name Two.Texture#_image\n     * @private\n     * @see {@link Two.Texture#image}\n     */\n    private _image;\n    /**\n     * @name Two.Texture#_loaded\n     * @private\n     * @see {@link Two.Texture#loaded}\n     */\n    private _loaded;\n    /**\n     * @name Two.Texture#_repeat\n     * @private\n     * @see {@link Two.Texture#repeat}\n     */\n    private _repeat;\n    /**\n     * @name Two.Texture#_scale\n     * @private\n     * @see {@link Two.Texture#scale}\n     */\n    private _scale;\n    /**\n     * @name Two.Texture#_offset\n     * @private\n     * @see {@link Two.Texture#offset}\n     */\n    private _offset;\n    id: string;\n    /**\n     * @name Two.Texture#loaded\n     * @property {Boolean} - Shorthand value to determine if image has been loaded into the texture.\n     */\n    loaded: boolean;\n    /**\n     * @name Two.Texture#repeat\n     * @property {String} - CSS style declaration to tile {@link Two.Path}. Valid values include: `'no-repeat'`, `'repeat'`, `'repeat-x'`, `'repeat-y'`.\n     * @see {@link https://www.w3.org/TR/2dcontext/#dom-context-2d-createpattern}\n     */\n    repeat: RepeatProperties;\n    /**\n     * @name Two.Texture#offset\n     * @property {Vector} - A two-component vector describing any pixel offset of the texture when applied to a {@link Two.Path}.\n     */\n    offset: Vector;\n    src: string;\n    /**\n     * @name Two.Texture#image\n     * @property {Element} - The corresponding DOM Element of the texture. Can be a `<img />`, `<canvas />`, or `<video />` element. See {@link Two.Texture.RegularExpressions} for a full list of supported elements.\n     * @nota-bene In headless environments this is a `Canvas.Image` object. See {@link https://github.com/Automattic/node-canvas} for more information on headless image objects.\n     */\n    image: HTMLImageElement | HTMLCanvasElement | HTMLVideoElement;\n    /**\n     * @name Two.Texture#clone\n     * @function\n     * @returns {Texture}\n     * @description Create a new instance of {@link Two.Texture} with the same properties of the current texture.\n     */\n    clone(): Texture;\n    /**\n     * @name Two.Texture#copy\n     * @function\n     * @param {Two.Texture} texture - The reference {@link Two.Texture}\n     * @description Copy the properties of one {@link Two.Texture} onto another.\n     */\n    copy(texture: Texture): Texture;\n    /**\n     * @name Two.Texture#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the texture.\n     */\n    toObject(): object;\n    /**\n     * @name Two.Texture#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    protected _update(bubbles?: boolean): Texture;\n    /**\n     * @name Two.Texture#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Texture;\n    /**\n     * @name Two.Gradient#dispose\n     * @function\n     * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n     */\n    dispose(): Texture;\n  }\n  import { Element as TwoElement } from 'two.js/src/element';\n  import { Vector } from 'two.js/src/vector';\n  import { Registry } from 'two.js/src/registry';\n}\n"
  },
  {
    "path": "src/effects/texture.js",
    "content": "import { root } from '../utils/root.js';\nimport { Events } from '../events.js';\nimport { Element } from '../element.js';\nimport { TwoError } from '../utils/error.js';\nimport { CanvasPolyfill } from '../utils/canvas-polyfill.js';\n\nimport { Vector } from '../vector.js';\nimport { Registry } from '../registry.js';\n\nlet anchor;\nconst regex = {\n  video: /\\.(mp4|webm|ogg)$/i,\n  image: /\\.(jpe?g|png|gif|tiff|webp)$/i,\n  effect: /texture|gradient/i,\n};\n\nif (root.document) {\n  anchor = document.createElement('a');\n}\n\n/**\n * @name Two.Texture\n * @class\n * @extends Two.Element\n * @param {String|HTMLImageElement} [src] - The URL path to an image file or an `<img />` element.\n * @param {Function} [callback] - An optional callback function once the image has been loaded.\n * @description Fundamental to work with bitmap data, a.k.a. pregenerated imagery, in Two.js. Supported formats include jpg, png, gif, and tiff. See {@link Two.Texture.RegularExpressions} for a full list of supported formats.\n */\nexport class Texture extends Element {\n  /**\n   * @name Two.Texture#_flagSrc\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Texture#src} needs updating.\n   */\n  _flagSrc = false;\n\n  /**\n   * @name Two.Texture#_flagImage\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Texture#image} needs updating.\n   */\n  _flagImage = false;\n\n  /**\n   * @name Two.Texture#_flagVideo\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Texture#video} needs updating.\n   */\n  _flagVideo = false;\n\n  /**\n   * @name Two.Texture#_flagLoaded\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Texture#loaded} needs updating.\n   */\n  _flagLoaded = false;\n\n  /**\n   * @name Two.Texture#_flagRepeat\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Texture#repeat} needs updating.\n   */\n  _flagRepeat = false;\n\n  /**\n   * @name Two.Texture#_flagOffset\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Texture#offset} needs updating.\n   */\n  _flagOffset = false;\n\n  /**\n   * @name Two.Texture#_flagScale\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Texture#scale} needs updating.\n   */\n  _flagScale = false;\n\n  /**\n   * @name Two.Texture#_src\n   * @private\n   * @see {@link Two.Texture#src}\n   */\n  _src = '';\n\n  /**\n   * @name Two.Texture#_image\n   * @private\n   * @see {@link Two.Texture#image}\n   */\n  _image = null;\n\n  /**\n   * @name Two.Texture#_loaded\n   * @private\n   * @see {@link Two.Texture#loaded}\n   */\n  _loaded = false;\n\n  /**\n   * @name Two.Texture#_repeat\n   * @private\n   * @see {@link Two.Texture#repeat}\n   */\n  _repeat = 'no-repeat';\n\n  /**\n   * @name Two.Texture#_scale\n   * @private\n   * @see {@link Two.Texture#scale}\n   */\n  _scale = 1;\n\n  /**\n   * @name Two.Texture#_offset\n   * @private\n   * @see {@link Two.Texture#offset}\n   */\n  _offset = null;\n\n  constructor(src, callback) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'texture';\n    this._renderer.flagOffset = FlagOffset.bind(this);\n    this._renderer.flagScale = FlagScale.bind(this);\n\n    /**\n     * @name Two.Texture#loaded\n     * @property {Boolean} - Shorthand value to determine if image has been loaded into the texture.\n     */\n    this.loaded = false;\n\n    /**\n     * @name Two.Texture#repeat\n     * @property {String} - CSS style declaration to tile {@link Two.Path}. Valid values include: `'no-repeat'`, `'repeat'`, `'repeat-x'`, `'repeat-y'`.\n     * @see {@link https://www.w3.org/TR/2dcontext/#dom-context-2d-createpattern}\n     */\n    this.repeat = 'no-repeat';\n\n    /**\n     * @name Two.Texture#offset\n     * @property {Two.Vector} - A two-component vector describing any pixel offset of the texture when applied to a {@link Two.Path}.\n     */\n    this.offset = new Vector();\n\n    if (typeof callback === 'function') {\n      const loaded = function () {\n        this.unbind(Events.Types.load, loaded);\n        if (typeof callback === 'function') {\n          callback();\n        }\n      }.bind(this);\n      this.bind(Events.Types.load, loaded);\n    }\n\n    /**\n     * @name Two.Texture#src\n     * @property {String} - The URL path to the image data.\n     * @nota-bene This property is ultimately serialized in a {@link Two.Registry} to cache retrieval.\n     */\n    if (typeof src === 'string') {\n      this.src = src;\n    } else if (typeof src === 'object') {\n      const elemString = Object.prototype.toString.call(src);\n      if (\n        elemString === '[object HTMLImageElement]' ||\n        elemString === '[object HTMLCanvasElement]' ||\n        elemString === '[object HTMLVideoElement]' ||\n        elemString === '[object Image]'\n      ) {\n        /**\n         * @name Two.Texture#image\n         * @property {Element} - The corresponding DOM Element of the texture. Can be a `<img />`, `<canvas />`, or `<video />` element. See {@link Two.Texture.RegularExpressions} for a full list of supported elements.\n         * @nota-bene In headless environments this is a `Canvas.Image` object. See {@link https://github.com/Automattic/node-canvas} for more information on headless image objects.\n         */\n        this.image = src;\n      }\n    }\n\n    this._update();\n  }\n\n  /**\n   * @name Two.Texture.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Texture}.\n   */\n  static Properties = ['src', 'loaded', 'repeat', 'scale', 'offset', 'image'];\n\n  /**\n   * @name Two.Texture.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Texture} to create a new instance\n   * @returns {Two.Texture}\n   * @description Create a new {@link Two.Texture} from an object notation of a {@link Two.Texture}.\n   * @nota-bene Works in conjunction with {@link Two.Texture#toObject}\n   */\n  fromObject(obj) {\n    const texture = new Texture().copy(obj);\n\n    if ('id' in obj) {\n      texture.id = obj.id;\n    }\n\n    return texture;\n  }\n\n  /**\n   * @name Two.Texture.RegularExpressions\n   * @property {Object} - A map of compatible DOM Elements categorized by media format.\n   */\n  static RegularExpressions = regex;\n\n  /**\n   * @name Two.Texture.ImageRegistry\n   * @property {Two.Registry} - A canonical listing of image data used in a single session of Two.js.\n   * @nota-bene This object is used to cache image data between different textures.\n   */\n  static ImageRegistry = new Registry();\n\n  /**\n   * @name Two.Texture.getAbsoluteURL\n   * @property {Function} - Serializes a URL as an absolute path for canonical attribution in {@link Two.Texture.ImageRegistry}.\n   * @param {String} path\n   * @returns {String} - The serialized absolute path.\n   */\n  static getAbsoluteURL(path) {\n    if (!anchor) {\n      // TODO: Fix for headless environments\n      return path;\n    }\n    anchor.href = path;\n    return anchor.href;\n  }\n\n  /**\n   * @name Two.Texture.loadHeadlessBuffer\n   * @property {Function} - Loads an image as a buffer in headless environments.\n   * @param {Two.Texture} texture - The {@link Two.Texture} to be loaded.\n   * @param {Function} onLoad - The callback function to be triggered once the image is loaded.\n   * @nota-bene - This function uses node's `fs.readFileSync` to spoof the `<img />` loading process in the browser.\n   */\n  static loadHeadlessBuffer(texture, onLoad) {\n    texture.image.onload = onLoad;\n    texture.image.src = texture.src;\n  }\n\n  /**\n   * @name Two.Texture.getTag\n   * @property {Function} - Retrieves the tag name of an image, video, or canvas node.\n   * @param {HTMLImageElement} image - The image to infer the tag name from.\n   * @returns {String} - Returns the tag name of an image, video, or canvas node.\n   */\n  static getTag(image) {\n    return (\n      (image && image.nodeName && image.nodeName.toLowerCase()) ||\n      // Headless environments\n      'img'\n    );\n  }\n\n  /**\n   * @name Two.Texture.getImage\n   * @property {Function} - Convenience function to set {@link Two.Texture#image} properties with canonical versions set in {@link Two.Texture.ImageRegistry}.\n   * @param {String} src - The URL path of the image.\n   * @returns {HTMLImageElement} - Returns either a cached version of the image or a new one that is registered in {@link Two.Texture.ImageRegistry}.\n   */\n  static getImage(src) {\n    const absoluteSrc = Texture.getAbsoluteURL(src);\n\n    if (Texture.ImageRegistry.contains(absoluteSrc)) {\n      return Texture.ImageRegistry.get(absoluteSrc);\n    }\n\n    let image;\n\n    if (CanvasPolyfill.Image) {\n      // TODO: Fix for headless environments\n      image = new CanvasPolyfill.Image();\n      CanvasPolyfill.shim(image, 'img');\n    } else if (root.document) {\n      if (regex.video.test(absoluteSrc)) {\n        image = document.createElement('video');\n      } else {\n        image = document.createElement('img');\n      }\n    } else {\n      console.warn('Two.js: no prototypical image defined for Two.Texture');\n    }\n\n    image.crossOrigin = 'anonymous';\n    image.referrerPolicy = 'no-referrer';\n\n    return image;\n  }\n\n  /**\n   * @name Two.Texture.Register\n   * @interface\n   * @description A collection of functions to register different types of textures. Used internally by a {@link Two.Texture}.\n   */\n  static Register = {\n    canvas: function (texture, callback) {\n      texture._src = '#' + texture.id;\n      Texture.ImageRegistry.add(texture.src, texture.image);\n      if (typeof callback === 'function') {\n        callback();\n      }\n    },\n    img: function (texture, callback) {\n      const image = texture.image;\n\n      const loaded = function (e) {\n        if (\n          !CanvasPolyfill.isHeadless &&\n          image.removeEventListener &&\n          typeof image.removeEventListener === 'function'\n        ) {\n          image.removeEventListener('load', loaded, false);\n          image.removeEventListener('error', error, false);\n        }\n        if (typeof callback === 'function') {\n          callback();\n        }\n      };\n      const error = function (e) {\n        if (\n          !CanvasPolyfill.isHeadless &&\n          typeof image.removeEventListener === 'function'\n        ) {\n          image.removeEventListener('load', loaded, false);\n          image.removeEventListener('error', error, false);\n        }\n        throw new TwoError('unable to load ' + texture.src);\n      };\n\n      if (\n        typeof image.width === 'number' &&\n        image.width > 0 &&\n        typeof image.height === 'number' &&\n        image.height > 0\n      ) {\n        loaded();\n      } else if (\n        !CanvasPolyfill.isHeadless &&\n        typeof image.addEventListener === 'function'\n      ) {\n        image.addEventListener('load', loaded, false);\n        image.addEventListener('error', error, false);\n      }\n\n      texture._src = Texture.getAbsoluteURL(texture._src);\n\n      if (\n        !CanvasPolyfill.isHeadless &&\n        image &&\n        image.getAttribute('two-src')\n      ) {\n        return;\n      }\n\n      if (!CanvasPolyfill.isHeadless) {\n        image.setAttribute('two-src', texture.src);\n      }\n\n      Texture.ImageRegistry.add(texture.src, image);\n\n      if (CanvasPolyfill.isHeadless) {\n        Texture.loadHeadlessBuffer(texture, loaded);\n      } else {\n        texture.image.src = texture.src;\n      }\n    },\n    video: function (texture, callback) {\n      if (CanvasPolyfill.isHeadless) {\n        throw new TwoError(\n          'video textures are not implemented in headless environments.'\n        );\n      }\n\n      const loaded = function (e) {\n        texture.image.removeEventListener('canplaythrough', loaded, false);\n        texture.image.removeEventListener('error', error, false);\n        texture.image.width = texture.image.videoWidth;\n        texture.image.height = texture.image.videoHeight;\n        if (typeof callback === 'function') {\n          callback();\n        }\n      };\n      const error = function (e) {\n        texture.image.removeEventListener('canplaythrough', loaded, false);\n        texture.image.removeEventListener('error', error, false);\n        throw new TwoError('unable to load ' + texture.src);\n      };\n\n      texture._src = Texture.getAbsoluteURL(texture._src);\n\n      if (!texture.image.getAttribute('two-src')) {\n        texture.image.setAttribute('two-src', texture.src);\n        Texture.ImageRegistry.add(texture.src, texture.image);\n      }\n\n      if (texture.image.readyState >= 4) {\n        loaded();\n      } else {\n        texture.image.addEventListener('canplaythrough', loaded, false);\n        texture.image.addEventListener('error', error, false);\n        texture.image.src = texture.src;\n        texture.image.load();\n      }\n    },\n  };\n\n  /**\n   * @name Two.Texture.load\n   * @function\n   * @param {Two.Texture} texture - The texture to load.\n   * @param {Function} callback - The function to be called once the texture is loaded.\n   */\n  static load(texture, callback) {\n    let image = texture.image;\n    let tag = Texture.getTag(image);\n\n    if (texture._flagImage) {\n      if (/canvas/i.test(tag)) {\n        Texture.Register.canvas(texture, callback);\n      } else {\n        texture._src =\n          (!CanvasPolyfill.isHeadless && image.getAttribute('two-src')) ||\n          image.src;\n        Texture.Register[tag](texture, callback);\n      }\n    }\n\n    if (texture._flagSrc) {\n      if (!image) {\n        image = Texture.getImage(texture.src);\n        texture.image = image;\n      }\n      tag = Texture.getTag(image);\n      Texture.Register[tag](texture, callback);\n    }\n  }\n\n  /**\n   * @name Two.Texture#clone\n   * @function\n   * @returns {Two.Texture}\n   * @description Create a new instance of {@link Two.Texture} with the same properties of the current texture.\n   */\n  clone() {\n    const clone = new Texture(this.src);\n    clone.repeat = this.repeat;\n    clone.offset.copy(this.offset);\n    clone.scale = this.scale;\n    return clone;\n  }\n\n  /**\n   * @name Two.Texture#copy\n   * @function\n   * @param {Two.Texture} texture - The reference {@link Two.Texture}\n   * @description Copy the properties of one {@link Two.Texture} onto another.\n   */\n  copy(texture) {\n    this.src = texture.src;\n    this.repeat = texture.repeat;\n    this.offset =\n      typeof texture.offset === 'number' || texture.offset instanceof Vector\n        ? texture.offset\n        : new Vector().copy(texture.offset);\n    this.scale =\n      typeof texture.scale === 'number' || texture.scale instanceof Vector\n        ? texture.scale\n        : new Vector().copy(texture.scale);\n    return this;\n  }\n\n  /**\n   * @name Two.Texture#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the texture.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n\n    result.renderer.type = 'texture';\n    result.src = this.src;\n    result.repeat = this.repeat;\n    result.offset = this.offset.toObject();\n    result.scale =\n      typeof this.scale === 'number' ? this.scale : this.scale.toObject();\n\n    return result;\n  }\n\n  /**\n   * @name Two.Texture#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagSrc || this._flagImage) {\n      this.trigger(Events.Types.change);\n\n      if (this._flagSrc || this._flagImage) {\n        this.loaded = false;\n        Texture.load(\n          this,\n          function () {\n            this.loaded = true;\n            this.trigger(Events.Types.change).trigger(Events.Types.load);\n          }.bind(this)\n        );\n      }\n    }\n\n    if (this._image && this._image.readyState >= 4) {\n      this._flagVideo = true;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Texture#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagSrc =\n      this._flagImage =\n      this._flagLoaded =\n      this._flagRepeat =\n      this._flagVideo =\n      this._flagScale =\n      this._flagOffset =\n        false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Texture#dispose\n   * @function\n   * @description Detach instance from renderer including any `<defs />` or textures stored in memory.\n   */\n  dispose() {\n    super.dispose();\n\n    // Remove gradient from SVG document\n    if ('elem' in this._renderer) {\n      const elem = this._renderer.elem;\n      elem.parentNode.removeChild(elem);\n    }\n    // Deallocate textures from the graphics card\n    if ('effect' in this._renderer) {\n      this._renderer.effect = null;\n    }\n    return this;\n  }\n}\n\nconst proto = {\n  src: {\n    enumerable: true,\n    get: function () {\n      return this._src;\n    },\n    set: function (v) {\n      this._src = v;\n      this._flagSrc = true;\n    },\n  },\n  loaded: {\n    enumerable: true,\n    get: function () {\n      return this._loaded;\n    },\n    set: function (v) {\n      this._loaded = v;\n      this._flagLoaded = true;\n    },\n  },\n  repeat: {\n    enumerable: true,\n    get: function () {\n      return this._repeat;\n    },\n    set: function (v) {\n      this._repeat = v;\n      this._flagRepeat = true;\n    },\n  },\n\n  image: {\n    enumerable: true,\n    get: function () {\n      return this._image;\n    },\n    set: function (image) {\n      const tag = Texture.getTag(image);\n      let index;\n\n      switch (tag) {\n        case 'canvas':\n          index = '#' + image.id;\n          break;\n        default:\n          index = image.src;\n      }\n\n      if (Texture.ImageRegistry.contains(index)) {\n        this._image = Texture.ImageRegistry.get(image.src);\n      } else {\n        this._image = image;\n      }\n\n      this._flagImage = true;\n    },\n  },\n\n  offset: {\n    enumerable: true,\n    get: function () {\n      return this._offset;\n    },\n    set: function (v) {\n      if (this._offset) {\n        this._offset.unbind(Events.Types.change, this._renderer.flagOffset);\n      }\n      this._offset = v;\n      this._offset.bind(Events.Types.change, this._renderer.flagOffset);\n      this._flagOffset = true;\n    },\n  },\n\n  scale: {\n    enumerable: true,\n    get: function () {\n      return this._scale;\n    },\n    set: function (v) {\n      if (this._scale instanceof Vector) {\n        this._scale.unbind(Events.Types.change, this._renderer.flagScale);\n      }\n\n      this._scale = v;\n\n      if (this._scale instanceof Vector) {\n        this._scale.bind(Events.Types.change, this._renderer.flagScale);\n      }\n\n      this._flagScale = true;\n    },\n  },\n};\n\n/**\n * @name Two.Texture.FlagOffset\n * @function\n * @description Cached method to let renderers know `offset` has been updated on a {@link Two.Texture}.\n */\nfunction FlagOffset() {\n  this._flagOffset = true;\n}\n\n/**\n * @name Two.Texture.FlagScale\n * @function\n * @description Cached method to let renderers know `scale` has been updated on a {@link Two.Texture}.\n */\nfunction FlagScale() {\n  this._flagScale = true;\n}\n"
  },
  {
    "path": "src/element.d.ts",
    "content": "declare module 'two.js/src/element' {\n  /**\n   * @name Two.Element\n   * @class\n   * @description The foundational object for the Two.js scenegraph.\n   */\n  export class Element extends Events {\n    static Properties: string[];\n    /**\n     * @name Two.Element.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Element} to create a new instance\n     * @returns {Two.Element}\n     * @description Create a new {@link Two.Element} from an object notation of a {@link Two.Element}.\n     * @nota-bene Works in conjunction with {@link Two.Element#toObject}\n     */\n    static fromObject(\n      obj:\n        | object\n        | {\n            renderer?: { type: string };\n            id?: string;\n            className?: string;\n          }\n    ): Element;\n    /**\n     * @name Two.Element#_flagId\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Element#id} needs updating.\n     */\n    private _flagId;\n    /**\n     * @name Two.Element#_flagClassName\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#className} need updating.\n     */\n    private _flagClassName;\n    /**\n     * @name Two.Element#renderer\n     * @property {Object} - Object access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n     * @nota-bene With the {@link Two.SVGRenderer} you can access the underlying SVG element created via `shape.renderer.elem`.\n     */\n    renderer: {\n      type: 'element' | 'group' | 'path' | 'text' | 'points' | string;\n      elem?:\n        | SVGGElement\n        | SVGPathElement\n        | SVGTextElement\n        | SVGPatternElement\n        | SVGDefsElement\n        | SVGGradientElement\n        | SVGLinearGradientElement\n        | SVGRadialGradientElement\n        | SVGImageElement\n        | SVGClipPathElement\n        | SVGStopElement\n        | SVGPatternElement;\n      onBeforeRender?: () => void;\n      onAfterRender?: () => void;\n    };\n    /**\n     * @name Two.Element#id\n     * @property {String} - Session specific unique identifier.\n     * @nota-bene In the {@link Two.SVGRenderer} change this to change the underlying SVG element's id too.\n     */\n    id: string;\n    /**\n     * @name Two.Element#className\n     * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n     * @nota-bene Only available for the SVG renderer.\n     */\n    className: string;\n    /**\n     * @name Two.Element#classList\n     * @property {String[]}\n     * @description A list of class strings stored if imported / interpreted  from an SVG element.\n     */\n    classList: string[];\n    /**\n     * @name Two.Element#flagReset\n     * @function\n     * @description Called internally by Two.js's renderer to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Element;\n    copy(element: Element): Element;\n    toObject(): object;\n    /**\n     * @name Two.Element#dispose\n     * @function\n     * @description Release the element's renderer object and detach any events.\n     * This cleans up renderer-specific resources and unbinds all event listeners.\n     */\n    dispose(): Element;\n  }\n  import { Events } from 'two.js/src/events';\n}\n"
  },
  {
    "path": "src/element.js",
    "content": "import { Events } from './events.js';\nimport { Constants } from './constants.js';\n\n/**\n * @name Two.Element\n * @class\n * @extends Two.Events\n * @description The foundational object for the Two.js scenegraph.\n */\nexport class Element extends Events {\n  /**\n   * @name Two.Element#_flagId\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Element#id} needs updating.\n   */\n  _flagId = false;\n\n  /**\n   * @name Two.Element#_flagClassName\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#className} need updating.\n   */\n  _flagClassName = false;\n\n  /**\n   * @name Two.Element#renderer\n   * @property {Object} - Object access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n   * @nota-bene With the {@link Two.SVGRenderer} you can access the underlying SVG element created via `shape.renderer.elem`.\n   */\n  _renderer = {};\n\n  /**\n   * @name Two.Element#id\n   * @property {String} - Session specific unique identifier.\n   * @nota-bene In the {@link Two.SVGRenderer} change this to change the underlying SVG element's id too.\n   */\n  _id = Constants.Identifier + Constants.uniqueId();\n\n  /**\n   * @name Two.Element#className\n   * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n   * @nota-bene Only rendered to DOM in the SVG renderer.\n   */\n  _className = '';\n\n  /**\n   * @name Two.Element#classList\n   * @property {String[]}\n   * @description A list of class strings stored if imported / interpreted  from an SVG element.\n   */\n  classList = [];\n\n  constructor() {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n  }\n\n  static Properties = ['renderer', 'id', 'className'];\n\n  /**\n   * @name Two.Element.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Element} to create a new instance\n   * @returns {Two.Element}\n   * @description Create a new {@link Two.Element} from an object notation of a {@link Two.Element}.\n   * @nota-bene Works in conjunction with {@link Two.Element#toObject}\n   */\n  static fromObject(obj) {\n    const elem = new Element().copy(obj);\n    if ('id' in obj) {\n      elem.id = obj.id;\n    }\n    return elem;\n  }\n\n  /**\n   * @name Two.Element#flagReset\n   * @function\n   * @description Called internally by Two.js's renderer to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagId = this._flagClassName = false;\n    return this;\n  }\n\n  copy(element) {\n    // Explicitly do not copy the id\n    // of an object to keep uniqueness\n    if (element.renderer && typeof element.renderer.type === 'string') {\n      this.renderer.type = element.renderer.type;\n    }\n    if (typeof element.className === 'string') {\n      this.className = element.className;\n    }\n    return this;\n  }\n\n  toObject() {\n    return {\n      renderer: { type: this.renderer.type },\n      id: this.id,\n      className: this.className,\n    };\n  }\n\n  /**\n   * @name Two.Element#dispose\n   * @function\n   * @description Release the element's renderer object and detach any events.\n   * This cleans up renderer-specific resources and unbinds all event listeners.\n   */\n  dispose() {\n    // Unbind all events\n    if (typeof this.unbind === 'function') {\n      this.unbind();\n    }\n\n    // Clean up renderer-specific resources\n    if (this._renderer) {\n      // SVG DOM element cleanup\n      if (this._renderer.elem && this._renderer.elem.parentNode) {\n        this._renderer.elem.parentNode.removeChild(this._renderer.elem);\n        delete this._renderer.elem;\n      }\n\n      // WebGL resource cleanup\n      if (this.type === 'WebGLRenderer' && this.renderer.ctx) {\n        const gl = this.renderer.ctx;\n\n        // Clean up textures\n        if (this._renderer.texture) {\n          gl.deleteTexture(this._renderer.texture);\n          delete this._renderer.texture;\n        }\n\n        // Clean up buffers\n        if (this._renderer.positionBuffer) {\n          gl.deleteBuffer(this._renderer.positionBuffer);\n          delete this._renderer.positionBuffer;\n        }\n\n        // Clean up any other WebGL effects\n        if (this._renderer.effect) {\n          this._renderer.effect = null;\n        }\n      }\n\n      // Canvas renderer cleanup - clear cached contexts and data\n      if (this.type === 'CanvasRenderer' && this._renderer.context) {\n        delete this._renderer.context;\n      }\n    }\n\n    // Preserve the renderer type for potential re-attachment\n    const rendererType = this._renderer.type;\n\n    // Clear renderer object but preserve type\n    this._renderer = { type: rendererType };\n\n    return this;\n  }\n}\n\nconst proto = {\n  renderer: {\n    enumerable: false,\n    get: function () {\n      return this._renderer;\n    },\n  },\n  id: {\n    enumerable: true,\n    get: function () {\n      return this._id;\n    },\n    set: function (v) {\n      const id = this._id;\n      if (v === this._id) {\n        return;\n      }\n      this._id = v;\n      this._flagId = true;\n      if (this.parent) {\n        delete this.parent.children.ids[id];\n        this.parent.children.ids[this._id] = this;\n      }\n    },\n  },\n  className: {\n    enumerable: true,\n    get: function () {\n      return this._className;\n    },\n    set: function (v) {\n      if (this._className !== v) {\n        this._flagClassName = true;\n        this.classList = v.split(/\\s+?/);\n        this._className = v;\n      }\n    },\n  },\n};\n"
  },
  {
    "path": "src/events.d.ts",
    "content": "declare module 'two.js/src/events' {\n  /**\n   * @name Two.Events\n   * @class\n   * @description Object inherited by many Two.js objects in order to facilitate custom events.\n   */\n  export class Events {\n    /**\n     * @name Two.Events.Types\n     * @property {Object} - Object of different types of Two.js specific events.\n     */\n    static Types: {\n      play: 'play';\n      pause: 'pause';\n      update: 'update';\n      render: 'render';\n      resize: 'resize';\n      change: 'change';\n      remove: 'remove';\n      insert: 'insert';\n      order: 'order';\n      load: 'load';\n    };\n    static Methods: (\n      | 'addEventListener'\n      | 'on'\n      | 'removeEventListener'\n      | 'off'\n      | 'unbind'\n      | 'dispatchEvent'\n      | 'trigger'\n      | 'listen'\n      | 'ignore'\n    )[];\n    private _events: {};\n    private _bound: boolean;\n    /**\n     * @name Two.Events#addEventListener\n     * @function\n     * @param {String} [name] - The name of the event to bind a function to.\n     * @param {Function} [handler] - The function to be invoked when the event is dispatched.\n     * @description Call to add a listener to a specific event name.\n     */\n    addEventListener(name?: string, handler?: Function): Events;\n    /**\n     * @name Two.Events#on\n     * @function\n     * @description Alias for {@link Two.Events#addEventListener}.\n     */\n    on(name?: string, handler?: Function): Events;\n    /**\n     * @name Two.Events#bind\n     * @function\n     * @description Alias for {@link Two.Events#addEventListener}.\n     */\n    bind(name?: string, handler?: Function): Events;\n    /**\n     * @name Two.Events#removeEventListener\n     * @function\n     * @param {String} [name] - The name of the event intended to be removed.\n     * @param {Function} [handler] - The handler intended to be reomved.\n     * @description Call to remove listeners from a specific event. If only `name` is passed then all the handlers attached to that `name` will be removed. If no arguments are passed then all handlers for every event on the obejct are removed.\n     */\n    removeEventListener(name?: string, handler?: Function): Events;\n    /**\n     * @name Two.Events#off\n     * @function\n     * @description Alias for {@link Two.Events#removeEventListener}.\n     */\n    off(name?: string, handler?: Function): Events;\n    /**\n     * @name Two.Events#unbind\n     * @function\n     * @description Alias for {@link Two.Events#removeEventListener}.\n     */\n    unbind(name?: string, handler?: Function): Events;\n    /**\n     * @name Two.Events#dispatchEvent\n     * @function\n     * @param {String} name - The name of the event to dispatch.\n     * @param args - Anything can be passed after the name and those will be passed on to handlers attached to the event in the order they are passed.\n     * @description Call to trigger a custom event. Any additional arguments passed after the name will be passed along to the attached handlers.\n     */\n    dispatchEvent(name: string, ...args: any[]): Events;\n    trigger(...args: any[]): any;\n    listen(obj: any, name: string, handler: Function): Events;\n    ignore(obj: any, name: string, handler: Function): Events;\n  }\n}\n"
  },
  {
    "path": "src/events.js",
    "content": "/**\n * @name Two.Events\n * @class\n * @description Object inherited by many Two.js objects in order to facilitate custom events.\n */\nexport class Events {\n\n  _events = {};\n  _bound = false;\n\n  constructor() {}\n\n  /**\n   * @name Two.Events#addEventListener\n   * @function\n   * @param {String} [name] - The name of the event to bind a function to.\n   * @param {Function} [handler] - The function to be invoked when the event is dispatched.\n   * @description Call to add a listener to a specific event name.\n   */\n  addEventListener(name, handler) {\n\n    const list = this._events[name] || (this._events[name] = []);\n    list.push(handler);\n    this._bound = true;\n\n    return this;\n\n  }\n\n  /**\n   * @name Two.Events#on\n   * @function\n   * @description Alias for {@link Two.Events#addEventListener}.\n   */\n  on() {\n    return this.addEventListener.apply(this, arguments);\n  }\n  /**\n   * @name Two.Events#bind\n   * @function\n   * @description Alias for {@link Two.Events#addEventListener}.\n   */\n  bind() {\n    return this.addEventListener.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Events#removeEventListener\n   * @function\n   * @param {String} [name] - The name of the event intended to be removed.\n   * @param {Function} [handler] - The handler intended to be removed.\n   * @description Call to remove listeners from a specific event. If only `name` is passed then all the handlers attached to that `name` will be removed. If no arguments are passed then all handlers for every event on the obejct are removed.\n   */\n  removeEventListener(name, handler) {\n\n    if (!this._events) {\n      return this;\n    }\n    if (!name && !handler) {\n      this._events = {};\n      this._bound = false;\n      return this;\n    }\n\n    const names = name ? [name] : Object.keys(this._events);\n    for (let i = 0, l = names.length; i < l; i++) {\n\n      name = names[i];\n      let list = this._events[name];\n\n      if (list) {\n        let events = [];\n        if (handler) {\n          for (let j = 0, k = list.length; j < k; j++) {\n            let e = list[j];\n            e = e.handler ? e.handler : e;\n            if (handler !== e) {\n              events.push(e);\n            }\n          }\n        }\n        this._events[name] = events;\n      }\n    }\n\n    return this;\n\n  }\n\n  /**\n   * @name Two.Events#off\n   * @function\n   * @description Alias for {@link Two.Events#removeEventListener}.\n   */\n  off() {\n    return this.removeEventListener.apply(this, arguments);\n  }\n  /**\n   * @name Two.Events#unbind\n   * @function\n   * @description Alias for {@link Two.Events#removeEventListener}.\n   */\n  unbind() {\n    return this.removeEventListener.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Events#dispatchEvent\n   * @function\n   * @param {String} name - The name of the event to dispatch.\n   * @param args - Anything can be passed after the name and those will be passed on to handlers attached to the event in the order they are passed.\n   * @description Call to trigger a custom event. Any additional arguments passed after the name will be passed along to the attached handlers.\n   */\n  dispatchEvent(name) {\n\n    if (!this._events) {\n      return this;\n    }\n\n    const args = Array.prototype.slice.call(arguments, 1);\n    const events = this._events[name];\n\n    if (events) {\n      for (let i = 0; i < events.length; i++) {\n        events[i].call(this, ...args);\n      }\n    }\n\n    return this;\n\n  }\n\n  trigger() {\n    return this.dispatchEvent.apply(this, arguments);\n  }\n\n  listen(obj, name, handler) {\n\n    const scope = this;\n\n    if (obj) {\n\n      // Add references about the object that assigned this listener\n      e.obj = obj;\n      e.name = name;\n      e.handler = handler;\n\n      obj.on(name, e);\n\n    }\n\n    function e() {\n      handler.apply(scope, arguments);\n    }\n\n    return scope;\n\n  }\n\n  ignore(obj, name, handler) {\n\n    obj.off(name, handler);\n    return this;\n\n  }\n\n  /**\n   * @name Two.Events.Types\n   * @property {Object} - Object of different types of Two.js specific events.\n   */\n  static Types = {\n    play: 'play',\n    pause: 'pause',\n    update: 'update',\n    render: 'render',\n    resize: 'resize',\n    change: 'change',\n    remove: 'remove',\n    insert: 'insert',\n    order: 'order',\n    load: 'load'\n  }\n\n  static Methods = [\n    'addEventListener', 'on', 'removeEventListener', 'off', 'unbind',\n    'dispatchEvent', 'trigger', 'listen', 'ignore'\n  ];\n\n}\n"
  },
  {
    "path": "src/group.d.ts",
    "content": "declare module 'two.js/src/group' {\n  type ChildParams =\n    | Parameters<typeof Path.fromObject>[0]\n    | Parameters<typeof Text.fromObject>[0]\n    | Parameters<typeof Points.fromObject>[0];\n  /**\n   * @name Two.Group\n   * @class\n   * @param {Shape[]} [children] - A list of objects that inherit {@link Two.Shape}. For instance, the array could be a {@link Two.Path}, {@link Two.Text}, and {@link Two.RoundedRectangle}.\n   * @description This is the primary class for grouping objects that are then drawn in Two.js. In Illustrator this is a group, in After Effects it would be a Null Object. Whichever the case, the `Two.Group` contains a transformation matrix and commands to style its children, but it by itself doesn't render to the screen.\n   * @nota-bene The {@link Two#scene} is an instance of `Two.Group`.\n   */\n  export class Group extends Shape {\n    static Children: Children;\n    static IsVisible(element: Element, visibleOnly?: boolean): boolean;\n    static VisitForHitTest(\n      group: Group,\n      context: {\n        x: number;\n        y: number;\n        visibleOnly: boolean;\n        results: Element[];\n      },\n      includeGroups: boolean,\n      filter: Function | null,\n      hitOptions: Pick<\n        SceneHitTestOptions,\n        'precision' | 'fill' | 'stroke' | 'tolerance' | 'ignoreVisibility'\n      >,\n      tolerance: number,\n      stopOnFirst: null\n    ): boolean;\n    /**\n     * @name Two.Group.InsertChildren\n     * @function\n     * @param {Shape[]} children - The objects to be inserted.\n     * @description Cached method to let renderers know children have been added to a {@link Two.Group}.\n     */\n    static InsertChildren(children: Shape[]): void;\n    /**\n     * @name Two.Group.RemoveChildren\n     * @function\n     * @param {Shape[]} children - The objects to be removed.\n     * @description Cached method to let renderers know children have been removed from a {@link Two.Group}.\n     */\n    static RemoveChildren(children: Shape[]): void;\n    /**\n     * @name Two.Group.OrderChildren\n     * @function\n     * @description Cached method to let renderers know order has been updated on a {@link Two.Group}.\n     */\n    static OrderChildren(children: Shape[]): void;\n    /**\n     * @name Two.Group.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Group}.\n     */\n    static Properties: string[];\n    static fromObject(\n      obj: Parameters<typeof Shape.fromObject>[0] & {\n        children?: (ChildParams | Parameters<typeof Group.fromObject>[0])[];\n        opacity?: number;\n        mask?: ChildParams;\n      }\n    ): Group;\n    constructor(children?: Shape[]);\n    constructor(...args: Shape[]);\n    /**\n     * @name Two.Group#_flagAdditions\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#additions} needs updating.\n     */\n    private _flagAdditions;\n    /**\n     * @name Two.Group#_flagSubtractions\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#subtractions} needs updating.\n     */\n    private _flagSubtractions;\n    /**\n     * @name Two.Group#_flagOrder\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#order} needs updating.\n     */\n    private _flagOrder;\n    /**\n     * @name Two.Group#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#visible} needs updating.\n     */\n    /**\n     * @name Two.Group#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#opacity} needs updating.\n     */\n    private _flagOpacity;\n    /**\n     * @name Two.Group#_flagBeginning\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#beginning} needs updating.\n     */\n    private _flagBeginning;\n    /**\n     * @name Two.Group#_flagEnding\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#ending} needs updating.\n     */\n    private _flagEnding;\n    /**\n     * @name Two.Group#_flagLength\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#length} needs updating.\n     */\n    private _flagLength;\n    /**\n     * @name Two.Group#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Group#mask} needs updating.\n     */\n    private _flagMask;\n    /**\n     * @name Two.Group#fill\n     * @property {(String|Gradient|Texture)} - The value of what all child shapes should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    fill: string | Gradient | Texture;\n    /**\n     * @name Two.Group#stroke\n     * @property {(String|Gradient|Texture)} - The value of what all child shapes should be outlined in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    stroke: string | Gradient | Texture;\n    /**\n     * @name Two.Group#linewidth\n     * @property {Number} - The thickness in pixels of the stroke for all child shapes.\n     */\n    linewidth: number;\n    /**\n     * @name Two.Group#opacity\n     * @property {Number} - The opaqueness of all child shapes.\n     * @nota-bene Becomes multiplied by the individual child's opacity property.\n     */\n    opacity: number;\n    /**\n     * @name Two.Group#visible\n     * @property {Boolean} - Display the path or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    visible: boolean;\n    /**\n     * @name Two.Group#cap\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty}\n     */\n    cap: CapProperties;\n    /**\n     * @name Two.Group#join\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty}\n     */\n    join: JoinProperties;\n    /**\n     * @name Two.Group#miter\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty}\n     */\n    miter: number;\n    /**\n     * @name Two.Group#closed\n     * @property {Boolean} - Determines whether a final line is drawn between the final point in the `vertices` array and the first point of all child shapes.\n     */\n    closed: boolean;\n    /**\n     * @name Two.Group#curved\n     * @property {Boolean} - When the child's path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n     */\n    curved: boolean;\n    /**\n     * @name Two.Group#automatic\n     * @property {Boolean} - Determines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n     */\n    automatic: boolean;\n    /**\n     * @name Two.Group#beginning\n     * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n     * @description {@link Two.Group#beginning} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#ending}.\n     */\n    beginning: number;\n    /**\n     * @name Two.Group#ending\n     * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n     * @description {@link Two.Group#ending} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#beginning}.\n     */\n    ending: number;\n    /**\n     * @name Two.Group#length\n     * @property {Number} - The sum of distances between all child lengths.\n     */\n    length: number;\n    /**\n     * @name Two.Group#mask\n     * @property {Shape} - The Two.js object to clip from a group's rendering.\n     */\n    mask: Shape | null | undefined;\n    /**\n     * @name Two.Group#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space for all child shapes.\n     * @description When `strokeAttenuation` is `false`, this property is applied to all child shapes, making their stroke widths automatically adjust to compensate for the group's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke widths scale normally with transformations.\n     */\n    strokeAttenuation: boolean;\n    /**\n     * @name Two.Group#additions\n     * @property {Shape[]}\n     * @description An automatically updated list of children that need to be appended to the renderer's scenegraph.\n     */\n    additions: Shape[];\n    /**\n     * @name Two.Group#subtractions\n     * @property {Shape[]}\n     * @description An automatically updated list of children that need to be removed from the renderer's scenegraph.\n     */\n    subtractions: Shape[];\n    /**\n     * @name Two.Group#children\n     * @property {Group.Children}\n     * @description A list of all the children in the scenegraph.\n     * @nota-bene Ther order of this list indicates the order each element is rendered to the screen.\n     */\n    children: Children;\n    /**\n     * @name Two.Group#copy\n     * @function\n     * @param {Two.Group} [group] - The reference {@link Two.Group}\n     * @returns {Two.Group}\n     * @description Copy the properties of one {@link Two.Group} onto another.\n     */\n    copy(group: Group): Group;\n    /**\n     * @name Two.Group#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the group.\n     */\n    toObject(): Object;\n    /**\n     * @name Two.Group#dispose\n     * @function\n     * @description Release the group's renderer resources and detach all events.\n     * This method recursively disposes all child objects, unbinds the children\n     * collection events, and preserves the renderer type for potential re-attachment\n     * to a new renderer.\n     */\n    dispose(): Group;\n    /**\n     * @name Two.Group#getShapesAtPoint\n     * @function\n     * @param {Number} x - X coordinate in world space.\n     * @param {Number} y - Y coordinate in world space.\n     * @param {SceneHitTestOptions} [options]\n     * @returns {Shape[]} Ordered list of intersecting shapes, front to back.\n     * @description Traverse the group hierarchy and return shapes that contain the specified point.\n     */\n    getShapesAtPoint(\n      x: number,\n      y: number,\n      options?: SceneHitTestOptions\n    ): Shape[];\n    /**\n     * @name Two.Group#corner\n     * @function\n     * @description Orient the children of the group to the upper left-hand corner of that group.\n     */\n    corner(): Group;\n    /**\n     * @name Two.Group#center\n     * @function\n     * @description Orient the children of the group to the center of that group.\n     */\n    center(): Group;\n    /**\n     * @name Two.Group#getById\n     * @function\n     * @description Recursively search for id. Returns the first element found.\n     * @returns {Shape} - Or `null` if nothing is found.\n     */\n    getById(id: string): Shape;\n    /**\n     * @name Two.Group#getByClassName\n     * @function\n     * @description Recursively search for classes. Returns an array of matching elements.\n     * @returns {Shape[]} - Or empty array if nothing is found.\n     */\n    getByClassName(className: string): Shape[];\n    /**\n     * @name Two.Group#getByType\n     * @function\n     * @description Recursively search for children of a specific type, e.g. {@link Two.Path}. Pass a reference to this type as the param. Returns an array of matching elements.\n     * @returns {Shape[]} - Empty array if nothing is found.\n     */\n    getByType(type: Shape): Shape[];\n    /**\n     * @name Two.Group#add\n     * @function\n     * @param {Element[]} objects - An array of objects to be added. Can also be supplied as individual arguments.\n     * @params {...Element} [args] - Alternatively pass shapes as each argument\n     * @description Add objects to the group.\n     */\n    add(objects: Shape | Shape[]): Group;\n    add(...args: Shape[]): Group;\n    /**\n     * @name Two.Group#remove\n     * @function\n     * @description Remove self from the scene / parent.\n     */\n    remove(): Shape;\n    /**\n     * @name Two.Group#remove\n     * @function\n     * @param {Shape[]} objects - An array of objects to be removed. Can also be supplied as individual arguments.\n     * @description Remove objects from the group.\n     */\n    remove(objects: Shape[]): Shape[];\n    /**\n     * @name Two.Group#remove\n     * @function\n     * @params {...Shape} [args] - Alternatively pass shapes as each argument\n     * @description Remove objects from the group.\n     */\n    remove(...args: Shape[]): Shape | Shape[];\n    /**\n     * @name Two.Group#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the group.\n     */\n    getBoundingClientRect(shallow?: boolean): BoundingBox;\n    /**\n     * @name Two.Group#noFill\n     * @function\n     * @description Apply `noFill` method to all child shapes.\n     */\n    noFill(): Group;\n    /**\n     * @name Two.Group#noStroke\n     * @function\n     * @description Apply `noStroke` method to all child shapes.\n     */\n    noStroke(): Group;\n    /**\n     * @name Two.Group#subdivide\n     * @function\n     * @description Apply `subdivide` method to all child shapes.\n     */\n    subdivide(limit?: number): Group;\n    /**\n     * @name Two.Group#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Group}\n     * @description Create a new instance of {@link Two.Group} with the same properties of the current group.\n     */\n    clone(parent?: Group): Group;\n    /**\n     * @name Two.Group#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    protected _update(bubbles?: boolean): Group;\n    /**\n     * @name Two.Group#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Group;\n  }\n  import { Shape } from 'two.js/src/shape';\n  import { Path, CapProperties, JoinProperties } from 'two.js/src/path';\n  import { Text } from 'two.js/src/text';\n  import { Points } from 'two.js/src/shapes/points';\n  import { Children } from 'two.js/src/children';\n  import { Gradient } from 'two.js/src/effects/gradient';\n  import { Texture } from 'two.js/src/effects/texture';\n  import { BoundingBox, SceneHitTestOptions } from 'two.js';\n}\n"
  },
  {
    "path": "src/group.js",
    "content": "import { Events } from './events.js';\nimport { _ } from './utils/underscore.js';\nimport { getEffectFromObject } from './utils/shape.js';\nimport { boundsContains } from './utils/hit-test.js';\n\nimport { Shape } from './shape.js';\nimport { Children } from './children.js';\nimport { Path } from './path.js';\nimport { ArcSegment } from './shapes/arc-segment.js';\nimport { Circle } from './shapes/circle.js';\nimport { Ellipse } from './shapes/ellipse.js';\nimport { Points } from './shapes/points.js';\nimport { Polygon } from './shapes/polygon.js';\nimport { Rectangle } from './shapes/rectangle.js';\nimport { RoundedRectangle } from './shapes/rounded-rectangle.js';\nimport { Star } from './shapes/star.js';\nimport { Text } from './text.js';\nimport { Element } from './element.js';\nimport { ImageSequence } from './effects/image-sequence.js';\nimport { Sprite } from './effects/sprite.js';\n\n// Constants\n\nconst min = Math.min,\n  max = Math.max;\n\nconst cache = {\n  getShapesAtPoint: {\n    results: [],\n    hitOptions: {},\n    context: {\n      x: 0,\n      y: 0,\n      visibleOnly: true,\n      results: null,\n    },\n    single: [],\n    output: [],\n    empty: [],\n  },\n};\n\n/**\n * @name Two.Group\n * @class\n * @extends Two.Shape\n * @param {Two.Shape[]} [children] - A list of objects that inherit {@link Two.Shape}. For instance, the array could be a {@link Two.Path}, {@link Two.Text}, and {@link Two.RoundedRectangle}.\n * @description This is the primary class for grouping objects that are then drawn in Two.js. In Illustrator this is a group, in After Effects it would be a Null Object. Whichever the case, the `Two.Group` contains a transformation matrix and commands to style its children, but it by itself doesn't render to the screen.\n * @nota-bene The {@link Two#scene} is an instance of `Two.Group`.\n */\nexport class Group extends Shape {\n  /**\n   * @name Two.Group#_flagAdditions\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#additions} needs updating.\n   */\n  _flagAdditions = false;\n\n  /**\n   * @name Two.Group#_flagSubtractions\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#subtractions} needs updating.\n   */\n  _flagSubtractions = false;\n\n  /**\n   * @name Two.Group#_flagOrder\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#order} needs updating.\n   */\n  _flagOrder = false;\n\n  /**\n   * @name Two.Group#_flagVisible\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#visible} needs updating.\n   */\n\n  /**\n   * @name Two.Group#_flagOpacity\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#opacity} needs updating.\n   */\n  _flagOpacity = true;\n\n  /**\n   * @name Two.Group#_flagBeginning\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#beginning} needs updating.\n   */\n  _flagBeginning = false;\n\n  /**\n   * @name Two.Group#_flagEnding\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#ending} needs updating.\n   */\n  _flagEnding = false;\n\n  /**\n   * @name Two.Group#_flagLength\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#length} needs updating.\n   */\n  _flagLength = false;\n\n  /**\n   * @name Two.Group#_flagMask\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Group#mask} needs updating.\n   */\n  _flagMask = false;\n\n  // Underlying Properties\n\n  /**\n   * @name Two.Group#fill\n   * @property {(String|Two.Gradient|Two.Texture)} - The value of what all child shapes should be filled in with.\n   * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n   */\n  _fill = '#fff';\n\n  /**\n   * @name Two.Group#stroke\n   * @property {(String|Two.Gradient|Two.Texture)} - The value of what all child shapes should be outlined in with.\n   * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n   */\n  _stroke = '#000';\n\n  /**\n   * @name Two.Group#linewidth\n   * @property {Number} - The thickness in pixels of the stroke for all child shapes.\n   */\n  _linewidth = 1.0;\n\n  /**\n   * @name Two.Group#opacity\n   * @property {Number} - The opaqueness of all child shapes.\n   * @nota-bene Becomes multiplied by the individual child's opacity property.\n   */\n  _opacity = 1.0;\n\n  /**\n   * @name Two.Group#visible\n   * @property {Boolean} - Display the path or not.\n   * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n   */\n  _visible = true;\n\n  /**\n   * @name Two.Group#cap\n   * @property {String}\n   * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty}\n   */\n  _cap = 'round';\n\n  /**\n   * @name Two.Group#join\n   * @property {String}\n   * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty}\n   */\n  _join = 'round';\n\n  /**\n   * @name Two.Group#miter\n   * @property {String}\n   * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty}\n   */\n  _miter = 4;\n\n  /**\n   * @name Two.Group#closed\n   * @property {Boolean} - Determines whether a final line is drawn between the final point in the `vertices` array and the first point of all child shapes.\n   */\n  _closed = true;\n\n  /**\n   * @name Two.Group#curved\n   * @property {Boolean} - When the child's path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n   */\n  _curved = false;\n\n  /**\n   * @name Two.Group#automatic\n   * @property {Boolean} - Determines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n   */\n  _automatic = true;\n\n  /**\n   * @name Two.Group#beginning\n   * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n   * @description {@link Two.Group#beginning} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n   * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#ending}.\n   */\n  _beginning = 0;\n\n  /**\n   * @name Two.Group#ending\n   * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n   * @description {@link Two.Group#ending} is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n   * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Group#beginning}.\n   */\n  _ending = 1.0;\n\n  /**\n   * @name Two.Group#length\n   * @property {Number} - The sum of distances between all child lengths.\n   */\n  _length = 0;\n\n  /**\n   * @name Two.Group#mask\n   * @property {Two.Shape} - The Two.js object to clip from a group's rendering.\n   */\n  _mask = null;\n\n  /**\n   * @name Two.Group#_strokeAttenuation\n   * @private\n   * @see {@link Two.Group#strokeAttenuation}\n   */\n  _strokeAttenuation = true;\n\n  constructor(children) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    //\n\n    this._renderer.type = 'group';\n\n    /**\n     * @name Two.Group#additions\n     * @property {Two.Shape[]}\n     * @description An automatically updated list of children that need to be appended to the renderer's scenegraph.\n     */\n    this.additions = [];\n\n    /**\n     * @name Two.Group#subtractions\n     * @property {Two.Shape[]}\n     * @description An automatically updated list of children that need to be removed from the renderer's scenegraph.\n     */\n    this.subtractions = [];\n\n    /**\n     * @name Two.Group#children\n     * @property {Two.Group.Children}\n     * @description A list of all the children in the scenegraph.\n     * @nota-bene Ther order of this list indicates the order each element is rendered to the screen.\n     */\n    this.children = Array.isArray(children)\n      ? children\n      : Array.prototype.slice.call(arguments);\n  }\n\n  static Children = Children;\n\n  /**\n   * @name Two.Group.InsertChildren\n   * @function\n   * @param {Two.Shape[]} children - The objects to be inserted.\n   * @description Cached method to let renderers know children have been added to a {@link Two.Group}.\n   */\n  static InsertChildren(children) {\n    for (let i = 0; i < children.length; i++) {\n      replaceParent.call(this, children[i], this);\n    }\n  }\n\n  /**\n   * @name Two.Group.RemoveChildren\n   * @function\n   * @param {Two.Shape[]} children - The objects to be removed.\n   * @description Cached method to let renderers know children have been removed from a {@link Two.Group}.\n   */\n  static RemoveChildren(children) {\n    for (let i = 0; i < children.length; i++) {\n      replaceParent.call(this, children[i]);\n    }\n  }\n\n  /**\n   * @name Two.Group.OrderChildren\n   * @function\n   * @description Cached method to let renderers know order has been updated on a {@link Two.Group}.\n   */\n  static OrderChildren(children) {\n    this._flagOrder = true;\n  }\n\n  /**\n   * @name Two.Group.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Group}.\n   */\n  static Properties = [\n    'fill',\n    'stroke',\n    'linewidth',\n    'cap',\n    'join',\n    'miter',\n\n    'closed',\n    'curved',\n    'automatic',\n  ];\n\n  static fromObject(obj) {\n    const group = new Group();\n\n    for (let i = 0; i < Group.Properties.length; i++) {\n      const k = Group.Properties[i];\n      if (k in obj) {\n        if (/(fill|stroke)/i.test(k)) {\n          group[k] =\n            typeof obj[k] === 'string' ? obj[k] : getEffectFromObject(obj[k]);\n        } else {\n          group[k] = obj[k];\n        }\n      }\n    }\n\n    if ('mask' in obj) {\n      group.mask = getShapeFromObject(obj.mask);\n    }\n    if ('id' in obj) {\n      group.id = obj.id;\n    }\n\n    group.children = obj.children.map(getShapeFromObject);\n\n    return group;\n\n    function getShapeFromObject(child) {\n      // All of the types of children Two.Group supports\n      if (child && child.renderer) {\n        switch (child.renderer.type) {\n          case 'arc-segment':\n            return ArcSegment.fromObject(child);\n          case 'circle':\n            return Circle.fromObject(child);\n          case 'element':\n            return Element.fromObject(child);\n          case 'ellipse':\n            return Ellipse.fromObject(child);\n          case 'group':\n            return Group.fromObject(child);\n          case 'image':\n            return Image.fromObject(child);\n          case 'image-sequence':\n            return ImageSequence.fromObject(child);\n          case 'path':\n            return Path.fromObject(child);\n          case 'points':\n            return Points.fromObject(child);\n          case 'polygon':\n            return Polygon.fromObject(child);\n          case 'rectangle':\n            return Rectangle.fromObject(child);\n          case 'rounded-rectangle':\n            return RoundedRectangle.fromObject(child);\n          case 'shape':\n            return Shape.fromObject(child);\n          case 'sprite':\n            return Sprite.fromObject(child);\n          case 'star':\n            return Star.fromObject(child);\n          case 'text':\n            return Text.fromObject(child);\n        }\n      }\n      // Commonly null for empty set\n      // properties like fill and stroke\n      return child;\n    }\n  }\n\n  static IsVisible(element, visibleOnly) {\n    if (!visibleOnly) {\n      return true;\n    }\n\n    let current = element;\n    while (current) {\n      if (typeof current.visible === 'boolean' && !current.visible) {\n        return false;\n      }\n      if (typeof current.opacity === 'number' && current.opacity <= 0) {\n        return false;\n      }\n      current = current.parent;\n    }\n\n    return true;\n  }\n\n  static VisitForHitTest(\n    group,\n    context,\n    includeGroups,\n    filter,\n    hitOptions,\n    tolerance,\n    stopOnFirst\n  ) {\n    const children = group && group.children;\n    if (!children) {\n      return false;\n    }\n\n    const results = context.results;\n    for (let i = children.length - 1; i >= 0; i--) {\n      const child = children[i];\n\n      if (!child) {\n        continue;\n      }\n\n      if (!Group.IsVisible(child, context.visibleOnly)) {\n        continue;\n      }\n\n      const rect =\n        typeof child.getBoundingClientRect === 'function'\n          ? child.getBoundingClientRect()\n          : null;\n\n      if (rect && !boundsContains(rect, context.x, context.y, tolerance)) {\n        continue;\n      }\n\n      if (child instanceof Group) {\n        if (\n          includeGroups &&\n          (!filter || filter(child)) &&\n          typeof child.contains === 'function' &&\n          child.contains(context.x, context.y, hitOptions)\n        ) {\n          results.push(child);\n          if (stopOnFirst) {\n            return true;\n          }\n        }\n        if (\n          Group.VisitForHitTest(\n            child,\n            context,\n            includeGroups,\n            filter,\n            hitOptions,\n            tolerance,\n            stopOnFirst\n          )\n        ) {\n          return true;\n        }\n        continue;\n      }\n\n      if (!(child instanceof Shape)) {\n        continue;\n      }\n\n      if (filter && !filter(child)) {\n        continue;\n      }\n\n      if (typeof child.contains !== 'function') {\n        continue;\n      }\n\n      if (child.contains(context.x, context.y, hitOptions)) {\n        results.push(child);\n        if (stopOnFirst) {\n          return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  /**\n   * @name Two.Group#copy\n   * @function\n   * @param {Two.Group} [group] - The reference {@link Two.Group}\n   * @returns {Two.Group}\n   * @description Copy the properties of one {@link Two.Group} onto another.\n   */\n  copy(group) {\n    super.copy.call(this, group);\n    console.warn(\n      'Two.js: attempting to copy group. Two.Group.children copying not supported.'\n    );\n    for (let i = 0; i < Group.Properties.length; i++) {\n      const k = Group.Properties[i];\n      if (k in group) {\n        this[k] = group[k];\n      }\n    }\n    return this;\n  }\n\n  /**\n   * @name Two.Group#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Group}\n   * @description Create a new instance of {@link Two.Group} with the same properties of the current group.\n   */\n  clone(parent) {\n    // /**\n    //  * TODO: Group has a gotcha in that it's at the moment required to be bound to\n    //  * an instance of two in order to add elements correctly. This needs to\n    //  * be rethought and fixed.\n    //  */\n\n    const clone = new Group();\n    const children = this.children.map(function (child) {\n      return child.clone();\n    });\n\n    clone.add(children);\n\n    clone.opacity = this.opacity;\n\n    if (this.mask) {\n      clone.mask = this.mask;\n    }\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.className = this.className;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone._update();\n  }\n\n  /**\n   * @name Two.Group#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the group.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n\n    result.renderer.type = 'group';\n    result.children = [];\n    result.opacity = this.opacity;\n    result.className = this.className;\n    result.mask = this.mask ? this.mask.toObject() : null;\n\n    _.each(\n      this.children,\n      (child, i) => {\n        result.children[i] = child.toObject();\n      },\n      this\n    );\n\n    return result;\n  }\n\n  /**\n   * @name Two.Group#dispose\n   * @function\n   * @returns {Two.Group}\n   * @description Release the group's renderer resources and detach all events.\n   * This method recursively disposes all child objects, unbinds the children\n   * collection events, and preserves the renderer type for potential re-attachment\n   * to a new renderer.\n   */\n  dispose() {\n    // Call parent dispose to preserve renderer type and unbind events\n    super.dispose();\n\n    // Recursively dispose all children\n    if (this.children) {\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (typeof child.dispose === 'function') {\n          child.dispose();\n        }\n      }\n    }\n\n    // Unbind children collection events\n    if (this.children && typeof this.children.unbind === 'function') {\n      try {\n        this.children.unbind();\n      } catch (e) {\n        // Ignore unbind errors for incomplete Collection objects\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Group#getShapesAtPoint\n   * @function\n   * @param {Number} x - X coordinate in world space.\n   * @param {Number} y - Y coordinate in world space.\n   * @param {Object} [options] - Hit test configuration.\n   * @param {Boolean} [options.visibleOnly=true] - Limit results to visible shapes.\n   * @param {Boolean} [options.includeGroups=false] - Include groups in the hit results.\n   * @param {('all'|'deepest')} [options.mode='all'] - Whether to return all intersecting shapes or only the top-most.\n   * @param {Boolean} [options.deepest] - Alias for `mode: 'deepest'`.\n   * @param {Number} [options.precision] - Segmentation precision for curved geometry.\n   * @param {Number} [options.tolerance=0] - Pixel tolerance applied to hit testing.\n   * @param {Boolean} [options.fill] - Override fill testing behaviour.\n   * @param {Boolean} [options.stroke] - Override stroke testing behaviour.\n   * @param {Function} [options.filter] - Predicate to filter shapes from the result set.\n   * @returns {Shape[]} Ordered list of intersecting shapes, front to back.\n   * @description Traverse the group hierarchy and return shapes that contain the specified point.\n   * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n   */\n  getShapesAtPoint(x, y, options) {\n    const opts = options || {};\n    const { results, hitOptions, context, single, empty } =\n      cache.getShapesAtPoint;\n\n    results.length = 0;\n\n    const mode = opts.mode === 'deepest' || opts.deepest ? 'deepest' : 'all';\n    const visibleOnly = opts.visibleOnly !== false;\n    const includeGroups = !!opts.includeGroups;\n    const filter = typeof opts.filter === 'function' ? opts.filter : null;\n    const tolerance = typeof opts.tolerance === 'number' ? opts.tolerance : 0;\n\n    if (typeof opts.precision === 'number') {\n      hitOptions.precision = opts.precision;\n    } else {\n      delete hitOptions.precision;\n    }\n    if (typeof opts.fill !== 'undefined') {\n      hitOptions.fill = opts.fill;\n    } else {\n      delete hitOptions.fill;\n    }\n    if (typeof opts.stroke !== 'undefined') {\n      hitOptions.stroke = opts.stroke;\n    } else {\n      delete hitOptions.stroke;\n    }\n    hitOptions.tolerance = tolerance;\n    hitOptions.ignoreVisibility = !visibleOnly;\n\n    const stopOnFirst = mode === 'deepest';\n    context.x = x;\n    context.y = y;\n    context.visibleOnly = visibleOnly;\n    context.results = results;\n\n    Group.VisitForHitTest(\n      this,\n      context,\n      includeGroups,\n      filter,\n      hitOptions,\n      tolerance,\n      stopOnFirst\n    );\n\n    if (stopOnFirst) {\n      if (results.length > 0) {\n        const first = results[0];\n        results.length = 0;\n        single[0] = first;\n        single.length = 1;\n        return single;\n      }\n      empty.length = 0;\n      return empty;\n    }\n\n    const hits = results.slice();\n    results.length = 0;\n    return hits;\n  }\n\n  /**\n   * @name Two.Group#corner\n   * @function\n   * @description Orient the children of the group to the upper left-hand corner of that group.\n   */\n  corner() {\n    const rect = this.getBoundingClientRect(true);\n\n    for (let i = 0; i < this.children.length; i++) {\n      const child = this.children[i];\n      child.translation.x -= rect.left;\n      child.translation.y -= rect.top;\n    }\n\n    if (this.mask) {\n      this.mask.translation.x -= rect.left;\n      this.mask.translation.y -= rect.top;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Group#center\n   * @function\n   * @description Orient the children of the group to the center of that group.\n   */\n  center() {\n    const rect = this.getBoundingClientRect(true);\n    const cx = rect.left + rect.width / 2 - this.translation.x;\n    const cy = rect.top + rect.height / 2 - this.translation.y;\n\n    for (let i = 0; i < this.children.length; i++) {\n      const child = this.children[i];\n      if (child.isShape) {\n        child.translation.x -= cx;\n        child.translation.y -= cy;\n      }\n    }\n\n    if (this.mask) {\n      this.mask.translation.x -= cx;\n      this.mask.translation.y -= cy;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Group#getById\n   * @function\n   * @description Recursively search for id. Returns the first element found.\n   * @returns {Two.Shape} - Or `null` if nothing is found.\n   */\n  getById(id) {\n    let found = null;\n    function search(node) {\n      if (node.id === id) {\n        return node;\n      } else if (node.children) {\n        if (node.children.ids[id]) {\n          return node.children.ids[id];\n        }\n        for (let i = 0; i < node.children.length; i++) {\n          found = search(node.children[i]);\n          if (found) {\n            return found;\n          }\n        }\n      }\n      return null;\n    }\n    return search(this);\n  }\n\n  /**\n   * @name Two.Group#getByClassName\n   * @function\n   * @description Recursively search for classes. Returns an array of matching elements.\n   * @returns {Two.Shape[]} - Or empty array if nothing is found.\n   */\n  getByClassName(className) {\n    const found = [];\n    function search(node) {\n      if (Array.prototype.indexOf.call(node.classList, className) >= 0) {\n        found.push(node);\n      }\n      if (node.children) {\n        for (let i = 0; i < node.children.length; i++) {\n          const child = node.children[i];\n          search(child);\n        }\n      }\n      return found;\n    }\n    return search(this);\n  }\n\n  /**\n   * @name Two.Group#getByType\n   * @function\n   * @description Recursively search for children of a specific type, e.g. {@link Two.Path}. Pass a reference to this type as the param. Returns an array of matching elements.\n   * @returns {Two.Shape[]} - Empty array if nothing is found.\n   */\n  getByType(type) {\n    const found = [];\n    function search(node) {\n      if (node instanceof type) {\n        found.push(node);\n      }\n      if (node.children) {\n        for (let i = 0; i < node.children.length; i++) {\n          const child = node.children[i];\n          search(child);\n        }\n      }\n      return found;\n    }\n    return search(this);\n  }\n\n  /**\n   * @name Two.Group#add\n   * @function\n   * @param {Two.Shape[]|...Two.Shape} objects - An array of objects to be added. Can also be supplied as individual arguments.\n   * @description Add objects to the group.\n   */\n  add(objects) {\n    // Allow to pass multiple objects either as array or as multiple arguments\n    // If it's an array also create copy of it in case we're getting passed\n    // a childrens array directly.\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    } else {\n      objects = objects.slice();\n    }\n\n    // Add the objects\n    for (let i = 0; i < objects.length; i++) {\n      const child = objects[i];\n      if (!(child && child.id)) {\n        continue;\n      }\n      const index = Array.prototype.indexOf.call(this.children, child);\n      if (index >= 0) {\n        this.children.splice(index, 1);\n      }\n      this.children.push(child);\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Group#remove\n   * @function\n   * @param {Two.Shape[]|...Two.Shape} [objects=self] - An array of objects to be removed. Can be also removed as individual arguments. If no arguments are passed, then it removes itself from its parent.\n   * @description Remove objects from the group.\n   */\n  remove(objects) {\n    const l = arguments.length,\n      grandparent = this.parent;\n\n    // Allow to call remove without arguments\n    // This will detach the object from its own parent.\n    if (l <= 0 && grandparent) {\n      grandparent.remove(this);\n      return this;\n    }\n\n    // Allow to pass multiple objects either as array or as multiple arguments\n    // If it's an array also create copy of it in case we're getting passed\n    // a childrens array directly.\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    } else {\n      objects = objects.slice();\n    }\n\n    // Remove the objects\n    for (let i = 0; i < objects.length; i++) {\n      const object = objects[i];\n      if (!object || !this.children.ids[object.id]) {\n        continue;\n      }\n      const index = this.children.indexOf(object);\n      if (index >= 0) {\n        this.children.splice(index, 1);\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Group#getBoundingClientRect\n   * @function\n   * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n   * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n   * @description Return an object with top, left, right, bottom, width, and height parameters of the group.\n   */\n  getBoundingClientRect(shallow) {\n    let rect, matrix, tc, lc, rc, bc;\n\n    // TODO: Update this to not __always__ update. Just when it needs to.\n    this._update(true);\n\n    // Variables need to be defined here, because of nested nature of groups.\n    let left = Infinity,\n      right = -Infinity,\n      top = Infinity,\n      bottom = -Infinity;\n\n    const regex = /texture|gradient/i;\n\n    matrix = shallow ? this.matrix : this.worldMatrix;\n\n    for (let i = 0; i < this.children.length; i++) {\n      const child = this.children[i];\n\n      if (!child.visible || regex.test(child._renderer.type)) {\n        continue;\n      }\n\n      rect = child.getBoundingClientRect(shallow);\n\n      tc =\n        typeof rect.top !== 'number' ||\n        _.isNaN(rect.top) ||\n        !isFinite(rect.top);\n      lc =\n        typeof rect.left !== 'number' ||\n        _.isNaN(rect.left) ||\n        !isFinite(rect.left);\n      rc =\n        typeof rect.right !== 'number' ||\n        _.isNaN(rect.right) ||\n        !isFinite(rect.right);\n      bc =\n        typeof rect.bottom !== 'number' ||\n        _.isNaN(rect.bottom) ||\n        !isFinite(rect.bottom);\n\n      if (tc || lc || rc || bc) {\n        continue;\n      }\n\n      if (shallow) {\n        const [ax, ay] = matrix.multiply(rect.left, rect.top);\n        const [bx, by] = matrix.multiply(rect.right, rect.top);\n        const [cx, cy] = matrix.multiply(rect.left, rect.bottom);\n        const [dx, dy] = matrix.multiply(rect.right, rect.bottom);\n\n        top = min(ay, by, cy, dy, top);\n        left = min(ax, bx, cx, dx, left);\n        right = max(ax, bx, cx, dx, right);\n        bottom = max(ay, by, cy, dy, bottom);\n      } else {\n        top = min(rect.top, top);\n        left = min(rect.left, left);\n        right = max(rect.right, right);\n        bottom = max(rect.bottom, bottom);\n      }\n    }\n\n    return {\n      top: top,\n      left: left,\n      right: right,\n      bottom: bottom,\n      width: right - left,\n      height: bottom - top,\n    };\n  }\n\n  /**\n   * @name Two.Group#noFill\n   * @function\n   * @description Apply `noFill` method to all child shapes.\n   */\n  noFill() {\n    this.children.forEach(function (child) {\n      child.noFill();\n    });\n    return this;\n  }\n\n  /**\n   * @name Two.Group#noStroke\n   * @function\n   * @description Apply `noStroke` method to all child shapes.\n   */\n  noStroke() {\n    this.children.forEach(function (child) {\n      child.noStroke();\n    });\n    return this;\n  }\n\n  /**\n   * @name Two.Group#subdivide\n   * @function\n   * @description Apply `subdivide` method to all child shapes.\n   */\n  subdivide() {\n    const args = arguments;\n    this.children.forEach(function (child) {\n      child.subdivide.apply(child, args);\n    });\n    return this;\n  }\n\n  /**\n   * @name Two.Group#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    let i, l, child;\n\n    if (this._flagBeginning || this._flagEnding) {\n      const beginning = Math.min(this._beginning, this._ending);\n      const ending = Math.max(this._beginning, this._ending);\n      const length = this.length;\n      let sum = 0;\n\n      const bd = beginning * length;\n      const ed = ending * length;\n\n      for (i = 0; i < this.children.length; i++) {\n        child = this.children[i];\n        l = child.length;\n\n        if (bd > sum + l) {\n          child.beginning = 1;\n          child.ending = 1;\n        } else if (ed < sum) {\n          child.beginning = 0;\n          child.ending = 0;\n        } else if (bd > sum && bd < sum + l) {\n          child.beginning = (bd - sum) / l;\n          child.ending = 1;\n        } else if (ed > sum && ed < sum + l) {\n          child.beginning = 0;\n          child.ending = (ed - sum) / l;\n        } else {\n          child.beginning = 0;\n          child.ending = 1;\n        }\n\n        sum += l;\n      }\n    }\n\n    return super._update.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Group#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    if (this._flagAdditions) {\n      this.additions.length = 0;\n      this._flagAdditions = false;\n    }\n\n    if (this._flagSubtractions) {\n      this.subtractions.length = 0;\n      this._flagSubtractions = false;\n    }\n\n    this._flagOrder =\n      this._flagMask =\n      this._flagOpacity =\n      this._flagBeginning =\n      this._flagEnding =\n        false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  visible: {\n    enumerable: true,\n    get: function () {\n      return this._visible;\n    },\n    set: function (v) {\n      this._flagVisible = this._visible !== v || this._flagVisible;\n      this._visible = v;\n    },\n  },\n  opacity: {\n    enumerable: true,\n    get: function () {\n      return this._opacity;\n    },\n    set: function (v) {\n      this._flagOpacity = this._opacity !== v || this._flagOpacity;\n      this._opacity = v;\n    },\n  },\n  beginning: {\n    enumerable: true,\n    get: function () {\n      return this._beginning;\n    },\n    set: function (v) {\n      this._flagBeginning = this._beginning !== v || this._flagBeginning;\n      this._beginning = v;\n    },\n  },\n  ending: {\n    enumerable: true,\n    get: function () {\n      return this._ending;\n    },\n    set: function (v) {\n      this._flagEnding = this._ending !== v || this._flagEnding;\n      this._ending = v;\n    },\n  },\n  length: {\n    enumerable: true,\n    get: function () {\n      if (this._flagLength || this._length <= 0) {\n        this._length = 0;\n        if (!this.children) {\n          return this._length;\n        }\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          this._length += child.length;\n        }\n      }\n      return this._length;\n    },\n  },\n  fill: {\n    enumerable: true,\n    get: function () {\n      return this._fill;\n    },\n    set: function (v) {\n      this._fill = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.fill = v;\n      }\n    },\n  },\n  stroke: {\n    enumerable: true,\n    get: function () {\n      return this._stroke;\n    },\n    set: function (v) {\n      this._stroke = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.stroke = v;\n      }\n    },\n  },\n  linewidth: {\n    enumerable: true,\n    get: function () {\n      return this._linewidth;\n    },\n    set: function (v) {\n      this._linewidth = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.linewidth = v;\n      }\n    },\n  },\n  join: {\n    enumerable: true,\n    get: function () {\n      return this._join;\n    },\n    set: function (v) {\n      this._join = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.join = v;\n      }\n    },\n  },\n  miter: {\n    enumerable: true,\n    get: function () {\n      return this._miter;\n    },\n    set: function (v) {\n      this._miter = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.miter = v;\n      }\n    },\n  },\n  cap: {\n    enumerable: true,\n    get: function () {\n      return this._cap;\n    },\n    set: function (v) {\n      this._cap = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.cap = v;\n      }\n    },\n  },\n  closed: {\n    enumerable: true,\n    get: function () {\n      return this._closed;\n    },\n    set: function (v) {\n      this._closed = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.closed = v;\n      }\n    },\n  },\n  curved: {\n    enumerable: true,\n    get: function () {\n      return this._curved;\n    },\n    set: function (v) {\n      this._curved = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.curved = v;\n      }\n    },\n  },\n  automatic: {\n    enumerable: true,\n    get: function () {\n      return this._automatic;\n    },\n    set: function (v) {\n      this._automatic = v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.automatic = v;\n      }\n    },\n  },\n  children: {\n    enumerable: true,\n    get: function () {\n      return this._children;\n    },\n    set: function (children) {\n      const insertChildren = Group.InsertChildren.bind(this);\n      const removeChildren = Group.RemoveChildren.bind(this);\n      const orderChildren = Group.OrderChildren.bind(this);\n\n      if (this._children) {\n        this._children.unbind();\n        if (this._children.length > 0) {\n          removeChildren(this._children);\n        }\n      }\n\n      this._children = new Children(children);\n      this._children.bind(Events.Types.insert, insertChildren);\n      this._children.bind(Events.Types.remove, removeChildren);\n      this._children.bind(Events.Types.order, orderChildren);\n\n      if (children.length > 0) {\n        insertChildren(children);\n      }\n    },\n  },\n  mask: {\n    enumerable: true,\n    get: function () {\n      return this._mask;\n    },\n    set: function (v) {\n      this._mask = v;\n      this._flagMask = true;\n      if (_.isObject(v) && !v.clip) {\n        v.clip = true;\n      }\n    },\n  },\n\n  /**\n   * @name Two.Group#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space for all child shapes.\n   * @description When `strokeAttenuation` is `false`, this property is applied to all child shapes, making their stroke widths automatically adjust to compensate for the group's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke widths scale normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function () {\n      return this._strokeAttenuation;\n    },\n    set: function (v) {\n      this._strokeAttenuation = !!v;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (child.strokeAttenuation !== undefined) {\n          child.strokeAttenuation = v;\n        }\n      }\n    },\n  },\n};\n\n// /**\n//  * Helper function used to sync parent-child relationship within the\n//  * `Two.Group.children` object.\n//  *\n//  * Set the parent of the passed object to another object\n//  * and updates parent-child relationships\n//  * Calling with one arguments will simply remove the parenting\n//  */\nfunction replaceParent(child, newParent) {\n  const parent = child.parent;\n  let index;\n\n  if (parent === newParent) {\n    add();\n    return;\n  }\n\n  if (parent && parent.children.ids[child.id]) {\n    index = Array.prototype.indexOf.call(parent.children, child);\n    parent.children.splice(index, 1);\n\n    splice();\n  }\n\n  if (newParent) {\n    add();\n    return;\n  }\n\n  splice();\n\n  if (parent._flagAdditions && parent.additions.length === 0) {\n    parent._flagAdditions = false;\n  }\n  if (parent._flagSubtractions && parent.subtractions.length === 0) {\n    parent._flagSubtractions = false;\n  }\n\n  delete child.parent;\n\n  function add() {\n    if (newParent.subtractions.length > 0) {\n      index = Array.prototype.indexOf.call(newParent.subtractions, child);\n\n      if (index >= 0) {\n        newParent.subtractions.splice(index, 1);\n      }\n    }\n\n    if (newParent.additions.length > 0) {\n      index = Array.prototype.indexOf.call(newParent.additions, child);\n\n      if (index >= 0) {\n        newParent.additions.splice(index, 1);\n      }\n    }\n\n    child.parent = newParent;\n    newParent.additions.push(child);\n    newParent._flagAdditions = true;\n  }\n\n  function splice() {\n    index = Array.prototype.indexOf.call(parent.additions, child);\n\n    if (index >= 0) {\n      parent.additions.splice(index, 1);\n    }\n\n    index = Array.prototype.indexOf.call(parent.subtractions, child);\n\n    if (index < 0) {\n      parent.subtractions.push(child);\n      parent._flagSubtractions = true;\n    }\n  }\n}\n"
  },
  {
    "path": "src/matrix.d.ts",
    "content": "declare module 'two.js/src/matrix' {\n  /**\n   * @name Two.Matrix\n   * @class\n   * @param {Number} [a=1] - The value for element at the first column and first row.\n   * @param {Number} [b=0] - The value for element at the second column and first row.\n   * @param {Number} [c=0] - The value for element at the third column and first row.\n   * @param {Number} [d=0] - The value for element at the first column and second row.\n   * @param {Number} [e=1] - The value for element at the second column and second row.\n   * @param {Number} [f=0] - The value for element at the third column and second row.\n   * @param {Number} [g=0] - The value for element at the first column and third row.\n   * @param {Number} [h=0] - The value for element at the second column and third row.\n   * @param {Number} [i=1] - The value for element at the third column and third row.\n   * @description A class to store 3 x 3 transformation matrix information. In addition to storing data `Two.Matrix` has suped up methods for commonplace mathematical operations.\n   * @nota-bene Order is based on how to construct transformation strings for the browser.\n   */\n  export class Matrix extends Events {\n    /**\n     * @name Two.Matrix.Identity\n     * @property {Number[]} - A stored reference to the default value of a 3 x 3 matrix.\n     */\n    static Identity: [1, 0, 0, 0, 1, 0, 0, 0, 1];\n    /**\n     * @name Two.Matrix.Multiply\n     * @function\n     * @param {Matrix} A\n     * @param {Matrix} B\n     * @param {Matrix} [C] - An optional matrix to apply the multiplication to.\n     * @returns {Matrix} - If an optional `C` matrix isn't passed then a new one is created and returned.\n     * @description Multiply two matrices together and return the result.\n     */\n    static Multiply(A: Matrix, B: Matrix, C?: Matrix): Matrix;\n    /**\n     * @name Two.Matrix.fromObject\n     * @function\n     * @param {Object} obj - The object notation of a Two.Matrix to create a new instance\n     * @returns {Two.Matrix}\n     * @description Create a new {@link Two.Matrix} from an object notation of a {@link Two.Matrix}.\n     * @nota-bene Works in conjunction with {@link Two.Matrix#toObject}\n     */\n    static fromObject(\n      obj:\n        | object\n        | {\n            elements?: number[];\n            manual?: boolean;\n          }\n    ): Matrix;\n    constructor(elements: number[]);\n    constructor(\n      a?: number,\n      b?: number,\n      c?: number,\n      d?: number,\n      e?: number,\n      f?: number\n    );\n    /**\n     * @name Two.Matrix#elements\n     * @property {Number[]} - The underlying data stored as an array.\n     */\n    elements: [\n      number,\n      number,\n      number,\n      number,\n      number,\n      number,\n      number,\n      number,\n      number\n    ];\n    /**\n     * @name Two.Matrix#manual\n     * @property {Boolean} - Determines whether Two.js automatically calculates the values for the matrix or if the developer intends to manage the matrix.\n     * @nota-bene - Setting to `true` nullifies {@link Two.Shape#translation}, {@link Two.Shape#rotation}, and {@link Two.Shape#scale}.\n     */\n    manual: boolean;\n    /**\n     * @name Two.Matrix#set\n     * @function\n     * @param {Number} a - The value for element at the first column and first row.\n     * @param {Number} b - The value for element at the second column and first row.\n     * @param {Number} c - The value for element at the third column and first row.\n     * @param {Number} d - The value for element at the first column and second row.\n     * @param {Number} e - The value for element at the second column and second row.\n     * @param {Number} f - The value for element at the third column and second row.\n     * @param {Number} g - The value for element at the first column and third row.\n     * @param {Number} h - The value for element at the second column and third row.\n     * @param {Number} i - The value for element at the third column and third row.\n     * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n     */\n    /**\n     * @name Two.Matrix#set\n     * @function\n     * @param {Number[]} a - The array of elements to apply.\n     * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n     */\n    set(\n      a: number[],\n      b: any,\n      c: any,\n      d: any,\n      e: any,\n      f: any,\n      g: any,\n      h: any,\n      i: any\n    ): any;\n    /**\n     * @name Two.Matrix#copy\n     * @function\n     * @description Copy the matrix of one to the current instance.\n     */\n    copy(m: any): Matrix;\n    /**\n     * @name Two.Matrix#identity\n     * @function\n     * @description Turn matrix to the identity, like resetting.\n     */\n    identity(): Matrix;\n    /**\n     * @name Two.Matrix#multiply\n     * @function\n     * @param {Number} a - The scalar to be multiplied.\n     * @description Multiply all components of the matrix against a single scalar value.\n     * @overloaded\n     */\n    multiply(a: number): Matrix;\n    /**\n     * @name Two.Matrix#multiply\n     * @function\n     * @param {Number} a - The x component to be multiplied.\n     * @param {Number} b - The y component to be multiplied.\n     * @param {Number} c - The z component to be multiplied.\n     * @description Multiply all components of a matrix against a 3 component vector.\n     * @overloaded\n     */\n    multiply(\n      a: number,\n      b: number,\n      c?: number\n    ): [x: number, y: number, z: number];\n    /**\n     * @name Two.Matrix#multiply\n     * @function\n     * @param {Number} a - The value at the first column and first row of the matrix to be multiplied.\n     * @param {Number} b - The value at the second column and first row of the matrix to be multiplied.\n     * @param {Number} c - The value at the third column and first row of the matrix to be multiplied.\n     * @param {Number} d - The value at the first column and second row of the matrix to be multiplied.\n     * @param {Number} e - The value at the second column and second row of the matrix to be multiplied.\n     * @param {Number} f - The value at the third column and second row of the matrix to be multiplied.\n     * @param {Number} g - The value at the first column and third row of the matrix to be multiplied.\n     * @param {Number} h - The value at the second column and third row of the matrix to be multiplied.\n     * @param {Number} i - The value at the third column and third row of the matrix to be multiplied.\n     * @description Multiply all components of a matrix against another matrix.\n     * @overloaded\n     */\n    multiply(\n      a: number,\n      b: number,\n      c: number,\n      d: number,\n      e: number,\n      f: number,\n      g: number,\n      h: number,\n      i: number\n    ): Matrix;\n    /**\n     * @name Two.Matrix#inverse\n     * @function\n     * @param {Matrix} [out] - The optional matrix to apply the inversion to.\n     * @description Return an inverted version of the matrix. If no optional one is passed a new matrix is created and returned.\n     */\n    inverse(out?: Matrix): Matrix;\n    /**\n     * @name Two.Matrix#scale\n     * @function\n     * @param {Number} scale - The one dimensional scale to apply to the matrix.\n     * @description Uniformly scale the transformation matrix.\n     */\n    /**\n     * @name Two.Matrix#scale\n     * @function\n     * @param {Number} sx - The horizontal scale factor.\n     * @param {Number} sy - The vertical scale factor\n     * @description Scale the transformation matrix in two dimensions.\n     */\n    scale(sx: number, sy: number, ...args: any[]): Matrix;\n    /**\n     * @name Two.Matrix#rotate\n     * @function\n     * @param {Number} Number - The amount to rotate in Number.\n     * @description Rotate the matrix.\n     */\n    rotate(Number: number): Matrix;\n    /**\n     * @name Two.Matrix#translate\n     * @function\n     * @param {Number} x - The horizontal translation value to apply.\n     * @param {Number} y - The vertical translation value to apply.\n     * @description Translate the matrix.\n     */\n    translate(x: number, y: number): Matrix;\n    /**\n     * @name Two.Matrix#skewX\n     * @function\n     * @param {Number} Number - The amount to skew in Number.\n     * @description Skew the matrix by an angle in the x axis direction.\n     */\n    skewX(Number: number): Matrix;\n    /**\n     * @name Two.Matrix#skewY\n     * @function\n     * @param {Number} Number - The amount to skew in Number.\n     * @description Skew the matrix by an angle in the y axis direction.\n     */\n    skewY(Number: number): Matrix;\n    /**\n     * @name Two.Matrix#toString\n     * @function\n     * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n     * @returns {String} - The transformation matrix as a 6 component string separated by spaces.\n     * @description Create a transform string. Used for the Two.js rendering APIs.\n     */\n    toString(fullMatrix?: boolean): string;\n    /**\n     * @name Two.Matrix#toTransformArray\n     * @function\n     * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 in the format for 2D transformations.\n     * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n     * @description Create a transform array. Used for the Two.js rendering APIs.\n     */\n    toTransformArray(fullMatrix?: boolean, output?: number[]): number[];\n    /**\n     * @name Two.Matrix#toArray\n     * @function\n     * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n     * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n     * @description Create a transform array. Used for the Two.js rendering APIs.\n     */\n    toArray(fullMatrix?: boolean, output?: number[]): number[];\n    /**\n     * @name Two.Matrix#toObject\n     * @function\n     * @description Create a JSON compatible object that represents information of the matrix.\n     */\n    toObject(): {\n      elements: number[];\n      manual: boolean;\n    };\n    /**\n     * @name Two.Matrix#clone\n     * @function\n     * @description Clone the current matrix.\n     */\n    clone(): Matrix;\n  }\n  import { Events } from 'two.js/src/events';\n}\n"
  },
  {
    "path": "src/matrix.js",
    "content": "import { NumArray, toFixed, setMatrix } from './utils/math.js';\nimport { Events } from './events.js';\n\n// Constants\n\nconst cos = Math.cos,\n  sin = Math.sin,\n  tan = Math.tan;\nconst array = [];\n\n/**\n * @name Two.Matrix\n * @class\n * @param {Number} [a=1] - The value for element at the first column and first row.\n * @param {Number} [b=0] - The value for element at the second column and first row.\n * @param {Number} [c=0] - The value for element at the third column and first row.\n * @param {Number} [d=0] - The value for element at the first column and second row.\n * @param {Number} [e=1] - The value for element at the second column and second row.\n * @param {Number} [f=0] - The value for element at the third column and second row.\n * @param {Number} [g=0] - The value for element at the first column and third row.\n * @param {Number} [h=0] - The value for element at the second column and third row.\n * @param {Number} [i=1] - The value for element at the third column and third row.\n * @description A class to store 3 x 3 transformation matrix information. In addition to storing data `Two.Matrix` has suped up methods for commonplace mathematical operations.\n * @nota-bene Order is based on how to construct transformation strings for the browser.\n */\nexport class Matrix extends Events {\n  /**\n   * @name Two.Matrix#elements\n   * @property {Number[]} - The underlying data stored as an array.\n   */\n  elements = new NumArray(9);\n\n  /**\n   * @name Two.Matrix#manual\n   * @property {Boolean} - Determines whether Two.js automatically calculates the values for the matrix or if the developer intends to manage the matrix.\n   * @nota-bene - Setting to `true` nullifies {@link Two.Shape#translation}, {@link Two.Shape#rotation}, and {@link Two.Shape#scale}.\n   */\n  manual = false;\n\n  constructor(a, b, c, d, e, f) {\n    super();\n\n    let elements = a;\n    if (!Array.isArray(elements)) {\n      elements = Array.prototype.slice.call(arguments);\n    }\n\n    // initialize the elements with default values.\n    this.identity();\n\n    if (elements.length > 0) {\n      this.set(elements);\n    }\n  }\n\n  //\n\n  /**\n   * @name Two.Matrix.Identity\n   * @property {Number[]} - A stored reference to the default value of a 3 x 3 matrix.\n   */\n  static Identity = [1, 0, 0, 0, 1, 0, 0, 0, 1];\n\n  /**\n   * @name Two.Matrix.Multiply\n   * @function\n   * @param {Number[]} A - The first {@link Two.Matrix} to multiply\n   * @param {Number[]} B - The second {@link Two.Matrix} to multiply\n   * @param {Number[]} [C] - An optional {@link Two.Matrix} to apply the result to\n   * @returns {Number[]} - If an optional `C` matrix isn't passed then a new one is created and returned.\n   * @description Multiply two matrices together and return the result.\n   */\n  static Multiply(A, B, C) {\n    if (B.length <= 3) {\n      // Multiply Vector\n\n      const e = A;\n      let x, y, z;\n\n      const a = B[0] || 0,\n        b = B[1] || 0,\n        c = B[2] || 0;\n\n      // Go down rows first\n      // a, d, g, b, e, h, c, f, i\n\n      x = e[0] * a + e[1] * b + e[2] * c;\n      y = e[3] * a + e[4] * b + e[5] * c;\n      z = e[6] * a + e[7] * b + e[8] * c;\n\n      return [x, y, z];\n    }\n\n    const A0 = A[0],\n      A1 = A[1],\n      A2 = A[2];\n    const A3 = A[3],\n      A4 = A[4],\n      A5 = A[5];\n    const A6 = A[6],\n      A7 = A[7],\n      A8 = A[8];\n\n    const B0 = B[0],\n      B1 = B[1],\n      B2 = B[2];\n    const B3 = B[3],\n      B4 = B[4],\n      B5 = B[5];\n    const B6 = B[6],\n      B7 = B[7],\n      B8 = B[8];\n\n    C = C || new NumArray(9);\n\n    C[0] = A0 * B0 + A1 * B3 + A2 * B6;\n    C[1] = A0 * B1 + A1 * B4 + A2 * B7;\n    C[2] = A0 * B2 + A1 * B5 + A2 * B8;\n    C[3] = A3 * B0 + A4 * B3 + A5 * B6;\n    C[4] = A3 * B1 + A4 * B4 + A5 * B7;\n    C[5] = A3 * B2 + A4 * B5 + A5 * B8;\n    C[6] = A6 * B0 + A7 * B3 + A8 * B6;\n    C[7] = A6 * B1 + A7 * B4 + A8 * B7;\n    C[8] = A6 * B2 + A7 * B5 + A8 * B8;\n\n    return C;\n  }\n\n  /**\n   * @name Two.Matrix.fromObject\n   * @function\n   * @param {Object} obj - The object notation of a Two.Matrix to create a new instance\n   * @returns {Two.Matrix}\n   * @description Create a new {@link Two.Matrix} from an object notation of a {@link Two.Matrix}.\n   * @nota-bene Works in conjunction with {@link Two.Matrix#toObject}\n   */\n  static fromObject(obj) {\n    return new Matrix().copy(obj);\n  }\n\n  /**\n   * @name Two.Matrix#set\n   * @function\n   * @param {Number} a - The value for element at the first column and first row\n   * @param {Number} b - The value for element at the second column and first row\n   * @param {Number} c - The value for element at the third column and first row\n   * @param {Number} d - The value for element at the first column and second row\n   * @param {Number} e - The value for element at the second column and second row\n   * @param {Number} f - The value for element at the third column and second row\n   * @param {Number} g - The value for element at the first column and third row\n   * @param {Number} h - The value for element at the second column and third row\n   * @param {Number} i - The value for element at the third column and third row\n   * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n   */\n\n  /**\n   * @name Two.Matrix#set\n   * @function\n   * @param {Number[]} a - The array of elements to apply\n   * @description Set an array of values onto the matrix. Order described in {@link Two.Matrix}.\n   */\n  set(a, b, c, d, e, f, g, h, i) {\n    if (typeof b === 'undefined') {\n      const elements = a;\n      a = elements[0];\n      b = elements[1];\n      c = elements[2];\n      d = elements[3];\n      e = elements[4];\n      f = elements[5];\n      g = elements[6];\n      h = elements[7];\n      i = elements[8];\n    }\n\n    this.elements[0] = a;\n    this.elements[1] = b;\n    this.elements[2] = c;\n    this.elements[3] = d;\n    this.elements[4] = e;\n    this.elements[5] = f;\n    this.elements[6] = g;\n    this.elements[7] = h;\n    this.elements[8] = i;\n\n    return this.trigger(Events.Types.change);\n  }\n\n  /**\n   * @name Two.Matrix#copy\n   * @function\n   * @param {Two.Matrix} m - The matrix to copy\n   * @description Copy the matrix of one to the current instance.\n   */\n  copy(m) {\n    this.elements[0] = m.elements[0];\n    this.elements[1] = m.elements[1];\n    this.elements[2] = m.elements[2];\n    this.elements[3] = m.elements[3];\n    this.elements[4] = m.elements[4];\n    this.elements[5] = m.elements[5];\n    this.elements[6] = m.elements[6];\n    this.elements[7] = m.elements[7];\n    this.elements[8] = m.elements[8];\n\n    this.manual = m.manual;\n\n    return this.trigger(Events.Types.change);\n  }\n\n  /**\n   * @name Two.Matrix#identity\n   * @function\n   * @description Turn matrix to the identity, like resetting.\n   */\n  identity() {\n    this.elements[0] = Matrix.Identity[0];\n    this.elements[1] = Matrix.Identity[1];\n    this.elements[2] = Matrix.Identity[2];\n    this.elements[3] = Matrix.Identity[3];\n    this.elements[4] = Matrix.Identity[4];\n    this.elements[5] = Matrix.Identity[5];\n    this.elements[6] = Matrix.Identity[6];\n    this.elements[7] = Matrix.Identity[7];\n    this.elements[8] = Matrix.Identity[8];\n\n    return this.trigger(Events.Types.change);\n  }\n\n  /**\n   * @name Two.Matrix#multiply\n   * @function\n   * @param {Number} s - The scalar to be multiplied.\n   * @description Multiply all components of the matrix against a single scalar value.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Matrix#multiply\n   * @function\n   * @param {Number} x - The `x` component to be multiplied.\n   * @param {Number} y - The `y` component to be multiplied.\n   * @param {Number} z - The `z` component to be multiplied.\n   * @description Multiply all components of a matrix against a 3 component vector.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Matrix#multiply\n   * @function\n   * @param {Number} a - The value at the first column and first row of the matrix to be multiplied.\n   * @param {Number} b - The value at the second column and first row of the matrix to be multiplied.\n   * @param {Number} c - The value at the third column and first row of the matrix to be multiplied.\n   * @param {Number} d - The value at the first column and second row of the matrix to be multiplied.\n   * @param {Number} e - The value at the second column and second row of the matrix to be multiplied.\n   * @param {Number} f - The value at the third column and second row of the matrix to be multiplied.\n   * @param {Number} g - The value at the first column and third row of the matrix to be multiplied.\n   * @param {Number} h - The value at the second column and third row of the matrix to be multiplied.\n   * @param {Number} i - The value at the third column and third row of the matrix to be multiplied.\n   * @description Multiply all components of a matrix against another matrix.\n   * @overloaded\n   */\n  multiply(a, b, c, d, e, f, g, h, i) {\n    // Multiply scalar\n\n    if (typeof b === 'undefined') {\n      this.elements[0] *= a;\n      this.elements[1] *= a;\n      this.elements[2] *= a;\n      this.elements[3] *= a;\n      this.elements[4] *= a;\n      this.elements[5] *= a;\n      this.elements[6] *= a;\n      this.elements[7] *= a;\n      this.elements[8] *= a;\n\n      return this.trigger(Events.Types.change);\n    }\n\n    if (typeof c === 'undefined') {\n      c = 1;\n    }\n\n    if (typeof d === 'undefined') {\n      // Multiply Vector\n\n      a = a || 0;\n      b = b || 0;\n      c = c || 0;\n      e = this.elements;\n\n      // Go down rows first\n      // a, d, g, b, e, h, c, f, i\n\n      const x = e[0] * a + e[1] * b + e[2] * c;\n      const y = e[3] * a + e[4] * b + e[5] * c;\n      const z = e[6] * a + e[7] * b + e[8] * c;\n\n      return [x, y, z];\n    }\n\n    // Multiple matrix\n\n    const A = this.elements;\n    const B = [a, b, c, d, e, f, g, h, i];\n\n    const A0 = A[0],\n      A1 = A[1],\n      A2 = A[2];\n    const A3 = A[3],\n      A4 = A[4],\n      A5 = A[5];\n    const A6 = A[6],\n      A7 = A[7],\n      A8 = A[8];\n\n    const B0 = B[0],\n      B1 = B[1],\n      B2 = B[2];\n    const B3 = B[3],\n      B4 = B[4],\n      B5 = B[5];\n    const B6 = B[6],\n      B7 = B[7],\n      B8 = B[8];\n\n    this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6;\n    this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7;\n    this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8;\n\n    this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6;\n    this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7;\n    this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8;\n\n    this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6;\n    this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7;\n    this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8;\n\n    return this.trigger(Events.Types.change);\n  }\n\n  /**\n   * @name Two.Matrix#inverse\n   * @function\n   * @param {Two.Matrix} [output] - The optional matrix to apply the inversion to.\n   * @description Return an inverted version of the matrix. If no optional one is passed a new matrix is created and returned.\n   */\n  inverse(output) {\n    const a = this.elements;\n    output = output || new Matrix();\n\n    const a00 = a[0],\n      a01 = a[1],\n      a02 = a[2];\n    const a10 = a[3],\n      a11 = a[4],\n      a12 = a[5];\n    const a20 = a[6],\n      a21 = a[7],\n      a22 = a[8];\n\n    const b01 = a22 * a11 - a12 * a21;\n    const b11 = -a22 * a10 + a12 * a20;\n    const b21 = a21 * a10 - a11 * a20;\n\n    // Calculate the determinant\n    let det = a00 * b01 + a01 * b11 + a02 * b21;\n\n    if (!det) {\n      return null;\n    }\n\n    det = 1.0 / det;\n\n    output.elements[0] = b01 * det;\n    output.elements[1] = (-a22 * a01 + a02 * a21) * det;\n    output.elements[2] = (a12 * a01 - a02 * a11) * det;\n    output.elements[3] = b11 * det;\n    output.elements[4] = (a22 * a00 - a02 * a20) * det;\n    output.elements[5] = (-a12 * a00 + a02 * a10) * det;\n    output.elements[6] = b21 * det;\n    output.elements[7] = (-a21 * a00 + a01 * a20) * det;\n    output.elements[8] = (a11 * a00 - a01 * a10) * det;\n\n    return output;\n  }\n\n  /**\n   * @name Two.Matrix#scale\n   * @function\n   * @param {Number} s - The one dimensional scale to apply to the matrix.\n   * @description Uniformly scale the transformation matrix.\n   */\n\n  /**\n   * @name Two.Matrix#scale\n   * @function\n   * @param {Number} sx - The horizontal scale factor.\n   * @param {Number} sy - The vertical scale factor\n   * @description Scale the transformation matrix in two dimensions.\n   */\n  scale(sx, sy) {\n    const l = arguments.length;\n    if (l <= 1) {\n      sy = sx;\n    }\n\n    return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1);\n  }\n\n  /**\n   * @name Two.Matrix#rotate\n   * @function\n   * @param {Number} n - The amount to rotate in Number.\n   * @description Rotate the matrix.\n   */\n  rotate(n) {\n    const c = cos(n);\n    const s = sin(n);\n\n    return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1);\n  }\n\n  /**\n   * @name Two.Matrix#translate\n   * @function\n   * @param {Number} x - The horizontal translation value to apply\n   * @param {Number} y - The vertical translation value to apply\n   * @description Translate the matrix to specific `x` / `y` values.\n   */\n  translate(x, y) {\n    return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1);\n  }\n\n  /**\n   * @name Two.Matrix#skewX\n   * @function\n   * @param {Number} n - The amount to skew\n   * @description Skew the matrix by an angle in the x axis direction.\n   */\n  skewX(n) {\n    const a = tan(n);\n\n    return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1);\n  }\n\n  /**\n   * @name Two.Matrix#skewY\n   * @function\n   * @param {Number} n - The amount to skew\n   * @description Skew the matrix by an angle in the y axis direction.\n   */\n  skewY(n) {\n    const a = tan(n);\n\n    return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1);\n  }\n\n  /**\n   * @name Two.Matrix#toString\n   * @function\n   * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n   * @returns {String} - The transformation matrix as a 6 component string separated by spaces.\n   * @description Create a transform string. Used for the Two.js rendering APIs.\n   */\n  toString(fullMatrix) {\n    array.length = 0;\n    this.toTransformArray(fullMatrix, array);\n\n    return array.map(toFixed).join(' ');\n  }\n\n  /**\n   * @name Two.Matrix#toTransformArray\n   * @function\n   * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 in the format for 2D transformations.\n   * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n   * @description Create a transform array. Used for the Two.js rendering APIs.\n   */\n  toTransformArray(fullMatrix, output) {\n    const elements = this.elements;\n    const hasOutput = !!output;\n\n    const a = elements[0];\n    const b = elements[1];\n    const c = elements[2];\n    const d = elements[3];\n    const e = elements[4];\n    const f = elements[5];\n\n    if (fullMatrix) {\n      const g = elements[6];\n      const h = elements[7];\n      const i = elements[8];\n\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = d;\n        output[2] = g;\n        output[3] = b;\n        output[4] = e;\n        output[5] = h;\n        output[6] = c;\n        output[7] = f;\n        output[8] = i;\n        return;\n      }\n\n      return [a, d, g, b, e, h, c, f, i];\n    }\n\n    if (hasOutput) {\n      output[0] = a;\n      output[1] = d;\n      output[2] = b;\n      output[3] = e;\n      output[4] = c;\n      output[5] = f;\n      return;\n    }\n\n    return [\n      a,\n      d,\n      b,\n      e,\n      c,\n      f, // Specific format see LN:19\n    ];\n  }\n\n  /**\n   * @name Two.Matrix#toArray\n   * @function\n   * @param {Boolean} [fullMatrix=false] - Return the full 9 elements of the matrix or just 6 for 2D transformations.\n   * @param {Number[]} [output] - An array empty or otherwise to apply the values to.\n   * @description Create a transform array. Used for the Two.js rendering APIs.\n   */\n  toArray(fullMatrix, output) {\n    const elements = this.elements;\n    const hasOutput = !!output;\n\n    const a = elements[0];\n    const b = elements[1];\n    const c = elements[2];\n    const d = elements[3];\n    const e = elements[4];\n    const f = elements[5];\n\n    if (fullMatrix) {\n      const g = elements[6];\n      const h = elements[7];\n      const i = elements[8];\n\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = b;\n        output[2] = c;\n        output[3] = d;\n        output[4] = e;\n        output[5] = f;\n        output[6] = g;\n        output[7] = h;\n        output[8] = i;\n        return;\n      }\n\n      return [a, b, c, d, e, f, g, h, i];\n    }\n\n    if (hasOutput) {\n      output[0] = a;\n      output[1] = b;\n      output[2] = c;\n      output[3] = d;\n      output[4] = e;\n      output[5] = f;\n      return;\n    }\n\n    return [a, b, c, d, e, f];\n  }\n\n  /**\n   * @name Two.Matrix#toObject\n   * @function\n   * @description Create a JSON compatible object that represents information of the matrix.\n   * @nota-bene Works in conjunction with {@link Two.Matrix.fromObject}\n   */\n  toObject() {\n    return {\n      renderer: { type: 'matrix' },\n      elements: this.toArray(true),\n      manual: !!this.manual,\n    };\n  }\n\n  /**\n   * @name Two.Matrix#clone\n   * @function\n   * @description Clone the current matrix.\n   */\n  clone() {\n    return new Matrix().copy(this);\n  }\n}\n\nsetMatrix(Matrix);\n"
  },
  {
    "path": "src/path.d.ts",
    "content": "declare module 'two.js/src/path' {\n  export type CapProperties = 'butt' | 'round' | 'square';\n  export type JoinProperties = 'miter' | 'round' | 'bevel';\n  /**\n   * @name Two.Path\n   * @class\n   * @extends Two.Shape\n   * @param {Two.Anchor[]} [vertices] - A list of {@link Two.Anchor}s that represent the order and coordinates to construct the rendered shape.\n   * @param {Boolean} [closed=false] - Describes whether the shape is closed or open.\n   * @param {Boolean} [curved=false] - Describes whether the shape automatically calculates bezier handles for each vertex.\n   * @param {Boolean} [manual=false] - Describes whether the developer controls how vertices are plotted or if Two.js automatically plots coordinates based on closed and curved booleans.\n   * @description This is the primary primitive class for creating all drawable shapes in Two.js. Unless specified methods return their instance of `Two.Path` for the purpose of chaining.\n   */\n  export class Path extends Shape {\n    /**\n     * @name Two.Path.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Path}.\n     */\n    static Properties: string[];\n    static Utils: {\n      getCurveLength: (\n        a: Anchor | Vector,\n        b: Anchor | Vector,\n        limit: number\n      ) => number;\n    };\n    /**\n     * @name Two.Path.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Path} to create a new instance\n     * @returns {Two.Path}\n     * @description Create a new {@link Two.Path} from an object notation of a {@link Two.Path}.\n     * @nota-bene Works in conjunction with {@link Two.Path#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Shape.fromObject>[0] & {\n        vertices?: ({ x: number; y: number } | Anchor | Vector)[];\n        fill?: string;\n        stroke?: string;\n        linewidth?: number;\n        opacity?: number;\n        visible?: boolean;\n        cap?: CapProperties;\n        join?: JoinProperties;\n        miter?: number;\n        closed?: boolean;\n        curved?: boolean;\n        automatic?: boolean;\n        beginning?: number;\n        ending?: number;\n        dashes?: number[] & {\n          offset?: number;\n        };\n      }\n    ): Path;\n    constructor(\n      vertices?: Anchor[],\n      closed?: boolean,\n      curved?: boolean,\n      manual?: boolean\n    );\n    /**\n     * @name Two.Path#_flagVertices\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#vertices} need updating.\n     */\n    private _flagVertices;\n    /**\n     * @name Two.Path#_flagLength\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#length} needs updating.\n     */\n    private _flagLength;\n    /**\n     * @name Two.Path#_flagFill\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#fill} needs updating.\n     */\n    private _flagFill;\n    /**\n     * @name Two.Path#_flagStroke\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#stroke} needs updating.\n     */\n    private _flagStroke;\n    /**\n     * @name Two.Path#_flagLinewidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#linewidth} needs updating.\n     */\n    private _flagLinewidth;\n    /**\n     * @name Two.Path#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#opacity} needs updating.\n     */\n    private _flagOpacity;\n    /**\n     * @name Two.Path#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#visible} needs updating.\n     */\n    private _flagVisible;\n    /**\n     * @name Two.Path#_flagCap\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#cap} needs updating.\n     */\n    private _flagCap;\n    /**\n     * @name Two.Path#_flagJoin\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#join} needs updating.\n     */\n    private _flagJoin;\n    /**\n     * @name Two.Path#_flagMiter\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#miter} needs updating.\n     */\n    private _flagMiter;\n    /**\n     * @name Two.Path#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#mask} needs updating.\n     */\n    private _flagMask;\n    /**\n     * @name Two.Path#_flagClip\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#clip} needs updating.\n     */\n    private _flagClip;\n    /**\n     * @name Two.Path#_length\n     * @private\n     * @see {@link Two.Path#length}\n     */\n    private _length;\n    /**\n     * @name Two.Path#_fill\n     * @private\n     * @see {@link Two.Path#fill}\n     */\n    private _fill;\n    /**\n     * @name Two.Path#_stroke\n     * @private\n     * @see {@link Two.Path#stroke}\n     */\n    private _stroke;\n    /**\n     * @name Two.Path#_linewidth\n     * @private\n     * @see {@link Two.Path#linewidth}\n     */\n    private _linewidth;\n    /**\n     * @name Two.Path#_opacity\n     * @private\n     * @see {@link Two.Path#opacity}\n     */\n    private _opacity;\n    /**\n     * @name Two.Path#_visible\n     * @private\n     * @see {@link Two.Path#visible}\n     */\n    private _visible;\n    /**\n     * @name Two.Path#_cap\n     * @private\n     * @see {@link Two.Path#cap}\n     */\n    private _cap;\n    /**\n     * @name Two.Path#_join\n     * @private\n     * @see {@link Two.Path#join}\n     */\n    private _join;\n    /**\n     * @name Two.Path#_miter\n     * @private\n     * @see {@link Two.Path#miter}\n     */\n    private _miter;\n    /**\n     * @name Two.Path#_closed\n     * @private\n     * @see {@link Two.Path#closed}\n     */\n    private _closed;\n    /**\n     * @name Two.Path#_curved\n     * @private\n     * @see {@link Two.Path#curved}\n     */\n    private _curved;\n    /**\n     * @name Two.Path#_automatic\n     * @private\n     * @see {@link Two.Path#automatic}\n     */\n    private _automatic;\n    /**\n     * @name Two.Path#_beginning\n     * @private\n     * @see {@link Two.Path#beginning}\n     */\n    private _beginning;\n    /**\n     * @name Two.Path#_ending\n     * @private\n     * @see {@link Two.Path#ending}\n     */\n    private _ending;\n    /**\n     * @name Two.Path#_mask\n     * @private\n     * @see {@link Two.Path#mask}\n     */\n    private _mask;\n    /**\n     * @name Two.Path#_clip\n     * @private\n     * @see {@link Two.Path#clip}\n     */\n    private _clip;\n    /**\n     * @name Two.Path#_dashes\n     * @private\n     * @see {@link Two.Path#dashes}\n     */\n    private _dashes;\n    /**\n     * @name Two.Path#_strokeAttenuation\n     * @private\n     * @see {@link Two.Path#strokeAttenuation}\n     */\n    private _strokeAttenuation;\n    /**\n     * @name Two.Path#closed\n     * @property {Boolean} - Determines whether a final line is drawn between the final point in the `vertices` array and the first point.\n     */\n    closed: boolean;\n    /**\n     * @name Two.Path#curved\n     * @property {Boolean} - When the path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n     */\n    curved: boolean;\n    /**\n     * @name Two.Path#beginning\n     * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n     * @description {@link Two.Path#beginning} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Path#ending}.\n     */\n    beginning: number;\n    /**\n     * @name Two.Path#ending\n     * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n     * @description {@link Two.Path#ending} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Path#beginning}.\n     */\n    ending: number;\n    /**\n     * @name Two.Path#fill\n     * @property {(String|Gradient|Texture)} - The value of what the path should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    fill: string | Gradient | Texture;\n    /**\n     * @name Two.Path#stroke\n     * @property {(String|Gradient|Texture)} - The value of what the path should be outlined in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    stroke: string | Gradient | Texture;\n    /**\n     * @name Two.Path#linewidth\n     * @property {Number} - The thickness in pixels of the stroke.\n     */\n    linewidth: number;\n    /**\n     * @name Two.Path#opacity\n     * @property {Number} - The opaqueness of the path.\n     * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n     */\n    opacity: number;\n    /**\n     * @name Two.Path#className\n     * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n     * @nota-bene Only available for the SVG renderer.\n     */\n    className: string;\n    /**\n     * @name Two.Path#visible\n     * @property {Boolean} - Display the path or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    visible: boolean;\n    /**\n     * @name Two.Path#cap\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty}\n     */\n    cap: CapProperties;\n    /**\n     * @name Two.Path#join\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty}\n     */\n    join: JoinProperties;\n    /**\n     * @name Two.Path#miter\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty}\n     */\n    miter: number;\n    /**\n     * @name Two.Path#vertices\n     * @property {Anchor[]} - An ordered list of anchor points for rendering the path.\n     * @description A list of {@link Two.Anchor} objects that consist of what form the path takes.\n     * @nota-bene The array when manipulating is actually a {@link Two.Collection}.\n     */\n    vertices: Anchor[];\n    /**\n     * @name Two.Path#automatic\n     * @property {Boolean} - Determines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n     */\n    automatic: boolean;\n    /**\n     * @name Two.Path#dashes\n     * @type {number[] & { offset?: number }}\n     * @property {Number[]} - Array of numbers. Odd indices represent dash length. Even indices represent dash space.\n     * @description A list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray} for more information on the SVG stroke-dasharray attribute.\n     */\n    dashes: number[] & {\n      offset?: number;\n    };\n    /**\n     * @name Two.Path#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n     * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n     */\n    strokeAttenuation: boolean;\n    /**\n     * @name Two.Path#copy\n     * @function\n     * @param {Two.Path} path - The reference {@link Two.Path}\n     * @description Copy the properties of one {@link Two.Path} onto another.\n     */\n    copy(path: Path): Path;\n    /**\n     * @name Two.Path#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Path}\n     * @description Create a new instance of {@link Two.Path} with the same properties of the current path.\n     */\n    clone(parent?: Group): Path;\n    /**\n     * @name Two.Path#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the path.\n     */\n    toObject(): object;\n    /**\n     * @name Two.Path#dispose\n     * @function\n     * @description Release the path's renderer resources and detach all events.\n     * This method cleans up vertices collection events, individual vertex events,\n     * control point events, and disposes fill/stroke effects (calling dispose()\n     * on Gradients and Textures for thorough cleanup) while preserving the\n     * renderer type for potential re-attachment to a new renderer.\n     */\n    dispose(): Path;\n    /**\n     * @name Two.Path#noFill\n     * @function\n     * @description Short hand method to set fill to `none`.\n     */\n    noFill(): Path;\n    /**\n     * @name Two.Path#noStroke\n     * @function\n     * @description Short hand method to set stroke to `none`.\n     */\n    noStroke(): Path;\n    /**\n     * @name Two.Path#corner\n     * @function\n     * @description Orient the vertices of the shape to the upper left-hand corner of the path.\n     */\n    corner(): Path;\n    /**\n     * @name Two.Path#center\n     * @function\n     * @description Orient the vertices of the shape to the center of the path.\n     */\n    center(): Path;\n    /**\n     * @name Two.Path#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n     */\n    getBoundingClientRect(shallow?: boolean): BoundingBox;\n    /**\n     * @name Two.Path#getPointAt\n     * @function\n     * @param {Number} t - Percentage value describing where on the {@link Two.Path} to estimate and assign coordinate values.\n     * @param {Vector} [obj] - Object to apply calculated x, y to. If none available returns new `Object`.\n     * @returns {Object}\n     * @description Given a float `t` from 0 to 1, return a point or assign a passed `obj`'s coordinates to that percentage on this {@link Two.Path}'s curve.\n     */\n    getPointAt(t: number, obj?: Vector): Vector;\n    /**\n     * @name Two.Path#plot\n     * @function\n     * @description Based on closed / curved and sorting of vertices plot where all points should be and where the respective handles should be too.\n     * @nota-bene While this method is public it is internally called by {@link Two.Path#_update} when `automatic = true`.\n     */\n    plot(): Path;\n    /**\n     * @name Two.Path#smooth\n     * @function\n     * @param {Object} [options] - Configuration for smoothing.\n     * @param {String} [options.type='continuous'] - Type of smoothing algorithm.\n     * @param {Number} [options.from=0] - Index of vertices to start smoothing\n     * @param {Number} [options.to=1] - Index of vertices to terminate smoothing\n     * @description Adjust vertex handles to generate smooth curves without toggling `automatic`.\n     */\n    smooth(): Path;\n    /**\n     * @name Two.Path#subdivide\n     * @function\n     * @param {Number} limit - How many times to recurse subdivisions.\n     * @description Insert a {@link Two.Anchor} at the midpoint between every item in {@link Two.Path#vertices}.\n     */\n    subdivide(limit: number): Path;\n    /**\n     * @name Two.Path#_updateLength\n     * @function\n     * @private\n     * @param {Number} [limit] -\n     * @param {Boolean} [silent=false] - If set to `true` then the path isn't updated before calculation. Useful for internal use.\n     * @description Recalculate the {@link Two.Path#length} value.\n     */\n    private _updateLength(limit: number, silent?: boolean): Path;\n    private _lengths: number[];\n    /**\n     * @name Two.Path#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    protected _update(bubbles?: boolean): Path;\n    /**\n     * @name Two.Path#contains\n     * @function\n     * @param {Number} x - x coordinate to hit test against\n     * @param {Number} y - y coordinate to hit test against\n     * @param {Object} [options] - Optional options object\n     * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `path.visible = false` shapes\n     * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n     * @returns {Boolean}\n     * @description Check to see if coordinates are within a {@link Two.Path}'s bounding rectangle\n     * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n     */\n    contains(\n      x: number,\n      y: number,\n      options?: { ignoreVisibility: boolean; tolerance: number }\n    ): boolean;\n    /**\n     * @name Two.Path#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Path;\n  }\n  import { Vector } from 'two.js/src/vector';\n  import { Anchor } from 'two.js/src/anchor';\n  import { Shape } from 'two.js/src/shape';\n  import { Gradient } from 'two.js/src/effects/gradient';\n  import { Group } from 'two.js/src/group';\n  import { Texture } from 'two.js/src/effects/texture';\n  import { BoundingBox } from 'two.js';\n  /**\n   * @name FlagVertices\n   * @private\n   * @function\n   * @description Cached method to let renderers know vertices have been updated on a {@link Two.Path}.\n   */\n  export function FlagVertices(): void;\n  export class FlagVertices {\n    _flagVertices: boolean;\n    _flagLength: boolean;\n  }\n  /**\n   * @name BindVertices\n   * @private\n   * @function\n   * @description Cached method to let {@link Two.Path} know vertices have been added to the instance.\n   */\n  export function BindVertices(items: Anchor[]): void;\n  /**\n   * @name UnbindVertices\n   * @private\n   * @function\n   * @description Cached method to let {@link Two.Path} know vertices have been removed from the instance.\n   */\n  export function UnbindVertices(items: Anchor[]): void;\n  /**\n   * @name FlagFill\n   * @private\n   * @function\n   * @description Cached method to let {@link Two.Path} know the fill has changed.\n   */\n  export function FlagFill(): void;\n  export class FlagFill {\n    _flagFill: boolean;\n  }\n  /**\n   * @name FlagFill\n   * @private\n   * @function\n   * @description Cached method to let {@link Two.Path} know the stroke has changed.\n   */\n  export function FlagStroke(): void;\n  export class FlagStroke {\n    _flagStroke: boolean;\n  }\n}\n"
  },
  {
    "path": "src/path.js",
    "content": "import { Commands } from './utils/path-commands.js';\nimport { Collection } from './collection.js';\nimport { lerp, mod, decomposeMatrix } from './utils/math.js';\nimport {\n  getComponentOnCubicBezier,\n  getCurveBoundingBox,\n  getCurveFromPoints,\n} from './utils/curves.js';\nimport {\n  contains,\n  getIdByLength,\n  getCurveLength,\n  getSubdivisions,\n  getEffectFromObject,\n} from './utils/shape.js';\nimport { _ } from './utils/underscore.js';\n\nimport { Shape } from './shape.js';\nimport { Events } from './events.js';\nimport { Vector } from './vector.js';\nimport { Anchor } from './anchor.js';\nimport { Matrix } from './matrix.js';\n\nimport { Gradient } from './effects/gradient.js';\nimport { LinearGradient } from './effects/linear-gradient.js';\nimport { RadialGradient } from './effects/radial-gradient.js';\nimport { Texture } from './effects/texture.js';\nimport {\n  buildPathHitParts,\n  pointInPolygons,\n  distanceToSegments,\n  hasVisibleFill,\n  hasVisibleStroke,\n} from './utils/hit-test.js';\nimport {\n  clearHandleComponent,\n  setHandleComponent,\n  inheritRelative,\n  isSegmentCurved,\n  splitSubdivisionSegment,\n  applyGlobalSmooth,\n  applyLocalSmooth,\n} from './utils/path.js';\n\n// Constants\n\nconst min = Math.min,\n  max = Math.max,\n  ceil = Math.ceil,\n  floor = Math.floor;\n\nconst vector = new Vector();\nconst hitTestMatrix = new Matrix();\n\n/**\n * @name Two.Path\n * @class\n * @extends Two.Shape\n * @param {Two.Anchor[]} [vertices] - A list of {@link Two.Anchor}s that represent the order and coordinates to construct the rendered shape.\n * @param {Boolean} [closed=false] - Describes whether the shape is closed or open.\n * @param {Boolean} [curved=false] - Describes whether the shape automatically calculates bezier handles for each vertex.\n * @param {Boolean} [manual=false] - Describes whether the developer controls how vertices are plotted or if Two.js automatically plots coordinates based on closed and curved booleans.\n * @description This is the primary primitive class for creating all drawable shapes in Two.js. Unless specified methods return their instance of `Two.Path` for the purpose of chaining.\n */\nexport class Path extends Shape {\n  /**\n   * @name Two.Path#_flagVertices\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#vertices} need updating.\n   */\n  _flagVertices = true;\n\n  /**\n   * @name Two.Path#_flagLength\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#length} needs updating.\n   */\n  _flagLength = true;\n\n  /**\n   * @name Two.Path#_flagFill\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#fill} needs updating.\n   */\n  _flagFill = true;\n\n  /**\n   * @name Two.Path#_flagStroke\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#stroke} needs updating.\n   */\n  _flagStroke = true;\n\n  /**\n   * @name Two.Path#_flagLinewidth\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#linewidth} needs updating.\n   */\n  _flagLinewidth = true;\n\n  /**\n   * @name Two.Path#_flagOpacity\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#opacity} needs updating.\n   */\n  _flagOpacity = true;\n\n  /**\n   * @name Two.Path#_flagVisible\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#visible} needs updating.\n   */\n  _flagVisible = true;\n\n  /**\n   * @name Two.Path#_flagCap\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#cap} needs updating.\n   */\n  _flagCap = true;\n\n  /**\n   * @name Two.Path#_flagJoin\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#join} needs updating.\n   */\n  _flagJoin = true;\n\n  /**\n   * @name Two.Path#_flagMiter\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#miter} needs updating.\n   */\n  _flagMiter = true;\n\n  /**\n   * @name Two.Path#_flagStrokeAttenuation\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#strokeAttenuation} needs updating.\n   */\n  _flagStrokeAttenuation = true;\n\n  /**\n   * @name Two.Path#_flagMask\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#mask} needs updating.\n   */\n  _flagMask = false;\n\n  /**\n   * @name Two.Path#_flagClip\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Path#clip} needs updating.\n   */\n  _flagClip = false;\n\n  // Underlying Properties\n\n  /**\n   * @name Two.Path#_length\n   * @private\n   * @see {@link Two.Path#length}\n   */\n  _length = 0;\n\n  /**\n   * @name Two.Path#_fill\n   * @private\n   * @see {@link Two.Path#fill}\n   */\n  _fill = '#fff';\n\n  /**\n   * @name Two.Path#_stroke\n   * @private\n   * @see {@link Two.Path#stroke}\n   */\n  _stroke = '#000';\n\n  /**\n   * @name Two.Path#_linewidth\n   * @private\n   * @see {@link Two.Path#linewidth}\n   */\n  _linewidth = 1;\n\n  /**\n   * @name Two.Path#_opacity\n   * @private\n   * @see {@link Two.Path#opacity}\n   */\n  _opacity = 1.0;\n\n  /**\n   * @name Two.Path#_visible\n   * @private\n   * @see {@link Two.Path#visible}\n   */\n  _visible = true;\n\n  /**\n   * @name Two.Path#_cap\n   * @private\n   * @see {@link Two.Path#cap}\n   */\n  _cap = 'round';\n\n  /**\n   * @name Two.Path#_join\n   * @private\n   * @see {@link Two.Path#join}\n   */\n  _join = 'round';\n\n  /**\n   * @name Two.Path#_miter\n   * @private\n   * @see {@link Two.Path#miter}\n   */\n  _miter = 4;\n\n  /**\n   * @name Two.Path#_closed\n   * @private\n   * @see {@link Two.Path#closed}\n   */\n  _closed = true;\n\n  /**\n   * @name Two.Path#_curved\n   * @private\n   * @see {@link Two.Path#curved}\n   */\n  _curved = false;\n\n  /**\n   * @name Two.Path#_automatic\n   * @private\n   * @see {@link Two.Path#automatic}\n   */\n  _automatic = true;\n\n  /**\n   * @name Two.Path#_beginning\n   * @private\n   * @see {@link Two.Path#beginning}\n   */\n  _beginning = 0;\n\n  /**\n   * @name Two.Path#_ending\n   * @private\n   * @see {@link Two.Path#ending}\n   */\n  _ending = 1.0;\n\n  /**\n   * @name Two.Path#_mask\n   * @private\n   * @see {@link Two.Path#mask}\n   */\n  _mask = null;\n\n  /**\n   * @name Two.Path#_clip\n   * @private\n   * @see {@link Two.Path#clip}\n   */\n  _clip = false;\n\n  /**\n   * @name Two.Path#_dashes\n   * @private\n   * @see {@link Two.Path#dashes}\n   */\n  _dashes = null;\n\n  /**\n   * @name Two.Path#_strokeAttenuation\n   * @private\n   * @see {@link Two.Path#strokeAttenuation}\n   */\n  _strokeAttenuation = true;\n\n  constructor(vertices, closed, curved, manual) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'path';\n    this._renderer.flagVertices = FlagVertices.bind(this);\n    this._renderer.bindVertices = BindVertices.bind(this);\n    this._renderer.unbindVertices = UnbindVertices.bind(this);\n\n    this._renderer.flagFill = FlagFill.bind(this);\n    this._renderer.flagStroke = FlagStroke.bind(this);\n    this._renderer.vertices = [];\n    this._renderer.collection = [];\n\n    /**\n     * @name Two.Path#closed\n     * @property {Boolean} - Determines whether a final line is drawn between the final point in the `vertices` array and the first point.\n     */\n    this.closed = !!closed;\n\n    /**\n     * @name Two.Path#curved\n     * @property {Boolean} - When the path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n     */\n    this.curved = !!curved;\n\n    /**\n     * @name Two.Path#beginning\n     * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n     * @description {@link Two.Path#beginning} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Path#ending}.\n     */\n    this.beginning = 0;\n\n    /**\n     * @name Two.Path#ending\n     * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n     * @description {@link Two.Path#ending} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     * @nota-bene This is great for animating in and out stroked paths in conjunction with {@link Two.Path#beginning}.\n     */\n    this.ending = 1;\n\n    // Style properties\n\n    /**\n     * @name Two.Path#fill\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the path should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    this.fill = '#fff';\n\n    /**\n     * @name Two.Path#stroke\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the path should be outlined in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    this.stroke = '#000';\n\n    /**\n     * @name Two.Path#linewidth\n     * @property {Number} - The thickness in pixels of the stroke.\n     */\n    this.linewidth = 1;\n\n    /**\n     * @name Two.Path#opacity\n     * @property {Number} - The opaqueness of the path.\n     * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n     */\n    this.opacity = 1;\n\n    /**\n     * @name Two.Path#className\n     * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n     * @nota-bene Only rendered to DOM in the SVG renderer.\n     */\n    this.className = '';\n\n    /**\n     * @name Two.Path#visible\n     * @property {Boolean} - Display the path or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    this.visible = true;\n\n    /**\n     * @name Two.Path#cap\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty}\n     */\n    this.cap = 'butt'; // Default of Adobe Illustrator\n\n    /**\n     * @name Two.Path#join\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty}\n     */\n    this.join = 'miter'; // Default of Adobe Illustrator\n\n    /**\n     * @name Two.Path#miter\n     * @property {String}\n     * @see {@link https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty}\n     */\n    this.miter = 4; // Default of Adobe Illustrator\n\n    /**\n     * @name Two.Path#vertices\n     * @property {Two.Anchor[]} - An ordered list of anchor points for rendering the path.\n     * @description A list of {@link Two.Anchor} objects that consist of what form the path takes.\n     * @nota-bene The array when manipulating is actually a {@link Two.Collection}.\n     */\n    this.vertices = vertices;\n\n    /**\n     * @name Two.Path#automatic\n     * @property {Boolean} - Determines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n     */\n    this.automatic = !manual;\n\n    /**\n     * @name Two.Path#dashes\n     * @property {Number[]} - Array of numbers. Odd indices represent dash length. Even indices represent dash space.\n     * @description A list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray} for more information on the SVG stroke-dasharray attribute.\n     */\n    this.dashes = [];\n\n    /**\n     * @name Two.Path#dashes#offset\n     * @property {Number} - A number in pixels to offset {@link Two.Path#dashes} display.\n     */\n    this.dashes.offset = 0;\n  }\n\n  /**\n   * @name Two.Path.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Path}.\n   */\n  static Properties = [\n    'fill',\n    'stroke',\n    'linewidth',\n    'opacity',\n    'visible',\n    'cap',\n    'join',\n    'miter',\n    'closed',\n    'curved',\n    'automatic',\n    'beginning',\n    'ending',\n    'dashes',\n    'strokeAttenuation',\n  ];\n\n  static Utils = {\n    getCurveLength,\n  };\n\n  /**\n   * @name Two.Path.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Path} to create a new instance\n   * @returns {Two.Path}\n   * @description Create a new {@link Two.Path} from an object notation of a {@link Two.Path}.\n   * @nota-bene Works in conjunction with {@link Two.Path#toObject}\n   */\n  static fromObject(obj) {\n    const fill =\n      typeof obj.fill === 'string' ? obj.fill : getEffectFromObject(obj.fill);\n    const stroke =\n      typeof obj.stroke === 'string'\n        ? obj.stroke\n        : getEffectFromObject(obj.stroke);\n    const path = new Path().copy({ ...obj, fill, stroke });\n\n    if ('id' in obj) {\n      path.id = obj.id;\n    }\n\n    return path;\n  }\n\n  /**\n   * @name Two.Path#copy\n   * @function\n   * @param {Two.Path} path - The reference {@link Two.Path}\n   * @description Copy the properties of one {@link Two.Path} onto another.\n   */\n  copy(path) {\n    super.copy.call(this, path);\n\n    if (path.vertices) {\n      this.vertices = [];\n      for (let j = 0; j < path.vertices.length; j++) {\n        const v = path.vertices[j];\n        if (v instanceof Anchor) {\n          this.vertices.push(path.vertices[j].clone());\n        } else {\n          this.vertices.push(new Anchor().copy(v));\n        }\n      }\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      if (k in path) {\n        this[k] = path[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Path}\n   * @description Create a new instance of {@link Two.Path} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new Path();\n\n    for (let j = 0; j < this.vertices.length; j++) {\n      clone.vertices.push(this.vertices[j].clone());\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    clone.className = this.className;\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone._update();\n  }\n\n  /**\n   * @name Two.Path#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   * @nota-bene Works in conjunction with {@link Two.Path.fromObject}\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n\n    result.renderer.type = 'path';\n    result.vertices = this.vertices.map((v) => v.toObject());\n\n    _.each(\n      Path.Properties,\n      (k) => {\n        if (typeof this[k] !== 'undefined') {\n          if (this[k].toObject) {\n            result[k] = this[k].toObject();\n          } else {\n            result[k] = this[k];\n          }\n        }\n      },\n      this\n    );\n\n    return result;\n  }\n\n  /**\n   * @name Two.Path#dispose\n   * @function\n   * @returns {Two.Path}\n   * @description Release the path's renderer resources and detach all events.\n   * This method cleans up vertices collection events, individual vertex events,\n   * control point events, and disposes fill/stroke effects (calling dispose()\n   * on Gradients and Textures for thorough cleanup) while preserving the\n   * renderer type for potential re-attachment to a new renderer.\n   */\n  dispose() {\n    // Call parent dispose to preserve renderer type and unbind events\n    super.dispose();\n\n    // Unbind vertices collection events\n    if (this.vertices && typeof this.vertices.unbind === 'function') {\n      try {\n        this.vertices.unbind();\n      } catch (e) {\n        // Ignore unbind errors for incomplete Collection objects\n      }\n    }\n\n    // Unbind individual vertex events and control point events\n    if (this.vertices) {\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        if (typeof v.unbind === 'function') {\n          v.unbind();\n        }\n        if (v.controls) {\n          if (v.controls.left && typeof v.controls.left.unbind === 'function') {\n            v.controls.left.unbind();\n          }\n          if (\n            v.controls.right &&\n            typeof v.controls.right.unbind === 'function'\n          ) {\n            v.controls.right.unbind();\n          }\n        }\n      }\n    }\n\n    // Dispose fill effect (more thorough than unbind)\n    if (\n      typeof this.fill === 'object' &&\n      typeof this.fill.dispose === 'function'\n    ) {\n      this.fill.dispose();\n    } else if (\n      typeof this.fill === 'object' &&\n      typeof this.fill.unbind === 'function'\n    ) {\n      this.fill.unbind();\n    }\n\n    // Dispose stroke effect (more thorough than unbind)\n    if (\n      typeof this.stroke === 'object' &&\n      typeof this.stroke.dispose === 'function'\n    ) {\n      this.stroke.dispose();\n    } else if (\n      typeof this.stroke === 'object' &&\n      typeof this.stroke.unbind === 'function'\n    ) {\n      this.stroke.unbind();\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#noFill\n   * @function\n   * @description Short hand method to set fill to `none`.\n   */\n  noFill() {\n    this.fill = 'none';\n    return this;\n  }\n\n  /**\n   * @name Two.Path#noStroke\n   * @function\n   * @description Short hand method to set stroke to `none`.\n   */\n  noStroke() {\n    this.stroke = 'none';\n    this.linewidth = 0;\n    return this;\n  }\n\n  /**\n   * @name Two.Path#corner\n   * @function\n   * @description Orient the vertices of the shape to the upper left-hand corner of the path.\n   */\n  corner() {\n    const rect = this.getBoundingClientRect(true);\n    const hw = rect.width / 2;\n    const hh = rect.height / 2;\n    const cx = rect.left + rect.width / 2;\n    const cy = rect.top + rect.height / 2;\n\n    for (let i = 0; i < this.vertices.length; i++) {\n      const v = this.vertices[i];\n      v.x -= cx;\n      v.y -= cy;\n      v.x += hw;\n      v.y += hh;\n    }\n\n    if (this.mask) {\n      this.mask.translation.x -= cx;\n      this.mask.translation.x += hw;\n      this.mask.translation.y -= cy;\n      this.mask.translation.y += hh;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#center\n   * @function\n   * @description Orient the vertices of the shape to the center of the path.\n   */\n  center() {\n    const rect = this.getBoundingClientRect(true);\n\n    const cx = rect.left + rect.width / 2 - this.translation.x;\n    const cy = rect.top + rect.height / 2 - this.translation.y;\n\n    for (let i = 0; i < this.vertices.length; i++) {\n      const v = this.vertices[i];\n      v.x -= cx;\n      v.y -= cy;\n    }\n\n    if (this.mask) {\n      this.mask.translation.x -= cx;\n      this.mask.translation.y -= cy;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#getBoundingClientRect\n   * @function\n   * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n   * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n   * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n   */\n  getBoundingClientRect(shallow) {\n    let matrix, border, l, i, v0, v1;\n\n    let left = Infinity,\n      right = -Infinity,\n      top = Infinity,\n      bottom = -Infinity;\n\n    // TODO: Update this to not __always__ update. Just when it needs to.\n    this._update(true);\n\n    matrix = shallow ? this.matrix : this.worldMatrix;\n\n    border = (this.linewidth || 0) / 2;\n    l = this._renderer.vertices.length;\n\n    if (\n      this.linewidth > 0 ||\n      (this.stroke && !/(transparent|none)/i.test(this.stroke))\n    ) {\n      if (this.matrix.manual) {\n        const { scaleX, scaleY } = decomposeMatrix(\n          matrix.elements[0],\n          matrix.elements[3],\n          matrix.elements[1],\n          matrix.elements[4],\n          matrix.elements[2],\n          matrix.elements[5]\n        );\n        if (typeof scaleX === 'number' && typeof scaleY === 'number') {\n          border = (Math.max(scaleX, scaleY) * (this.linewidth || 0)) / 2;\n        }\n      } else {\n        border *=\n          typeof this.scale === 'number'\n            ? this.scale\n            : Math.max(this.scale.x, this.scale.y);\n      }\n    }\n\n    if (l <= 0) {\n      return {\n        width: 0,\n        height: 0,\n      };\n    }\n\n    for (i = 0; i < l; i++) {\n      v1 = this._renderer.vertices[i];\n      // If i = 0, then this \"wraps around\" to the last vertex. Otherwise, it's the previous vertex.\n      // This is important for handling cyclic paths.\n      v0 = this._renderer.vertices[(i + l - 1) % l];\n\n      const [v0x, v0y] = matrix.multiply(v0.x, v0.y);\n      const [v1x, v1y] = matrix.multiply(v1.x, v1.y);\n\n      if (v0.controls && v1.controls) {\n        let rx = v0.controls.right.x;\n        let ry = v0.controls.right.y;\n\n        if (v0.relative) {\n          rx += v0.x;\n          ry += v0.y;\n        }\n\n        let [c0x, c0y] = matrix.multiply(rx, ry);\n\n        let lx = v1.controls.left.x;\n        let ly = v1.controls.left.y;\n\n        if (v1.relative) {\n          lx += v1.x;\n          ly += v1.y;\n        }\n\n        let [c1x, c1y] = matrix.multiply(lx, ly);\n\n        const bb = getCurveBoundingBox(v0x, v0y, c0x, c0y, c1x, c1y, v1x, v1y);\n\n        top = min(bb.min.y - border, top);\n        left = min(bb.min.x - border, left);\n        right = max(bb.max.x + border, right);\n        bottom = max(bb.max.y + border, bottom);\n      } else {\n        if (i <= 1) {\n          top = min(v0y - border, top);\n          left = min(v0x - border, left);\n          right = max(v0x + border, right);\n          bottom = max(v0y + border, bottom);\n        }\n\n        top = min(v1y - border, top);\n        left = min(v1x - border, left);\n        right = max(v1x + border, right);\n        bottom = max(v1y + border, bottom);\n      }\n    }\n\n    return {\n      top: top,\n      left: left,\n      right: right,\n      bottom: bottom,\n      width: right - left,\n      height: bottom - top,\n    };\n  }\n\n  /**\n   * @name Two.Path#contains\n   * @function\n   * @param {Number} x - x coordinate to hit test against\n   * @param {Number} y - y coordinate to hit test against\n   * @param {Object} [options] - Optional options object\n   * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `path.visible = false` shapes\n   * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n   * @returns {Boolean}\n   * @description Check to see if coordinates are within a {@link Two.Path}'s bounding rectangle\n   * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n   */\n  contains(x, y, options) {\n    const opts = options || {};\n    const ignoreVisibility = opts.ignoreVisibility === true;\n\n    if (!ignoreVisibility && this.visible === false) {\n      return false;\n    }\n\n    if (\n      !ignoreVisibility &&\n      typeof this.opacity === 'number' &&\n      this.opacity <= 0\n    ) {\n      return false;\n    }\n\n    const tolerance = typeof opts.tolerance === 'number' ? opts.tolerance : 0;\n\n    this._update(true);\n\n    const rect = this.getBoundingClientRect();\n\n    if (\n      !rect ||\n      x < rect.left - tolerance ||\n      x > rect.right + tolerance ||\n      y < rect.top - tolerance ||\n      y > rect.bottom + tolerance\n    ) {\n      return false;\n    }\n\n    const matrix = this.worldMatrix;\n    const inverse = matrix && matrix.inverse(hitTestMatrix);\n\n    if (!inverse) {\n      return super.contains(x, y, opts);\n    }\n\n    const [localX, localY] = inverse.multiply(x, y, 1);\n    const precision =\n      typeof opts.precision === 'number' && !Number.isNaN(opts.precision)\n        ? Math.max(1, Math.floor(opts.precision))\n        : 8;\n\n    const fillTest = hasVisibleFill(this, opts.fill);\n    const strokeTest = hasVisibleStroke(this, opts.stroke);\n\n    const { polygons, segments } = buildPathHitParts(this, precision);\n\n    if (fillTest && polygons.length > 0) {\n      if (pointInPolygons(polygons, localX, localY)) {\n        return true;\n      }\n    }\n\n    if (strokeTest && segments.length > 0) {\n      const linewidth = typeof this.linewidth === 'number' ? this.linewidth : 0;\n      if (linewidth > 0) {\n        const distance = distanceToSegments(segments, localX, localY);\n        if (distance <= linewidth / 2 + tolerance) {\n          return true;\n        }\n      }\n    }\n\n    if (!fillTest && !strokeTest) {\n      return super.contains(x, y, opts);\n    }\n\n    if (fillTest && polygons.length === 0) {\n      return super.contains(x, y, opts);\n    }\n\n    return false;\n  }\n\n  /**\n   * @name Two.Path#getPointAt\n   * @function\n   * @param {Number} t - Percentage value describing where on the {@link Two.Path} to estimate and assign coordinate values.\n   * @param {Two.Vector} [obj] - Object to apply calculated x, y to. If none available returns new `Object`.\n   * @returns {Object}\n   * @description Given a float `t` from 0 to 1, return a point or assign a passed `obj`'s coordinates to that percentage on this {@link Two.Path}'s curve.\n   */\n  getPointAt(t, obj) {\n    let ia, ib, result;\n    let x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right;\n    let target = this.length * Math.min(Math.max(t, 0), 1);\n    const length = this.vertices.length;\n    const last = length - 1;\n\n    let a = null;\n    let b = null;\n\n    for (let i = 0, l = this._lengths.length, sum = 0; i < l; i++) {\n      if (sum + this._lengths[i] >= target) {\n        if (this._closed) {\n          ia = mod(i, length);\n          ib = mod(i - 1, length);\n          if (i === 0) {\n            ia = ib;\n            ib = i;\n          }\n        } else {\n          ia = i;\n          ib = Math.min(Math.max(i - 1, 0), last);\n        }\n\n        a = this.vertices[ia];\n        b = this.vertices[ib];\n        target -= sum;\n        if (this._lengths[i] !== 0) {\n          t = target / this._lengths[i];\n        } else {\n          t = 0;\n        }\n\n        break;\n      }\n\n      sum += this._lengths[i];\n    }\n\n    if (a === null || b === null) {\n      return null;\n    }\n\n    if (!a) {\n      return b;\n    } else if (!b) {\n      return a;\n    }\n\n    right = b.controls && b.controls.right;\n    left = a.controls && a.controls.left;\n\n    x1 = b.x;\n    y1 = b.y;\n    x2 = (right || b).x;\n    y2 = (right || b).y;\n    x3 = (left || a).x;\n    y3 = (left || a).y;\n    x4 = a.x;\n    y4 = a.y;\n\n    if (right && b.relative) {\n      x2 += b.x;\n      y2 += b.y;\n    }\n\n    if (left && a.relative) {\n      x3 += a.x;\n      y3 += a.y;\n    }\n\n    x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n    y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n\n    // Higher order points for control calculation.\n    const t1x = lerp(x1, x2, t);\n    const t1y = lerp(y1, y2, t);\n    const t2x = lerp(x2, x3, t);\n    const t2y = lerp(y2, y3, t);\n    const t3x = lerp(x3, x4, t);\n    const t3y = lerp(y3, y4, t);\n\n    // Calculate the returned points control points.\n    const brx = lerp(t1x, t2x, t);\n    const bry = lerp(t1y, t2y, t);\n    const alx = lerp(t2x, t3x, t);\n    const aly = lerp(t2y, t3y, t);\n\n    if (_.isObject(obj)) {\n      obj.x = x;\n      obj.y = y;\n\n      if (obj instanceof Anchor) {\n        obj.controls.left.x = brx;\n        obj.controls.left.y = bry;\n        obj.controls.right.x = alx;\n        obj.controls.right.y = aly;\n\n        if (!(typeof obj.relative === 'boolean') || obj.relative) {\n          obj.controls.left.x -= x;\n          obj.controls.left.y -= y;\n          obj.controls.right.x -= x;\n          obj.controls.right.y -= y;\n        }\n      }\n\n      obj.t = t;\n\n      return obj;\n    }\n\n    result = new Anchor(\n      x,\n      y,\n      brx - x,\n      bry - y,\n      alx - x,\n      aly - y,\n      this._curved ? Commands.curve : Commands.line\n    );\n\n    result.t = t;\n\n    return result;\n  }\n\n  /**\n   * @name Two.Path#plot\n   * @function\n   * @description Based on closed / curved and sorting of vertices plot where all points should be and where the respective handles should be too.\n   * @nota-bene While this method is public it is internally called by {@link Two.Path#_update} when `automatic = true`.\n   */\n  plot() {\n    if (this.curved) {\n      getCurveFromPoints(this._collection, this.closed);\n      return this;\n    }\n\n    for (let i = 0; i < this._collection.length; i++) {\n      this._collection[i].command = i === 0 ? Commands.move : Commands.line;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#smooth\n   * @function\n   * @param {Object} [options] - Configuration for smoothing.\n   * @param {String} [options.type='continuous'] - Type of smoothing algorithm.\n   * @param {Number} [options.from=0] - Index of vertices to start smoothing\n   * @param {Number} [options.to=1] - Index of vertices to terminate smoothing\n   * @description Adjust vertex handles to generate smooth curves without toggling `automatic`.\n   */\n  smooth(options) {\n    const opts = options || {};\n    const type = opts.type || 'continuous';\n    const vertices = this._collection;\n    const length = vertices.length;\n\n    if (length < 2) {\n      return this;\n    }\n\n    const closed =\n      this._closed ||\n      (length > 0 &&\n        vertices[length - 1] &&\n        vertices[length - 1].command === Commands.close);\n\n    const resolveIndex = (value, defaultIndex) => {\n      if (value === undefined || value === null) {\n        return defaultIndex;\n      }\n\n      if (typeof value === 'number') {\n        if (closed) {\n          return mod(value, length);\n        }\n        let index = value;\n        if (index < 0) {\n          index += length;\n        }\n        return Math.min(Math.max(index, 0), length - 1);\n      }\n\n      const idx = vertices.indexOf(value);\n      return idx !== -1 ? idx : defaultIndex;\n    };\n\n    const loop = closed && opts.from === undefined && opts.to === undefined;\n    let from = resolveIndex(opts.from, 0);\n    let to = resolveIndex(opts.to, length - 1);\n\n    if (from > to) {\n      if (closed) {\n        from -= length;\n      } else {\n        const temp = from;\n        from = to;\n        to = temp;\n      }\n    }\n\n    const rangeLength = to - from + 1;\n    for (let i = 0; i < rangeLength; i += 1) {\n      const index = mod(from + i, length);\n      const anchor = vertices[index];\n      const isOpenStart = !closed && index === 0;\n      if (anchor.command === Commands.move && !isOpenStart) {\n        anchor.command = Commands.line;\n      }\n    }\n\n    if (type === 'continuous' || type === 'asymmetric') {\n      applyGlobalSmooth(\n        vertices,\n        from,\n        to,\n        closed,\n        loop,\n        type === 'asymmetric'\n      );\n    } else if (type === 'catmull-rom' || type === 'geometric') {\n      const range = {\n        type,\n        factor: opts.factor,\n      };\n      applyLocalSmooth(vertices, from, to, closed, loop, range);\n    } else {\n      throw new Error(\n        `Path.smooth does not support type \"${type}\". Try 'continuous', 'asymmetric', 'catmull-rom', or 'geometric'.`\n      );\n    }\n\n    this._automatic = false;\n    this._flagVertices = true;\n    this._flagLength = true;\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#subdivide\n   * @function\n   * @param {Number} limit - How many times to recurse subdivisions.\n   * @description Insert a {@link Two.Anchor} at the midpoint between every item in {@link Two.Path#vertices}.\n   */\n  subdivide(limit) {\n    this._update();\n\n    const vertices = this.vertices;\n    const length = vertices.length;\n    if (length < 2) {\n      return this;\n    }\n\n    const points = [];\n    let prevOriginal = null;\n    let subpathStartOriginal = null;\n\n    for (let i = 0; i < length; i += 1) {\n      const currentOriginal = vertices[i];\n\n      if (!prevOriginal || currentOriginal.command === Commands.move) {\n        const clone = currentOriginal.clone();\n        points.push(clone);\n        prevOriginal = currentOriginal;\n        subpathStartOriginal = currentOriginal;\n        continue;\n      }\n\n      const isCurve = isSegmentCurved(currentOriginal, prevOriginal);\n\n      if (isCurve) {\n        const subdivided = getSubdivisions(\n          currentOriginal,\n          prevOriginal,\n          limit\n        );\n        const steps = subdivided.length;\n        const prevClone = points[points.length - 1];\n        let startSegment = prevClone.clone();\n        let endSegment = currentOriginal.clone();\n        let prevCloneRef = prevClone;\n        let prevT = 0;\n\n        if (steps <= 1) {\n          const currentClone = currentOriginal.clone();\n          points.push(currentClone);\n        } else {\n          for (let j = 1; j < steps; j += 1) {\n            const globalT = j / steps;\n            const denom = 1 - prevT;\n            const localT =\n              denom <= Number.EPSILON ? globalT : (globalT - prevT) / denom;\n\n            const split = splitSubdivisionSegment(\n              startSegment,\n              endSegment,\n              localT\n            );\n\n            setHandleComponent(\n              prevCloneRef,\n              'right',\n              split.startOut.x - prevCloneRef.x,\n              split.startOut.y - prevCloneRef.y\n            );\n\n            const newAnchor = split.anchor;\n            points.push(newAnchor);\n\n            prevCloneRef = newAnchor;\n            startSegment = newAnchor.clone();\n            prevT = globalT;\n\n            setHandleComponent(\n              endSegment,\n              'left',\n              split.endIn.x - endSegment.x,\n              split.endIn.y - endSegment.y\n            );\n          }\n\n          const currentClone = currentOriginal.clone();\n          currentClone.controls.left.copy(endSegment.controls.left);\n          points.push(currentClone);\n        }\n      } else {\n        const subdivided = getSubdivisions(\n          currentOriginal,\n          prevOriginal,\n          limit\n        );\n\n        for (let j = 1; j < subdivided.length; j += 1) {\n          const anchor = subdivided[j];\n          inheritRelative(anchor, prevOriginal);\n          clearHandleComponent(anchor, 'left');\n          clearHandleComponent(anchor, 'right');\n          anchor.command = Commands.line;\n          points.push(anchor);\n        }\n\n        const currentClone = currentOriginal.clone();\n        points.push(currentClone);\n      }\n\n      prevOriginal = currentOriginal;\n\n      if (currentOriginal.command === Commands.close) {\n        prevOriginal = subpathStartOriginal;\n      }\n    }\n\n    this._automatic = false;\n    this._curved = false;\n    this.vertices = points;\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#_updateLength\n   * @function\n   * @private\n   * @param {Number} [limit] -\n   * @param {Boolean} [silent=false] - If set to `true` then the path isn't updated before calculation. Useful for internal use.\n   * @description Recalculate the {@link Two.Path#length} value.\n   */\n  _updateLength(limit, silent) {\n    // TODO: DRYness (function above)\n    if (!silent) {\n      this._update();\n    }\n\n    const length = this.vertices.length;\n    const last = length - 1;\n    const closed = false; //this._closed || this.vertices[last]._command === Commands.close;\n\n    let b = this.vertices[last];\n    let sum = 0;\n\n    if (typeof this._lengths === 'undefined') {\n      this._lengths = [];\n    }\n\n    _.each(\n      this.vertices,\n      function (a, i) {\n        if ((i <= 0 && !closed) || a.command === Commands.move) {\n          b = a;\n          this._lengths[i] = 0;\n          return;\n        }\n\n        this._lengths[i] = getCurveLength(a, b, limit);\n        sum += this._lengths[i];\n\n        b = a;\n      },\n      this\n    );\n\n    this._length = sum;\n    this._flagLength = false;\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices) {\n      if (this._automatic) {\n        this.plot();\n      }\n\n      if (this._flagLength) {\n        this._updateLength(undefined, true);\n      }\n\n      const l = this._collection.length;\n      const closed = this._closed;\n\n      const beginning = Math.min(this._beginning, this._ending);\n      const ending = Math.max(this._beginning, this._ending);\n\n      const bid = getIdByLength(this, beginning * this._length);\n      const eid = getIdByLength(this, ending * this._length);\n\n      const low = ceil(bid);\n      const high = floor(eid);\n\n      let left, right, prev, next, v, i;\n\n      this._renderer.vertices.length = 0;\n\n      for (i = 0; i < l; i++) {\n        if (this._renderer.collection.length <= i) {\n          // Expected to be `relative` anchor points.\n          this._renderer.collection.push(new Anchor());\n        }\n\n        if (i > high && !right) {\n          v = this._renderer.collection[i].copy(this._collection[i]);\n          this.getPointAt(ending, v);\n          v.command = this._renderer.collection[i].command;\n          this._renderer.vertices.push(v);\n\n          right = v;\n          prev = this._collection[i - 1];\n\n          // Project control over the percentage `t`\n          // of the in-between point\n          if (prev && prev.controls) {\n            if (v.relative) {\n              v.controls.right.clear();\n            } else {\n              v.controls.right.copy(v);\n            }\n\n            if (prev.relative) {\n              this._renderer.collection[i - 1].controls.right\n                .copy(prev.controls.right)\n                .lerp(Vector.zero, 1 - v.t);\n            } else {\n              this._renderer.collection[i - 1].controls.right\n                .copy(prev.controls.right)\n                .lerp(prev, 1 - v.t);\n            }\n          }\n        } else if (i >= low && i <= high) {\n          v = this._renderer.collection[i].copy(this._collection[i]);\n          this._renderer.vertices.push(v);\n\n          if (i === high && contains(this, ending)) {\n            right = v;\n            if (!closed && right.controls) {\n              if (right.relative) {\n                right.controls.right.clear();\n              } else {\n                right.controls.right.copy(right);\n              }\n            }\n          } else if (i === low && contains(this, beginning)) {\n            left = v;\n            left.command = Commands.move;\n            if (!closed && left.controls) {\n              if (left.relative) {\n                left.controls.left.clear();\n              } else {\n                left.controls.left.copy(left);\n              }\n            }\n          }\n        }\n      }\n\n      // Prepend the trimmed point if necessary.\n      if (low > 0 && !left) {\n        i = low - 1;\n\n        v = this._renderer.collection[i].copy(this._collection[i]);\n        this.getPointAt(beginning, v);\n        v.command = Commands.move;\n        this._renderer.vertices.unshift(v);\n\n        next = this._collection[i + 1];\n\n        // Project control over the percentage `t`\n        // of the in-between point\n        if (next && next.controls) {\n          v.controls.left.clear();\n\n          if (next.relative) {\n            this._renderer.collection[i + 1].controls.left\n              .copy(next.controls.left)\n              .lerp(Vector.zero, v.t);\n          } else {\n            vector.copy(next);\n            this._renderer.collection[i + 1].controls.left\n              .copy(next.controls.left)\n              .lerp(next, v.t);\n          }\n        }\n      }\n    }\n\n    Shape.prototype._update.apply(this, arguments);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Path#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagVertices =\n      this._flagLength =\n      this._flagFill =\n      this._flagStroke =\n      this._flagLinewidth =\n      this._flagOpacity =\n      this._flagVisible =\n      this._flagCap =\n      this._flagJoin =\n      this._flagMiter =\n      this._flagClip =\n      this._flagStrokeAttenuation =\n        false;\n\n    Shape.prototype.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  linewidth: {\n    enumerable: true,\n    get: function () {\n      return this._linewidth;\n    },\n    set: function (v) {\n      this._linewidth = v;\n      this._flagLinewidth = true;\n    },\n  },\n  opacity: {\n    enumerable: true,\n    get: function () {\n      return this._opacity;\n    },\n    set: function (v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n    },\n  },\n  visible: {\n    enumerable: true,\n    get: function () {\n      return this._visible;\n    },\n    set: function (v) {\n      this._visible = v;\n      this._flagVisible = true;\n    },\n  },\n  cap: {\n    enumerable: true,\n    get: function () {\n      return this._cap;\n    },\n    set: function (v) {\n      this._cap = v;\n      this._flagCap = true;\n    },\n  },\n  join: {\n    enumerable: true,\n    get: function () {\n      return this._join;\n    },\n    set: function (v) {\n      this._join = v;\n      this._flagJoin = true;\n    },\n  },\n  miter: {\n    enumerable: true,\n    get: function () {\n      return this._miter;\n    },\n    set: function (v) {\n      this._miter = v;\n      this._flagMiter = true;\n    },\n  },\n\n  fill: {\n    enumerable: true,\n    get: function () {\n      return this._fill;\n    },\n    set: function (f) {\n      if (\n        this._fill instanceof Gradient ||\n        this._fill instanceof LinearGradient ||\n        this._fill instanceof RadialGradient ||\n        this._fill instanceof Texture\n      ) {\n        this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n      }\n\n      this._fill = f;\n      this._flagFill = true;\n\n      if (\n        this._fill instanceof Gradient ||\n        this._fill instanceof LinearGradient ||\n        this._fill instanceof RadialGradient ||\n        this._fill instanceof Texture\n      ) {\n        this._fill.bind(Events.Types.change, this._renderer.flagFill);\n      }\n    },\n  },\n\n  stroke: {\n    enumerable: true,\n    get: function () {\n      return this._stroke;\n    },\n    set: function (f) {\n      if (\n        this._stroke instanceof Gradient ||\n        this._stroke instanceof LinearGradient ||\n        this._stroke instanceof RadialGradient ||\n        this._stroke instanceof Texture\n      ) {\n        this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n      }\n\n      this._stroke = f;\n      this._flagStroke = true;\n\n      if (\n        this._stroke instanceof Gradient ||\n        this._stroke instanceof LinearGradient ||\n        this._stroke instanceof RadialGradient ||\n        this._stroke instanceof Texture\n      ) {\n        this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n      }\n    },\n  },\n\n  /**\n   * @name Two.Path#length\n   * @property {Number} - The sum of distances between all {@link Two.Path#vertices}.\n   */\n  length: {\n    get: function () {\n      if (this._flagLength) {\n        this._updateLength();\n      }\n      return this._length;\n    },\n  },\n\n  closed: {\n    enumerable: true,\n    get: function () {\n      return this._closed;\n    },\n    set: function (v) {\n      this._closed = !!v;\n      this._flagVertices = true;\n    },\n  },\n\n  curved: {\n    enumerable: true,\n    get: function () {\n      return this._curved;\n    },\n    set: function (v) {\n      this._curved = !!v;\n      this._flagVertices = true;\n    },\n  },\n\n  automatic: {\n    enumerable: true,\n    get: function () {\n      return this._automatic;\n    },\n    set: function (v) {\n      if (v === this._automatic) {\n        return;\n      }\n      this._automatic = !!v;\n      const method = this._automatic ? 'ignore' : 'listen';\n      _.each(this.vertices, function (v) {\n        v[method]();\n      });\n    },\n  },\n\n  beginning: {\n    enumerable: true,\n    get: function () {\n      return this._beginning;\n    },\n    set: function (v) {\n      this._beginning = v;\n      this._flagVertices = true;\n    },\n  },\n\n  ending: {\n    enumerable: true,\n    get: function () {\n      return this._ending;\n    },\n    set: function (v) {\n      this._ending = v;\n      this._flagVertices = true;\n    },\n  },\n\n  vertices: {\n    enumerable: true,\n\n    get: function () {\n      return this._collection;\n    },\n\n    set: function (vertices) {\n      const bindVertices = this._renderer.bindVertices;\n      const unbindVertices = this._renderer.unbindVertices;\n\n      // Remove previous listeners\n      if (this._collection) {\n        this._collection\n          .unbind(Events.Types.insert, bindVertices)\n          .unbind(Events.Types.remove, unbindVertices);\n      }\n\n      // Create new Collection with copy of vertices\n      if (vertices instanceof Collection) {\n        this._collection = vertices;\n      } else {\n        this._collection = new Collection(vertices || []);\n      }\n\n      // Listen for Collection changes and bind / unbind\n      this._collection\n        .bind(Events.Types.insert, bindVertices)\n        .bind(Events.Types.remove, unbindVertices);\n\n      // Bind Initial Vertices\n      bindVertices(this._collection);\n    },\n  },\n\n  /**\n   * @name Two.Path#mask\n   * @property {Two.Shape} - The shape whose alpha property becomes a clipping area for the path.\n   * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n   */\n  mask: {\n    enumerable: true,\n\n    get: function () {\n      return this._mask;\n    },\n\n    set: function (v) {\n      this._mask = v;\n      this._flagMask = true;\n      if (_.isObject(v) && !v.clip) {\n        v.clip = true;\n      }\n    },\n  },\n\n  /**\n   * @name Two.Path#clip\n   * @property {Boolean} - Tells Two.js renderer if this object represents a mask for another object (or not).\n   */\n  clip: {\n    enumerable: true,\n    get: function () {\n      return this._clip;\n    },\n    set: function (v) {\n      this._clip = v;\n      this._flagClip = true;\n    },\n  },\n\n  dashes: {\n    enumerable: true,\n    get: function () {\n      return this._dashes;\n    },\n    set: function (v) {\n      if (typeof v.offset !== 'number') {\n        v.offset = (this.dashes && this._dashes.offset) || 0;\n      }\n      this._dashes = v;\n    },\n  },\n\n  /**\n   * @name Two.Path#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n   * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function () {\n      return this._strokeAttenuation;\n    },\n    set: function (v) {\n      this._strokeAttenuation = !!v;\n      this._flagStrokeAttenuation = true;\n      this._flagLinewidth = true;\n    },\n  },\n};\n\n// Utility functions\n\n/**\n * @name FlagVertices\n * @private\n * @function\n * @description Cached method to let renderers know vertices have been updated on a {@link Two.Path}.\n */\nfunction FlagVertices() {\n  this._flagVertices = true;\n  this._flagLength = true;\n  if (this.parent) {\n    this.parent._flagLength = true;\n  }\n}\n\n/**\n * @name BindVertices\n * @private\n * @function\n * @description Cached method to let {@link Two.Path} know vertices have been added to the instance.\n */\nfunction BindVertices(items) {\n  // This function is called a lot\n  // when importing a large SVG\n  let i = items.length;\n  while (i--) {\n    items[i].bind(Events.Types.change, this._renderer.flagVertices);\n  }\n\n  this._renderer.flagVertices();\n}\n\n/**\n * @name UnbindVertices\n * @private\n * @function\n * @description Cached method to let {@link Two.Path} know vertices have been removed from the instance.\n */\nfunction UnbindVertices(items) {\n  let i = items.length;\n  while (i--) {\n    items[i].unbind(Events.Types.change, this._renderer.flagVertices);\n  }\n\n  this._renderer.flagVertices();\n}\n\n/**\n * @name FlagFill\n * @private\n * @function\n * @description Cached method to let {@link Two.Path} know the fill has changed.\n */\nfunction FlagFill() {\n  this._flagFill = true;\n}\n\n/**\n * @name FlagFill\n * @private\n * @function\n * @description Cached method to let {@link Two.Path} know the stroke has changed.\n */\nfunction FlagStroke() {\n  this._flagStroke = true;\n}\n\nexport { FlagVertices, BindVertices, UnbindVertices, FlagFill, FlagStroke };\n"
  },
  {
    "path": "src/registry.d.ts",
    "content": "declare module 'two.js/src/registry' {\n  /**\n   * @name Two.Registry\n   * @class\n   * @description An arbitrary class to manage a directory of things. Mainly used for keeping tabs of textures in Two.js.\n   */\n  export class Registry {\n    map: {};\n    /**\n     * @name Two.Registry#add\n     * @function\n     * @param {String} id - A unique identifier.\n     * @param obj - Any type of variable to be registered to the directory.\n     * @description Adds any value to the directory. Assigned by the `id`.\n     */\n    add(id: string, obj: any): Registry;\n    /**\n     * @name Two.Registry#remove\n     * @function\n     * @param {String} id - A unique identifier.\n     * @description Remove any value from the directory by its `id`.\n     */\n    remove(id: string): Registry;\n    /**\n     * @name Two.Registry#get\n     * @function\n     * @param {String} id - A unique identifier.\n     * @returns {?Object} The associated value. If unavailable then `undefined` is returned.\n     * @description Get a registered value by its `id`.\n     */\n    get(id: string): any | null;\n  }\n}\n"
  },
  {
    "path": "src/registry.js",
    "content": "/**\n * @name Two.Registry\n * @class\n * @description An arbitrary class to manage a directory of things. Mainly used for keeping tabs of textures in Two.js.\n */\nexport class Registry {\n\n  map = {};\n\n  constructor() {}\n\n  /**\n   * @name Two.Registry#add\n   * @function\n   * @param {String} id - A unique identifier.\n   * @param obj - Any type of variable to be registered to the directory.\n   * @description Adds any value to the directory. Assigned by the `id`.\n   */\n  add(id, obj) {\n    this.map[id] = obj;\n    return this;\n  }\n\n  /**\n   * @name Two.Registry#remove\n   * @function\n   * @param {String} id - A unique identifier.\n   * @description Remove any value from the directory by its `id`.\n   */\n  remove(id) {\n    delete this.map[id];\n    return this;\n  }\n\n  /**\n   * @name Two.Registry#get\n   * @function\n   * @param {String} id - A unique identifier.\n   * @returns {?Object} The associated value. If unavailable then `undefined` is returned.\n   * @description Get a registered value by its `id`.\n   */\n  get(id) {\n    return this.map[id];\n  }\n\n  /**\n   * @name Two.Registry#contains\n   * @function\n   * @param {String} id - A unique identifier.\n   * @returns {Boolean}\n   * @description Convenience method to see if a value is registered to an `id` already.\n   */\n  contains(id) {\n    return id in this.map;\n  }\n\n}\n"
  },
  {
    "path": "src/renderers/canvas.d.ts",
    "content": "declare module 'two.js/src/renderers/canvas' {\n  /**\n     * @name Two.CanvasRenderer\n     * @class\n\n     * @param {Object} [parameters] - This object is inherited when constructing a new instance of {@link Two}.\n     * @param {Element} [parameters.domElement] - The `<canvas />` to draw to. If none given a new one will be constructed.\n     * @param {Boolean} [parameters.overdraw] - Determines whether the canvas should clear the background or not. Defaults to `true`.\n     * @param {Boolean} [parameters.smoothing=true] - Determines whether the canvas should antialias drawing. Set it to `false` when working with pixel art. `false` can lead to better performance, since it would use a cheaper interpolation algorithm.\n     * @description This class is used by {@link Two} when constructing with `type` of `Two.Types.canvas`. It takes Two.js' scenegraph and renders it to a `<canvas />`.\n     */\n  export class Renderer extends Events {\n    /**\n     * @name Two.CanvasRenderer.Utils\n     * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />`.\n     */\n    static Utils: {\n      isHidden: RegExp;\n      alignments: {\n        left: string;\n        middle: string;\n        right: string;\n      };\n      group: {\n        renderChild: (child: any) => void;\n        render: (ctx: any) => any;\n      };\n      path: {\n        render: (ctx: any, forced: any, parentClipped: any) => any;\n      };\n      points: {\n        render: (ctx: any, forced: any, parentClipped: any) => any;\n      };\n      text: {\n        render: (ctx: any, forced: any, parentClipped: any) => any;\n      };\n      'linear-gradient': {\n        render: (ctx: any, parent: any) => any;\n      };\n      'radial-gradient': {\n        render: (ctx: any, parent: any) => any;\n      };\n      texture: {\n        render: (ctx: any) => any;\n      };\n      renderSvgArcCommand: (\n        ctx: any,\n        ax: any,\n        ay: any,\n        rx: any,\n        ry: any,\n        largeArcFlag: any,\n        sweepFlag: any,\n        xAxisRotation: any,\n        x: any,\n        y: any\n      ) => void;\n    };\n    constructor(params?: any);\n    /**\n     * @name Two.CanvasRenderer#domElement\n     * @property {Element} - The `<canvas />` associated with the Two.js scene.\n     */\n    domElement: HTMLElement;\n    /**\n     * @name Two.CanvasRenderer#ctx\n     * @property {Canvas2DContext} - Associated two dimensional context to render on the `<canvas />`.\n     */\n    ctx: CanvasRenderingContext2D;\n    /**\n     * @name Two.CanvasRenderer#overdraw\n     * @property {Boolean} - Determines whether the canvas clears the background each draw call.\n     * @default true\n     */\n    overdraw: boolean;\n    /**\n     * @name Two.CanvasRenderer#scene\n     * @property {Group} - The root group of the scenegraph.\n     */\n    scene: Group;\n    /**\n     * @name Two.CanvasRenderer#setSize\n     * @function\n     * @fires resize\n     * @param {Number} width - The new width of the renderer.\n     * @param {Number} height - The new height of the renderer.\n     * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n     * @description Change the size of the renderer.\n     */\n    setSize(width: number, height: number, ratio?: number): Renderer;\n    width: number;\n    height: number;\n    ratio: number;\n    /**\n     * @name Two.CanvasRenderer#render\n     * @function\n     * @description Render the current scene to the `<canvas />`.\n     */\n    render(): Renderer;\n  }\n  import { Events } from 'two.js/src/events';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/renderers/canvas.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport {\n  decomposeMatrix,\n  mod,\n  TWO_PI,\n  getEffectiveStrokeWidth,\n} from '../utils/math.js';\nimport { Curve } from '../utils/curves.js';\nimport { Events } from '../events.js';\nimport { getRatio } from '../utils/device-pixel-ratio.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Group } from '../group.js';\nimport { Vector } from '../vector.js';\nimport { Constants } from '../constants.js';\n\n// Constants\nconst emptyArray = [];\nconst max = Math.max,\n  min = Math.min,\n  abs = Math.abs,\n  sin = Math.sin,\n  cos = Math.cos,\n  acos = Math.acos,\n  sqrt = Math.sqrt;\n\nconst canvas = {\n  isHidden: /(undefined|none|transparent)/i,\n\n  alignments: {\n    left: 'start',\n    middle: 'center',\n    right: 'end',\n  },\n\n  baselines: {\n    top: 'top',\n    middle: 'middle',\n    bottom: 'bottom',\n    baseline: 'alphabetic',\n  },\n\n  getRendererType: function (type) {\n    return type in canvas ? type : 'path';\n  },\n\n  group: {\n    renderChild: function (child) {\n      const prop = canvas.getRendererType(child._renderer.type);\n      canvas[prop].render.call(child, this.ctx, true, this.clip);\n    },\n\n    render: function (ctx) {\n      if (!this._visible) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      const matrix = this._matrix.elements;\n      const parent = this.parent;\n      this._renderer.opacity =\n        this._opacity *\n        (parent && parent._renderer ? parent._renderer.opacity : 1);\n\n      const mask = this._mask;\n      // const clip = this._clip;\n\n      const defaultMatrix = isDefaultMatrix(matrix);\n      const shouldIsolate = !defaultMatrix || !!mask;\n\n      if (!this._renderer.context) {\n        this._renderer.context = {};\n      }\n\n      this._renderer.context.ctx = ctx;\n      // this._renderer.context.clip = clip;\n\n      if (shouldIsolate) {\n        ctx.save();\n        if (!defaultMatrix) {\n          ctx.transform(\n            matrix[0],\n            matrix[3],\n            matrix[1],\n            matrix[4],\n            matrix[2],\n            matrix[5]\n          );\n        }\n      }\n\n      if (mask) {\n        const prop = canvas.getRendererType(mask._renderer.type);\n        canvas[prop].render.call(mask, ctx, true);\n      }\n\n      if (this._opacity > 0 && this._scale !== 0) {\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          const prop = canvas.getRendererType(child._renderer.type);\n          canvas[prop].render.call(child, ctx);\n        }\n      }\n\n      if (shouldIsolate) {\n        ctx.restore();\n      }\n\n      // Commented two-way functionality of clips / masks with groups and\n      // polygons. Uncomment when this bug is fixed:\n      // https://code.google.com/p/chromium/issues/detail?id=370951\n\n      // if (clip) {\n      //   ctx.clip();\n      // }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  path: {\n    render: function (ctx, forced, parentClipped) {\n      let matrix,\n        stroke,\n        linewidth,\n        fill,\n        opacity,\n        visible,\n        cap,\n        join,\n        miter,\n        closed,\n        commands,\n        length,\n        last,\n        prev,\n        a,\n        b,\n        c,\n        d,\n        ux,\n        uy,\n        vx,\n        vy,\n        ar,\n        bl,\n        br,\n        cl,\n        x,\n        y,\n        mask,\n        clip,\n        defaultMatrix,\n        isOffset,\n        dashes,\n        po;\n\n      po =\n        this.parent && this.parent._renderer\n          ? this.parent._renderer.opacity\n          : 1;\n      mask = this._mask;\n      clip = this._clip;\n      opacity = this._opacity * (po || 1);\n      visible = this._visible;\n\n      if (!forced && (!visible || clip || opacity === 0)) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      matrix = this._matrix.elements;\n      stroke = this._stroke;\n      linewidth = this._linewidth;\n      fill = this._fill;\n      cap = this._cap;\n      join = this._join;\n      miter = this._miter;\n      closed = this._closed;\n      commands = this._renderer.vertices; // Commands\n      length = commands.length;\n      last = length - 1;\n      defaultMatrix = isDefaultMatrix(matrix);\n      dashes = this.dashes;\n\n      // Transform\n      if (!defaultMatrix) {\n        ctx.save();\n        ctx.transform(\n          matrix[0],\n          matrix[3],\n          matrix[1],\n          matrix[4],\n          matrix[2],\n          matrix[5]\n        );\n      }\n\n      // Commented two-way functionality of clips / masks with groups and\n      // polygons. Uncomment when this bug is fixed:\n      // https://code.google.com/p/chromium/issues/detail?id=370951\n      if (mask) {\n        const prop = canvas.getRendererType(mask._renderer.type);\n        canvas[prop].render.call(mask, ctx, true);\n      }\n\n      // Styles\n      if (fill) {\n        if (typeof fill === 'string') {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = canvas.getRendererType(fill._renderer.type);\n          canvas[prop].render.call(fill, ctx, this);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === 'string') {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = canvas.getRendererType(stroke._renderer.type);\n          canvas[prop].render.call(stroke, ctx, this);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(this);\n        }\n        if (miter) {\n          ctx.miterLimit = miter;\n        }\n        if (join) {\n          ctx.lineJoin = join;\n        }\n        if (!closed && cap) {\n          ctx.lineCap = cap;\n        }\n      }\n      if (typeof opacity === 'number') {\n        ctx.globalAlpha = opacity;\n      }\n\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n\n      ctx.beginPath();\n\n      let rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n\n      for (let i = 0; i < length; i++) {\n        b = commands[i];\n\n        x = b.x;\n        y = b.y;\n\n        switch (b.command) {\n          case Commands.close:\n            ctx.closePath();\n            break;\n\n          case Commands.arc:\n            rx = b.rx;\n            ry = b.ry;\n            xAxisRotation = b.xAxisRotation;\n            largeArcFlag = b.largeArcFlag;\n            sweepFlag = b.sweepFlag;\n\n            prev = closed ? mod(i - 1, length) : max(i - 1, 0);\n            a = commands[prev];\n\n            ax = a.x;\n            ay = a.y;\n\n            canvas.renderSvgArcCommand(\n              ctx,\n              ax,\n              ay,\n              rx,\n              ry,\n              largeArcFlag,\n              sweepFlag,\n              xAxisRotation,\n              x,\n              y\n            );\n            break;\n\n          case Commands.curve:\n            prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0);\n\n            a = commands[prev];\n\n            ar = (a.controls && a.controls.right) || Vector.zero;\n            bl = (b.controls && b.controls.left) || Vector.zero;\n\n            if (a._relative) {\n              vx = ar.x + a.x;\n              vy = ar.y + a.y;\n            } else {\n              vx = ar.x;\n              vy = ar.y;\n            }\n\n            if (b._relative) {\n              ux = bl.x + b.x;\n              uy = bl.y + b.y;\n            } else {\n              ux = bl.x;\n              uy = bl.y;\n            }\n\n            ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n\n            if (i >= last && closed) {\n              c = d;\n\n              br = (b.controls && b.controls.right) || Vector.zero;\n              cl = (c.controls && c.controls.left) || Vector.zero;\n\n              if (b._relative) {\n                vx = br.x + b.x;\n                vy = br.y + b.y;\n              } else {\n                vx = br.x;\n                vy = br.y;\n              }\n\n              if (c._relative) {\n                ux = cl.x + c.x;\n                uy = cl.y + c.y;\n              } else {\n                ux = cl.x;\n                uy = cl.y;\n              }\n\n              x = c.x;\n              y = c.y;\n\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n            }\n\n            break;\n\n          case Commands.line:\n            ctx.lineTo(x, y);\n            break;\n\n          case Commands.move:\n            d = b;\n            ctx.moveTo(x, y);\n            break;\n        }\n      }\n\n      // Loose ends\n\n      if (closed) {\n        ctx.closePath();\n      }\n\n      if (!clip && !parentClipped) {\n        if (!canvas.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!canvas.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n      }\n\n      if (!defaultMatrix) {\n        ctx.restore();\n      }\n\n      if (clip && !parentClipped) {\n        ctx.clip();\n      }\n\n      if (dashes && dashes.length > 0) {\n        ctx.setLineDash(emptyArray);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  points: {\n    render: function (ctx, forced, parentClipped) {\n      let me,\n        stroke,\n        linewidth,\n        fill,\n        opacity,\n        visible,\n        size,\n        commands,\n        length,\n        b,\n        x,\n        y,\n        defaultMatrix,\n        isOffset,\n        dashes,\n        po;\n\n      po =\n        this.parent && this.parent._renderer\n          ? this.parent._renderer.opacity\n          : 1;\n      opacity = this._opacity * (po || 1);\n      visible = this._visible;\n\n      if (!forced && (!visible || opacity === 0)) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      me = this._matrix.elements;\n      stroke = this._stroke;\n      linewidth = this._linewidth;\n      fill = this._fill;\n      commands = this._renderer.collection; // Commands\n      length = commands.length;\n      defaultMatrix = isDefaultMatrix(me);\n      dashes = this.dashes;\n      size = this._size;\n\n      // Transform\n      if (!defaultMatrix) {\n        ctx.save();\n        ctx.transform(me[0], me[3], me[1], me[4], me[2], me[5]);\n      }\n\n      // Styles\n      if (fill) {\n        if (typeof fill === 'string') {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = canvas.getRendererType(fill._renderer.type);\n          canvas[prop].render.call(fill, ctx, this);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === 'string') {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = canvas.getRendererType(stroke._renderer.type);\n          canvas[prop].render.call(stroke, ctx, this);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(this);\n        }\n      }\n      if (typeof opacity === 'number') {\n        ctx.globalAlpha = opacity;\n      }\n\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n\n      ctx.beginPath();\n\n      let radius = size * 0.5,\n        m;\n\n      if (!this._sizeAttenuation) {\n        m = this.worldMatrix.elements;\n        m = decomposeMatrix(m[0], m[3], m[1], m[4], m[2], m[5]);\n        radius /= Math.max(m.scaleX, m.scaleY);\n      }\n\n      for (let i = 0; i < length; i++) {\n        b = commands[i];\n\n        x = b.x;\n        y = b.y;\n\n        ctx.moveTo(x + radius, y);\n        ctx.arc(x, y, radius, 0, TWO_PI);\n      }\n\n      if (!parentClipped) {\n        if (!canvas.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!canvas.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n      }\n\n      // Loose ends\n\n      if (!defaultMatrix) {\n        ctx.restore();\n      }\n\n      if (dashes && dashes.length > 0) {\n        ctx.setLineDash(emptyArray);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  text: {\n    render: function (ctx, forced, parentClipped) {\n      const po =\n        this.parent && this.parent._renderer\n          ? this.parent._renderer.opacity\n          : 1;\n      const opacity = this._opacity * po;\n      const visible = this._visible;\n      const mask = this._mask;\n      const clip = this._clip;\n\n      if (!forced && (!visible || clip || opacity === 0)) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      const matrix = this._matrix.elements;\n      const stroke = this._stroke;\n      const linewidth = this._linewidth;\n      const fill = this._fill;\n      const decoration = this._decoration;\n      const direction = this._direction;\n      const defaultMatrix = isDefaultMatrix(matrix);\n      const isOffset =\n        fill._renderer &&\n        fill._renderer.offset &&\n        stroke._renderer &&\n        stroke._renderer.offset;\n      const dashes = this.dashes;\n      const alignment = canvas.alignments[this._alignment] || this._alignment;\n      const baseline = canvas.baselines[this._baseline] || this._baseline;\n\n      let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n\n      // Transform\n      if (!defaultMatrix) {\n        ctx.save();\n        ctx.transform(\n          matrix[0],\n          matrix[3],\n          matrix[1],\n          matrix[4],\n          matrix[2],\n          matrix[5]\n        );\n      }\n\n      // Commented two-way functionality of clips / masks with groups and\n      // polygons. Uncomment when this bug is fixed:\n      // https://code.google.com/p/chromium/issues/detail?id=370951\n      if (mask) {\n        const prop = canvas.getRendererType(mask._renderer.type);\n        canvas[prop].render.call(mask, ctx, true);\n      }\n\n      if (!isOffset) {\n        ctx.font = [\n          this._style,\n          this._weight,\n          this._size + 'px/' + this._leading + 'px',\n          this._family,\n        ].join(' ');\n      }\n\n      ctx.textAlign = alignment;\n      ctx.textBaseline = baseline;\n      ctx.direction = direction;\n\n      // Styles\n      if (fill) {\n        if (typeof fill === 'string') {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = canvas.getRendererType(fill._renderer.type);\n          canvas[prop].render.call(fill, ctx, this);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === 'string') {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = canvas.getRendererType(stroke._renderer.type);\n          canvas[prop].render.call(stroke, ctx, this);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(this);\n        }\n      }\n      if (typeof opacity === 'number') {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n\n      if (!clip && !parentClipped) {\n        if (!canvas.isHidden.test(fill)) {\n          if (fill._renderer && fill._renderer.offset) {\n            sx = fill._renderer.scale.x;\n            sy = fill._renderer.scale.y;\n\n            ctx.save();\n            ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n            ctx.scale(sx, sy);\n\n            a = this._size / fill._renderer.scale.y;\n            b = this._leading / fill._renderer.scale.y;\n            ctx.font = [\n              this._style,\n              this._weight,\n              a + 'px/',\n              b + 'px',\n              this._family,\n            ].join(' ');\n\n            c = fill._renderer.offset.x / fill._renderer.scale.x;\n            d = fill._renderer.offset.y / fill._renderer.scale.y;\n\n            ctx.fillText(this.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.fillText(this.value, 0, 0);\n          }\n        }\n\n        if (!canvas.isHidden.test(stroke)) {\n          if (stroke._renderer && stroke._renderer.offset) {\n            sx = stroke._renderer.scale.x;\n            sy = stroke._renderer.scale.y;\n\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(sx, sy);\n\n            a = this._size / stroke._renderer.scale.y;\n            b = this._leading / stroke._renderer.scale.y;\n            ctx.font = [\n              this._style,\n              this._weight,\n              a + 'px/',\n              b + 'px',\n              this._family,\n            ].join(' ');\n\n            c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n            d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n            e = linewidth / stroke._renderer.scale.x;\n\n            ctx.lineWidth = e;\n            ctx.strokeText(this.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.strokeText(this.value, 0, 0);\n          }\n        }\n      }\n\n      // Handle text-decoration\n      if (/(underline|strikethrough)/i.test(decoration)) {\n        const metrics = ctx.measureText(this.value);\n        let scalar = 1;\n\n        switch (decoration) {\n          case 'underline':\n            y1 = metrics.actualBoundingBoxDescent;\n            y2 = metrics.actualBoundingBoxDescent;\n            break;\n          case 'strikethrough':\n            y1 = 0;\n            y2 = 0;\n            scalar = 0.5;\n            break;\n        }\n\n        switch (baseline) {\n          case 'top':\n            y1 += this._size * scalar;\n            y2 += this._size * scalar;\n            break;\n          case 'baseline':\n          case 'bottom':\n            y1 -= this._size * scalar;\n            y2 -= this._size * scalar;\n            break;\n        }\n\n        switch (alignment) {\n          case 'left':\n          case 'start':\n            x1 = 0;\n            x2 = metrics.width;\n            break;\n          case 'right':\n          case 'end':\n            x1 = -metrics.width;\n            x2 = 0;\n            break;\n          default:\n            x1 = -metrics.width / 2;\n            x2 = metrics.width / 2;\n        }\n\n        ctx.lineWidth = Math.max(Math.floor(this._size / 15), 1);\n        ctx.strokeStyle = ctx.fillStyle;\n\n        ctx.beginPath();\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n        ctx.stroke();\n      }\n\n      if (!defaultMatrix) {\n        ctx.restore();\n      }\n\n      // TODO: Test for text\n      if (clip && !parentClipped) {\n        ctx.clip();\n      }\n\n      if (dashes && dashes.length > 0) {\n        ctx.setLineDash(emptyArray);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  'linear-gradient': {\n    render: function (ctx, parent) {\n      if (!parent) {\n        return;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      if (\n        !this._renderer.effect ||\n        this._flagEndPoints ||\n        this._flagStops ||\n        this._flagUnits\n      ) {\n        let rect;\n        let lx = this.left._x;\n        let ly = this.left._y;\n        let rx = this.right._x;\n        let ry = this.right._y;\n\n        if (/objectBoundingBox/i.test(this._units)) {\n          // Convert objectBoundingBox units to userSpaceOnUse units\n          rect = parent.getBoundingClientRect(true);\n          lx = (lx - 0.5) * rect.width;\n          ly = (ly - 0.5) * rect.height;\n          rx = (rx - 0.5) * rect.width;\n          ry = (ry - 0.5) * rect.height;\n        }\n\n        this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  'radial-gradient': {\n    render: function (ctx, parent) {\n      if (!parent) {\n        return;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      if (\n        !this._renderer.effect ||\n        this._flagCenter ||\n        this._flagFocal ||\n        this._flagRadius ||\n        this._flagStops ||\n        this._flagUnits\n      ) {\n        let rect;\n        let cx = this.center._x;\n        let cy = this.center._y;\n        let fx = this.focal._x;\n        let fy = this.focal._y;\n        let radius = this._radius;\n\n        if (/objectBoundingBox/i.test(this._units)) {\n          // Convert objectBoundingBox units to userSpaceOnUse units\n          rect = parent.getBoundingClientRect(true);\n          cx = (cx - 0.5) * rect.width * 0.5;\n          cy = (cy - 0.5) * rect.height * 0.5;\n          fx = (fx - 0.5) * rect.width * 0.5;\n          fy = (fy - 0.5) * rect.height * 0.5;\n          radius *= Math.min(rect.width, rect.height);\n        }\n\n        this._renderer.effect = ctx.createRadialGradient(\n          cx,\n          cy,\n          0,\n          fx,\n          fy,\n          radius\n        );\n\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  texture: {\n    render: function (ctx) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      const image = this.image;\n\n      if (\n        !this._renderer.effect ||\n        ((this._flagLoaded ||\n          this._flagImage ||\n          this._flagVideo ||\n          this._flagRepeat) &&\n          this.loaded)\n      ) {\n        this._renderer.effect = ctx.createPattern(this.image, this._repeat);\n      }\n\n      if (this._flagOffset || this._flagLoaded || this._flagScale) {\n        if (!(this._renderer.offset instanceof Vector)) {\n          this._renderer.offset = new Vector();\n        }\n\n        this._renderer.offset.x = -this._offset.x;\n        this._renderer.offset.y = -this._offset.y;\n\n        if (image) {\n          this._renderer.offset.x += image.width / 2;\n          this._renderer.offset.y += image.height / 2;\n\n          if (this._scale instanceof Vector) {\n            this._renderer.offset.x *= this._scale.x;\n            this._renderer.offset.y *= this._scale.y;\n          } else {\n            this._renderer.offset.x *= this._scale;\n            this._renderer.offset.y *= this._scale;\n          }\n        }\n      }\n\n      if (this._flagScale || this._flagLoaded) {\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n\n        if (this._scale instanceof Vector) {\n          this._renderer.scale.copy(this._scale);\n        } else {\n          this._renderer.scale.set(this._scale, this._scale);\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  renderSvgArcCommand: function (\n    ctx,\n    ax,\n    ay,\n    rx,\n    ry,\n    largeArcFlag,\n    sweepFlag,\n    xAxisRotation,\n    x,\n    y\n  ) {\n    xAxisRotation = (xAxisRotation * Math.PI) / 180;\n\n    // Ensure radii are positive\n    rx = abs(rx);\n    ry = abs(ry);\n\n    // Compute (x1′, y1′)\n    const dx2 = (ax - x) / 2.0;\n    const dy2 = (ay - y) / 2.0;\n    const x1p = cos(xAxisRotation) * dx2 + sin(xAxisRotation) * dy2;\n    const y1p = -sin(xAxisRotation) * dx2 + cos(xAxisRotation) * dy2;\n\n    // Compute (cx′, cy′)\n    const x1ps = x1p * x1p;\n    const y1ps = y1p * y1p;\n    let rxs = rx * rx;\n    let rys = ry * ry;\n\n    // Ensure radii are large enough\n    const cr = x1ps / rxs + y1ps / rys;\n\n    if (cr > 1) {\n      // scale up rx,ry equally so cr == 1\n      const s = sqrt(cr);\n      rx = s * rx;\n      ry = s * ry;\n      rxs = rx * rx;\n      rys = ry * ry;\n    }\n\n    const dq = rxs * y1ps + rys * x1ps;\n    const pq = (rxs * rys - dq) / dq;\n    let q = sqrt(max(0, pq));\n\n    if (largeArcFlag === sweepFlag) q = -q;\n\n    const cxp = (q * rx * y1p) / ry;\n    const cyp = (-q * ry * x1p) / rx;\n\n    // Step 3: Compute (cx, cy) from (cx′, cy′)\n    const cx =\n      cos(xAxisRotation) * cxp - sin(xAxisRotation) * cyp + (ax + x) / 2;\n    const cy =\n      sin(xAxisRotation) * cxp + cos(xAxisRotation) * cyp + (ay + y) / 2;\n\n    // Step 4: Compute θ1 and Δθ\n    const startAngle = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n    const delta =\n      svgAngle(\n        (x1p - cxp) / rx,\n        (y1p - cyp) / ry,\n        (-x1p - cxp) / rx,\n        (-y1p - cyp) / ry\n      ) % TWO_PI;\n\n    const endAngle = startAngle + delta;\n\n    const clockwise = sweepFlag === 0;\n\n    renderArcEstimate(\n      ctx,\n      cx,\n      cy,\n      rx,\n      ry,\n      startAngle,\n      endAngle,\n      clockwise,\n      xAxisRotation\n    );\n  },\n};\n\n/**\n * @name Two.CanvasRenderer\n * @class\n * @extends Two.Events\n * @param {Object} [parameters] - This object is inherited when constructing a new instance of {@link Two}.\n * @param {Element} [parameters.domElement] - The `<canvas />` to draw to. If none given a new one will be constructed.\n * @param {Boolean} [parameters.overdraw] - Determines whether the canvas should clear the background or not. Defaults to `true`.\n * @param {Boolean} [parameters.smoothing=true] - Determines whether the canvas should antialias drawing. Set it to `false` when working with pixel art. `false` can lead to better performance, since it would use a cheaper interpolation algorithm.\n * @description This class is used by {@link Two} when constructing with `type` of `Two.Types.canvas`. It takes Two.js' scenegraph and renders it to a `<canvas />`.\n */\nexport class Renderer extends Events {\n  constructor(params) {\n    super();\n\n    // It might not make a big difference on GPU backed canvases.\n    const smoothing = params.smoothing !== false;\n\n    /**\n     * @name Two.CanvasRenderer#domElement\n     * @property {Element} - The `<canvas />` associated with the Two.js scene.\n     */\n    this.domElement = params.domElement || document.createElement('canvas');\n\n    /**\n     * @name Two.CanvasRenderer#ctx\n     * @property {Canvas2DContext} - Associated two dimensional context to render on the `<canvas />`.\n     */\n    this.ctx = this.domElement.getContext('2d');\n\n    /**\n     * @name Two.CanvasRenderer#overdraw\n     * @property {Boolean} - Determines whether the canvas clears the background each draw call.\n     * @default true\n     */\n    this.overdraw = params.overdraw || false;\n\n    if (typeof this.ctx.imageSmoothingEnabled !== 'undefined') {\n      this.ctx.imageSmoothingEnabled = smoothing;\n    }\n\n    /**\n     * @name Two.CanvasRenderer#scene\n     * @property {Two.Group} - The root group of the scenegraph.\n     */\n    this.scene = new Group();\n    this.scene.parent = this;\n  }\n\n  /**\n   * @name Two.CanvasRenderer.Utils\n   * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />`.\n   */\n  static Utils = canvas;\n\n  /**\n   * @name Two.CanvasRenderer#setSize\n   * @function\n   * @fires resize\n   * @param {Number} width - The new width of the renderer.\n   * @param {Number} height - The new height of the renderer.\n   * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n   * @description Change the size of the renderer.\n   */\n  setSize(width, height, ratio) {\n    this.width = width;\n    this.height = height;\n\n    this.ratio = typeof ratio === 'undefined' ? getRatio(this.ctx) : ratio;\n\n    this.domElement.width = width * this.ratio;\n    this.domElement.height = height * this.ratio;\n\n    if (this.domElement.style) {\n      _.extend(this.domElement.style, {\n        width: width + 'px',\n        height: height + 'px',\n      });\n    }\n\n    return this.trigger(Events.Types.resize, width, height, ratio);\n  }\n\n  /**\n   * @name Two.CanvasRenderer#render\n   * @function\n   * @description Render the current scene to the `<canvas />`.\n   */\n  render() {\n    const isOne = this.ratio === 1;\n\n    if (!isOne) {\n      this.ctx.save();\n      this.ctx.scale(this.ratio, this.ratio);\n    }\n\n    if (!this.overdraw) {\n      this.ctx.clearRect(0, 0, this.width, this.height);\n    }\n\n    canvas.group.render.call(this.scene, this.ctx);\n\n    if (!isOne) {\n      this.ctx.restore();\n    }\n\n    return this;\n  }\n}\n\nfunction renderArcEstimate(\n  ctx,\n  ox,\n  oy,\n  rx,\n  ry,\n  startAngle,\n  endAngle,\n  clockwise,\n  xAxisRotation\n) {\n  const delta = endAngle - startAngle;\n  const epsilon = Curve.Tolerance.epsilon;\n  const samePoints = Math.abs(delta) < epsilon;\n\n  // ensures that deltaAngle is 0 .. 2 PI\n  let deltaAngle = mod(delta, TWO_PI);\n\n  if (deltaAngle < epsilon) {\n    if (samePoints) {\n      deltaAngle = 0;\n    } else {\n      deltaAngle = TWO_PI;\n    }\n  }\n\n  if (clockwise === true && !samePoints) {\n    if (deltaAngle === TWO_PI) {\n      deltaAngle = -TWO_PI;\n    } else {\n      deltaAngle = deltaAngle - TWO_PI;\n    }\n  }\n\n  for (let i = 0; i < Constants.Resolution; i++) {\n    const t = i / (Constants.Resolution - 1);\n\n    const angle = startAngle + t * deltaAngle;\n    let x = ox + rx * Math.cos(angle);\n    let y = oy + ry * Math.sin(angle);\n\n    if (xAxisRotation !== 0) {\n      const cos = Math.cos(xAxisRotation);\n      const sin = Math.sin(xAxisRotation);\n\n      const tx = x - ox;\n      const ty = y - oy;\n\n      // Rotate the point about the center of the ellipse.\n      x = tx * cos - ty * sin + ox;\n      y = tx * sin + ty * cos + oy;\n    }\n\n    ctx.lineTo(x, y);\n  }\n}\n\nfunction svgAngle(ux, uy, vx, vy) {\n  const dot = ux * vx + uy * vy;\n  const len = sqrt(ux * ux + uy * uy) * sqrt(vx * vx + vy * vy);\n  // floating point precision, slightly over values appear\n  let ang = acos(max(-1, min(1, dot / len)));\n  if (ux * vy - uy * vx < 0) {\n    ang = -ang;\n  }\n\n  return ang;\n}\n\n// Returns true if this is a non-transforming matrix\nfunction isDefaultMatrix(m) {\n  return (\n    m[0] === 1 &&\n    m[3] === 0 &&\n    m[1] === 0 &&\n    m[4] === 1 &&\n    m[2] === 0 &&\n    m[5] === 0\n  );\n}\n"
  },
  {
    "path": "src/renderers/svg.d.ts",
    "content": "declare module 'two.js/src/renderers/svg' {\n  /**\n     * @name Two.SVGRenderer\n     * @class\n\n     * @param {Object} [parameters] - This object is inherited when constructing a new instance of {@link Two}.\n     * @param {Element} [parameters.domElement] - The `<svg />` to draw to. If none given a new one will be constructed.\n     * @description This class is used by {@link Two} when constructing with `type` of `Two.Types.svg` (the default type). It takes Two.js' scenegraph and renders it to a `<svg />`.\n     */\n  export class Renderer extends Events {\n    /**\n     * @name Two.SVGRenderer.Utils\n     * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<svg />`.\n     */\n    static Utils: {\n      version: number;\n      ns: string;\n      xlink: string;\n      alignments: {\n        left: string;\n        center: string;\n        right: string;\n      };\n      createElement: (name: any, attrs: any) => SVGElement;\n      setAttributes: (elem: any, attrs: any) => any;\n      removeAttributes: (elem: any, attrs: any) => any;\n      toString: (points: any, closed: any) => string;\n      pointsToString: (points: any, size: any) => string;\n      getClip: (shape: any, domElement: any) => any;\n      group: {\n        appendChild: (object: any) => void;\n        removeChild: (object: any) => void;\n        orderChild: (object: any) => void;\n        renderChild: (child: any) => void;\n        render: (domElement: any) => any;\n      };\n      path: {\n        render: (domElement: any) => any;\n      };\n      points: {\n        render: (domElement: any) => any;\n      };\n      text: {\n        render: (domElement: any) => any;\n      };\n      'linear-gradient': {\n        render: (domElement: any, silent: any) => any;\n      };\n      'radial-gradient': {\n        render: (domElement: any, silent: any) => any;\n      };\n      texture: {\n        render: (domElement: any, silent: any) => any;\n      };\n    };\n    constructor(params?: any);\n    /**\n     * @name Two.SVGRenderer#domElement\n     * @property {Element} - The `<svg />` associated with the Two.js scene.\n     */\n    domElement: any;\n    /**\n     * @name Two.SVGRenderer#scene\n     * @property {Group} - The root group of the scenegraph.\n     */\n    scene: Group;\n    /**\n     * @name Two.SVGRenderer#defs\n     * @property {SvgDefintionsElement} - The `<defs />` to apply gradients, patterns, and bitmap imagery.\n     */\n    defs: SVGDefsElement;\n    /**\n     * @name Two.SVGRenderer#setSize\n     * @function\n     * @param {Number} width - The new width of the renderer.\n     * @param {Number} height - The new height of the renderer.\n     * @description Change the size of the renderer.\n     * @nota-bene Triggers a `Two.Events.resize`.\n     */\n    setSize(width: number, height: number): any;\n    width: number;\n    height: number;\n    /**\n     * @name Two.SVGRenderer#render\n     * @function\n     * @description Render the current scene to the `<svg />`.\n     */\n    render(): Renderer;\n  }\n  import { Events } from 'two.js/src/events';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/renderers/svg.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { decomposeMatrix, mod, toFixed, getEffectiveStrokeWidth } from '../utils/math.js';\nimport { Events } from '../events.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Group } from '../group.js';\nimport { Vector } from '../vector.js';\n\nconst svg = {\n  version: 1.1,\n\n  ns: 'http://www.w3.org/2000/svg',\n  xlink: 'http://www.w3.org/1999/xlink',\n\n  alignments: {\n    left: 'start',\n    center: 'middle',\n    right: 'end',\n  },\n\n  baselines: {\n    top: 'hanging',\n    middle: 'middle',\n    bottom: 'ideographic',\n    baseline: 'alphabetic',\n  },\n\n  // Create an svg namespaced element.\n  createElement: function (name, attrs) {\n    const tag = name;\n    const elem = document.createElementNS(svg.ns, tag);\n    if (tag === 'svg') {\n      attrs = _.defaults(attrs || {}, {\n        version: svg.version,\n      });\n    }\n    if (attrs && Object.keys(attrs).length > 0) {\n      svg.setAttributes(elem, attrs);\n    }\n    return elem;\n  },\n\n  // Add attributes from an svg element.\n  setAttributes: function (elem, attrs) {\n    const keys = Object.keys(attrs);\n    for (let i = 0; i < keys.length; i++) {\n      if (/href/.test(keys[i])) {\n        elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]);\n      } else {\n        elem.setAttribute(keys[i], attrs[keys[i]]);\n      }\n    }\n    return this;\n  },\n\n  // Remove attributes from an svg element.\n  removeAttributes: function (elem, attrs) {\n    for (let key in attrs) {\n      elem.removeAttribute(key);\n    }\n    return this;\n  },\n\n  // Turn a set of vertices into a string for the d property of a path\n  // element. It is imperative that the string collation is as fast as\n  // possible, because this call will be happening multiple times a\n  // second.\n  toString: function (points, closed) {\n    let l = points.length,\n      last = l - 1,\n      d, // The elusive last Commands.move point\n      string = '';\n\n    for (let i = 0; i < l; i++) {\n      const b = points[i];\n\n      const prev = closed ? mod(i - 1, l) : Math.max(i - 1, 0);\n      const a = points[prev];\n\n      let command, c;\n      let vx, vy, ux, uy, ar, bl, br, cl;\n      let rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n\n      // Access x and y directly,\n      // bypassing the getter\n      let x = toFixed(b.x);\n      let y = toFixed(b.y);\n\n      switch (b.command) {\n        case Commands.close:\n          command = Commands.close;\n          break;\n\n        case Commands.arc:\n          rx = b.rx;\n          ry = b.ry;\n          xAxisRotation = b.xAxisRotation;\n          largeArcFlag = b.largeArcFlag;\n          sweepFlag = b.sweepFlag;\n\n          command =\n            Commands.arc +\n            ' ' +\n            rx +\n            ' ' +\n            ry +\n            ' ' +\n            xAxisRotation +\n            ' ' +\n            largeArcFlag +\n            ' ' +\n            sweepFlag +\n            ' ' +\n            x +\n            ' ' +\n            y;\n          break;\n\n        case Commands.curve:\n          ar = (a.controls && a.controls.right) || Vector.zero;\n          bl = (b.controls && b.controls.left) || Vector.zero;\n\n          if (a.relative) {\n            vx = toFixed(ar.x + a.x);\n            vy = toFixed(ar.y + a.y);\n          } else {\n            vx = toFixed(ar.x);\n            vy = toFixed(ar.y);\n          }\n\n          if (b.relative) {\n            ux = toFixed(bl.x + b.x);\n            uy = toFixed(bl.y + b.y);\n          } else {\n            ux = toFixed(bl.x);\n            uy = toFixed(bl.y);\n          }\n\n          command =\n            (i === 0 ? Commands.move : Commands.curve) +\n            ' ' +\n            vx +\n            ' ' +\n            vy +\n            ' ' +\n            ux +\n            ' ' +\n            uy +\n            ' ' +\n            x +\n            ' ' +\n            y;\n          break;\n\n        case Commands.move:\n          d = b;\n          command = Commands.move + ' ' + x + ' ' + y;\n          break;\n\n        default:\n          command = b.command + ' ' + x + ' ' + y;\n      }\n\n      // Add a final point and close it off\n\n      if (i >= last && closed) {\n        if (b.command === Commands.curve) {\n          // Make sure we close to the most previous Commands.move\n          c = d;\n\n          br = (b.controls && b.controls.right) || b;\n          cl = (c.controls && c.controls.left) || c;\n\n          if (b.relative) {\n            vx = toFixed(br.x + b.x);\n            vy = toFixed(br.y + b.y);\n          } else {\n            vx = toFixed(br.x);\n            vy = toFixed(br.y);\n          }\n\n          if (c.relative) {\n            ux = toFixed(cl.x + c.x);\n            uy = toFixed(cl.y + c.y);\n          } else {\n            ux = toFixed(cl.x);\n            uy = toFixed(cl.y);\n          }\n\n          x = toFixed(c.x);\n          y = toFixed(c.y);\n\n          command +=\n            ' C ' + vx + ' ' + vy + ' ' + ux + ' ' + uy + ' ' + x + ' ' + y;\n        }\n\n        if (b.command !== Commands.close) {\n          command += ' Z';\n        }\n      }\n\n      string += command + ' ';\n    }\n\n    return string;\n  },\n\n  pointsToString: function (points, size) {\n    let string = '';\n    const r = size * 0.5;\n\n    for (let i = 0; i < points.length; i++) {\n      const x = points[i].x;\n      const y = points[i].y - r;\n\n      string += Commands.move + ' ' + x + ' ' + y + ' ';\n      string += 'a ' + r + ' ' + r + ' 0 1 0 0.001 0 Z';\n    }\n\n    return string;\n  },\n\n  getClip: function (shape, domElement) {\n    let clip = shape._renderer.clip;\n\n    if (!clip) {\n      clip = shape._renderer.clip = svg.createElement('clipPath', {\n        'clip-rule': 'nonzero',\n      });\n    }\n\n    if (clip.parentNode === null) {\n      domElement.defs.appendChild(clip);\n    }\n\n    return clip;\n  },\n\n  getRendererType: function (type) {\n    return type in svg ? type : 'path';\n  },\n\n  defs: {\n    update: function (domElement) {\n      const { defs } = domElement;\n      if (defs._flagUpdate) {\n        const children = Array.prototype.slice.call(defs.children, 0);\n        for (let i = 0; i < children.length; i++) {\n          const child = children[i];\n          const id = child.id;\n          const selector = `[fill=\"url(#${id})\"],[stroke=\"url(#${id})\"],[clip-path=\"url(#${id})\"]`;\n          const exists = domElement.querySelector(selector);\n          if (!exists) {\n            defs.removeChild(child);\n          }\n        }\n        defs._flagUpdate = false;\n      }\n    },\n  },\n\n  group: {\n    // TODO: Can speed up.\n    // TODO: How does this effect a f\n    appendChild: function (object) {\n      const elem = object._renderer.elem;\n\n      if (!elem) {\n        return;\n      }\n\n      const tag = elem.nodeName;\n\n      if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) {\n        return;\n      }\n\n      this.elem.appendChild(elem);\n    },\n\n    removeChild: function (object) {\n      const elem = object._renderer.elem;\n\n      if (!elem || elem.parentNode != this.elem) {\n        return;\n      }\n\n      const tag = elem.nodeName;\n\n      if (!tag) {\n        return;\n      }\n\n      // Defer subtractions while clipping.\n      if (object._clip) {\n        return;\n      }\n\n      this.elem.removeChild(elem);\n    },\n\n    orderChild: function (object) {\n      this.elem.appendChild(object._renderer.elem);\n    },\n\n    renderChild: function (child) {\n      const prop = svg.getRendererType(child._renderer.type);\n      svg[prop].render.call(child, this);\n    },\n\n    render: function (domElement) {\n      // Shortcut for hidden objects.\n      // Doesn't reset the flags, so changes are stored and\n      // applied once the object is visible again\n      if (\n        (!this._visible && !this._flagVisible) ||\n        (this._opacity === 0 && !this._flagOpacity)\n      ) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      if (!this._renderer.elem) {\n        this._renderer.elem = svg.createElement('g', {\n          id: this.id,\n        });\n        domElement.appendChild(this._renderer.elem);\n      }\n\n      // _Update styles for the <g>\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const context = {\n        domElement: domElement,\n        elem: this._renderer.elem,\n      };\n\n      if (flagMatrix) {\n        this._renderer.elem.setAttribute(\n          'transform',\n          'matrix(' + this._matrix.toString() + ')'\n        );\n      }\n\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        const prop = svg.getRendererType(child._renderer.type);\n        svg[prop].render.call(child, domElement);\n      }\n\n      if (this._flagId) {\n        this._renderer.elem.setAttribute('id', this._id);\n      }\n\n      if (this._flagOpacity) {\n        this._renderer.elem.setAttribute('opacity', this._opacity);\n      }\n\n      if (this._flagVisible) {\n        this._renderer.elem.setAttribute(\n          'display',\n          this._visible ? 'inline' : 'none'\n        );\n      }\n\n      if (this._flagClassName) {\n        this._renderer.elem.setAttribute('class', this.classList.join(' '));\n      }\n\n      if (this._flagAdditions) {\n        this.additions.forEach(svg.group.appendChild, context);\n      }\n\n      if (this._flagSubtractions) {\n        this.subtractions.forEach(svg.group.removeChild, context);\n      }\n\n      if (this._flagOrder) {\n        this.children.forEach(svg.group.orderChild, context);\n      }\n\n      // Commented two-way functionality of clips / masks with groups and\n      // polygons. Uncomment when this bug is fixed:\n      // https://code.google.com/p/chromium/issues/detail?id=370951\n\n      // if (this._flagClip) {\n\n      //   clip = svg.getClip(this, domElement);\n      //   elem = this._renderer.elem;\n\n      //   if (this._clip) {\n      //     elem.removeAttribute('id');\n      //     clip.setAttribute('id', this.id);\n      //     clip.appendChild(elem);\n      //   } else {\n      //     clip.removeAttribute('id');\n      //     elem.setAttribute('id', this.id);\n      //     this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore\n      //   }\n\n      // }\n\n      if (this._flagMask) {\n        if (this._mask) {\n          const prop = svg.getRendererType(this._mask._renderer.type);\n          svg[prop].render.call(this._mask, domElement);\n          this._renderer.elem.setAttribute(\n            'clip-path',\n            'url(#' + this._mask.id + ')'\n          );\n        } else {\n          this._renderer.elem.removeAttribute('clip-path');\n        }\n      }\n\n      if (this.dataset) {\n        Object.assign(this._renderer.elem.dataset, this.dataset);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  path: {\n    render: function (domElement) {\n      // Shortcut for hidden objects.\n      // Doesn't reset the flags, so changes are stored and\n      // applied once the object is visible again\n      if (this._opacity === 0 && !this._flagOpacity) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      // Collect any attribute that needs to be changed here\n      const changed = {};\n\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n\n      if (flagMatrix) {\n        changed.transform = 'matrix(' + this._matrix.toString() + ')';\n      }\n\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n\n      if (this._flagVertices) {\n        const vertices = svg.toString(this._renderer.vertices, this._closed);\n        changed.d = vertices;\n      }\n\n      if (this._fill && this._fill._renderer) {\n        this._renderer.hasFillEffect = true;\n        this._fill._update();\n        const prop = svg.getRendererType(this._fill._renderer.type);\n        svg[prop].render.call(this._fill, domElement, true);\n      }\n\n      if (this._flagFill) {\n        changed.fill =\n          this._fill && this._fill.id\n            ? 'url(#' + this._fill.id + ')'\n            : this._fill;\n        if (\n          this._renderer.hasFillEffect &&\n          typeof this._fill.id === 'undefined'\n        ) {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasFillEffect;\n        }\n      }\n\n      if (this._stroke && this._stroke._renderer) {\n        this._renderer.hasStrokeEffect = true;\n        this._stroke._update();\n        const prop = svg.getRendererType(this._stroke._renderer.type);\n        svg[prop].render.call(this._stroke, domElement, true);\n      }\n\n      if (this._flagStroke) {\n        changed.stroke =\n          this._stroke && this._stroke.id\n            ? 'url(#' + this._stroke.id + ')'\n            : this._stroke;\n        if (\n          this._renderer.hasStrokeEffect &&\n          typeof this._stroke.id === 'undefined'\n        ) {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasStrokeEffect;\n        }\n      }\n\n      if (this._flagLinewidth) {\n        changed['stroke-width'] = getEffectiveStrokeWidth(this);\n      }\n\n      if (this._flagOpacity) {\n        changed['stroke-opacity'] = this._opacity;\n        changed['fill-opacity'] = this._opacity;\n      }\n\n      if (this._flagClassName) {\n        changed['class'] = this.classList.join(' ');\n      }\n\n      if (this._flagVisible) {\n        changed.visibility = this._visible ? 'visible' : 'hidden';\n      }\n\n      if (this._flagCap) {\n        changed['stroke-linecap'] = this._cap;\n      }\n\n      if (this._flagJoin) {\n        changed['stroke-linejoin'] = this._join;\n      }\n\n      if (this._flagMiter) {\n        changed['stroke-miterlimit'] = this._miter;\n      }\n\n      if (this.dashes && this.dashes.length > 0) {\n        changed['stroke-dasharray'] = this.dashes.join(' ');\n        changed['stroke-dashoffset'] = this.dashes.offset || 0;\n      }\n\n      // If there is no attached DOM element yet,\n      // create it with all necessary attributes.\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement('path', changed);\n        domElement.appendChild(this._renderer.elem);\n\n        // Otherwise apply all pending attributes\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n\n      if (this._flagClip) {\n        const clip = svg.getClip(this, domElement);\n        const elem = this._renderer.elem;\n\n        if (this._clip) {\n          elem.removeAttribute('id');\n          clip.setAttribute('id', this.id);\n          clip.appendChild(elem);\n        } else {\n          clip.removeAttribute('id');\n          elem.setAttribute('id', this.id);\n          this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore\n        }\n      }\n\n      // Commented two-way functionality of clips / masks with groups and\n      // polygons. Uncomment when this bug is fixed:\n      // https://code.google.com/p/chromium/issues/detail?id=370951\n\n      if (this._flagMask) {\n        if (this._mask) {\n          const prop = svg.getRendererType(this._mask._renderer.type);\n          svg[prop].render.call(this._mask, domElement);\n          this._renderer.elem.setAttribute(\n            'clip-path',\n            'url(#' + this._mask.id + ')'\n          );\n        } else {\n          this._renderer.elem.removeAttribute('clip-path');\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  points: {\n    render: function (domElement) {\n      // Shortcut for hidden objects.\n      // Doesn't reset the flags, so changes are stored and\n      // applied once the object is visible again\n      if (this._opacity === 0 && !this._flagOpacity) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      // Collect any attribute that needs to be changed here\n      const changed = {};\n\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n\n      if (flagMatrix) {\n        changed.transform = 'matrix(' + this._matrix.toString() + ')';\n      }\n\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n\n      if (this._flagVertices || this._flagSize || this._flagSizeAttenuation) {\n        let size = this._size;\n        if (!this._sizeAttenuation) {\n          const me = this.worldMatrix.elements;\n          const m = decomposeMatrix(me[0], me[3], me[1], me[4], me[2], me[5]);\n          size /= Math.max(m.scaleX, m.scaleY);\n        }\n        const vertices = svg.pointsToString(this._renderer.collection, size);\n        changed.d = vertices;\n      }\n\n      if (this._fill && this._fill._renderer) {\n        this._renderer.hasFillEffect = true;\n        this._fill._update();\n        const prop = svg.getRendererType(this._fill._renderer.type);\n        svg[prop].render.call(this._fill, domElement, true);\n      }\n\n      if (this._flagFill) {\n        changed.fill =\n          this._fill && this._fill.id\n            ? 'url(#' + this._fill.id + ')'\n            : this._fill;\n        if (\n          this._renderer.hasFillEffect &&\n          typeof this._fill.id === 'undefined'\n        ) {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasFillEffect;\n        }\n      }\n\n      if (this._stroke && this._stroke._renderer) {\n        this._renderer.hasStrokeEffect = true;\n        this._stroke._update();\n        const prop = svg.getRendererType(this._stroke._renderer.type);\n        svg[prop].render.call(this._stroke, domElement, true);\n      }\n\n      if (this._flagStroke) {\n        changed.stroke =\n          this._stroke && this._stroke.id\n            ? 'url(#' + this._stroke.id + ')'\n            : this._stroke;\n        if (\n          this._renderer.hasStrokeEffect &&\n          typeof this._stroke.id === 'undefined'\n        ) {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasStrokeEffect;\n        }\n      }\n\n      if (this._flagLinewidth) {\n        changed['stroke-width'] = getEffectiveStrokeWidth(this);\n      }\n\n      if (this._flagOpacity) {\n        changed['stroke-opacity'] = this._opacity;\n        changed['fill-opacity'] = this._opacity;\n      }\n\n      if (this._flagClassName) {\n        changed['class'] = this.classList.join(' ');\n      }\n\n      if (this._flagVisible) {\n        changed.visibility = this._visible ? 'visible' : 'hidden';\n      }\n\n      if (this.dashes && this.dashes.length > 0) {\n        changed['stroke-dasharray'] = this.dashes.join(' ');\n        changed['stroke-dashoffset'] = this.dashes.offset || 0;\n      }\n\n      // If there is no attached DOM element yet,\n      // create it with all necessary attributes.\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement('path', changed);\n        domElement.appendChild(this._renderer.elem);\n\n        // Otherwise apply all pending attributes\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  text: {\n    render: function (domElement) {\n      this._update();\n\n      const changed = {};\n\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n\n      if (flagMatrix) {\n        changed.transform = 'matrix(' + this._matrix.toString() + ')';\n      }\n\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n\n      if (this._flagFamily) {\n        changed['font-family'] = this._family;\n      }\n      if (this._flagSize) {\n        changed['font-size'] = this._size;\n      }\n      if (this._flagLeading) {\n        changed['line-height'] = this._leading;\n      }\n      if (this._flagAlignment) {\n        changed['text-anchor'] =\n          svg.alignments[this._alignment] || this._alignment;\n      }\n      if (this._flagBaseline) {\n        changed['dominant-baseline'] =\n          svg.baselines[this._baseline] || this._baseline;\n      }\n      if (this._flagStyle) {\n        changed['font-style'] = this._style;\n      }\n      if (this._flagWeight) {\n        changed['font-weight'] = this._weight;\n      }\n      if (this._flagDecoration) {\n        changed['text-decoration'] = this._decoration;\n      }\n      if (this._flagDirection) {\n        changed['direction'] = this._direction;\n      }\n      if (this._fill && this._fill._renderer) {\n        this._renderer.hasFillEffect = true;\n        this._fill._update();\n        const prop = svg.getRendererType(this._fill._renderer.type);\n        svg[prop].render.call(this._fill, domElement, true);\n      }\n      if (this._flagFill) {\n        changed.fill =\n          this._fill && this._fill.id\n            ? 'url(#' + this._fill.id + ')'\n            : this._fill;\n        if (\n          this._renderer.hasFillEffect &&\n          typeof this._fill.id === 'undefined'\n        ) {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasFillEffect;\n        }\n      }\n      if (this._stroke && this._stroke._renderer) {\n        this._renderer.hasStrokeEffect = true;\n        this._stroke._update();\n        const prop = svg.getRendererType(this._stroke._renderer.type);\n        svg[prop].render.call(this._stroke, domElement, true);\n      }\n      if (this._flagStroke) {\n        changed.stroke =\n          this._stroke && this._stroke.id\n            ? 'url(#' + this._stroke.id + ')'\n            : this._stroke;\n        if (\n          this._renderer.hasStrokeEffect &&\n          typeof this._stroke.id === 'undefined'\n        ) {\n          domElement.defs._flagUpdate = true;\n          delete this._renderer.hasStrokeEffect;\n        }\n      }\n      if (this._flagLinewidth) {\n        changed['stroke-width'] = getEffectiveStrokeWidth(this);\n      }\n      if (this._flagOpacity) {\n        changed.opacity = this._opacity;\n      }\n      if (this._flagClassName) {\n        changed['class'] = this.classList.join(' ');\n      }\n      if (this._flagVisible) {\n        changed.visibility = this._visible ? 'visible' : 'hidden';\n      }\n      if (this.dashes && this.dashes.length > 0) {\n        changed['stroke-dasharray'] = this.dashes.join(' ');\n        changed['stroke-dashoffset'] = this.dashes.offset || 0;\n      }\n\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n\n        this._renderer.elem = svg.createElement('text', changed);\n        domElement.appendChild(this._renderer.elem);\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n\n      if (this._flagClip) {\n        const clip = svg.getClip(this, domElement);\n        const elem = this._renderer.elem;\n\n        if (this._clip) {\n          elem.removeAttribute('id');\n          clip.setAttribute('id', this.id);\n          clip.appendChild(elem);\n        } else {\n          clip.removeAttribute('id');\n          elem.setAttribute('id', this.id);\n          this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore\n        }\n      }\n\n      // Commented two-way functionality of clips / masks with groups and\n      // polygons. Uncomment when this bug is fixed:\n      // https://code.google.com/p/chromium/issues/detail?id=370951\n\n      if (this._flagMask) {\n        if (this._mask) {\n          const prop = svg.getRendererType(this._mask._renderer.type);\n          svg[prop].render.call(this._mask, domElement);\n          this._renderer.elem.setAttribute(\n            'clip-path',\n            'url(#' + this._mask.id + ')'\n          );\n        } else {\n          this._renderer.elem.removeAttribute('clip-path');\n        }\n      }\n\n      if (this._flagValue) {\n        this._renderer.elem.textContent = this._value;\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  'linear-gradient': {\n    render: function (domElement, silent) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      if (!silent) {\n        this._update();\n      }\n\n      const changed = {};\n\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n\n      if (this._flagEndPoints) {\n        changed.x1 = this.left._x;\n        changed.y1 = this.left._y;\n        changed.x2 = this.right._x;\n        changed.y2 = this.right._y;\n      }\n\n      if (this._flagSpread) {\n        changed.spreadMethod = this._spread;\n      }\n\n      if (this._flagUnits) {\n        changed.gradientUnits = this._units;\n      }\n\n      // If there is no attached DOM element yet,\n      // create it with all necessary attributes.\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement('linearGradient', changed);\n\n        // Otherwise apply all pending attributes\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n\n      if (this._renderer.elem.parentNode === null) {\n        domElement.defs.appendChild(this._renderer.elem);\n      }\n\n      if (this._flagStops) {\n        const lengthChanged =\n          this._renderer.elem.childNodes.length !== this.stops.length;\n\n        if (lengthChanged) {\n          while (this._renderer.elem.lastChild) {\n            this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n          }\n        }\n\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          const attrs = {};\n\n          if (stop._flagOffset) {\n            attrs.offset = 100 * stop._offset + '%';\n          }\n          if (stop._flagColor) {\n            attrs['stop-color'] = stop._color;\n          }\n          if (stop._flagOpacity) {\n            attrs['stop-opacity'] = stop._opacity;\n          }\n\n          if (!stop._renderer.elem) {\n            stop._renderer.elem = svg.createElement('stop', attrs);\n          } else {\n            svg.setAttributes(stop._renderer.elem, attrs);\n          }\n\n          if (lengthChanged) {\n            this._renderer.elem.appendChild(stop._renderer.elem);\n          }\n          stop.flagReset();\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  'radial-gradient': {\n    render: function (domElement, silent) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      if (!silent) {\n        this._update();\n      }\n\n      const changed = {};\n\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n      if (this._flagCenter) {\n        changed.cx = this.center._x;\n        changed.cy = this.center._y;\n      }\n      if (this._flagFocal) {\n        changed.fx = this.focal._x;\n        changed.fy = this.focal._y;\n      }\n      if (this._flagRadius) {\n        changed.r = this._radius;\n      }\n      if (this._flagSpread) {\n        changed.spreadMethod = this._spread;\n      }\n\n      if (this._flagUnits) {\n        changed.gradientUnits = this._units;\n      }\n\n      // If there is no attached DOM element yet,\n      // create it with all necessary attributes.\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        this._renderer.elem = svg.createElement('radialGradient', changed);\n\n        // Otherwise apply all pending attributes\n      } else {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n\n      if (this._renderer.elem.parentNode === null) {\n        domElement.defs.appendChild(this._renderer.elem);\n      }\n\n      if (this._flagStops) {\n        const lengthChanged =\n          this._renderer.elem.childNodes.length !== this.stops.length;\n\n        if (lengthChanged) {\n          while (this._renderer.elem.lastChild) {\n            this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n          }\n        }\n\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          const attrs = {};\n\n          if (stop._flagOffset) {\n            attrs.offset = 100 * stop._offset + '%';\n          }\n          if (stop._flagColor) {\n            attrs['stop-color'] = stop._color;\n          }\n          if (stop._flagOpacity) {\n            attrs['stop-opacity'] = stop._opacity;\n          }\n\n          if (!stop._renderer.elem) {\n            stop._renderer.elem = svg.createElement('stop', attrs);\n          } else {\n            svg.setAttributes(stop._renderer.elem, attrs);\n          }\n\n          if (lengthChanged) {\n            this._renderer.elem.appendChild(stop._renderer.elem);\n          }\n          stop.flagReset();\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  texture: {\n    render: function (domElement, silent) {\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      if (!silent) {\n        this._update();\n      }\n\n      const changed = {};\n      const styles = { x: 0, y: 0 };\n      const image = this.image;\n\n      if (this._flagId) {\n        changed.id = this._id;\n      }\n\n      if (this._flagLoaded && this.loaded) {\n        switch (image.nodeName.toLowerCase()) {\n          case 'canvas':\n            styles.href = styles['xlink:href'] = image.toDataURL('image/png');\n            break;\n          case 'img':\n          case 'image':\n            styles.href = styles['xlink:href'] = this.src;\n            break;\n        }\n      }\n\n      if (this._flagOffset || this._flagLoaded || this._flagScale) {\n        changed.x = this._offset.x;\n        changed.y = this._offset.y;\n\n        if (image) {\n          changed.x -= image.width / 2;\n          changed.y -= image.height / 2;\n\n          if (this._scale instanceof Vector) {\n            changed.x *= this._scale.x;\n            changed.y *= this._scale.y;\n          } else {\n            changed.x *= this._scale;\n            changed.y *= this._scale;\n          }\n        }\n\n        if (changed.x > 0) {\n          changed.x *= -1;\n        }\n        if (changed.y > 0) {\n          changed.y *= -1;\n        }\n      }\n\n      if (this._flagScale || this._flagLoaded || this._flagRepeat) {\n        changed.width = 0;\n        changed.height = 0;\n\n        if (image) {\n          changed.width = image.width;\n          changed.height = image.height;\n\n          // TODO: Hack / Band-aid\n          switch (this._repeat) {\n            case 'no-repeat':\n              changed.width += 1;\n              changed.height += 1;\n              break;\n          }\n\n          if (this._scale instanceof Vector) {\n            changed.width *= this._scale.x;\n            changed.height *= this._scale.y;\n          } else {\n            changed.width *= this._scale;\n            changed.height *= this._scale;\n          }\n\n          if (/no-repeat/i.test(this._repeat)) {\n            styles.preserveAspectRatio = 'xMidYMid';\n          } else {\n            styles.preserveAspectRatio = 'none';\n          }\n\n          styles.width = changed.width;\n          styles.height = changed.height;\n        }\n      }\n\n      if (this._flagScale || this._flagLoaded) {\n        if (!this._renderer.image) {\n          this._renderer.image = svg.createElement('image', styles);\n        } else {\n          svg.setAttributes(this._renderer.image, styles);\n        }\n      }\n\n      if (!this._renderer.elem) {\n        changed.id = this._id;\n        changed.patternUnits = 'userSpaceOnUse';\n        this._renderer.elem = svg.createElement('pattern', changed);\n      } else if (Object.keys(changed).length !== 0) {\n        svg.setAttributes(this._renderer.elem, changed);\n      }\n\n      if (this._renderer.elem.parentNode === null) {\n        domElement.defs.appendChild(this._renderer.elem);\n      }\n\n      if (\n        this._renderer.elem &&\n        this._renderer.image &&\n        !this._renderer.appended\n      ) {\n        this._renderer.elem.appendChild(this._renderer.image);\n        this._renderer.appended = true;\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n};\n\n/**\n * @name Two.SVGRenderer\n * @class\n * @extends Two.Events\n * @param {Object} [parameters] - This object is inherited when constructing a new instance of {@link Two}.\n * @param {Element} [parameters.domElement] - The `<svg />` to draw to. If none given a new one will be constructed.\n * @description This class is used by {@link Two} when constructing with `type` of `Two.Types.svg` (the default type). It takes Two.js' scenegraph and renders it to a `<svg />`.\n */\nexport class Renderer extends Events {\n  constructor(params) {\n    super();\n\n    /**\n     * @name Two.SVGRenderer#domElement\n     * @property {Element} - The `<svg />` associated with the Two.js scene.\n     */\n    this.domElement = params.domElement || svg.createElement('svg');\n\n    /**\n     * @name Two.SVGRenderer#scene\n     * @property {Two.Group} - The root group of the scenegraph.\n     */\n    this.scene = new Group();\n    this.scene.parent = this;\n\n    /**\n     * @name Two.SVGRenderer#defs\n     * @property {SvgDefintionsElement} - The `<defs />` to apply gradients, patterns, and bitmap imagery.\n     */\n    this.defs = svg.createElement('defs');\n    this.defs._flagUpdate = false;\n    this.domElement.appendChild(this.defs);\n    this.domElement.defs = this.defs;\n    this.domElement.style.overflow = 'hidden';\n  }\n\n  /**\n   * @name Two.SVGRenderer.Utils\n   * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<svg />`.\n   */\n  static Utils = svg;\n\n  /**\n   * @name Two.SVGRenderer#setSize\n   * @function\n   * @param {Number} width - The new width of the renderer.\n   * @param {Number} height - The new height of the renderer.\n   * @description Change the size of the renderer.\n   * @nota-bene Triggers a `Two.Events.resize`.\n   */\n  setSize(width, height) {\n    this.width = width;\n    this.height = height;\n\n    svg.setAttributes(this.domElement, {\n      width: width,\n      height: height,\n    });\n\n    return this.trigger(Events.Types.resize, width, height);\n  }\n\n  /**\n   * @name Two.SVGRenderer#render\n   * @function\n   * @description Render the current scene to the `<svg />`.\n   */\n  render() {\n    svg.group.render.call(this.scene, this.domElement);\n    svg.defs.update(this.domElement);\n\n    return this;\n  }\n}\n"
  },
  {
    "path": "src/renderers/webgl.d.ts",
    "content": "declare module 'two.js/src/renderers/webgl' {\n  /**\n     * @name Two.WebGLRenderer\n     * @class\n\n     * @param {Object} [parameters] - This object is inherited when constructing a new instance of {@link Two}.\n     * @param {Element} [parameters.domElement] - The `<canvas />` to draw to. If none given a new one will be constructed.\n     * @param {HTMLCanvasElement} [parameters.offscreenElement] - The offscreen two dimensional `<canvas />` to render each element on WebGL texture updates.\n     * @param {Boolean} [parameters.antialias] - Determines whether the canvas should clear render with antialias on.\n     * @description This class is used by {@link Two} when constructing with `type` of `Two.Types.webgl`. It takes Two.js' scenegraph and renders it to a `<canvas />` through the WebGL api.\n     * @see {@link https://www.khronos.org/registry/webgl/specs/latest/1.0/}\n     */\n  export class Renderer extends Events {\n    /**\n     * @name Two.WebGLRenderer.Utils\n     * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />` through the WebGL API.\n     */\n    static Utils: {\n      precision: number;\n      isHidden: RegExp;\n      canvas: any;\n      alignments: {\n        left: string;\n        middle: string;\n        right: string;\n      };\n      matrix: Matrix;\n      group: {\n        removeChild: (child: any, gl: any) => void;\n        /**\n                 * @function\n                 // * @type {(gl: any, programs: any) => any}\n                 * @param {WebGLContext} gl\n                 * @param {Object} programs\n                 */\n        render: (gl: any, programs: any) => any;\n      };\n      path: {\n        updateCanvas: (elem: any) => void;\n        getBoundingClientRect: (vertices: any, border: any, rect: any) => void;\n        render: (gl: any, programs: any, forcedParent: any) => any;\n      };\n      points: {\n        updateCanvas: (elem: any) => void;\n        render: (gl: any, programs: any, forcedParent: any) => any;\n      };\n      text: {\n        updateCanvas: (elem: any) => void;\n        getBoundingClientRect: (elem: any, rect: any) => void;\n        render: (gl: any, programs: any, forcedParent: any) => any;\n      };\n      'linear-gradient': {\n        render: (ctx: any, parent: any) => any;\n      };\n      'radial-gradient': {\n        render: (ctx: any, parent: any) => any;\n      };\n      texture: {\n        render: (ctx: any, elem: any) => any;\n      };\n      updateTexture: (gl: any, elem: any) => void;\n      program: {\n        create: (gl: any, shaders: any) => any;\n      };\n      TextureRegistry: Registry;\n    };\n    constructor(params?: any);\n    /**\n     * @name Two.WebGLRenderer#domElement\n     * @property {Element} - The `<canvas />` associated with the Two.js scene.\n     */\n    domElement: HTMLCanvasElement;\n    /**\n     * @name Two.WebGLRenderer#scene\n     * @property {Group} - The root group of the scenegraph.\n     */\n    scene: Group;\n    _renderer: {\n      type: string;\n      matrix: number[];\n      scale: number;\n      opacity: number;\n    };\n    _flagMatrix: boolean;\n    /**\n     * @name Two.WebGLRenderer#overdraw\n     * @property {Boolean} - Determines whether the canvas clears the background each draw call.\n     * @default true\n     */\n    overdraw: any;\n    ctx: any;\n    /**\n     * @name Two.WebGLRenderer#programs\n     * @property {Object} - Associated WebGL programs to render all elements from the scenegraph.\n     */\n    programs: {\n      current: any;\n      buffers: {\n        position: any;\n      };\n      resolution: {\n        width: number;\n        height: number;\n        ratio: number;\n        flagged: boolean;\n      };\n    };\n    /**\n     * @name Two.WebGLRenderer#setSize\n     * @function\n     * @fires resize\n     * @param {Number} width - The new width of the renderer.\n     * @param {Number} height - The new height of the renderer.\n     * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n     * @description Change the size of the renderer.\n     */\n    setSize(width: number, height: number, ratio?: number): any;\n    width: number;\n    height: number;\n    ratio: number;\n    /**\n     * @name Two.WebGLRenderer#render\n     * @function\n     * @description Render the current scene to the `<canvas />`.\n     */\n    render(): Renderer;\n  }\n  import { Events } from 'two.js/src/events';\n  import { Group } from 'two.js/src/group';\n  import { Matrix } from 'two.js/src/matrix';\n  import { Registry } from 'two.js/src/registry';\n}\n"
  },
  {
    "path": "src/renderers/webgl.js",
    "content": "import { Commands } from '../utils/path-commands.js';\n\nimport { root } from '../utils/root.js';\nimport {\n  getPoT,\n  mod,\n  NumArray,\n  TWO_PI,\n  getEffectiveStrokeWidth,\n} from '../utils/math.js';\nimport { shaders } from '../utils/shaders.js';\nimport { Events } from '../events.js';\nimport { TwoError } from '../utils/error.js';\nimport { getRatio } from '../utils/device-pixel-ratio.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Group } from '../group.js';\nimport { Vector } from '../vector.js';\nimport { Matrix } from '../matrix.js';\nimport { Registry } from '../registry.js';\n\nimport { LinearGradient } from '../effects/linear-gradient.js';\nimport { RadialGradient } from '../effects/radial-gradient.js';\nimport { Texture } from '../effects/texture.js';\n\nimport { Renderer as CanvasRenderer } from './canvas.js';\n\n// Constants\n\nconst multiplyMatrix = Matrix.Multiply,\n  identity = [1, 0, 0, 0, 1, 0, 0, 0, 1],\n  transformation = new NumArray(9),\n  CanvasUtils = CanvasRenderer.Utils,\n  vector = new Vector();\n\nconst quad = new NumArray([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);\n\nconst webgl = {\n  precision: 0.9,\n\n  isHidden: /(undefined|none|transparent)/i,\n\n  canvas: root.document\n    ? root.document.createElement('canvas')\n    : { getContext: function () {} },\n\n  alignments: {\n    left: 'start',\n    middle: 'center',\n    right: 'end',\n  },\n\n  matrix: new Matrix(),\n\n  group: {\n    removeChild: function (child, gl) {\n      if (child.children) {\n        for (let i = 0; i < child.children.length; i++) {\n          webgl.group.removeChild(child.children[i], gl);\n        }\n      }\n      // Deallocate texture to free up gl memory.\n      if (child._renderer.texture) {\n        gl.deleteTexture(child._renderer.texture);\n        delete child._renderer.texture;\n      }\n      // Deallocate vertices to free up gl memory.\n      if (child._renderer.positionBuffer) {\n        gl.deleteBuffer(child._renderer.positionBuffer);\n        delete child._renderer.positionBuffer;\n      }\n    },\n\n    /**\n     * @function\n     // * @type {(gl: any, programs: any) => any}\n     * @param {WebGLContext} gl\n     * @param {Object} programs\n     */\n    render: function (gl, programs) {\n      if (!this._visible) {\n        return;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      const parent = this.parent;\n      const flagParentMatrix =\n        (parent._matrix && parent._matrix.manual) || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n\n      if (flagParentMatrix || flagMatrix) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n\n        // Reduce amount of object / array creation / deletion\n        this._matrix.toTransformArray(true, transformation);\n\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix,\n        );\n\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n\n        if (this._scale instanceof Vector) {\n          this._renderer.scale.x = this._scale.x;\n          this._renderer.scale.y = this._scale.y;\n        } else {\n          this._renderer.scale.x = this._scale;\n          this._renderer.scale.y = this._scale;\n        }\n\n        if (!/renderer/i.test(parent._renderer.type)) {\n          this._renderer.scale.x *= parent._renderer.scale.x;\n          this._renderer.scale.y *= parent._renderer.scale.y;\n        }\n\n        if (flagParentMatrix) {\n          this._flagMatrix = true;\n        }\n      }\n\n      if (this._mask) {\n        // Stencil away everything that isn't rendered by the mask\n        gl.clear(gl.STENCIL_BUFFER_BIT);\n        gl.enable(gl.STENCIL_TEST);\n\n        gl.stencilFunc(gl.ALWAYS, 1, 0);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n        // Don't draw the element onto the canvas, only onto the stencil buffer\n        gl.colorMask(false, false, false, false);\n\n        const prop = CanvasRenderer.Utils.getRendererType(\n          this._mask._renderer.type,\n        );\n        webgl[prop].render.call(this._mask, gl, programs, this);\n\n        gl.stencilFunc(gl.EQUAL, 1, 0xff);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n        gl.colorMask(true, true, true, true);\n      }\n\n      this._flagOpacity = parent._flagOpacity || this._flagOpacity;\n\n      this._renderer.opacity =\n        this._opacity *\n        (parent && parent._renderer ? parent._renderer.opacity : 1);\n\n      let i;\n      if (this._flagSubtractions) {\n        for (i = 0; i < this.subtractions.length; i++) {\n          webgl.group.removeChild(this.subtractions[i], gl);\n        }\n      }\n\n      for (i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        const prop = CanvasRenderer.Utils.getRendererType(child._renderer.type);\n        webgl[prop].render.call(child, gl, programs);\n      }\n\n      if (this._mask) {\n        gl.disable(gl.STENCIL_TEST);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  path: {\n    updateCanvas: function (gl, elem) {\n      let prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y;\n      let isOffset;\n\n      const commands = elem._renderer.vertices;\n      const canvas = this.canvas;\n      const ctx = this.ctx;\n      const ratio = gl.renderer.ratio;\n\n      // Styles\n      const scale = vector.copy(elem._renderer.scale).multiply(ratio);\n      const stroke = elem._stroke;\n      const linewidth = elem._linewidth;\n      const fill = elem._fill;\n      const opacity = elem._renderer.opacity || elem._opacity;\n      const cap = elem._cap;\n      const join = elem._join;\n      const miter = elem._miter;\n      const closed = elem._closed;\n      const dashes = elem.dashes;\n      const length = commands.length;\n      const last = length - 1;\n\n      canvas.width = Math.max(\n        Math.ceil(elem._renderer.rect.width * scale.x),\n        1,\n      );\n      canvas.height = Math.max(\n        Math.ceil(elem._renderer.rect.height * scale.y),\n        1,\n      );\n\n      const centroid = elem._renderer.rect.centroid;\n      const cx = centroid.x;\n      const cy = centroid.y;\n\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n      if (fill) {\n        if (typeof fill === 'string') {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = CanvasRenderer.Utils.getRendererType(\n            fill._renderer.type,\n          );\n          webgl[prop].render.call(fill, ctx, elem);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === 'string') {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = CanvasRenderer.Utils.getRendererType(\n            stroke._renderer.type,\n          );\n          webgl[prop].render.call(stroke, ctx, elem);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(elem);\n        }\n        if (miter) {\n          ctx.miterLimit = miter;\n        }\n        if (join) {\n          ctx.lineJoin = join;\n        }\n        if (!closed && cap) {\n          ctx.lineCap = cap;\n        }\n      }\n      if (typeof opacity === 'number') {\n        ctx.globalAlpha = opacity;\n      }\n\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n\n      let d, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n      ctx.save();\n      ctx.scale(scale.x, scale.y);\n\n      ctx.translate(cx, cy);\n\n      ctx.beginPath();\n      for (let i = 0; i < commands.length; i++) {\n        const b = commands[i];\n\n        x = b.x;\n        y = b.y;\n\n        switch (b.command) {\n          case Commands.close:\n            ctx.closePath();\n            break;\n\n          case Commands.arc:\n            rx = b.rx;\n            ry = b.ry;\n            xAxisRotation = b.xAxisRotation;\n            largeArcFlag = b.largeArcFlag;\n            sweepFlag = b.sweepFlag;\n\n            prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0);\n            a = commands[prev];\n\n            ax = a.x;\n            ay = a.y;\n\n            CanvasUtils.renderSvgArcCommand(\n              ctx,\n              ax,\n              ay,\n              rx,\n              ry,\n              largeArcFlag,\n              sweepFlag,\n              xAxisRotation,\n              x,\n              y,\n            );\n            break;\n\n          case Commands.curve:\n            prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0);\n\n            a = commands[prev];\n\n            ar = (a.controls && a.controls.right) || Vector.zero;\n            bl = (b.controls && b.controls.left) || Vector.zero;\n\n            if (a._relative) {\n              vx = ar.x + a.x;\n              vy = ar.y + a.y;\n            } else {\n              vx = ar.x;\n              vy = ar.y;\n            }\n\n            if (b._relative) {\n              ux = bl.x + b.x;\n              uy = bl.y + b.y;\n            } else {\n              ux = bl.x;\n              uy = bl.y;\n            }\n\n            ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n\n            if (i >= last && closed) {\n              c = d;\n\n              br = (b.controls && b.controls.right) || Vector.zero;\n              cl = (c.controls && c.controls.left) || Vector.zero;\n\n              if (b._relative) {\n                vx = br.x + b.x;\n                vy = br.y + b.y;\n              } else {\n                vx = br.x;\n                vy = br.y;\n              }\n\n              if (c._relative) {\n                ux = cl.x + c.x;\n                uy = cl.y + c.y;\n              } else {\n                ux = cl.x;\n                uy = cl.y;\n              }\n\n              x = c.x;\n              y = c.y;\n\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n            }\n\n            break;\n\n          case Commands.line:\n            ctx.lineTo(x, y);\n            break;\n\n          case Commands.move:\n            d = b;\n            ctx.moveTo(x, y);\n            break;\n        }\n      }\n\n      // Loose ends\n\n      if (closed) {\n        ctx.closePath();\n      }\n\n      if (!webgl.isHidden.test(fill)) {\n        isOffset = fill._renderer && fill._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n          ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n        }\n        ctx.fill();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n\n      if (!webgl.isHidden.test(stroke)) {\n        isOffset = stroke._renderer && stroke._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n          ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n          ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n        }\n        ctx.stroke();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n\n      ctx.restore();\n    },\n\n    // Returns the rect of a set of verts. Typically takes vertices that are\n    // \"centered\" around 0 and returns them to be anchored upper-left.\n    getBoundingClientRect: function (vertices, border, rect) {\n      let left = Infinity,\n        right = -Infinity,\n        top = Infinity,\n        bottom = -Infinity,\n        width,\n        height;\n\n      vertices.forEach(function (v) {\n        const x = v.x,\n          y = v.y,\n          controls = v.controls;\n        let a, b, c, d, cl, cr;\n\n        top = Math.min(y, top);\n        left = Math.min(x, left);\n        right = Math.max(x, right);\n        bottom = Math.max(y, bottom);\n\n        if (!v.controls) {\n          return;\n        }\n\n        cl = controls.left;\n        cr = controls.right;\n\n        if (!cl || !cr) {\n          return;\n        }\n\n        a = v._relative ? cl.x + x : cl.x;\n        b = v._relative ? cl.y + y : cl.y;\n        c = v._relative ? cr.x + x : cr.x;\n        d = v._relative ? cr.y + y : cr.y;\n\n        if (!a || !b || !c || !d) {\n          return;\n        }\n\n        top = Math.min(b, d, top);\n        left = Math.min(a, c, left);\n        right = Math.max(a, c, right);\n        bottom = Math.max(b, d, bottom);\n      });\n\n      // Expand borders\n\n      if (typeof border === 'number') {\n        top -= border;\n        left -= border;\n        right += border;\n        bottom += border;\n      }\n\n      width = right - left;\n      height = bottom - top;\n\n      rect.top = top;\n      rect.left = left;\n      rect.right = right;\n      rect.bottom = bottom;\n      rect.width = width;\n      rect.height = height;\n\n      if (!rect.centroid) {\n        rect.centroid = {};\n      }\n\n      rect.centroid.x = -left;\n      rect.centroid.y = -top;\n    },\n\n    render: function (gl, programs, forcedParent) {\n      if (!this._visible || !this._opacity) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      // Calculate what changed\n\n      const parent = forcedParent || this.parent;\n      const prop = CanvasRenderer.Utils.getRendererType(this._renderer.type);\n      const program = programs[prop];\n      const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const parentChanged = this._renderer.parent !== parent;\n      const flagTexture =\n        this._flagVertices ||\n        this._flagFill ||\n        (this._fill instanceof LinearGradient &&\n          (this._fill._flagSpread ||\n            this._fill._flagStops ||\n            this._fill._flagEndPoints)) ||\n        (this._fill instanceof RadialGradient &&\n          (this._fill._flagSpread ||\n            this._fill._flagStops ||\n            this._fill._flagRadius ||\n            this._fill._flagCenter ||\n            this._fill._flagFocal)) ||\n        (this._fill instanceof Texture &&\n          ((this._fill._flagLoaded && this._fill.loaded) ||\n            this._fill._flagImage ||\n            this._fill._flagVideo ||\n            this._fill._flagRepeat ||\n            this._fill._flagOffset ||\n            this._fill._flagScale)) ||\n        (this._stroke instanceof LinearGradient &&\n          (this._stroke._flagSpread ||\n            this._stroke._flagStops ||\n            this._stroke._flagEndPoints)) ||\n        (this._stroke instanceof RadialGradient &&\n          (this._stroke._flagSpread ||\n            this._stroke._flagStops ||\n            this._stroke._flagRadius ||\n            this._stroke._flagCenter ||\n            this._stroke._flagFocal)) ||\n        (this._stroke instanceof Texture &&\n          ((this._stroke._flagLoaded && this._stroke.loaded) ||\n            this._stroke._flagImage ||\n            this._stroke._flagVideo ||\n            this._stroke._flagRepeat ||\n            this._stroke._flagOffset ||\n            this._fill._flagScale)) ||\n        this._flagStroke ||\n        this._flagLinewidth ||\n        this._flagOpacity ||\n        parent._flagOpacity ||\n        this._flagVisible ||\n        this._flagCap ||\n        this._flagJoin ||\n        this._flagMiter ||\n        this._flagScale ||\n        (this.dashes && this.dashes.length > 0) ||\n        !this._renderer.texture;\n\n      if (flagParentMatrix || flagMatrix || parentChanged) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n\n        // Reduce amount of object / array creation / deletion\n\n        this._matrix.toTransformArray(true, transformation);\n\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix,\n        );\n\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x * parent._renderer.scale.x;\n          sy = this._scale.y * parent._renderer.scale.y;\n        } else {\n          sx = this._scale * parent._renderer.scale.x;\n          sy = this._scale * parent._renderer.scale.y;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n\n        if (parentChanged) {\n          this._renderer.parent = parent;\n        }\n      }\n\n      if (this._mask) {\n        // Stencil away everything that isn't rendered by the mask\n        gl.clear(gl.STENCIL_BUFFER_BIT);\n        gl.enable(gl.STENCIL_TEST);\n\n        gl.stencilFunc(gl.ALWAYS, 1, 0);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n        // Don't draw the element onto the canvas, only onto the stencil buffer\n        gl.colorMask(false, false, false, false);\n\n        const prop = CanvasRenderer.Utils.getRendererType(\n          this._mask._renderer.type,\n        );\n        webgl[prop].render.call(this._mask, gl, programs, this);\n\n        gl.stencilFunc(gl.EQUAL, 1, 0xff);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n        gl.colorMask(true, true, true, true);\n      }\n\n      if (flagTexture) {\n        if (!this._renderer.rect) {\n          this._renderer.rect = {};\n        }\n\n        this._renderer.opacity = this._opacity * parent._renderer.opacity;\n\n        webgl.path.getBoundingClientRect(\n          this._renderer.vertices,\n          this._linewidth,\n          this._renderer.rect,\n        );\n\n        webgl.updateTexture.call(webgl, gl, this);\n      } else {\n        // We still need to update child Two elements on the fill and\n        // stroke properties.\n        if (this._fill && this._fill._update) {\n          this._fill._update();\n        }\n        if (this._stroke && this._stroke._update) {\n          this._stroke._update();\n        }\n      }\n\n      if ((this._clip && !forcedParent) || !this._renderer.texture) {\n        return this;\n      }\n\n      if (programs.current !== program) {\n        gl.useProgram(program);\n\n        gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n        gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n        gl.enableVertexAttribArray(program.position);\n        gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n        if (!programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, 'u_resolution'),\n            programs.resolution.width,\n            programs.resolution.height,\n          );\n        }\n\n        programs.current = program;\n      }\n\n      if (programs.resolution.flagged) {\n        gl.uniform2f(\n          gl.getUniformLocation(program, 'u_resolution'),\n          programs.resolution.width,\n          programs.resolution.height,\n        );\n      }\n\n      // Draw Texture\n      gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n\n      // Draw Rect\n      const rect = this._renderer.rect;\n      gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n      gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n      gl.drawArrays(gl.TRIANGLES, 0, 6);\n\n      if (this._mask) {\n        gl.disable(gl.STENCIL_TEST);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  points: {\n    // The canvas is a texture that is a rendering of one vertex\n    updateCanvas: function (gl, elem) {\n      let isOffset;\n\n      const canvas = this.canvas;\n      const ctx = this.ctx;\n      const ratio = gl.renderer.ratio;\n\n      // Styles\n      const stroke = elem._stroke;\n      const linewidth = elem._linewidth;\n      const fill = elem._fill;\n      const opacity = elem._renderer.opacity || elem._opacity;\n      const dashes = elem.dashes;\n      const size = elem._size * ratio;\n      const closed = elem._closed;\n      let dimension = size;\n\n      if (!webgl.isHidden.test(stroke)) {\n        dimension += linewidth;\n      }\n\n      canvas.width = getPoT(dimension);\n      canvas.height = canvas.width;\n\n      const aspect = dimension / canvas.width;\n\n      const cx = canvas.width / 2;\n      const cy = canvas.height / 2;\n\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n      if (fill) {\n        if (typeof fill === 'string') {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = CanvasRenderer.Utils.getRendererType(\n            fill._renderer.type,\n          );\n          webgl[prop].render.call(fill, ctx, elem);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === 'string') {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = CanvasRenderer.Utils.getRendererType(\n            stroke._renderer.type,\n          );\n          webgl[prop].render.call(stroke, ctx, elem);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(elem) / aspect;\n        }\n      }\n      if (typeof opacity === 'number') {\n        ctx.globalAlpha = opacity;\n      }\n\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n\n      ctx.save();\n      ctx.translate(cx, cy);\n      ctx.scale(webgl.precision, webgl.precision); // Precision for even rendering\n      ctx.beginPath();\n      ctx.arc(0, 0, (size / aspect) * 0.5, 0, TWO_PI);\n      ctx.restore();\n\n      // Loose ends\n\n      if (closed) {\n        ctx.closePath();\n      }\n\n      if (!webgl.isHidden.test(fill)) {\n        isOffset = fill._renderer && fill._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n          ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n        }\n        ctx.fill();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n\n      if (!webgl.isHidden.test(stroke)) {\n        isOffset = stroke._renderer && stroke._renderer.offset;\n        if (isOffset) {\n          ctx.save();\n          ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n          ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n          ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n        }\n        ctx.stroke();\n        if (isOffset) {\n          ctx.restore();\n        }\n      }\n    },\n\n    render: function (gl, programs, forcedParent) {\n      if (!this._visible || !this._opacity) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      // Calculate what changed\n\n      let size = this._size;\n      const parent = forcedParent || this.parent;\n      const program = programs[this._renderer.type];\n      const sizeAttenuation = this._sizeAttenuation;\n      const stroke = this._stroke;\n      const linewidth = this._linewidth;\n      const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const parentChanged = this._renderer.parent !== parent;\n      const commands = this._renderer.vertices;\n      const length = this._renderer.collection.length;\n      const flagVertices = this._flagVertices;\n      const flagTexture =\n        this._flagFill ||\n        (this._fill instanceof LinearGradient &&\n          (this._fill._flagSpread ||\n            this._fill._flagStops ||\n            this._fill._flagEndPoints)) ||\n        (this._fill instanceof RadialGradient &&\n          (this._fill._flagSpread ||\n            this._fill._flagStops ||\n            this._fill._flagRadius ||\n            this._fill._flagCenter ||\n            this._fill._flagFocal)) ||\n        (this._fill instanceof Texture &&\n          ((this._fill._flagLoaded && this._fill.loaded) ||\n            this._fill._flagImage ||\n            this._fill._flagVideo ||\n            this._fill._flagRepeat ||\n            this._fill._flagOffset ||\n            this._fill._flagScale)) ||\n        (this._stroke instanceof LinearGradient &&\n          (this._stroke._flagSpread ||\n            this._stroke._flagStops ||\n            this._stroke._flagEndPoints)) ||\n        (this._stroke instanceof RadialGradient &&\n          (this._stroke._flagSpread ||\n            this._stroke._flagStops ||\n            this._stroke._flagRadius ||\n            this._stroke._flagCenter ||\n            this._stroke._flagFocal)) ||\n        (this._stroke instanceof Texture &&\n          ((this._stroke._flagLoaded && this._stroke.loaded) ||\n            this._stroke._flagImage ||\n            this._stroke._flagVideo ||\n            this._stroke._flagRepeat ||\n            this._stroke._flagOffset ||\n            this._fill._flagScale)) ||\n        this._flagStroke ||\n        this._flagLinewidth ||\n        this._flagOpacity ||\n        parent._flagOpacity ||\n        this._flagVisible ||\n        this._flagScale ||\n        (this.dashes && this.dashes.length > 0) ||\n        !this._renderer.texture;\n\n      if (flagParentMatrix || flagMatrix || parentChanged) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n\n        // Reduce amount of object / array creation / deletion\n\n        this._matrix.toTransformArray(true, transformation);\n\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix,\n        );\n\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x * parent._renderer.scale.x;\n          sy = this._scale.y * parent._renderer.scale.y;\n        } else {\n          sx = this._scale * parent._renderer.scale.x;\n          sy = this._scale * parent._renderer.scale.y;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n\n        if (parentChanged) {\n          this._renderer.parent = parent;\n        }\n      }\n\n      if (flagVertices) {\n        const positionBuffer = this._renderer.positionBuffer;\n        if (positionBuffer) {\n          gl.deleteBuffer(positionBuffer);\n        }\n\n        // Bind the vertex buffer\n        this._renderer.positionBuffer = gl.createBuffer();\n        gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.positionBuffer);\n        gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n        gl.enableVertexAttribArray(program.position);\n        gl.bufferData(gl.ARRAY_BUFFER, commands, gl.STATIC_DRAW);\n      }\n\n      if (flagTexture) {\n        this._renderer.opacity = this._opacity * parent._renderer.opacity;\n\n        webgl.updateTexture.call(webgl, gl, this);\n      } else {\n        // We still need to update child Two elements on the fill and\n        // stroke properties.\n        if (this._fill && this._fill._update) {\n          this._fill._update();\n        }\n        if (this._stroke && this._stroke._update) {\n          this._stroke._update();\n        }\n      }\n\n      if ((this._clip && !forcedParent) || !this._renderer.texture) {\n        return this;\n      }\n\n      if (!webgl.isHidden.test(stroke)) {\n        size += linewidth;\n      }\n      size /= webgl.precision;\n      if (sizeAttenuation) {\n        size *= Math.max(this._renderer.scale.x, this._renderer.scale.y);\n      }\n\n      if (programs.current !== program) {\n        gl.useProgram(program);\n        if (!programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, 'u_resolution'),\n            programs.resolution.width,\n            programs.resolution.height,\n          );\n        }\n        programs.current = program;\n      }\n\n      if (programs.resolution.flagged) {\n        gl.uniform2f(\n          gl.getUniformLocation(program, 'u_resolution'),\n          programs.resolution.width,\n          programs.resolution.height,\n        );\n      }\n\n      // Draw Texture\n      gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n\n      // Draw Points\n      gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n      gl.uniform1f(program.size, size * programs.resolution.ratio);\n      gl.drawArrays(gl.POINTS, 0, length);\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  text: {\n    updateCanvas: function (gl, elem) {\n      const canvas = this.canvas;\n      const ctx = this.ctx;\n      const ratio = gl.renderer.ratio;\n\n      // Styles\n      const scale = vector.copy(elem._renderer.scale).multiply(ratio);\n      const stroke = elem._stroke;\n      const linewidth = elem._linewidth;\n      const fill = elem._fill;\n      const opacity = elem._renderer.opacity || elem._opacity;\n      const dashes = elem.dashes;\n      const decoration = elem._decoration;\n      const direction = elem._direction;\n\n      canvas.width = Math.max(\n        Math.ceil(elem._renderer.rect.width * scale.x),\n        1,\n      );\n      canvas.height = Math.max(\n        Math.ceil(elem._renderer.rect.height * scale.y),\n        1,\n      );\n\n      const centroid = elem._renderer.rect.centroid;\n      const cx = centroid.x;\n      const cy = centroid.y;\n\n      let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n      const isOffset =\n        fill._renderer &&\n        fill._renderer.offset &&\n        stroke._renderer &&\n        stroke._renderer.offset;\n\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n      if (!isOffset) {\n        ctx.font = [\n          elem._style,\n          elem._weight,\n          elem._size + 'px/' + elem._leading + 'px',\n          elem._family,\n        ].join(' ');\n      }\n\n      ctx.textAlign = 'center';\n      ctx.textBaseline = 'middle';\n      ctx.textDirection = direction;\n\n      // Styles\n      if (fill) {\n        if (typeof fill === 'string') {\n          ctx.fillStyle = fill;\n        } else {\n          const prop = CanvasRenderer.Utils.getRendererType(\n            fill._renderer.type,\n          );\n          webgl[prop].render.call(fill, ctx, elem);\n          ctx.fillStyle = fill._renderer.effect;\n        }\n      }\n      if (stroke) {\n        if (typeof stroke === 'string') {\n          ctx.strokeStyle = stroke;\n        } else {\n          const prop = CanvasRenderer.Utils.getRendererType(\n            stroke._renderer.type,\n          );\n          webgl[prop].render.call(stroke, ctx, elem);\n          ctx.strokeStyle = stroke._renderer.effect;\n        }\n        if (linewidth) {\n          ctx.lineWidth = getEffectiveStrokeWidth(elem);\n        }\n      }\n      if (typeof opacity === 'number') {\n        ctx.globalAlpha = opacity;\n      }\n      if (dashes && dashes.length > 0) {\n        ctx.lineDashOffset = dashes.offset || 0;\n        ctx.setLineDash(dashes);\n      }\n\n      ctx.save();\n      ctx.scale(scale.x, scale.y);\n      ctx.translate(cx, cy);\n\n      if (!webgl.isHidden.test(fill)) {\n        if (fill._renderer && fill._renderer.offset) {\n          sx = fill._renderer.scale.x;\n          sy = fill._renderer.scale.y;\n\n          ctx.save();\n          ctx.translate(-fill._renderer.offset.x, -fill._renderer.offset.y);\n          ctx.scale(sx, sy);\n\n          a = elem._size / fill._renderer.scale.y;\n          b = elem._leading / fill._renderer.scale.y;\n          ctx.font = [\n            elem._style,\n            elem._weight,\n            a + 'px/',\n            b + 'px',\n            elem._family,\n          ].join(' ');\n\n          c = fill._renderer.offset.x / fill._renderer.scale.x;\n          d = fill._renderer.offset.y / fill._renderer.scale.y;\n\n          ctx.fillText(elem.value, c, d);\n          ctx.restore();\n        } else {\n          ctx.fillText(elem.value, 0, 0);\n        }\n      }\n\n      if (!webgl.isHidden.test(stroke)) {\n        if (stroke._renderer && stroke._renderer.offset) {\n          sx = stroke._renderer.scale.x;\n          sy = stroke._renderer.scale.y;\n\n          ctx.save();\n          ctx.translate(-stroke._renderer.offset.x, -stroke._renderer.offset.y);\n          ctx.scale(sx, sy);\n\n          a = elem._size / stroke._renderer.scale.y;\n          b = elem._leading / stroke._renderer.scale.y;\n          ctx.font = [\n            elem._style,\n            elem._weight,\n            a + 'px/',\n            b + 'px',\n            elem._family,\n          ].join(' ');\n\n          c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n          d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n          e = linewidth / stroke._renderer.scale.x;\n\n          ctx.lineWidth = e;\n          ctx.strokeText(elem.value, c, d);\n          ctx.restore();\n        } else {\n          ctx.strokeText(elem.value, 0, 0);\n        }\n      }\n\n      // Handle text-decoration\n      if (/(underline|strikethrough)/i.test(decoration)) {\n        const metrics = ctx.measureText(elem.value);\n\n        switch (decoration) {\n          case 'underline':\n            y1 = metrics.actualBoundingBoxDescent;\n            y2 = metrics.actualBoundingBoxDescent;\n            break;\n          case 'strikethrough':\n            y1 = 0;\n            y2 = 0;\n            break;\n        }\n\n        x1 = -metrics.width / 2;\n        x2 = metrics.width / 2;\n\n        ctx.lineWidth = Math.max(Math.floor(elem._size / 15), 1);\n        ctx.strokeStyle = ctx.fillStyle;\n\n        ctx.beginPath();\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n        ctx.stroke();\n      }\n\n      ctx.restore();\n    },\n\n    getBoundingClientRect: function (elem, rect) {\n      const ctx = webgl.ctx;\n\n      ctx.font = [\n        elem._style,\n        elem._weight,\n        elem._size + 'px/' + elem._leading + 'px',\n        elem._family,\n      ].join(' ');\n\n      ctx.textAlign = 'center';\n      ctx.textBaseline =\n        CanvasRenderer.Utils.baselines[elem._baseline] || elem._baseline;\n\n      const metrics = ctx.measureText(elem._value);\n      let width = metrics.width;\n      // TODO: Why does the height need to be scaled by 15%\n      // in order to not cut off / mask the bitmap data.\n      let height =\n        1.15 *\n        (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);\n\n      if (this._linewidth && !webgl.isHidden.test(this._stroke)) {\n        width += this._linewidth * 2;\n        height += this._linewidth * 2;\n      }\n\n      const w = width / 2;\n      const h = height / 2;\n\n      switch (webgl.alignments[elem._alignment] || elem._alignment) {\n        case webgl.alignments.left:\n          if (elem.direction === 'ltr') {\n            rect.left = 0;\n            rect.right = width;\n          } else {\n            rect.left = -width;\n            rect.right = 0;\n          }\n          break;\n        case webgl.alignments.right:\n          if (elem.direction === 'ltr') {\n            rect.left = -width;\n            rect.right = 0;\n          } else {\n            rect.left = 0;\n            rect.right = width;\n          }\n          break;\n        default:\n          rect.left = -w;\n          rect.right = w;\n      }\n\n      // TODO: Gradients aren't inherited...\n      switch (elem._baseline) {\n        case 'bottom':\n          rect.top = -height;\n          rect.bottom = 0;\n          break;\n        case 'top':\n          rect.top = 0;\n          rect.bottom = height;\n          break;\n        case 'baseline':\n          rect.top = -h * 1.5; // TODO: Improve calculation based on text metrics\n          rect.bottom = h * 0.5;\n          break;\n        default:\n          rect.top = -h;\n          rect.bottom = h;\n      }\n\n      rect.width = width;\n      rect.height = height;\n\n      if (!rect.centroid) {\n        rect.centroid = {};\n      }\n\n      // TODO:\n      rect.centroid.x = w;\n      rect.centroid.y = h;\n    },\n\n    render: function (gl, programs, forcedParent) {\n      if (!this._visible || !this._opacity) {\n        return this;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      // Calculate what changed\n\n      const parent = forcedParent || this.parent;\n      const program = programs[this._renderer.type];\n      const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n      const flagMatrix = this._matrix.manual || this._flagMatrix;\n      const parentChanged = this._renderer.parent !== parent;\n      const flagTexture =\n        this._flagVertices ||\n        this._flagFill ||\n        (this._fill instanceof LinearGradient &&\n          (this._fill._flagSpread ||\n            this._fill._flagStops ||\n            this._fill._flagEndPoints)) ||\n        (this._fill instanceof RadialGradient &&\n          (this._fill._flagSpread ||\n            this._fill._flagStops ||\n            this._fill._flagRadius ||\n            this._fill._flagCenter ||\n            this._fill._flagFocal)) ||\n        (this._fill instanceof Texture &&\n          ((this._fill._flagLoaded && this._fill.loaded) ||\n            this._fill._flagImage ||\n            this._fill._flagVideo ||\n            this._fill._flagRepeat ||\n            this._fill._flagOffset ||\n            this._fill._flagScale)) ||\n        (this._stroke instanceof LinearGradient &&\n          (this._stroke._flagSpread ||\n            this._stroke._flagStops ||\n            this._stroke._flagEndPoints)) ||\n        (this._stroke instanceof RadialGradient &&\n          (this._stroke._flagSpread ||\n            this._stroke._flagStops ||\n            this._stroke._flagRadius ||\n            this._stroke._flagCenter ||\n            this._stroke._flagFocal)) ||\n        (this._stroke instanceof Texture &&\n          ((this._stroke._flagLoaded && this._stroke.loaded) ||\n            this._stroke._flagImage ||\n            this._stroke._flagVideo ||\n            this._stroke._flagRepeat ||\n            this._stroke._flagOffset ||\n            this._fill._flagScale)) ||\n        this._flagStroke ||\n        this._flagLinewidth ||\n        this._flagOpacity ||\n        parent._flagOpacity ||\n        this._flagVisible ||\n        this._flagScale ||\n        this._flagValue ||\n        this._flagFamily ||\n        this._flagSize ||\n        this._flagLeading ||\n        this._flagAlignment ||\n        this._flagBaseline ||\n        this._flagStyle ||\n        this._flagWeight ||\n        this._flagDecoration ||\n        (this.dashes && this.dashes.length > 0) ||\n        !this._renderer.texture;\n\n      if (flagParentMatrix || flagMatrix || parentChanged) {\n        if (!this._renderer.matrix) {\n          this._renderer.matrix = new NumArray(9);\n        }\n\n        // Reduce amount of object / array creation / deletion\n\n        this._matrix.toTransformArray(true, transformation);\n\n        multiplyMatrix(\n          transformation,\n          parent._renderer.matrix,\n          this._renderer.matrix,\n        );\n\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x * parent._renderer.scale.x;\n          sy = this._scale.y * parent._renderer.scale.y;\n        } else {\n          sx = this._scale * parent._renderer.scale.x;\n          sy = this._scale * parent._renderer.scale.y;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n\n        if (parentChanged) {\n          this._renderer.parent = parent;\n        }\n      }\n\n      if (this._mask) {\n        // Stencil away everything that isn't rendered by the mask\n        gl.clear(gl.STENCIL_BUFFER_BIT);\n        gl.enable(gl.STENCIL_TEST);\n\n        gl.stencilFunc(gl.ALWAYS, 1, 0);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n        // Don't draw the element onto the canvas, only onto the stencil buffer\n        gl.colorMask(false, false, false, false);\n\n        const prop = CanvasRenderer.Utils.getRendererType(\n          this._mask._renderer.type,\n        );\n        webgl[prop].render.call(this._mask, gl, programs, this);\n\n        gl.stencilFunc(gl.EQUAL, 1, 0xff);\n        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n        gl.colorMask(true, true, true, true);\n      }\n\n      if (flagTexture) {\n        if (!this._renderer.rect) {\n          this._renderer.rect = {};\n        }\n\n        this._renderer.opacity = this._opacity * parent._renderer.opacity;\n\n        webgl.text.getBoundingClientRect(this, this._renderer.rect);\n\n        webgl.updateTexture.call(webgl, gl, this);\n      } else {\n        // We still need to update child Two elements on the fill and\n        // stroke properties.\n        if (this._fill && this._fill._update) {\n          this._fill._update();\n        }\n        if (this._stroke && this._stroke._update) {\n          this._stroke._update();\n        }\n      }\n\n      if ((this._clip && !forcedParent) || !this._renderer.texture) {\n        return this;\n      }\n\n      if (programs.current !== program) {\n        gl.useProgram(program);\n\n        gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n        gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n        gl.enableVertexAttribArray(program.position);\n        gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n        if (!programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, 'u_resolution'),\n            programs.resolution.width,\n            programs.resolution.height,\n          );\n        }\n\n        programs.current = program;\n      }\n\n      if (programs.resolution.flagged) {\n        gl.uniform2f(\n          gl.getUniformLocation(program, 'u_resolution'),\n          programs.resolution.width,\n          programs.resolution.height,\n        );\n      }\n\n      // Draw Texture\n      gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n\n      // Draw Rect\n      const rect = this._renderer.rect;\n      gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n      gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n      gl.drawArrays(gl.TRIANGLES, 0, 6);\n\n      if (this._mask) {\n        gl.disable(gl.STENCIL_TEST);\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  'linear-gradient': {\n    render: function (ctx, parent) {\n      if (!ctx.canvas.getContext('2d') || !parent) {\n        return;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      if (\n        !this._renderer.effect ||\n        this._flagEndPoints ||\n        this._flagStops ||\n        this._flagUnits\n      ) {\n        let rect;\n        let lx = this.left._x;\n        let ly = this.left._y;\n        let rx = this.right._x;\n        let ry = this.right._y;\n\n        if (/objectBoundingBox/i.test(this._units)) {\n          // Convert objectBoundingBox units to userSpaceOnUse units\n          rect = parent.getBoundingClientRect(true);\n          lx = (lx - 0.5) * rect.width;\n          ly = (ly - 0.5) * rect.height;\n          rx = (rx - 0.5) * rect.width;\n          ry = (ry - 0.5) * rect.height;\n        }\n\n        this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  'radial-gradient': {\n    render: function (ctx, parent) {\n      if (!ctx.canvas.getContext('2d') || !parent) {\n        return;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      if (\n        !this._renderer.effect ||\n        this._flagCenter ||\n        this._flagFocal ||\n        this._flagRadius ||\n        this._flagStops ||\n        this._flagUnits\n      ) {\n        let rect;\n        let cx = this.center._x;\n        let cy = this.center._y;\n        let fx = this.focal._x;\n        let fy = this.focal._y;\n        let radius = this._radius;\n\n        if (/objectBoundingBox/i.test(this._units)) {\n          // Convert objectBoundingBox units to userSpaceOnUse units\n          rect = parent.getBoundingClientRect(true);\n          cx = (cx - 0.5) * rect.width * 0.5;\n          cy = (cy - 0.5) * rect.height * 0.5;\n          fx = (fx - 0.5) * rect.width * 0.5;\n          fy = (fy - 0.5) * rect.height * 0.5;\n          radius *= Math.min(rect.width, rect.height);\n        }\n\n        this._renderer.effect = ctx.createRadialGradient(\n          cx,\n          cy,\n          0,\n          fx,\n          fy,\n          radius,\n        );\n\n        for (let i = 0; i < this.stops.length; i++) {\n          const stop = this.stops[i];\n          this._renderer.effect.addColorStop(stop._offset, stop._color);\n        }\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  texture: {\n    render: function (ctx, elem) {\n      if (!ctx.canvas.getContext('2d')) {\n        return;\n      }\n\n      if (_.isFunction(this._renderer.onBeforeRender)) {\n        this._renderer.onBeforeRender();\n      }\n\n      this._update();\n\n      const image = this.image;\n\n      if (\n        (this._flagLoaded ||\n          this._flagImage ||\n          this._flagVideo ||\n          this._flagRepeat) &&\n        this.loaded\n      ) {\n        this._renderer.effect = ctx.createPattern(image, this._repeat);\n      } else if (!this._renderer.effect) {\n        return this.flagReset();\n      }\n\n      if (this._flagOffset || this._flagLoaded || this._flagScale) {\n        if (!(this._renderer.offset instanceof Vector)) {\n          this._renderer.offset = new Vector();\n        }\n\n        this._renderer.offset.x = -this._offset.x;\n        this._renderer.offset.y = -this._offset.y;\n\n        if (image) {\n          this._renderer.offset.x += image.width / 2;\n          this._renderer.offset.y += image.height / 2;\n\n          if (this._scale instanceof Vector) {\n            this._renderer.offset.x *= this._scale.x;\n            this._renderer.offset.y *= this._scale.y;\n          } else {\n            this._renderer.offset.x *= this._scale;\n            this._renderer.offset.y *= this._scale;\n          }\n        }\n      }\n\n      if (this._flagScale || this._flagLoaded) {\n        if (!(this._renderer.scale instanceof Vector)) {\n          this._renderer.scale = new Vector();\n        }\n\n        let sx, sy;\n        if (this._scale instanceof Vector) {\n          sx = this._scale.x;\n          sy = this._scale.y;\n        } else {\n          sx = this._scale;\n          sy = this._scale;\n        }\n        this._renderer.scale.x = sx < 0 ? -sx : sx;\n        this._renderer.scale.y = sy < 0 ? -sy : sy;\n      }\n\n      if (_.isFunction(this._renderer.onAfterRender)) {\n        this._renderer.onAfterRender();\n      }\n\n      return this.flagReset();\n    },\n  },\n\n  updateTexture: function (gl, elem) {\n    const prop = CanvasRenderer.Utils.getRendererType(elem._renderer.type);\n    this[prop].updateCanvas.call(webgl, gl, elem);\n\n    if (this.canvas.width <= 0 || this.canvas.height <= 0) {\n      if (elem._renderer.texture) {\n        gl.deleteTexture(elem._renderer.texture);\n      }\n      delete elem._renderer.texture;\n      return;\n    }\n\n    if (!elem._renderer.texture) {\n      elem._renderer.texture = gl.createTexture();\n    }\n\n    gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture);\n\n    // Upload the image into the texture.\n    gl.texImage2D(\n      gl.TEXTURE_2D,\n      0,\n      gl.RGBA,\n      gl.RGBA,\n      gl.UNSIGNED_BYTE,\n      this.canvas,\n    );\n\n    // Set the parameters so we can render any size image.\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n\n    // if ('EXT_texture_filter_anisotropic' in gl.extensions) {\n    //   const e = gl.extensions.EXT_texture_filter_anisotropic;\n    //   const maxAnisotropy = gl.getParameter(e.MAX_TEXTURE_MAX_ANISOTROPY_EXT);\n    //   gl.texParameterf(gl.TEXTURE_2D, e.TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy);\n    // }\n  },\n\n  program: {\n    create: function (gl, shaders) {\n      let program, linked, error;\n      program = gl.createProgram();\n      _.each(shaders, function (s) {\n        gl.attachShader(program, s);\n      });\n\n      gl.linkProgram(program);\n      linked = gl.getProgramParameter(program, gl.LINK_STATUS);\n      if (!linked) {\n        error = gl.getProgramInfoLog(program);\n        gl.deleteProgram(program);\n        throw new TwoError('unable to link program: ' + error);\n      }\n\n      return program;\n    },\n  },\n\n  extensions: {\n    init: function (gl) {\n      const extensions = {};\n      const names = [\n        'EXT_texture_filter_anisotropic',\n        'WEBGL_compressed_texture_s3tc',\n        'OES_texture_float_linear',\n        'WEBGL_multisampled_render_to_texture',\n      ];\n      for (let i = 0; i < names.length; i++) {\n        const name = names[i];\n        extensions[name] = webgl.extensions.get(gl, name);\n      }\n      return extensions;\n    },\n    get: function (gl, name) {\n      return (\n        gl.getExtension(name) ||\n        gl.getExtension(`MOZ_${name}`) ||\n        gl.getExtension(`WEBKIT_${name}`)\n      );\n    },\n  },\n\n  TextureRegistry: new Registry(),\n};\n\nwebgl.ctx = webgl.canvas.getContext('2d');\n\n/**\n * @name Two.WebGLRenderer\n * @class\n * @extends Two.Events\n * @param {Object} [parameters] - This object is inherited when constructing a new instance of {@link Two}.\n * @param {Element} [parameters.domElement] - The `<canvas />` to draw to. If none given a new one will be constructed.\n * @param {HTMLCanvasElement} [parameters.offscreenElement] - The offscreen two dimensional `<canvas />` to render each element on WebGL texture updates.\n * @param {Boolean} [parameters.antialias] - Determines whether the canvas should clear render with antialias on.\n * @description This class is used by {@link Two} when constructing with `type` of `Two.Types.webgl`. It takes Two.js' scenegraph and renders it to a `<canvas />` through the WebGL api.\n * @see {@link https://www.khronos.org/registry/webgl/specs/latest/1.0/}\n */\nexport class Renderer extends Events {\n  constructor(params) {\n    super();\n\n    let gl, program, vs, fs;\n\n    /**\n     * @name Two.WebGLRenderer#domElement\n     * @property {Element} - The `<canvas />` associated with the Two.js scene.\n     */\n    this.domElement = params.domElement || document.createElement('canvas');\n\n    if (typeof params.offscreenElement !== 'undefined') {\n      webgl.canvas = params.offscreenElement;\n      webgl.ctx = webgl.canvas.getContext('2d');\n    }\n\n    /**\n     * @name Two.WebGLRenderer#scene\n     * @property {Two.Group} - The root group of the scenegraph.\n     */\n    this.scene = new Group();\n    this.scene.parent = this;\n\n    this._renderer = {\n      type: 'renderer',\n      matrix: new NumArray(identity),\n      scale: 1,\n      opacity: 1,\n    };\n    this._flagMatrix = true;\n\n    // http://games.greggman.com/game/webgl-and-alpha/\n    // http://www.khronos.org/registry/webgl/specs/latest/#5.2\n    params = _.defaults(params || {}, {\n      antialias: false,\n      alpha: true,\n      premultipliedAlpha: true,\n      stencil: true,\n      preserveDrawingBuffer: true,\n      overdraw: false,\n    });\n\n    /**\n     * @name Two.WebGLRenderer#overdraw\n     * @property {Boolean} - Determines whether the canvas clears the background each draw call.\n     * @default true\n     */\n    this.overdraw = params.overdraw;\n\n    /**\n     * @name Two.WebGLRenderer#ctx\n     * @property {WebGLContext} - Associated two dimensional context to render on the `<canvas />`.\n     */\n    gl = this.ctx =\n      this.domElement.getContext('webgl', params) ||\n      this.domElement.getContext('experimental-webgl', params);\n\n    if (!this.ctx) {\n      throw new TwoError(\n        'unable to create a webgl context. Try using another renderer.',\n      );\n    }\n\n    // Compile Base Shaders to draw in pixel space.\n    vs = shaders.create(gl, shaders.path.vertex, shaders.types.vertex);\n    fs = shaders.create(gl, shaders.path.fragment, shaders.types.fragment);\n\n    /**\n     * @name Two.WebGLRenderer#programs\n     * @property {Object} - Associated WebGL programs to render all elements from the scenegraph.\n     */\n    this.programs = {\n      current: null,\n      buffers: {\n        position: gl.createBuffer(),\n      },\n      resolution: {\n        width: 0,\n        height: 0,\n        ratio: 1,\n        flagged: false,\n      },\n    };\n\n    program = this.programs.path = webgl.program.create(gl, [vs, fs]);\n    this.programs.text = this.programs.path;\n\n    gl.extensions = webgl.extensions.init(gl);\n    gl.renderer = this;\n\n    // Create and bind the drawing buffer\n\n    // look up where the vertex data needs to go.\n    program.position = gl.getAttribLocation(program, 'a_position');\n    program.matrix = gl.getUniformLocation(program, 'u_matrix');\n    program.rect = gl.getUniformLocation(program, 'u_rect');\n\n    // Bind the vertex buffer\n    const positionBuffer = gl.createBuffer();\n    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n    gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n    gl.enableVertexAttribArray(program.position);\n    gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n    // Compile Base Shaders to draw in pixel space.\n    vs = shaders.create(gl, shaders.points.vertex, shaders.types.vertex);\n    fs = shaders.create(gl, shaders.points.fragment, shaders.types.fragment);\n\n    program = this.programs.points = webgl.program.create(gl, [vs, fs]);\n\n    // Create and bind the drawing buffer\n\n    // look up where the vertex data needs to go.\n    program.position = gl.getAttribLocation(program, 'a_position');\n    program.matrix = gl.getUniformLocation(program, 'u_matrix');\n    program.size = gl.getUniformLocation(program, 'u_size');\n\n    // Setup some initial statements of the gl context\n    gl.enable(gl.BLEND);\n\n    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n\n    gl.blendEquation(gl.FUNC_ADD);\n    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n  }\n\n  /**\n   * @name Two.WebGLRenderer.Utils\n   * @property {Object} - A massive object filled with utility functions and properties to render Two.js objects to a `<canvas />` through the WebGL API.\n   */\n  static Utils = webgl;\n\n  /**\n   * @name Two.WebGLRenderer#setSize\n   * @function\n   * @fires resize\n   * @param {Number} width - The new width of the renderer.\n   * @param {Number} height - The new height of the renderer.\n   * @param {Number} [ratio] - The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen.\n   * @description Change the size of the renderer.\n   */\n  setSize(width, height, ratio) {\n    let w, h;\n    const ctx = this.ctx;\n\n    this.width = width;\n    this.height = height;\n\n    this.ratio = typeof ratio === 'undefined' ? getRatio(ctx) : ratio;\n\n    this.domElement.width = width * this.ratio;\n    this.domElement.height = height * this.ratio;\n\n    if (_.isObject(this.domElement.style)) {\n      _.extend(this.domElement.style, {\n        width: width + 'px',\n        height: height + 'px',\n      });\n    }\n\n    // Set for this.stage parent scaling to account for HDPI\n    this._renderer.matrix[0] =\n      this._renderer.matrix[4] =\n      this._renderer.scale =\n        this.ratio;\n\n    this._flagMatrix = true;\n\n    w = width * this.ratio;\n    h = height * this.ratio;\n\n    ctx.viewport(0, 0, w, h);\n\n    this.programs.resolution.width = w;\n    this.programs.resolution.height = h;\n    this.programs.resolution.ratio = this.ratio;\n    this.programs.resolution.flagged = true;\n\n    return this.trigger(Events.Types.resize, width, height, ratio);\n  }\n\n  /**\n   * @name Two.WebGLRenderer#render\n   * @function\n   * @description Render the current scene to the `<canvas />`.\n   */\n  render() {\n    const gl = this.ctx;\n\n    if (!this.overdraw) {\n      gl.clear(gl.COLOR_BUFFER_BIT);\n    }\n\n    webgl.group.render.call(this.scene, gl, this.programs);\n    this._flagMatrix = false;\n    this.programs.resolution.flagged = true;\n\n    return this;\n  }\n}\n"
  },
  {
    "path": "src/shape.d.ts",
    "content": "declare module 'two.js/src/shape' {\n  export interface ShapeHitTestOptions {\n    precision?: number;\n    tolerance?: number;\n    fill?: boolean;\n    stroke?: boolean;\n    ignoreVisibility?: boolean;\n  }\n  /**\n     * @name Two.Shape\n     * @class\n\n     * @description The foundational transformation object for the Two.js scenegraph.\n     */\n  export class Shape extends TwoElement {\n    static Properties: (\n      | 'position'\n      | 'rotation'\n      | 'scale'\n      | 'skewX'\n      | 'skewY'\n      | 'matrix'\n      | 'worldMatrix'\n      | string\n    )[];\n    /**\n     * @name Two.Shape.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Shape} to create a new instance\n     * @returns {Two.Shape}\n     * @description Create a new {@link Two.Shape} from an object notation of a {@link Two.Shape}.\n     * @nota-bene Works in conjunction with {@link Two.Shape#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof TwoElement.fromObject>[0] &\n        ((\n          | {\n              translation?: { x: number; y: number } | Vector;\n              position?: never;\n            }\n          | {\n              position?: { x: number; y: number } | Vector;\n              translation?: never;\n            }\n        ) & {\n          rotation?: number;\n          scale?: number | { x: number; y: number } | Vector;\n          skewX?: number;\n          skewY?: number;\n          matrix?: Parameters<typeof Matrix.fromObject>[0];\n        })\n    ): Shape;\n    /**\n     * @name Two.Shape#_flagMatrix\n     * @private\n     * @property {Boolean} - Determines whether the matrix needs updating.\n     */\n    private _flagMatrix;\n    /**\n     * @name Two.Shape#_flagScale\n     * @private\n     * @property {Boolean} - Determines whether the scale needs updating.\n     */\n    private _flagScale;\n    /**\n     * @name Two.Shape#_matrix\n     * @private\n     * @property {Matrix} - The matrix value of the shape's position, rotation, and scale.\n     */\n    private _matrix;\n    /**\n     * @name Two.Shape#_worldMatrix\n     * @private\n     * @property {Matrix} - The matrix value of the shape's position, rotation, and scale in the scene.\n     */\n    private _worldMatrix;\n    /**\n     * @name Two.Shape#_position\n     * @private\n     * @property {Vector} - The translation values as a {@link Two.Vector}.\n     */\n    private _position;\n    /**\n     * @name Two.Shape#_rotation\n     * @private\n     * @property {Number} - The rotation value in Number.\n     */\n    private _rotation;\n    /**\n     * @name Two.Shape#_scale\n     * @private\n     * @property {Number|Vector} - The scale value in Number. Can be a vector for non-uniform scaling.\n     */\n    private _scale;\n    /**\n     * @name Two.Shape#_skewX\n     * @private\n     * @property {Number} - The rotation value in Number.\n     */\n    private _skewX;\n    /**\n     * @name Two.Shape#_skewY\n     * @private\n     * @property {Number} - The rotation value in Number.\n     */\n    private _skewY;\n    isShape: true;\n    /**\n     * @name Two.Shape#id\n     * @property {String} - Session specific unique identifier.\n     * @nota-bene In the {@link Two.SVGRenderer} change this to change the underlying SVG element's id too.\n     */\n    id: string;\n    /**\n     * @name Two.Shape#matrix\n     * @property {Matrix}\n     * @description The transformation matrix of the shape.\n     * @nota-bene {@link Two.Shape#position}, {@link Two.Shape#rotation}, {@link Two.Shape#scale}, {@link Two.Shape#skewX}, and {@link Two.Shape#skewY} apply their values to the matrix when changed. The matrix is what is sent to the renderer to be drawn.\n     */\n    matrix: Matrix;\n    /**\n     * @name Two.Shape#worldMatrix\n     * @property {Matrix}\n     * @description The transformation matrix of the shape in the scene.\n     */\n    worldMatrix: Matrix;\n    /**\n     * @name Two.Shape#position\n     * @property {Vector} - The x and y value for where the shape is placed relative to its parent.\n     */\n    position: Vector;\n    /**\n     * @name Two.Shape#rotation\n     * @property {Number} - The value in Number for how much the shape is rotated relative to its parent.\n     */\n    rotation: number;\n    /**\n     * @name Two.Shape#scale\n     * @property {Number} - The value for how much the shape is scaled relative to its parent.\n     * @nota-bene This value can be replaced with a {@link Two.Vector} to do non-uniform scaling. e.g: `shape.scale = new Two.Vector(2, 1);`\n     */\n    scale: number | Vector;\n    /**\n     * @name Two.Shape#skewX\n     * @property {Number} - The value in Number for how much the shape is skewed relative to its parent.\n     * @description Skew the shape by an angle in the x axis direction.\n     */\n    skewX: number;\n    /**\n     * @name Two.Shape#skewY\n     * @property {Number} - The value in Number for how much the shape is skewed relative to its parent.\n     * @description Skew the shape by an angle in the y axis direction.\n     */\n    skewY: number;\n    set translation(arg: Vector);\n    /**\n     * @name Two.Shape#translation\n     * @description Alias for {@link Two.Shape#position}.\n     */\n    get translation(): Vector;\n    /**\n     * @name Two.Shape#addTo\n     * @function\n     * @param {Group} group - The parent the shape adds itself to.\n     * @description Convenience method to add itself to the scenegraph.\n     */\n    addTo(group: Group): Shape;\n    /**\n     * @name Two.Shape#remove\n     * @function\n     * @description Remove self from the scene / parent.\n     */\n    remove(): Shape;\n    /**\n     * @name Two.Shape#copy\n     * @function\n     * @param {Two.Shape} shape\n     * @description Copy the properties of one {@link Two.Shape} onto another.\n     */\n    copy(shape: Shape): Shape;\n    /**\n     * @name Two.Shape#clone\n     * @function\n     * @param {Group} [parent] - Optional argument to automatically add the shape to a scenegraph.\n     * @returns {Shape}\n     * @description Create a new {@link Two.Shape} with the same values as the current shape.\n     */\n    clone(parent?: Group): Shape;\n    /**\n     * @name Two.Shape#toObject\n     * @function\n     * @description Create a JSON compatible object that represents information of the shape.\n     * @nota-bene Works in conjunction with {@link Two.Shape.fromObject}\n     */\n    toObject(): object;\n    /**\n     * @name Two.Shape#dispose\n     * @function\n     * @description Release the shape's bound objects by unbinding relevant events.\n     */\n    dispose(): Shape;\n    /**\n     * @name Two.Shape#contains\n     * @function\n     * @param {Number} x - x coordinate to hit test against\n     * @param {Number} y - y coordinate to hit test against\n     * @param {Object} [options] - Optional options object\n     * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `shape.visible = false` shapes\n     * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n     * @description Check to see if coordinates are within a {@link Two.Shape}'s bounding rectangle\n     */\n    contains(x: number, y: number, options?: ShapeHitTestOptions): boolean;\n    /**\n     * @name Two.Shape#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    protected _update(bubbles?: boolean): Shape;\n    /**\n     * @name Two.Shape#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Shape;\n  }\n  import { Element as TwoElement } from 'two.js/src/element';\n  import { Matrix } from 'two.js/src/matrix';\n  import { Vector } from 'two.js/src/vector';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/shape.js",
    "content": "import { Events } from './events.js';\nimport { Element } from './element.js';\nimport { Matrix } from './matrix.js';\nimport { Vector } from './vector.js';\nimport { getComputedMatrix } from './utils/math.js';\n\n/**\n * @name Two.Shape\n * @class\n * @extends Two.Element\n * @description The foundational transformation object for the Two.js scenegraph.\n */\nexport class Shape extends Element {\n  /**\n   * @name Two.Shape#_flagMatrix\n   * @private\n   * @property {Boolean} - Determines whether the matrix needs updating.\n   */\n  _flagMatrix = true;\n\n  /**\n   * @name Two.Shape#_flagScale\n   * @private\n   * @property {Boolean} - Determines whether the scale needs updating.\n   */\n  _flagScale = false;\n\n  // Underlying Properties\n\n  /**\n   * @name Two.Shape#_matrix\n   * @private\n   * @property {Two.Matrix} - The matrix value of the shape's position, rotation, and scale.\n   */\n  _matrix = null;\n\n  /**\n   * @name Two.Shape#_worldMatrix\n   * @private\n   * @property {Two.Matrix} - The matrix value of the shape's position, rotation, and scale in the scene.\n   */\n  _worldMatrix = null;\n\n  /**\n   * @name Two.Shape#_position\n   * @private\n   * @property {Two.Vector} - The translation values as a {@link Two.Vector}.\n   */\n  _position = null;\n\n  /**\n   * @name Two.Shape#_rotation\n   * @private\n   * @property {Number} - The rotation value in radians.\n   */\n  _rotation = 0;\n\n  /**\n   * @name Two.Shape#_scale\n   * @private\n   * @property {Number|Two.Vector} - The scale value in Number. Can be a vector for non-uniform scaling.\n   */\n  _scale = 1;\n\n  /**\n   * @name Two.Shape#_skewX\n   * @private\n   * @property {Number} - The rotation value in Number.\n   */\n  _skewX = 0;\n\n  /**\n   * @name Two.Shape#_skewY\n   * @private\n   * @property {Number} - The rotation value in Number.\n   */\n  _skewY = 0;\n\n  constructor() {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    /**\n     * @name Two.Shape#renderer\n     * @property {Object}\n     * @description Object access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n     * @nota-bene With the {@link Two.SVGRenderer} you can access the underlying SVG element created via `shape.renderer.elem`.\n     */\n    this._renderer.flagMatrix = FlagMatrix.bind(this);\n    this.isShape = true;\n\n    /**\n     * @name Two.Shape#matrix\n     * @property {Two.Matrix}\n     * @description The transformation matrix of the shape.\n     * @nota-bene {@link Two.Shape#position}, {@link Two.Shape#rotation}, {@link Two.Shape#scale}, {@link Two.Shape#skewX}, and {@link Two.Shape#skewY} apply their values to the matrix when changed. The matrix is what is sent to the renderer to be drawn.\n     */\n    this.matrix = new Matrix();\n\n    /**\n     * @name Two.Shape#worldMatrix\n     * @property {Two.Matrix}\n     * @description The transformation matrix of the shape in the scene.\n     */\n    this.worldMatrix = new Matrix();\n\n    /**\n     * @name Two.Shape#position\n     * @property {Two.Vector} - The x and y value for where the shape is placed relative to its parent.\n     */\n    this.position = new Vector();\n\n    /**\n     * @name Two.Shape#rotation\n     * @property {Number} - The value in Number for how much the shape is rotated relative to its parent.\n     */\n    this.rotation = 0;\n\n    /**\n     * @name Two.Shape#scale\n     * @property {Number} - The value for how much the shape is scaled relative to its parent.\n     * @nota-bene This value can be replaced with a {@link Two.Vector} to do non-uniform scaling. e.g: `shape.scale = new Two.Vector(2, 1);`\n     */\n    this.scale = 1;\n\n    /**\n     * @name Two.Shape#skewX\n     * @property {Number} - The value in Number for how much the shape is skewed relative to its parent.\n     * @description Skew the shape by an angle in the x axis direction.\n     */\n    this.skewX = 0;\n\n    /**\n     * @name Two.Shape#skewY\n     * @property {Number} - The value in Number for how much the shape is skewed relative to its parent.\n     * @description Skew the shape by an angle in the y axis direction.\n     */\n    this.skewY = 0;\n  }\n\n  static Properties = [\n    'position',\n    'rotation',\n    'scale',\n    'skewX',\n    'skewY',\n    'matrix',\n    'worldMatrix',\n  ];\n\n  /**\n   * @name Two.Shape.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Shape} to create a new instance\n   * @returns {Two.Shape}\n   * @description Create a new {@link Two.Shape} from an object notation of a {@link Two.Shape}.\n   * @nota-bene Works in conjunction with {@link Two.Shape#toObject}\n   */\n  static fromObject(obj) {\n    const shape = new Shape().copy(obj);\n\n    if ('id' in obj) {\n      shape.id = obj.id;\n    }\n\n    return shape;\n  }\n\n  get renderer() {\n    return this._renderer;\n  }\n  set renderer(v) {\n    this._renderer = v;\n  }\n\n  /**\n   * @name Two.Shape#translation\n   * @description Alias for {@link Two.Shape#position}.\n   */\n  get translation() {\n    return proto.position.get.apply(this, arguments);\n  }\n  set translation(v) {\n    proto.position.set.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Shape#addTo\n   * @function\n   * @param {Two.Group} group - The parent the shape adds itself to.\n   * @description Convenience method to add itself to the scenegraph.\n   */\n  addTo(group) {\n    group.add(this);\n    return this;\n  }\n\n  /**\n   * @name Two.Shape#remove\n   * @function\n   * @description Remove self from the scene / parent.\n   */\n  remove() {\n    if (!this.parent) {\n      return this;\n    }\n\n    this.parent.remove(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Shape#contains\n   * @function\n   * @param {Number} x - x coordinate to hit test against\n   * @param {Number} y - y coordinate to hit test against\n   * @param {Object} [options] - Optional options object\n   * @param {Boolean} [options.ignoreVisibility] - If `true`, hit test against `shape.visible = false` shapes\n   * @param {Number} [options.tolerance] - Padding to hit test against in pixels\n   * @returns {Boolean}\n   * @description Check to see if coordinates are within a {@link Two.Shape}'s bounding rectangle\n   * @nota-bene Expects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n   */\n  contains(x, y, options) {\n    const opts = options || {};\n    const ignoreVisibility = opts.ignoreVisibility === true;\n\n    if (!ignoreVisibility && 'visible' in this && this.visible === false) {\n      return false;\n    }\n\n    if (\n      !ignoreVisibility &&\n      'opacity' in this &&\n      typeof this.opacity === 'number' &&\n      this.opacity <= 0\n    ) {\n      return false;\n    }\n\n    if (typeof this.getBoundingClientRect !== 'function') {\n      return false;\n    }\n\n    const tolerance = typeof opts.tolerance === 'number' ? opts.tolerance : 0;\n\n    this._update(true);\n\n    const rect = this.getBoundingClientRect();\n\n    if (!rect) {\n      return false;\n    }\n\n    return (\n      x >= rect.left - tolerance &&\n      x <= rect.right + tolerance &&\n      y >= rect.top - tolerance &&\n      y <= rect.bottom + tolerance\n    );\n  }\n\n  /**\n   * @name Two.Shape#copy\n   * @function\n   * @param {Two.Shape} shape\n   * @description Copy the properties of one {@link Two.Shape} onto another.\n   */\n  copy(shape) {\n    super.copy.call(this, shape);\n\n    if ('position' in shape) {\n      if (shape.position instanceof Vector) {\n        this.position = shape.position;\n      } else {\n        this.position.copy(shape.position);\n      }\n    }\n    if ('rotation' in shape) {\n      this.rotation = shape.rotation;\n    }\n    if ('scale' in shape) {\n      this.scale =\n        typeof shape.scale === 'number' || shape.scale instanceof Vector\n          ? shape.scale\n          : new Vector(shape.scale.x, shape.scale.y);\n    }\n    if ('skewX' in shape) {\n      this.skewX = shape.skewX;\n    }\n    if ('skewY' in shape) {\n      this.skewY = shape.skewY;\n    }\n\n    if ('matrix' in shape && shape.matrix.manual) {\n      this.matrix.copy(shape.matrix);\n      this.matrix.manual = true;\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Shape#clone\n   * @function\n   * @param {Two.Group} [parent] - Optional argument to automatically add the shape to a scenegraph.\n   * @returns {Two.Shape}\n   * @description Create a new {@link Two.Shape} with the same values as the current shape.\n   */\n  clone(parent) {\n    const clone = new Shape();\n\n    clone.position.copy(this.position);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone._update();\n  }\n\n  /**\n   * @name Two.Shape#toObject\n   * @function\n   * @description Create a JSON compatible object that represents information of the shape.\n   * @nota-bene Works in conjunction with {@link Two.Shape.fromObject}\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer = { type: 'shape' };\n    result.isShape = true;\n    result.translation = this.translation.toObject();\n    result.rotation = this.translation.rotation;\n    result.scale =\n      this.scale instanceof Vector ? this.scale.toObject() : this.scale;\n    result.skewX = this.skewX;\n    result.skewY = this.skewY;\n    result.matrix = this.matrix.toObject();\n    return result;\n  }\n\n  /**\n   * @name Two.Shape#dispose\n   * @function\n   * @description Release the shape's bound objects by unbinding relevant events.\n   */\n  dispose() {\n    // Call parent dispose to preserve renderer type and unbind events\n    super.dispose();\n\n    if (\n      typeof this.translation === 'object' &&\n      typeof this.translation.unbind === 'function'\n    ) {\n      this.translation.unbind();\n    }\n    if (\n      typeof this.scale === 'object' &&\n      typeof this.scale.unbind === 'function'\n    ) {\n      this.scale.unbind();\n    }\n  }\n\n  /**\n   * @name Two.Shape#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update(bubbles) {\n    if (!this._matrix.manual && this._flagMatrix) {\n      this._matrix.identity().translate(this.position.x, this.position.y);\n      this._matrix.rotate(this.rotation);\n\n      if (this._scale instanceof Vector) {\n        this._matrix.scale(this._scale.x, this._scale.y);\n      } else {\n        this._matrix.scale(this._scale);\n      }\n\n      this._matrix.skewX(this.skewX);\n      this._matrix.skewY(this.skewY);\n    }\n\n    if (bubbles) {\n      if (this.parent && this.parent._update) {\n        this.parent._update();\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Shape#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagMatrix = this._flagScale = false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  position: {\n    enumerable: true,\n    get: function () {\n      return this._position;\n    },\n    set: function (v) {\n      if (this._position) {\n        this._position.unbind(Events.Types.change, this._renderer.flagMatrix);\n      }\n      this._position = v;\n      this._position.bind(Events.Types.change, this._renderer.flagMatrix);\n      FlagMatrix.call(this);\n    },\n  },\n  rotation: {\n    enumerable: true,\n    get: function () {\n      return this._rotation;\n    },\n    set: function (v) {\n      this._rotation = v;\n      this._flagMatrix = true;\n    },\n  },\n  scale: {\n    enumerable: true,\n    get: function () {\n      return this._scale;\n    },\n    set: function (v) {\n      if (this._scale instanceof Vector) {\n        this._scale.unbind(Events.Types.change, this._renderer.flagMatrix);\n      }\n      this._scale = v;\n      if (this._scale instanceof Vector) {\n        this._scale.bind(Events.Types.change, this._renderer.flagMatrix);\n      }\n      this._flagMatrix = true;\n      this._flagScale = true;\n    },\n  },\n  skewX: {\n    enumerable: true,\n    get: function () {\n      return this._skewX;\n    },\n    set: function (v) {\n      this._skewX = v;\n      this._flagMatrix = true;\n    },\n  },\n  skewY: {\n    enumerable: true,\n    get: function () {\n      return this._skewY;\n    },\n    set: function (v) {\n      this._skewY = v;\n      this._flagMatrix = true;\n    },\n  },\n  matrix: {\n    enumerable: true,\n    get: function () {\n      return this._matrix;\n    },\n    set: function (v) {\n      this._matrix = v;\n      this._flagMatrix = true;\n    },\n  },\n  worldMatrix: {\n    enumerable: true,\n    get: function () {\n      // TODO: Make DRY\n      getComputedMatrix(this, this._worldMatrix);\n      return this._worldMatrix;\n    },\n    set: function (v) {\n      this._worldMatrix = v;\n    },\n  },\n};\n\n/**\n * @name FlagMatrix\n * @function\n * @private\n * @description Utility function used in conjunction with event handlers to update the flagMatrix of a shape.\n */\nfunction FlagMatrix() {\n  this._flagMatrix = true;\n}\n"
  },
  {
    "path": "src/shapes/arc-segment.d.ts",
    "content": "declare module 'two.js/src/shapes/arc-segment' {\n  /**\n   * @name Two.ArcSegment\n   * @class\n   * @param {Number} [x=0] - The x position of the arc segment.\n   * @param {Number} [y=0] - The y position of the arc segment.\n   * @param {Number} [innerRadius=0] - The inner radius value of the arc segment.\n   * @param {Number} [outerRadius=0] - The outer radius value of the arc segment.\n   * @param {Number} [startAngle=0] - The start angle of the arc segment in Number.\n   * @param {Number} [endAngle=6.2831] - The end angle of the arc segment in Number.\n   * @param {Number} [resolution=24] - The number of vertices used to construct the arc segment.\n   */\n  export class ArcSegment extends Path {\n    static Properties: (\n      | 'startAngle'\n      | 'endAngle'\n      | 'innerRadius'\n      | 'outerRadius'\n      | string\n    )[];\n    /**\n     * @name Two.ArcSegment.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.ArcSegment} to create a new instance\n     * @returns {Two.ArcSegment}\n     * @description Create a new {@link Two.ArcSegment} from an object notation of a {@link Two.ArcSegment}.\n     * @nota-bene Works in conjunction with {@link Two.ArcSegment#toObject}\n     */\n    fromObject(\n      obj: Parameters<typeof Path.fromObject>[0] & {\n        startAngle?: number;\n        endAngle?: number;\n        innerRadius?: number;\n        outerRadius?: number;\n      }\n    ): ArcSegment;\n    constructor(\n      ox?: number,\n      oy?: number,\n      ir?: number,\n      or?: number,\n      sa?: number,\n      ea?: number,\n      res?: number\n    );\n    /**\n     * @name Two.ArcSegment#_flagStartAngle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#startAngle} needs updating.\n     */\n    private _flagStartAngle;\n    /**\n     * @name Two.ArcSegment#_flagEndAngle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#endAngle} needs updating.\n     */\n    private _flagEndAngle;\n    /**\n     * @name Two.ArcSegment#_flagInnerRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#innerRadius} needs updating.\n     */\n    private _flagInnerRadius;\n    /**\n     * @name Two.ArcSegment#_flagOuterRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.ArcSegment#outerRadius} needs updating.\n     */\n    private _flagOuterRadius;\n    /**\n     * @name Two.ArcSegment#_startAngle\n     * @private\n     * @see {@link Two.ArcSegment#startAngle}\n     */\n    private _startAngle;\n    /**\n     * @name Two.ArcSegment#_endAngle\n     * @private\n     * @see {@link Two.ArcSegment#endAngle}\n     */\n    private _endAngle;\n    /**\n     * @name Two.ArcSegment#_innerRadius\n     * @private\n     * @see {@link Two.ArcSegment#innerRadius}\n     */\n    private _innerRadius;\n    /**\n     * @name Two.ArcSegment#_outerRadius\n     * @private\n     * @see {@link Two.ArcSegment#outerRadius}\n     */\n    private _outerRadius;\n    /**\n     * @name Two.ArcSegment#innerRadius\n     * @property {Number} - The size of the inner radius of the arc segment.\n     */\n    innerRadius: number;\n    /**\n     * @name Two.ArcSegment#outerRadius\n     * @property {Number} - The size of the outer radius of the arc segment.\n     */\n    outerRadius: number;\n    /**\n     * @name Two.ArcSegment#startAngle\n     * @property {Number} - The angle of one side for the arc segment.\n     */\n    startAngle: number;\n    /**\n     * @name Two.ArcSegment#endAngle\n     * @property {Number} - The angle of the other side for the arc segment.\n     */\n    endAngle: number;\n    /**\n     * @name Two.ArcSegment#copy\n     * @function\n     * @param {Two.ArcSegment} arcSegment - The reference {@link Two.ArcSegment}\n     * @description Copy the properties of one {@link Two.ArcSegment} onto another.\n     */\n    copy(arcSegment: ArcSegment): ArcSegment;\n    /**\n     * @name Two.ArcSegment#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.ArcSegment}\n     * @description Create a new instance of {@link Two.ArcSegment} with the same properties of the current path.\n     */\n    clone(parent: Group): ArcSegment;\n  }\n  import { Path } from 'two.js/src/path';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/shapes/arc-segment.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { mod, HALF_PI, TWO_PI } from '../utils/math.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\nimport { Constants } from '../constants.js';\n\n/**\n * @name Two.ArcSegment\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the arc segment.\n * @param {Number} [y=0] - The y position of the arc segment.\n * @param {Number} [innerRadius=0] - The inner radius value of the arc segment.\n * @param {Number} [outerRadius=0] - The outer radius value of the arc segment.\n * @param {Number} [startAngle=0] - The start angle of the arc segment in Number.\n * @param {Number} [endAngle=6.2831] - The end angle of the arc segment in Number.\n * @param {Number} [resolution=24] - The number of vertices used to construct the arc segment.\n */\nexport class ArcSegment extends Path {\n  /**\n   * @name Two.ArcSegment#_flagStartAngle\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.ArcSegment#startAngle} needs updating.\n   */\n  _flagStartAngle = false;\n  /**\n   * @name Two.ArcSegment#_flagEndAngle\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.ArcSegment#endAngle} needs updating.\n   */\n  _flagEndAngle = false;\n  /**\n   * @name Two.ArcSegment#_flagInnerRadius\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.ArcSegment#innerRadius} needs updating.\n   */\n  _flagInnerRadius = false;\n  /**\n   * @name Two.ArcSegment#_flagOuterRadius\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.ArcSegment#outerRadius} needs updating.\n   */\n  _flagOuterRadius = false;\n\n  /**\n   * @name Two.ArcSegment#_startAngle\n   * @private\n   * @see {@link Two.ArcSegment#startAngle}\n   */\n  _startAngle = 0;\n  /**\n   * @name Two.ArcSegment#_endAngle\n   * @private\n   * @see {@link Two.ArcSegment#endAngle}\n   */\n  _endAngle = TWO_PI;\n  /**\n   * @name Two.ArcSegment#_innerRadius\n   * @private\n   * @see {@link Two.ArcSegment#innerRadius}\n   */\n  _innerRadius = 0;\n  /**\n   * @name Two.ArcSegment#_outerRadius\n   * @private\n   * @see {@link Two.ArcSegment#outerRadius}\n   */\n  _outerRadius = 0;\n\n  constructor(x, y, ir, or, sa, ea, res) {\n    const amount = res || Constants.Resolution * 3;\n    const points = [];\n    for (let i = 0; i < amount; i++) {\n      points.push(new Anchor());\n    }\n\n    super(points, true, false, true);\n\n    this._renderer.type = 'arc-segment';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    /**\n     * @name Two.ArcSegment#innerRadius\n     * @property {Number} - The size of the inner radius of the arc segment.\n     */\n    if (typeof ir === 'number') {\n      this.innerRadius = ir;\n    }\n\n    /**\n     * @name Two.ArcSegment#outerRadius\n     * @property {Number} - The size of the outer radius of the arc segment.\n     */\n    if (typeof or === 'number') {\n      this.outerRadius = or;\n    }\n\n    /**\n     * @name Two.ArcSegment#startAngle\n     * @property {Number} - The angle of one side for the arc segment.\n     */\n    if (typeof sa === 'number') {\n      this.startAngle = sa;\n    }\n\n    /**\n     * @name Two.ArcSegment#endAngle\n     * @property {Number} - The angle of the other side for the arc segment.\n     */\n    if (typeof ea === 'number') {\n      this.endAngle = ea;\n    }\n\n    this._update();\n\n    if (typeof x === 'number') {\n      this.translation.x = x;\n    }\n    if (typeof y === 'number') {\n      this.translation.y = y;\n    }\n  }\n\n  /**\n   * @name Two.ArcSegment.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.ArcSegment}.\n   */\n  static Properties = ['startAngle', 'endAngle', 'innerRadius', 'outerRadius'];\n\n  /**\n   * @name Two.ArcSegment.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.ArcSegment} to create a new instance\n   * @returns {Two.ArcSegment}\n   * @description Create a new {@link Two.ArcSegment} from an object notation of a {@link Two.ArcSegment}.\n   * @nota-bene Works in conjunction with {@link Two.ArcSegment#toObject}\n   */\n  static fromObject(obj) {\n    const segment = new ArcSegment().copy(obj);\n\n    if ('id' in obj) {\n      segment.id = obj.id;\n    }\n\n    return segment;\n  }\n\n  /**\n   * @name Two.ArcSegment#copy\n   * @function\n   * @param {Two.ArcSegment} arcSegment - The reference {@link Two.ArcSegment}\n   * @description Copy the properties of one {@link Two.ArcSegment} onto another.\n   */\n  copy(arcSegment) {\n    super.copy.call(this, arcSegment);\n\n    for (let i = 0; i < ArcSegment.Properties.length; i++) {\n      const k = ArcSegment.Properties[i];\n      if (k in arcSegment && typeof arcSegment[k] === 'number') {\n        this[k] = arcSegment[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.ArcSegment#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (\n      this._flagVertices ||\n      this._flagStartAngle ||\n      this._flagEndAngle ||\n      this._flagInnerRadius ||\n      this._flagOuterRadius\n    ) {\n      const sa = this._startAngle;\n      const ea = this._endAngle;\n\n      const ir = this._innerRadius;\n      const or = this._outerRadius;\n\n      const connected = mod(sa, TWO_PI) === mod(ea, TWO_PI);\n      const punctured = ir > 0;\n\n      const vertices = this.vertices;\n      let length = punctured ? vertices.length / 2 : vertices.length;\n      let command,\n        id = 0;\n      let i, last, pct, v, theta, step, x, y, amp;\n\n      if (connected) {\n        length--;\n      } else if (!punctured) {\n        length -= 2;\n      }\n\n      /**\n       * Outer Circle\n       */\n      for (i = 0, last = length - 1; i < length; i++) {\n        pct = i / last;\n        v = vertices[id];\n        theta = pct * (ea - sa) + sa;\n        step = (ea - sa) / length;\n\n        x = or * Math.cos(theta);\n        y = or * Math.sin(theta);\n\n        switch (i) {\n          case 0:\n            command = Commands.move;\n            break;\n          default:\n            command = Commands.curve;\n        }\n\n        v.command = command;\n        v.x = x;\n        v.y = y;\n        v.controls.left.clear();\n        v.controls.right.clear();\n\n        if (v.command === Commands.curve) {\n          amp = (or * step) / Math.PI;\n          v.controls.left.x = amp * Math.cos(theta - HALF_PI);\n          v.controls.left.y = amp * Math.sin(theta - HALF_PI);\n          v.controls.right.x = amp * Math.cos(theta + HALF_PI);\n          v.controls.right.y = amp * Math.sin(theta + HALF_PI);\n          if (i === 1) {\n            v.controls.left.multiplyScalar(2);\n          }\n          if (i === last) {\n            v.controls.right.multiplyScalar(2);\n          }\n        }\n\n        id++;\n      }\n\n      if (punctured) {\n        if (connected) {\n          vertices[id].command = Commands.close;\n          id++;\n        } else {\n          length--;\n          last = length - 1;\n        }\n\n        /**\n         * Inner Circle\n         */\n        for (i = 0; i < length; i++) {\n          pct = i / last;\n          v = vertices[id];\n          theta = (1 - pct) * (ea - sa) + sa;\n          step = (ea - sa) / length;\n\n          x = ir * Math.cos(theta);\n          y = ir * Math.sin(theta);\n          command = Commands.curve;\n          if (i <= 0) {\n            command = connected ? Commands.move : Commands.line;\n          }\n\n          v.command = command;\n          v.x = x;\n          v.y = y;\n          v.controls.left.clear();\n          v.controls.right.clear();\n\n          if (v.command === Commands.curve) {\n            amp = (ir * step) / Math.PI;\n            v.controls.left.x = amp * Math.cos(theta + HALF_PI);\n            v.controls.left.y = amp * Math.sin(theta + HALF_PI);\n            v.controls.right.x = amp * Math.cos(theta - HALF_PI);\n            v.controls.right.y = amp * Math.sin(theta - HALF_PI);\n            if (i === 1) {\n              v.controls.left.multiplyScalar(2);\n            }\n            if (i === last) {\n              v.controls.right.multiplyScalar(2);\n            }\n          }\n\n          id++;\n        }\n\n        // Final Point\n        vertices[id].copy(vertices[0]);\n        vertices[id].command = Commands.line;\n      } else if (!connected) {\n        vertices[id].command = Commands.line;\n        vertices[id].x = 0;\n        vertices[id].y = 0;\n        id++;\n\n        // Final Point\n        vertices[id].copy(vertices[0]);\n        vertices[id].command = Commands.line;\n      }\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.ArcSegment#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    super.flagReset.call(this);\n\n    this._flagStartAngle =\n      this._flagEndAngle =\n      this._flagInnerRadius =\n      this._flagOuterRadius =\n        false;\n\n    return this;\n  }\n\n  /**\n   * @name Two.ArcSegment#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.ArcSegment}\n   * @description Create a new instance of {@link Two.ArcSegment} with the same properties of the current path.\n   */\n  clone(parent) {\n    const ir = this.innerRadius;\n    const or = this.outerRadius;\n    const sa = this.startAngle;\n    const ea = this.endAngle;\n    const resolution = this.vertices.length;\n\n    const clone = new ArcSegment(0, 0, ir, or, sa, ea, resolution);\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.ArcSegment#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n\n    object.renderer.type = 'arc-segment';\n\n    for (let i = 0; i < ArcSegment.Properties.length; i++) {\n      const k = ArcSegment.Properties[i];\n      object[k] = this[k];\n    }\n\n    return object;\n  }\n}\n\nconst proto = {\n  startAngle: {\n    enumerable: true,\n    get: function () {\n      return this._startAngle;\n    },\n    set: function (v) {\n      this._startAngle = v;\n      this._flagStartAngle = true;\n    },\n  },\n  endAngle: {\n    enumerable: true,\n    get: function () {\n      return this._endAngle;\n    },\n    set: function (v) {\n      this._endAngle = v;\n      this._flagEndAngle = true;\n    },\n  },\n  innerRadius: {\n    enumerable: true,\n    get: function () {\n      return this._innerRadius;\n    },\n    set: function (v) {\n      this._innerRadius = v;\n      this._flagInnerRadius = true;\n    },\n  },\n  outerRadius: {\n    enumerable: true,\n    get: function () {\n      return this._outerRadius;\n    },\n    set: function (v) {\n      this._outerRadius = v;\n      this._flagOuterRadius = true;\n    },\n  },\n};\n"
  },
  {
    "path": "src/shapes/circle.d.ts",
    "content": "declare module 'two.js/src/shapes/circle' {\n  /**\n   * @name Two.Circle\n   * @class\n   * @param {Number} [x=0] - The x position of the circle.\n   * @param {Number} [y=0] - The y position of the circle.\n   * @param {Number} [radius=0] - The radius value of the circle.\n   * @param {Number} [resolution=4] - The number of vertices used to construct the circle.\n   */\n  export class Circle extends Path {\n    /**\n     * @name Two.Circle#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Circle#radius} needs updating.\n     */\n    private _flagRadius;\n    /**\n     * @name Two.Circle#_radius\n     * @private\n     * @see {@link Two.Circle#radius}\n     */\n    private _radius;\n    /**\n     * @name Two.Circle.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Circle}.\n     */\n    static Properties: ('radius' | string)[];\n    /**\n     * @name Two.Circle.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Circle} to create a new instance\n     * @returns {Two.Circle}\n     * @description Create a new {@link Two.Circle} from an object notation of a {@link Two.Circle}.\n     * @nota-bene Works in conjunction with {@link Two.Circle#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Path.fromObject>[0] & {\n        radius?: number;\n      }\n    ): Circle;\n    constructor(x?: number, y?: number, radius?: number, resolution?: number);\n    /**\n     * @name Two.Circle#radius\n     * @property {Number} - The size of the radius of the circle.\n     */\n    radius: number;\n    /**\n     * @name Two.Circle#copy\n     * @function\n     * @param {Two.Circle} circle - The reference {@link Two.Circle}\n     * @description Copy the properties of one {@link Two.Circle} onto another.\n     */\n    copy(circle: Circle): Circle;\n    /**\n     * @name Two.Circle#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Circle}\n     * @description Create a new instance of {@link Two.Circle} with the same properties of the current path.\n     */\n    clone(parent: Group): Circle;\n  }\n  import { Path } from 'two.js/src/path';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/shapes/circle.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { HALF_PI, TWO_PI } from '../utils/math.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\n\nconst cos = Math.cos,\n  sin = Math.sin;\n\n/**\n * @name Two.Circle\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the circle.\n * @param {Number} [y=0] - The y position of the circle.\n * @param {Number} [radius=0] - The radius value of the circle.\n * @param {Number} [resolution=4] - The number of vertices used to construct the circle.\n */\nexport class Circle extends Path {\n  /**\n   * @name Two.Circle#_flagRadius\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Circle#radius} needs updating.\n   */\n  _flagRadius = false;\n\n  /**\n   * @name Two.Circle#_radius\n   * @private\n   * @see {@link Two.Circle#radius}\n   */\n  _radius = 0;\n\n  constructor(ox, oy, r, resolution) {\n    // At least 2 vertices are required for proper circlage\n    const amount = resolution ? Math.max(resolution, 2) : 4;\n    const points = [];\n    for (let i = 0; i < amount; i++) {\n      points.push(new Anchor(0, 0, 0, 0, 0, 0));\n    }\n\n    super(points, true, true, true);\n\n    this._renderer.type = 'circle';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    /**\n     * @name Two.Circle#radius\n     * @property {Number} - The size of the radius of the circle.\n     */\n    if (typeof r === 'number') {\n      this.radius = r;\n    }\n\n    this._update();\n\n    if (typeof ox === 'number') {\n      this.translation.x = ox;\n    }\n    if (typeof oy === 'number') {\n      this.translation.y = oy;\n    }\n  }\n\n  /**\n   * @name Two.Circle.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Circle}.\n   */\n  static Properties = ['radius'];\n\n  /**\n   * @name Two.Circle.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Circle} to create a new instance\n   * @returns {Two.Circle}\n   * @description Create a new {@link Two.Circle} from an object notation of a {@link Two.Circle}.\n   * @nota-bene Works in conjunction with {@link Two.Circle#toObject}\n   */\n  static fromObject(obj) {\n    const circle = new Circle().copy(obj);\n\n    if ('id' in obj) {\n      circle.id = obj.id;\n    }\n\n    return circle;\n  }\n\n  /**\n   * @name Two.Circle#copy\n   * @function\n   * @param {Two.Circle} circle - The reference {@link Two.Circle}\n   * @description Copy the properties of one {@link Two.Circle} onto another.\n   */\n  copy(circle) {\n    super.copy.call(this, circle);\n\n    for (let i = 0; i < Circle.Properties.length; i++) {\n      const k = Circle.Properties[i];\n      if (k in circle && typeof circle[k] === 'number') {\n        this[k] = circle[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Circle#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagRadius) {\n      let length = this.vertices.length;\n\n      if (!this._closed && length > 2) {\n        length -= 1;\n      }\n\n      // Coefficient for approximating circular arcs with Bezier curves\n      const c = (4 / 3) * Math.tan(Math.PI / (length * 2));\n      const radius = this._radius;\n      const rc = radius * c;\n\n      for (let i = 0; i < this.vertices.length; i++) {\n        const pct = i / length;\n        const theta = pct * TWO_PI;\n\n        const x = radius * cos(theta);\n        const y = radius * sin(theta);\n\n        const lx = rc * cos(theta - HALF_PI);\n        const ly = rc * sin(theta - HALF_PI);\n\n        const rx = rc * cos(theta + HALF_PI);\n        const ry = rc * sin(theta + HALF_PI);\n\n        const v = this.vertices[i];\n\n        v.command = i === 0 ? Commands.move : Commands.curve;\n        v.set(x, y);\n        v.controls.left.set(lx, ly);\n        v.controls.right.set(rx, ry);\n      }\n    }\n\n    super._update.call(this);\n    return this;\n  }\n\n  /**\n   * @name Two.Circle#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagRadius = false;\n\n    super.flagReset.call(this);\n    return this;\n  }\n\n  /**\n   * @name Two.Circle#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Circle}\n   * @description Create a new instance of {@link Two.Circle} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new Circle(0, 0, this.radius, this.vertices.length);\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Circle#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n\n    object.renderer.type = 'circle';\n\n    for (let i = 0; i < Circle.Properties.length; i++) {\n      const k = Circle.Properties[i];\n      object[k] = this[k];\n    }\n\n    return object;\n  }\n}\n\nconst proto = {\n  radius: {\n    enumerable: true,\n    get: function () {\n      return this._radius;\n    },\n    set: function (v) {\n      this._radius = v;\n      this._flagRadius = true;\n    },\n  },\n};\n"
  },
  {
    "path": "src/shapes/ellipse.d.ts",
    "content": "declare module 'two.js/src/shapes/ellipse' {\n  /**\n   * @name Two.Ellipse\n   * @class\n   * @param {Number} [x=0] - The x position of the ellipse.\n   * @param {Number} [y=0] - The y position of the ellipse.\n   * @param {Number} [rx=0] - The radius value of the ellipse in the x direction.\n   * @param {Number} [ry=0] - The radius value of the ellipse in the y direction.\n   * @param {Number} [resolution=4] - The number of vertices used to construct the ellipse.\n   */\n  export class Ellipse extends Path {\n    /**\n     * @name Two.Ellipse.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Ellipse}.\n     */\n    static Properties: ('width' | 'height' | string)[];\n    /**\n     * @name Two.Ellipse.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Ellipse} to create a new instance\n     * @returns {Two.Ellipse}\n     * @description Create a new {@link Two.Ellipse} from an object notation of a {@link Two.Ellipse}.\n     * @nota-bene Works in conjunction with {@link Two.Ellipse#toObject}\n     */\n    fromObject(\n      obj: Parameters<typeof Path.fromObject>[0] & {\n        width?: number;\n        height?: number;\n      }\n    ): Ellipse;\n    constructor(\n      x?: number,\n      y?: number,\n      rx?: number,\n      ry?: number,\n      resolution?: number\n    );\n    /**\n     * @name Two.Ellipse#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Ellipse#width} needs updating.\n     */\n    private _flagWidth;\n    /**\n     * @name Two.Ellipse#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Ellipse#height} needs updating.\n     */\n    private _flagHeight;\n    /**\n     * @name Two.Ellipse#_width\n     * @private\n     * @see {@link Two.Ellipse#width}\n     */\n    private _width;\n    /**\n     * @name Two.Ellipse#_height\n     * @private\n     * @see {@link Two.Ellipse#height}\n     */\n    private _height;\n    width: number;\n    height: number;\n    /**\n     * @name Two.Ellipse#copy\n     * @function\n     * @param {Two.Ellipse} ellipse - The reference {@link Two.Ellipse}\n     * @description Copy the properties of one {@link Two.Ellipse} onto another.\n     */\n    copy(ellipse: Ellipse): Ellipse;\n  }\n  import { Path } from 'two.js/src/path';\n}\n"
  },
  {
    "path": "src/shapes/ellipse.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { HALF_PI, TWO_PI } from '../utils/math.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\n\nconst cos = Math.cos,\n  sin = Math.sin;\n\n/**\n * @name Two.Ellipse\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the ellipse.\n * @param {Number} [y=0] - The y position of the ellipse.\n * @param {Number} [rx=0] - The radius value of the ellipse in the x direction.\n * @param {Number} [ry=0] - The radius value of the ellipse in the y direction.\n * @param {Number} [resolution=4] - The number of vertices used to construct the ellipse.\n */\nexport class Ellipse extends Path {\n  /**\n   * @name Two.Ellipse#_flagWidth\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Ellipse#width} needs updating.\n   */\n  _flagWidth = false;\n  /**\n   * @name Two.Ellipse#_flagHeight\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Ellipse#height} needs updating.\n   */\n  _flagHeight = false;\n\n  /**\n   * @name Two.Ellipse#_width\n   * @private\n   * @see {@link Two.Ellipse#width}\n   */\n  _width = 0;\n  /**\n   * @name Two.Ellipse#_height\n   * @private\n   * @see {@link Two.Ellipse#height}\n   */\n  _height = 0;\n\n  constructor(x, y, rx, ry, resolution) {\n    if (typeof ry !== 'number' && typeof rx === 'number') {\n      ry = rx;\n    }\n\n    // At least 2 vertices are required for proper circlage\n    const amount = resolution ? Math.max(resolution, 2) : 4;\n    const points = [];\n    for (let i = 0; i < amount; i++) {\n      points.push(new Anchor());\n    }\n\n    super(points, true, true, true);\n\n    this._renderer.type = 'ellipse';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    /**\n     * @name Two.Ellipse#width\n     * @property {Number} - The width of the ellipse.\n     */\n    if (typeof rx === 'number') {\n      this.width = rx * 2;\n    }\n\n    /**\n     * @name Two.Ellipse#height\n     * @property {Number} - The height of the ellipse.\n     */\n    if (typeof ry === 'number') {\n      this.height = ry * 2;\n    }\n\n    this._update();\n\n    if (typeof x === 'number') {\n      this.translation.x = x;\n    }\n    if (typeof y === 'number') {\n      this.translation.y = y;\n    }\n  }\n\n  /**\n   * @name Two.Ellipse.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Ellipse}.\n   */\n  static Properties = ['width', 'height'];\n\n  /**\n   * @name Two.Ellipse.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Ellipse} to create a new instance\n   * @returns {Two.Ellipse}\n   * @description Create a new {@link Two.Ellipse} from an object notation of a {@link Two.Ellipse}.\n   * @nota-bene Works in conjunction with {@link Two.Ellipse#toObject}\n   */\n  static fromObject(obj) {\n    const ellipse = new Ellipse().copy(obj);\n\n    if ('id' in obj) {\n      ellipse.id = obj.id;\n    }\n\n    return ellipse;\n  }\n\n  /**\n   * @name Two.Ellipse#copy\n   * @function\n   * @param {Two.Ellipse} ellipse - The reference {@link Two.Ellipse}\n   * @description Copy the properties of one {@link Two.Ellipse} onto another.\n   */\n  copy(ellipse) {\n    super.copy.call(this, ellipse);\n\n    for (let i = 0; i < Ellipse.Properties.length; i++) {\n      const k = Ellipse.Properties[i];\n      if (k in ellipse && typeof ellipse[k] === 'number') {\n        this[k] = ellipse[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Ellipse#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagWidth || this._flagHeight) {\n      let length = this.vertices.length;\n\n      if (!this._closed && length > 2) {\n        length -= 1;\n      }\n\n      // Coefficient for approximating circular arcs with Bezier curves\n      const c = (4 / 3) * Math.tan(Math.PI / (this.vertices.length * 2));\n      const radiusX = this._width / 2;\n      const radiusY = this._height / 2;\n\n      for (let i = 0; i < this.vertices.length; i++) {\n        const pct = i / length;\n        const theta = pct * TWO_PI;\n\n        const x = radiusX * cos(theta);\n        const y = radiusY * sin(theta);\n\n        const lx = radiusX * c * cos(theta - HALF_PI);\n        const ly = radiusY * c * sin(theta - HALF_PI);\n\n        const rx = radiusX * c * cos(theta + HALF_PI);\n        const ry = radiusY * c * sin(theta + HALF_PI);\n\n        const v = this.vertices[i];\n\n        v.command = i === 0 ? Commands.move : Commands.curve;\n        v.set(x, y);\n        v.controls.left.set(lx, ly);\n        v.controls.right.set(rx, ry);\n      }\n    }\n\n    super._update.call(this);\n    return this;\n  }\n\n  /**\n   * @name Two.Ellipse#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = false;\n\n    super.flagReset.call(this);\n    return this;\n  }\n\n  /**\n   * @name Two.Ellipse#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Ellipse}\n   * @description Create a new instance of {@link Two.Ellipse} with the same properties of the current path.\n   */\n  clone(parent) {\n    const rx = this.width / 2;\n    const ry = this.height / 2;\n    const resolution = this.vertices.length;\n    const clone = new Ellipse(0, 0, rx, ry, resolution);\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Ellipse#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n\n    object.renderer.type = 'ellipse';\n\n    for (let i = 0; i < Ellipse.Properties.length; i++) {\n      const k = Ellipse.Properties[i];\n      object[k] = this[k];\n    }\n\n    return object;\n  }\n}\n\nconst proto = {\n  width: {\n    enumerable: true,\n    get: function () {\n      return this._width;\n    },\n    set: function (v) {\n      this._width = v;\n      this._flagWidth = true;\n    },\n  },\n  height: {\n    enumerable: true,\n    get: function () {\n      return this._height;\n    },\n    set: function (v) {\n      this._height = v;\n      this._flagHeight = true;\n    },\n  },\n};\n"
  },
  {
    "path": "src/shapes/line.d.ts",
    "content": "declare module 'two.js/src/shapes/line' {\n  /**\n     * @name Two.Line\n     * @class\n\n     * @param {Number} [x1=0] - The x position of the first vertex on the line.\n     * @param {Number} [y1=0] - The y position of the first vertex on the line.\n     * @param {Number} [x2=0] - The x position of the second vertex on the line.\n     * @param {Number} [y2=0] - The y position of the second vertex on the line.\n     */\n  export class Line extends Path {\n    static Properties: ('left' | 'right' | string)[];\n    constructor(x1?: number, y1?: number, x2?: number, y2?: number);\n\n    /**\n     * @name Two.Line#left\n     * @property {Anchor} - the first vertex on the line.\n     */\n    left: Anchor;\n\n    /**\n     * @name Two.Line#right\n     * @property {Anchor} - the second vertex on the line.\n     */\n    right: Anchor;\n  }\n  import { Path } from 'two.js/src/path';\n  import { Anchor } from 'two.js/src/anchor';\n}\n"
  },
  {
    "path": "src/shapes/line.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { TwoError } from '../utils/error.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\n\n/**\n * @name Two.Line\n * @class\n * @extends Two.Path\n * @param {Number} [x1=0] - The x position of the first vertex on the line.\n * @param {Number} [y1=0] - The y position of the first vertex on the line.\n * @param {Number} [x2=0] - The x position of the second vertex on the line.\n * @param {Number} [y2=0] - The y position of the second vertex on the line.\n */\nexport class Line extends Path {\n  constructor(x1, y1, x2, y2) {\n    const points = [new Anchor(x1, y1), new Anchor(x2, y2)];\n    super(points);\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this.vertices[0].command = Commands.move;\n    this.vertices[1].command = Commands.line;\n\n    this.automatic = false;\n  }\n\n  static Properties = ['left', 'right'];\n}\n\nconst proto = {\n  left: {\n    enumerable: true,\n    get: function () {\n      return this.vertices[0];\n    },\n    set: function (v) {\n      if (_.isObject(v)) {\n        this.vertices.splice(0, 1, v);\n        this.vertices[0].command = Commands.move;\n      } else {\n        const error = new TwoError('Two.Line.left argument is not an object.');\n        console.warn(error.name, error.message);\n      }\n    },\n  },\n  right: {\n    enumerable: true,\n    get: function () {\n      return this.vertices[1];\n    },\n    set: function (v) {\n      if (_.isObject(v)) {\n        this.vertices.splice(1, 1, v);\n        this.vertices[1].command = Commands.line;\n      } else {\n        const error = new TwoError('Two.Line.right argument is not an object.');\n        console.warn(error.name, error.message);\n      }\n    },\n  },\n};\n"
  },
  {
    "path": "src/shapes/points.d.ts",
    "content": "declare module 'two.js/src/shapes/points' {\n  /**\n   * @name Two.Points\n   * @class\n   * @param {Vector[]} [vertices] - A list of {@link Two.Vector}s that represent the order and coordinates to construct a rendered set of points.\n   * @description This is a primary primitive class for quickly and easily drawing points in Two.js. Unless specified methods return their instance of `Two.Points` for the purpose of chaining.\n   */\n  export class Points extends Shape {\n    static Properties: (\n      | 'fill'\n      | 'stroke'\n      | 'linewidth'\n      | 'opacity'\n      | 'visible'\n      | 'size'\n      | 'sizeAttenuation'\n      | 'beginning'\n      | 'ending'\n      | 'dashes'\n      | string\n    )[];\n    static fromObject(\n      obj: Parameters<typeof Shape.fromObject>[0] & {\n        fill?: string;\n        stroke?: string;\n        linewidth?: number;\n        opacity?: number;\n        visible?: boolean;\n        size?: number;\n        sizeAttenuation?: boolean;\n        beginning?: number;\n        ending?: number;\n        dashes: number[] & {\n          offset?: number;\n        };\n      }\n    ): Points;\n    constructor(vertices?: Vector[]);\n    private _flagVertices;\n    private _flagLength;\n    private _flagFill;\n    private _flagStroke;\n    private _flagLinewidth;\n    private _flagOpacity;\n    private _flagVisible;\n    private _flagSize;\n    private _flagSizeAttenuation;\n    private _length;\n    private _fill;\n    private _stroke;\n    private _linewidth;\n    private _opacity;\n    private _visible;\n    private _size;\n    private _sizeAttenuation;\n    private _beginning;\n    private _ending;\n    private _dashes;\n    private _strokeAttenuation;\n    /**\n     * @name Two.Points#size\n     * @property {Number} - Number describing the diameter each point should have\n     * @description Set the size of each point in the collection of {@link Two.Points}\n     */\n    size: number;\n    /**\n     * @name Two.Points#sizeAttenuation\n     * @property {Boolean} - Boolean dictating whether Two.js should scale the size of the points based on its matrix hierarcy.\n     * @description Set to `true` if you'd like the size of the points to be relative to the scale of its parents; `false` to disregard. Default is `false`.\n     */\n    sizeAttenuation: boolean;\n    /**\n     * @name Two.Points#beginning\n     * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n     * @description {@link Two.Points#beginning} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     */\n    beginning: number;\n    /**\n     * @name Two.Points#ending\n     * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n     * @description {@link Two.Points#ending} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     */\n    ending: number;\n    /**\n     * @name Two.Points#fill\n     * @property {(String|Gradient|Texture)} - The value of what the path should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    fill: string | Gradient | Texture;\n    /**\n     * @name Two.Points#stroke\n     * @property {(String|Gradient|Texture)} - The value of what the path should be outlined in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    stroke: string | Gradient | Texture;\n    /**\n     * @name Two.Points#className\n     * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n     * @nota-bene Only available for the SVG renderer.\n     */\n    /**\n     * @name Two.Points#linewidth\n     * @property {Number} - The thickness in pixels of the stroke.\n     */\n    linewidth: number;\n\n    /**\n     * @name Two.Points#opacity\n     * @property {Number} - The opaqueness of the path.\n     * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n     */\n    opacity: number;\n    className: string;\n    /**\n     * @name Two.Points#visible\n     * @property {Boolean} - Display the points or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    visible: boolean;\n    /**\n     * @name Two.Points#vertices\n     * @property {Vector[]} - An ordered list of vector points for rendering points.\n     * @description A list of {@link Two.Vector} objects that consist of which coordinates to draw points at.\n     * @nota-bene The array when manipulating is actually a {@link Two.Collection}.\n     */\n    vertices: (Anchor | Vector)[];\n    /**\n     * @name Two.Points#dashes\n     * @type {number[] & { offset?: number }}\n     * @property {Number[]} - Array of numbers. Odd indices represent dash length. Even indices represent dash space.\n     * @description A list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray} for more information on the SVG stroke-dasharray attribute.\n     */\n    dashes: number[] & {\n      offset?: number;\n    };\n    /**\n     * @name Two.Points#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n     * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n     */\n    strokeAttenuation: boolean;\n    /**\n     * @name Two.Points#copy\n     * @function\n     * @param {Two.Points} points - The reference {@link Two.Points}\n     * @description Copy the properties of one {@link Two.Points} onto another.\n     */\n    copy(points: Points): Points;\n    /**\n     * @name Two.Points#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Points}\n     * @description Create a new instance of {@link Two.Points} with the same properties of the current path.\n     */\n    clone(parent: Group): Points;\n    /**\n     * @name Two.Points#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the points object.\n     */\n    toObject(): object;\n    /**\n     * @name Two.Points#dispose\n     * @function\n     * @description Release the points' renderer resources and detach all events.\n     * This method cleans up vertices collection events, individual vertex events,\n     * and disposes fill/stroke effects (calling dispose() on Gradients and\n     * Textures for thorough cleanup) while preserving the renderer type for\n     * potential re-attachment to a new renderer.\n     */\n    dispose(): Points;\n    /**\n     * @name Two.Points#noFill\n     * @function\n     * @description Short hand method to set fill to `none`.\n     */\n    noFill: () => Points;\n    /**\n     * @name Two.Points#noStroke\n     * @function\n     * @description Short hand method to set stroke to `none`.\n     */\n    noStroke: () => Points;\n    /**\n     * @name Two.Points#corner\n     * @function\n     * @description Orient the vertices of the shape to the upper left-hand corner of the points object.\n     */\n    corner: () => Points;\n    /**\n     * @name Two.Points#center\n     * @function\n     * @description Orient the vertices of the shape to the center of the points object.\n     */\n    center: () => Points;\n    /**\n     * @name Two.Points#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n     */\n    getBoundingClientRect: (shallow?: boolean) => BoundingBox;\n    /**\n     * @name Two.Points#subdivide\n     * @function\n     * @param {Number} limit - How many times to recurse subdivisions.\n     * @description Insert a {@link Two.Vector} at the midpoint between every item in {@link Two.Points#vertices}.\n     */\n    subdivide(limit: number): Points;\n    /**\n     * @name Two.Points#_updateLength\n     * @function\n     * @private\n     * @param {Number} [limit] -\n     * @param {Boolean} [silent=false] - If set to `true` then the points object isn't updated before calculation. Useful for internal use.\n     * @description Recalculate the {@link Two.Points#length} value.\n     */\n    private _updateLength(limit?: number, silent?: boolean): Points;\n    /**\n     * @name Two.Points#_update\n     * @function\n     * @private\n     * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n     * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n     * @nota-bene Try not to call this method more than once a frame.\n     */\n    protected _update(bubbles?: boolean): Points;\n    /**\n     * @name Two.Points#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Points;\n  }\n  import { Shape } from 'two.js/src/shape';\n  import { Group } from 'two.js/src/group';\n  import { Gradient } from 'two.js/src/effects/gradient';\n  import { Texture } from 'two.js/src/effects/texture';\n  import { BoundingBox } from 'two.js';\n  import { Vector } from 'two.js/src/vector';\n  import { Anchor } from 'two.js/src/anchor';\n}\n"
  },
  {
    "path": "src/shapes/points.js",
    "content": "import { subdivide } from '../utils/curves.js';\nimport { getIdByLength } from '../utils/shape.js';\nimport { _ } from '../utils/underscore.js';\n\nimport { Collection } from '../collection.js';\nimport { Events } from '../events.js';\nimport { Anchor } from '../anchor.js';\nimport { Shape } from '../shape.js';\nimport {\n  Path,\n  FlagVertices,\n  BindVertices,\n  UnbindVertices,\n  FlagFill,\n  FlagStroke,\n} from '../path.js';\n\nimport { Gradient } from '../effects/gradient.js';\nimport { LinearGradient } from '../effects/linear-gradient.js';\nimport { RadialGradient } from '../effects/radial-gradient.js';\nimport { Texture } from '../effects/texture.js';\nimport { getEffectFromObject } from '../utils/shape.js';\n\nconst ceil = Math.ceil,\n  floor = Math.floor;\n\n/**\n * @name Two.Points\n * @class\n * @extends Two.Shape\n * @param {Two.Vector[]} [vertices] - A list of {@link Two.Vector}s that represent the order and coordinates to construct a rendered set of points.\n * @description This is a primary primitive class for quickly and easily drawing points in Two.js. Unless specified methods return their instance of `Two.Points` for the purpose of chaining.\n */\nexport class Points extends Shape {\n  _flagVertices = true;\n  _flagLength = true;\n  _flagFill = true;\n  _flagStroke = true;\n  _flagLinewidth = true;\n  _flagOpacity = true;\n  _flagVisible = true;\n  _flagSize = true;\n  _flagSizeAttenuation = true;\n  _flagStrokeAttenuation = true;\n\n  _length = 0;\n  _fill = '#fff';\n  _stroke = '#000';\n  _linewidth = 1;\n  _opacity = 1.0;\n  _visible = true;\n  _size = 1;\n  _sizeAttenuation = false;\n  _beginning = 0;\n  _ending = 1.0;\n  _dashes = null;\n  _strokeAttenuation = true;\n\n  constructor(vertices) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'points';\n    this._renderer.flagVertices = FlagVertices.bind(this);\n    this._renderer.bindVertices = BindVertices.bind(this);\n    this._renderer.unbindVertices = UnbindVertices.bind(this);\n\n    this._renderer.flagFill = FlagFill.bind(this);\n    this._renderer.flagStroke = FlagStroke.bind(this);\n    this._renderer.vertices = null;\n    this._renderer.collection = null;\n\n    /**\n     * @name Two.Points#size\n     * @property {Number} - Number describing the diameter each point should have\n     * @description Set the size of each point in the collection of {@link Two.Points}\n     */\n    this.size = 1;\n\n    /**\n     * @name Two.Points#sizeAttenuation\n     * @property {Boolean} - Boolean dictating whether Two.js should scale the size of the points based on its matrix hierarchy.\n     * @description Set to `true` if you'd like the size of the points to be relative to the scale of its parents; `false` to disregard. Default is `false`.\n     */\n    this.sizeAttenuation = false;\n\n    /**\n     * @name Two.Points#beginning\n     * @property {Number} - Number between zero and one to state the beginning of where the path is rendered.\n     * @description {@link Two.Points#beginning} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     */\n    this.beginning = 0;\n\n    /**\n     * @name Two.Points#ending\n     * @property {Number} - Number between zero and one to state the ending of where the path is rendered.\n     * @description {@link Two.Points#ending} is a percentage value that represents at what percentage into the path should the renderer start drawing.\n     */\n    this.ending = 1;\n\n    // Style properties\n\n    /**\n     * @name Two.Points#fill\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the path should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    this.fill = '#fff';\n\n    /**\n     * @name Two.Points#stroke\n     * @property {(String|Two.Gradient|Two.Texture)} - The value of what the path should be outlined in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    this.stroke = '#000';\n\n    /**\n     * @name Two.Points#linewidth\n     * @property {Number} - The thickness in pixels of the stroke.\n     */\n    this.linewidth = 1;\n\n    /**\n     * @name Two.Points#opacity\n     * @property {Number} - The opaqueness of the path.\n     * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n     */\n    this.opacity = 1;\n\n    /**\n     * @name Two.Points#className\n     * @property {String} - A class to be applied to the element to be compatible with CSS styling.\n     * @nota-bene Only rendered to DOM in the SVG renderer.\n     */\n    this.className = '';\n\n    /**\n     * @name Two.Points#visible\n     * @property {Boolean} - Display the points or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    this.visible = true;\n\n    /**\n     * @name Two.Points#vertices\n     * @property {Two.Vector[]} - An ordered list of vector points for rendering points.\n     * @description A list of {@link Two.Vector} objects that consist of which coordinates to draw points at.\n     * @nota-bene The array when manipulating is actually a {@link Two.Collection}.\n     */\n    this.vertices = vertices;\n\n    /**\n     * @name Two.Points#dashes\n     * @property {Number[]} - Array of numbers. Odd indices represent dash length. Even indices represent dash space.\n     * @description A list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray} for more information on the SVG stroke-dasharray attribute.\n     */\n    this.dashes = [];\n\n    /**\n     * @name Two.Points#dashes#offset\n     * @property {Number} - A number in pixels to offset {@link Two.Points#dashes} display.\n     */\n    this.dashes.offset = 0;\n  }\n\n  /**\n   * @name Two.Points.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Points}.\n   */\n  static Properties = [\n    'fill',\n    'stroke',\n    'linewidth',\n    'opacity',\n    'visible',\n    'size',\n    'sizeAttenuation',\n    'beginning',\n    'ending',\n    'dashes',\n    'strokeAttenuation',\n  ];\n\n  /**\n   * @name Two.Points.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Points} to create a new instance\n   * @returns {Two.Points}\n   * @description Create a new {@link Two.Points} from an object notation of a {@link Two.Points}.\n   * @nota-bene Works in conjunction with {@link Two.Points#toObject}\n   */\n  static fromObject(obj) {\n    const fill =\n      typeof obj.fill === 'string' ? obj.fill : getEffectFromObject(obj.fill);\n    const stroke =\n      typeof obj.stroke === 'string'\n        ? obj.stroke\n        : getEffectFromObject(obj.stroke);\n    const points = new Points().copy({ ...obj, fill, stroke });\n\n    if ('id' in obj) {\n      points.id = obj.id;\n    }\n\n    return points;\n  }\n\n  /**\n   * @name Two.Points#copy\n   * @function\n   * @param {Two.Points} points - The reference {@link Two.Points}\n   * @description Copy the properties of one {@link Two.Points} onto another.\n   */\n  copy(points) {\n    super.copy.call(this, points);\n\n    for (let j = 0; j < points.vertices.length; j++) {\n      const v = points.vertices[j];\n      if (v instanceof Anchor) {\n        this.vertices.push(points.vertices[j].clone());\n      } else {\n        this.vertices.push(new Anchor().copy(v));\n      }\n    }\n\n    for (let i = 0; i < Points.Properties.length; i++) {\n      const k = Points.Properties[i];\n      if (k in points) {\n        this[k] = points[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Points#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Points}\n   * @description Create a new instance of {@link Two.Points} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new Points();\n\n    for (let j = 0; j < this.vertices.length; j++) {\n      clone.vertices.push(this.vertices[j].clone());\n    }\n\n    for (let i = 0; i < Points.Properties.length; i++) {\n      const k = Points.Properties[i];\n      clone[k] = this[k];\n    }\n\n    clone.className = this.className;\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone._update();\n  }\n\n  /**\n   * @name Two.Points#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the points object.\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n\n    result.renderer.type = 'points';\n    result.vertices = this.vertices.map((v) => v.toObject());\n\n    _.each(\n      Points.Properties,\n      function (k) {\n        if (typeof this[k] !== 'undefined') {\n          if (this[k].toObject) {\n            result[k] = this[k].toObject();\n          } else {\n            result[k] = this[k];\n          }\n        }\n      },\n      this\n    );\n\n    return result;\n  }\n\n  /**\n   * @name Two.Points#dispose\n   * @function\n   * @returns {Two.Points}\n   * @description Release the points' renderer resources and detach all events.\n   * This method cleans up vertices collection events, individual vertex events,\n   * and disposes fill/stroke effects (calling dispose() on Gradients and\n   * Textures for thorough cleanup) while preserving the renderer type for\n   * potential re-attachment to a new renderer.\n   */\n  dispose() {\n    // Call parent dispose to preserve renderer type and unbind events\n    super.dispose();\n\n    // Unbind vertices collection events\n    if (this.vertices && typeof this.vertices.unbind === 'function') {\n      try {\n        this.vertices.unbind();\n      } catch (e) {\n        // Ignore unbind errors for incomplete Collection objects\n      }\n    }\n\n    // Unbind individual vertex events\n    if (this.vertices) {\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        if (typeof v.unbind === 'function') {\n          v.unbind();\n        }\n      }\n    }\n\n    // Dispose fill effect (more thorough than unbind)\n    if (\n      typeof this.fill === 'object' &&\n      typeof this.fill.dispose === 'function'\n    ) {\n      this.fill.dispose();\n    } else if (\n      typeof this.fill === 'object' &&\n      typeof this.fill.unbind === 'function'\n    ) {\n      this.fill.unbind();\n    }\n\n    // Dispose stroke effect (more thorough than unbind)\n    if (\n      typeof this.stroke === 'object' &&\n      typeof this.stroke.dispose === 'function'\n    ) {\n      this.stroke.dispose();\n    } else if (\n      typeof this.stroke === 'object' &&\n      typeof this.stroke.unbind === 'function'\n    ) {\n      this.stroke.unbind();\n    }\n    return this;\n  }\n\n  /**\n   * @name Two.Points#noFill\n   * @function\n   * @description Short hand method to set fill to `none`.\n   */\n  noFill = Path.prototype.noFill;\n\n  /**\n   * @name Two.Points#noStroke\n   * @function\n   * @description Short hand method to set stroke to `none`.\n   */\n  noStroke = Path.prototype.noStroke;\n\n  /**\n   * @name Two.Points#corner\n   * @function\n   * @description Orient the vertices of the shape to the upper left-hand corner of the points object.\n   */\n  corner = Path.prototype.corner;\n\n  /**\n   * @name Two.Points#center\n   * @function\n   * @description Orient the vertices of the shape to the center of the points object.\n   */\n  center = Path.prototype.center;\n\n  /**\n   * @name Two.Points#getBoundingClientRect\n   * @function\n   * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n   * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n   * @description Return an object with top, left, right, bottom, width, and height parameters of the path.\n   */\n  getBoundingClientRect = Path.prototype.getBoundingClientRect;\n\n  /**\n   * @name Two.Points#subdivide\n   * @function\n   * @param {Number} limit - How many times to recurse subdivisions.\n   * @description Insert a {@link Two.Vector} at the midpoint between every item in {@link Two.Points#vertices}.\n   */\n  subdivide(limit) {\n    // TODO: DRYness (function below)\n    this._update();\n    let points = [];\n    for (let i = 0; i < this.vertices.length; i++) {\n      const a = this.vertices[i];\n      const b = this.vertices[i - 1];\n\n      if (!b) {\n        continue;\n      }\n\n      const x1 = a.x;\n      const y1 = a.y;\n      const x2 = b.x;\n      const y2 = b.y;\n      const subdivisions = subdivide(x1, y1, x1, y1, x2, y2, x2, y2, limit);\n\n      points = points.concat(subdivisions);\n    }\n\n    this.vertices = points;\n    return this;\n  }\n\n  /**\n   * @name Two.Points#_updateLength\n   * @function\n   * @private\n   * @param {Number} [limit] -\n   * @param {Boolean} [silent=false] - If set to `true` then the points object isn't updated before calculation. Useful for internal use.\n   * @description Recalculate the {@link Two.Points#length} value.\n   */\n  _updateLength = Path.prototype._updateLength;\n\n  /**\n   * @name Two.Points#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices) {\n      if (this._flagLength) {\n        this._updateLength(undefined, true);\n      }\n\n      const beginning = Math.min(this._beginning, this._ending);\n      const ending = Math.max(this._beginning, this._ending);\n\n      const bid = getIdByLength(this, beginning * this._length);\n      const eid = getIdByLength(this, ending * this._length);\n\n      const low = ceil(bid);\n      const high = floor(eid);\n\n      let j = 0,\n        v;\n\n      this._renderer.vertices = [];\n      this._renderer.collection = [];\n\n      for (let i = 0; i < this._collection.length; i++) {\n        if (i >= low && i <= high) {\n          v = this._collection[i];\n          this._renderer.collection.push(v);\n          this._renderer.vertices[j * 2 + 0] = v.x;\n          this._renderer.vertices[j * 2 + 1] = v.y;\n          j++;\n        }\n      }\n    }\n\n    super._update.apply(this, arguments);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Points#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagVertices =\n      this._flagLength =\n      this._flagFill =\n      this._flagStroke =\n      this._flagLinewidth =\n      this._flagOpacity =\n      this._flagVisible =\n      this._flagSize =\n      this._flagSizeAttenuation =\n        false;\n\n    super.flagReset.call(this);\n\n    return this;\n  }\n}\n\nconst proto = {\n  linewidth: {\n    enumerable: true,\n    get: function () {\n      return this._linewidth;\n    },\n    set: function (v) {\n      this._linewidth = v;\n      this._flagLinewidth = true;\n    },\n  },\n  opacity: {\n    enumerable: true,\n    get: function () {\n      return this._opacity;\n    },\n    set: function (v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n    },\n  },\n  visible: {\n    enumerable: true,\n    get: function () {\n      return this._visible;\n    },\n    set: function (v) {\n      this._visible = v;\n      this._flagVisible = true;\n    },\n  },\n  size: {\n    enumerable: true,\n    get: function () {\n      return this._size;\n    },\n    set: function (v) {\n      this._size = v;\n      this._flagSize = true;\n    },\n  },\n  sizeAttenuation: {\n    enumerable: true,\n    get: function () {\n      return this._sizeAttenuation;\n    },\n    set: function (v) {\n      this._sizeAttenuation = v;\n      this._flagSizeAttenuation = true;\n    },\n  },\n\n  fill: {\n    enumerable: true,\n    get: function () {\n      return this._fill;\n    },\n    set: function (f) {\n      if (\n        this._fill instanceof Gradient ||\n        this._fill instanceof LinearGradient ||\n        this._fill instanceof RadialGradient ||\n        this._fill instanceof Texture\n      ) {\n        this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n      }\n\n      this._fill = f;\n      this._flagFill = true;\n\n      if (\n        this._fill instanceof Gradient ||\n        this._fill instanceof LinearGradient ||\n        this._fill instanceof RadialGradient ||\n        this._fill instanceof Texture\n      ) {\n        this._fill.bind(Events.Types.change, this._renderer.flagFill);\n      }\n    },\n  },\n\n  stroke: {\n    enumerable: true,\n    get: function () {\n      return this._stroke;\n    },\n    set: function (f) {\n      if (\n        this._stroke instanceof Gradient ||\n        this._stroke instanceof LinearGradient ||\n        this._stroke instanceof RadialGradient ||\n        this._stroke instanceof Texture\n      ) {\n        this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n      }\n\n      this._stroke = f;\n      this._flagStroke = true;\n\n      if (\n        this._stroke instanceof Gradient ||\n        this._stroke instanceof LinearGradient ||\n        this._stroke instanceof RadialGradient ||\n        this._stroke instanceof Texture\n      ) {\n        this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n      }\n    },\n  },\n\n  /**\n   * @name Two.Points#length\n   * @property {Number} - The sum of distances between all {@link Two.Points#vertices}.\n   */\n  length: {\n    get: function () {\n      if (this._flagLength) {\n        this._updateLength();\n      }\n      return this._length;\n    },\n  },\n\n  beginning: {\n    enumerable: true,\n    get: function () {\n      return this._beginning;\n    },\n    set: function (v) {\n      this._beginning = v;\n      this._flagVertices = true;\n    },\n  },\n\n  ending: {\n    enumerable: true,\n    get: function () {\n      return this._ending;\n    },\n    set: function (v) {\n      this._ending = v;\n      this._flagVertices = true;\n    },\n  },\n\n  vertices: {\n    enumerable: true,\n\n    get: function () {\n      return this._collection;\n    },\n\n    set: function (vertices) {\n      const bindVertices = this._renderer.bindVertices;\n      const unbindVertices = this._renderer.unbindVertices;\n\n      // Remove previous listeners\n      if (this._collection) {\n        this._collection\n          .unbind(Events.Types.insert, bindVertices)\n          .unbind(Events.Types.remove, unbindVertices);\n      }\n\n      // Create new Collection with copy of vertices\n      if (vertices instanceof Collection) {\n        this._collection = vertices;\n      } else {\n        this._collection = new Collection(vertices || []);\n      }\n\n      // Listen for Collection changes and bind / unbind\n      this._collection\n        .bind(Events.Types.insert, bindVertices)\n        .bind(Events.Types.remove, unbindVertices);\n\n      // Bind Initial Vertices\n      bindVertices(this._collection);\n    },\n  },\n\n  dashes: {\n    enumerable: true,\n    get: function () {\n      return this._dashes;\n    },\n    set: function (v) {\n      if (typeof v.offset !== 'number') {\n        v.offset = (this.dashes && this._dashes.offset) || 0;\n      }\n      this._dashes = v;\n    },\n  },\n\n  /**\n   * @name Two.Points#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n   * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function () {\n      return this._strokeAttenuation;\n    },\n    set: function (v) {\n      this._strokeAttenuation = !!v;\n      this._flagStrokeAttenuation = true;\n      this._flagLinewidth = true;\n    },\n  },\n};\n"
  },
  {
    "path": "src/shapes/polygon.d.ts",
    "content": "declare module 'two.js/src/shapes/polygon' {\n  /**\n   * @name Two.Polygon\n   * @class\n   * @param {Number} [x=0] - The x position of the polygon.\n   * @param {Number} [y=0] - The y position of the polygon.\n   * @param {Number} [radius=0] - The radius value of the polygon.\n   * @param {Number} [sides=12] - The number of vertices used to construct the polygon.\n   */\n  export class Polygon extends Path {\n    /**\n     * @name Two.Polygon.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Polygon}.\n     */\n    static Properties: ('width' | 'height' | 'sides' | string)[];\n    /**\n     * @name Two.Polygon.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Polygon} to create a new instance\n     * @returns {Two.Polygon}\n     * @description Create a new {@link Two.Polygon} from an object notation of a {@link Two.Polygon}.\n     * @nota-bene Works in conjunction with {@link Two.Polygon#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Path.fromObject>[0] & {\n        width?: number;\n        height?: number;\n        sides?: number;\n      }\n    ): Polygon;\n    /**\n     * @name Two.Polygon#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#width} needs updating.\n     */\n    private _flagWidth;\n    /**\n     * @name Two.Polygon#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#height} needs updating.\n     */\n    private _flagHeight;\n    /**\n     * @name Two.Polygon#_flagSides\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Polygon#sides} needs updating.\n     */\n    private _flagSides;\n    /**\n     * @name Two.Polygon#_width\n     * @private\n     * @see {@link Two.Polygon#width}\n     */\n    private _width;\n    /**\n     * @name Two.Polygon#_height\n     * @private\n     * @see {@link Two.Polygon#height}\n     */\n    private _height;\n    /**\n     * @name Two.Polygon#_sides\n     * @private\n     * @see {@link Two.Polygon#sides}\n     */\n    private _sides;\n    constructor(x?: number, y?: number, radius?: number, sides?: number);\n    closed: boolean;\n    width: number;\n    height: number;\n    sides: number;\n    /**\n     * @name Two.Polygon#copy\n     * @function\n     * @param {Two.Polygon} polygon - The reference {@link Two.Polygon}\n     * @description Copy the properties of one {@link Two.Polygon} onto another.\n     */\n    copy(polygon: Polygon): Polygon;\n    /**\n     * @name Two.Polygon#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Polygon}\n     * @description Create a new instance of {@link Two.Polygon} with the same properties of the current path.\n     */\n    clone(parent: Group): Polygon;\n  }\n  import { Path } from 'two.js/src/path';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/shapes/polygon.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { TWO_PI } from '../utils/math.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\n\nconst cos = Math.cos,\n  sin = Math.sin;\n\n/**\n * @name Two.Polygon\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the polygon.\n * @param {Number} [y=0] - The y position of the polygon.\n * @param {Number} [radius=0] - The radius value of the polygon.\n * @param {Number} [sides=12] - The number of vertices used to construct the polygon.\n */\nexport class Polygon extends Path {\n  /**\n   * @name Two.Polygon#_flagWidth\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Polygon#width} needs updating.\n   */\n  _flagWidth = false;\n  /**\n   * @name Two.Polygon#_flagHeight\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Polygon#height} needs updating.\n   */\n  _flagHeight = false;\n  /**\n   * @name Two.Polygon#_flagSides\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Polygon#sides} needs updating.\n   */\n  _flagSides = false;\n\n  /**\n   * @name Two.Polygon#_radius\n   * @private\n   * @see {@link Two.Polygon#radius}\n   */\n  _radius = 0;\n  /**\n   * @name Two.Polygon#_width\n   * @private\n   * @see {@link Two.Polygon#width}\n   */\n  _width = 0;\n  /**\n   * @name Two.Polygon#_height\n   * @private\n   * @see {@link Two.Polygon#height}\n   */\n  _height = 0;\n  /**\n   * @name Two.Polygon#_sides\n   * @private\n   * @see {@link Two.Polygon#sides}\n   */\n  _sides = 0;\n\n  constructor(x, y, radius, sides) {\n    sides = Math.max(sides || 0, 3);\n\n    super();\n\n    this._renderer.type = 'polygon';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this.closed = true;\n    this.automatic = false;\n\n    /**\n     * @name Two.Polygon#radius\n     * @property {Number} - The radius value of the polygon.\n     * @nota-bene This property is tied to {@link Two.Polygon#width} and {@link Two.Polygon#height}. When you set `radius`, it affects `width` and `height`. Likewise, if you set `width` or `height` it will change the `radius`.\n     */\n    if (typeof radius === 'number') {\n      this.radius = radius;\n    }\n\n    /**\n     * @name Two.Polygon#width\n     * @property {Number} - The size of the width of the polygon.\n     * @nota-bene This property is tied to {@link Two.Polygon#radius}. When you set `radius`, it affects the `width`. Likewise, if you set `width` it will change the `radius`.\n     */\n\n    /**\n     * @name Two.Polygon#height\n     * @property {Number} - The size of the height of the polygon.\n     * @nota-bene This property is tied to {@link Two.Polygon#radius}. When you set `radius`, it affects the `height`. Likewise, if you set `height` it will change the `radius`.\n     */\n\n    /**\n     * @name Two.Polygon#sides\n     * @property {Number} - The amount of sides the polyogn has.\n     */\n    if (typeof sides === 'number') {\n      this.sides = sides;\n    }\n\n    this._update();\n\n    if (typeof x === 'number') {\n      this.translation.x = x;\n    }\n    if (typeof y === 'number') {\n      this.translation.y = y;\n    }\n  }\n\n  /**\n   * @name Two.Polygon.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Polygon}.\n   */\n  static Properties = ['width', 'height', 'sides'];\n\n  /**\n   * @name Two.Polygon.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Polygon} to create a new instance\n   * @returns {Two.Polygon}\n   * @description Create a new {@link Two.Polygon} from an object notation of a {@link Two.Polygon}.\n   * @nota-bene Works in conjunction with {@link Two.Polygon#toObject}\n   */\n  static fromObject(obj) {\n    const polygon = new Polygon().copy(obj);\n\n    if ('id' in obj) {\n      polygon.id = obj.id;\n    }\n\n    return polygon;\n  }\n\n  /**\n   * @name Two.Polygon#copy\n   * @function\n   * @param {Two.Polygon} polygon - The reference {@link Two.Polygon}\n   * @description Copy the properties of one {@link Two.Polygon} onto another.\n   */\n  copy(polygon) {\n    super.copy.call(this, polygon);\n\n    for (let i = 0; i < Polygon.Properties.length; i++) {\n      const k = Polygon.Properties[i];\n      if (k in polygon && typeof polygon[k] === 'number') {\n        this[k] = polygon[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Polygon#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (\n      this._flagVertices ||\n      this._flagWidth ||\n      this._flagHeight ||\n      this._flagSides\n    ) {\n      const sides = this._sides;\n      const amount = sides + 1;\n      let length = this.vertices.length;\n\n      if (length > sides) {\n        this.vertices.splice(sides - 1, length - sides);\n        length = sides;\n      }\n\n      for (let i = 0; i < amount; i++) {\n        const pct = (i + 0.5) / sides;\n        const theta = TWO_PI * pct + Math.PI / 2;\n        const x = (this._width * cos(theta)) / 2;\n        const y = (this._height * sin(theta)) / 2;\n\n        if (i >= length) {\n          this.vertices.push(new Anchor(x, y));\n        } else {\n          this.vertices[i].set(x, y);\n        }\n\n        this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n      }\n    }\n\n    super._update.call(this);\n    return this;\n  }\n\n  /**\n   * @name Two.Polygon#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = this._flagSides = false;\n    super.flagReset.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Polygon#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Polygon}\n   * @description Create a new instance of {@link Two.Polygon} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new Polygon(0, 0, 0, this.sides);\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n    clone.width = this.width;\n    clone.height = this.height;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Polygon#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n\n    object.renderer.type = 'polygon';\n\n    for (let i = 0; i < Polygon.Properties.length; i++) {\n      const k = Polygon.Properties[i];\n      object[k] = this[k];\n    }\n\n    return object;\n  }\n}\n\nconst proto = {\n  radius: {\n    enumerable: true,\n    get: function () {\n      return this._radius;\n    },\n    set: function (v) {\n      this._radius = v;\n      this.width = v * 2;\n      this.height = v * 2;\n    },\n  },\n  width: {\n    enumerable: true,\n    get: function () {\n      return this._width;\n    },\n    set: function (v) {\n      this._width = v;\n      this._flagWidth = true;\n      this._radius = Math.max(this.width, this.height) / 2;\n    },\n  },\n  height: {\n    enumerable: true,\n    get: function () {\n      return this._height;\n    },\n    set: function (v) {\n      this._height = v;\n      this._flagHeight = true;\n      this._radius = Math.max(this.width, this.height) / 2;\n    },\n  },\n  sides: {\n    enumerable: true,\n    get: function () {\n      return this._sides;\n    },\n    set: function (v) {\n      this._sides = v;\n      this._flagSides = true;\n    },\n  },\n};\n"
  },
  {
    "path": "src/shapes/rectangle.d.ts",
    "content": "declare module 'two.js/src/shapes/rectangle' {\n  /**\n   * @name Two.Rectangle\n   * @class\n   * @param {Number} [x=0] - The x position of the rectangle.\n   * @param {Number} [y=0] - The y position of the rectangle.\n   * @param {Number} [width=1] - The width value of the rectangle.\n   * @param {Number} [height=1] - The width value of the rectangle.\n   */\n  export class Rectangle extends Path {\n    /**\n     * @name Two.Rectangle.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Rectangle}.\n     */\n    static Properties: ('width' | 'height' | 'origin' | string)[];\n    /**\n     * @name Two.Rectangle.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Rectangle} to create a new instance\n     * @returns {Two.Rectangle}\n     * @description Create a new {@link Two.Rectangle} from an object notation of a {@link Two.Rectangle}.\n     * @nota-bene Works in conjunction with {@link Two.Rectangle#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Path.fromObject>[0] & {\n        width?: number;\n        height?: number;\n        origin?: { x: number; y: number } | Vector;\n      }\n    ): Rectangle;\n    constructor(x?: number, y?: number, width?: number, height?: number);\n    /**\n     * @name Two.Rectangle#width\n     * @property {Number} - The size of the width of the rectangle.\n     */\n    width: number;\n    /**\n     * @name Two.Rectangle#height\n     * @property {Number} - The size of the height of the rectangle.\n     */\n    height: number;\n    /**\n     * @name Two.Rectangle#origin\n     * @property {Number} - A two-component vector describing the origin offset to draw the rectangle. Default is `0, 0`.\n     */\n    origin: Vector;\n    /**\n     * @name Two.Rectangle#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Rectangle#width} needs updating.\n     */\n    private _flagWidth;\n    /**\n     * @name Two.Rectangle#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Rectangle#height} needs updating.\n     */\n    private _flagHeight;\n    /**\n     * @name Two.Rectangle#_width\n     * @private\n     * @see {@link Two.Rectangle#width}\n     */\n    private _width;\n    /**\n     * @name Two.Rectangle#_height\n     * @private\n     * @see {@link Two.Rectangle#height}\n     */\n    private _height;\n    private _origin: Vector;\n    /**\n     * @name Two.Rectangle#copy\n     * @function\n     * @param {Two.Rectangle} rectangle - The reference {@link Two.Rectangle}\n     * @description Copy the properties of one {@link Two.Rectangle} onto another.\n     */\n    copy(rectangle: Rectangle): Rectangle;\n    /**\n     * @name Two.Rectangle#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Rectangle}\n     * @description Create a new instance of {@link Two.Rectangle} with the same properties of the current path.\n     */\n    clone(parent: Group): Rectangle;\n  }\n  import { Path } from 'two.js/src/path';\n  import { Vector } from 'two.js/src/vector';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/shapes/rectangle.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { Events } from '../events.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\nimport { Vector } from '../vector.js';\n\n/**\n * @name Two.Rectangle\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the rectangle.\n * @param {Number} [y=0] - The y position of the rectangle.\n * @param {Number} [width=1] - The width value of the rectangle.\n * @param {Number} [height=1] - The width value of the rectangle.\n */\nexport class Rectangle extends Path {\n  constructor(x, y, width, height) {\n    const points = [\n      new Anchor(),\n      new Anchor(),\n      new Anchor(),\n      new Anchor(),\n      // new Anchor() // TODO: Figure out how to handle this for `beginning` / `ending` animations\n    ];\n\n    super(points, true, false, true);\n\n    this._renderer.type = 'rectangle';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    /**\n     * @name Two.Rectangle#width\n     * @property {Number} - The size of the width of the rectangle.\n     */\n    this.width = typeof width === 'number' ? width : 1;\n    /**\n     * @name Two.Rectangle#height\n     * @property {Number} - The size of the height of the rectangle.\n     */\n    this.height = typeof height === 'number' ? height : 1;\n\n    /**\n     * @name Two.Rectangle#origin\n     * @property {Number} - A two-component vector describing the origin offset to draw the rectangle. Default is `0, 0`.\n     */\n    this.origin = new Vector();\n\n    if (typeof x === 'number') {\n      this.translation.x = x;\n    }\n    if (typeof y === 'number') {\n      this.translation.y = y;\n    }\n\n    this._update();\n  }\n\n  /**\n   * @name Two.Rectangle.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Rectangle}.\n   */\n  static Properties = ['width', 'height', 'origin'];\n\n  /**\n   * @name Two.Rectangle.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Rectangle} to create a new instance\n   * @returns {Two.Rectangle}\n   * @description Create a new {@link Two.Rectangle} from an object notation of a {@link Two.Rectangle}.\n   * @nota-bene Works in conjunction with {@link Two.Rectangle#toObject}\n   */\n  static fromObject(obj) {\n    const rectangle = new Rectangle().copy(obj);\n\n    if ('id' in obj) {\n      rectangle.id = obj.id;\n    }\n\n    return rectangle;\n  }\n\n  /**\n   * @name Two.Rectangle#copy\n   * @function\n   * @param {Two.Rectangle} rectangle - The reference {@link Two.Rectangle}\n   * @description Copy the properties of one {@link Two.Rectangle} onto another.\n   */\n  copy(rectangle) {\n    super.copy.call(this, rectangle);\n\n    for (let i = 0; i < Rectangle.Properties.length; i++) {\n      const k = Rectangle.Properties[i];\n      if (k in rectangle) {\n        if (typeof rectangle[k] === 'number') {\n          this[k] = rectangle[k];\n        } else if (this[k] instanceof Vector) {\n          this[k].copy(rectangle[k]);\n        }\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Rectangle#_flagWidth\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Rectangle#width} needs updating.\n   */\n  _flagWidth = false;\n  /**\n   * @name Two.Rectangle#_flagHeight\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Rectangle#height} needs updating.\n   */\n  _flagHeight = false;\n\n  /**\n   * @name Two.Rectangle#_width\n   * @private\n   * @see {@link Two.Rectangle#width}\n   */\n  _width = 0;\n  /**\n   * @name Two.Rectangle#_height\n   * @private\n   * @see {@link Two.Rectangle#height}\n   */\n  _height = 0;\n\n  _origin = null;\n\n  /**\n   * @name Two.Rectangle#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (this._flagVertices || this._flagWidth || this._flagHeight) {\n      const xr = this._width / 2;\n      const yr = this._height / 2;\n\n      if (!this._closed && this.vertices.length === 4) {\n        this.vertices.push(new Anchor());\n      }\n\n      this.vertices[0].set(-xr, -yr).sub(this._origin).command = Commands.move;\n      this.vertices[1].set(xr, -yr).sub(this._origin).command = Commands.line;\n      this.vertices[2].set(xr, yr).sub(this._origin).command = Commands.line;\n      this.vertices[3].set(-xr, yr).sub(this._origin).command = Commands.line;\n      // FYI: Two.Sprite and Two.ImageSequence have 4 verts\n      if (this.vertices[4]) {\n        this.vertices[4].set(-xr, -yr).sub(this._origin).command =\n          Commands.line;\n      }\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Rectangle#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = false;\n    super.flagReset.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Rectangle#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Rectangle}\n   * @description Create a new instance of {@link Two.Rectangle} with the same properties of the current path.\n   */\n  clone(parent) {\n    const clone = new Rectangle(0, 0, this.width, this.height);\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      if (clone[k] instanceof Vector) {\n        clone[k].copy(this[k]);\n      } else {\n        clone[k] = this[k];\n      }\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Rectangle#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n    object.renderer.type = 'rectangle';\n    object.width = this.width;\n    object.height = this.height;\n    object.origin = this.origin.toObject();\n    return object;\n  }\n}\n\nconst proto = {\n  width: {\n    enumerable: true,\n    get: function () {\n      return this._width;\n    },\n    set: function (v) {\n      this._width = v;\n      this._flagWidth = true;\n    },\n  },\n  height: {\n    enumerable: true,\n    get: function () {\n      return this._height;\n    },\n    set: function (v) {\n      this._height = v;\n      this._flagHeight = true;\n    },\n  },\n  origin: {\n    enumerable: true,\n    get: function () {\n      return this._origin;\n    },\n    set: function (v) {\n      if (this._origin) {\n        this._origin.unbind(Events.Types.change, this._renderer.flagVertices);\n      }\n      this._origin = v;\n      this._origin.bind(Events.Types.change, this._renderer.flagVertices);\n      this._renderer.flagVertices();\n    },\n  },\n};\n"
  },
  {
    "path": "src/shapes/rounded-rectangle.d.ts",
    "content": "declare module 'two.js/src/shapes/rounded-rectangle' {\n  /**\n     * @name Two.RoundedRectangle\n     * @class\n\n     * @param {Number} [x=0] - The x position of the rounded rectangle.\n     * @param {Number} [y=0] - The y position of the rounded rectangle.\n     * @param {Number} [width=0] - The width value of the rounded rectangle.\n     * @param {Number} [height=0] - The width value of the rounded rectangle.\n     * @param {Number} [radius=0] - The radius value of the rounded rectangle.\n     * @param {Number} [resolution=12] - The number of vertices used to construct the rounded rectangle.\n     */\n  export class RoundedRectangle extends Path {\n    /**\n     * @name Two.RoundedRectangle.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.RoundedRectangle}.\n     */\n    static Properties: ('width' | 'height' | 'radius' | string)[];\n    /**\n     * @name Two.RoundedRectangle.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.RoundedRectangle} to create a new instance\n     * @returns {Two.RoundedRectangle}\n     * @description Create a new {@link Two.RoundedRectangle} from an object notation of a {@link Two.RoundedRectangle}.\n     * @nota-bene Works in conjunction with {@link Two.RoundedRectangle#toObject}\n     */\n    fromObject(\n      obj: Parameters<typeof Path.fromObject>[0] & {\n        width?: number;\n        height?: number;\n        radius?: number;\n      }\n    ): RoundedRectangle;\n    constructor(\n      x?: number,\n      y?: number,\n      width?: number,\n      height?: number,\n      radius?: number | Vector\n    );\n    /**\n     * @name Two.RoundedRectangle#_flagWidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#width} needs updating.\n     */\n    private _flagWidth;\n    /**\n     * @name Two.RoundedRectangle#_flagHeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#height} needs updating.\n     */\n    private _flagHeight;\n    /**\n     * @name Two.RoundedRectangle#_flagRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#radius} needs updating.\n     */\n    private _flagRadius;\n    /**\n     * @name Two.RoundedRectangle#_width\n     * @private\n     * @see {@link Two.RoundedRectangle#width}\n     */\n    private _width;\n    /**\n     * @name Two.RoundedRectangle#_height\n     * @private\n     * @see {@link Two.RoundedRectangle#height}\n     */\n    private _height;\n    /**\n     * @name Two.RoundedRectangle#_radius\n     * @private\n     * @see {@link Two.RoundedRectangle#radius}\n     */\n    private _radius;\n    width: number;\n    height: number;\n    radius: number | Vector;\n    /**\n     * @name Two.RoundedRectangle.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.RoundedRectangle} to create a new instance\n     * @returns {Two.RoundedRectangle}\n     * @description Create a new {@link Two.RoundedRectangle} from an object notation of a {@link Two.RoundedRectangle}.\n     * @nota-bene Works in conjunction with {@link Two.RoundedRectangle#toObject}\n     */\n    copy(roundedRectangle: RoundedRectangle): RoundedRectangle;\n  }\n  import { Path } from 'two.js/src/path';\n  import { Vector } from 'two.js/src/vector';\n}\n"
  },
  {
    "path": "src/shapes/rounded-rectangle.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { Events } from '../events.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\nimport { Vector } from '../vector.js';\n\n/**\n * @name Two.RoundedRectangle\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the rounded rectangle.\n * @param {Number} [y=0] - The y position of the rounded rectangle.\n * @param {Number} [width=0] - The width value of the rounded rectangle.\n * @param {Number} [height=0] - The width value of the rounded rectangle.\n * @param {Number|Two.Vector} [radius=0] - The radius value of the rounded rectangle.\n * @param {Number} [resolution=12] - The number of vertices used to construct the rounded rectangle.\n */\nexport class RoundedRectangle extends Path {\n  /**\n   * @name Two.RoundedRectangle#_flagWidth\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#width} needs updating.\n   */\n  _flagWidth = false;\n  /**\n   * @name Two.RoundedRectangle#_flagHeight\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#height} needs updating.\n   */\n  _flagHeight = false;\n  /**\n   * @name Two.RoundedRectangle#_flagRadius\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.RoundedRectangle#radius} needs updating.\n   */\n  _flagRadius = false;\n\n  /**\n   * @name Two.RoundedRectangle#_width\n   * @private\n   * @see {@link Two.RoundedRectangle#width}\n   */\n  _width = 0;\n  /**\n   * @name Two.RoundedRectangle#_height\n   * @private\n   * @see {@link Two.RoundedRectangle#height}\n   */\n  _height = 0;\n  /**\n   * @name Two.RoundedRectangle#_radius\n   * @private\n   * @see {@link Two.RoundedRectangle#radius}\n   */\n  _radius = 12;\n\n  constructor(x, y, width, height, radius) {\n    if (\n      typeof radius === 'undefined' &&\n      typeof width === 'number' &&\n      typeof height === 'number'\n    ) {\n      radius = Math.floor(Math.min(width, height) / 12);\n    }\n\n    const points = [];\n    for (let i = 0; i < 10; i++) {\n      points.push(\n        new Anchor(0, 0, 0, 0, 0, 0, i === 0 ? Commands.move : Commands.curve)\n      );\n    }\n\n    // points[points.length - 1].command = Two.Commands.close;\n\n    super(points);\n\n    this._renderer.type = 'rounded-rectangle';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this.closed = true;\n    this.automatic = false;\n\n    this._renderer.flagRadius = FlagRadius.bind(this);\n\n    /**\n     * @name Two.RoundedRectangle#width\n     * @property {Number} - The width of the rounded rectangle.\n     */\n    if (typeof width === 'number') {\n      this.width = width;\n    }\n\n    /**\n     * @name Two.RoundedRectangle#height\n     * @property {Number} - The height of the rounded rectangle.\n     */\n    if (typeof height === 'number') {\n      this.height = height;\n    }\n\n    /**\n     * @name Two.RoundedRectangle#radius\n     * @property {Number} - The size of the radius of the rounded rectangle.\n     */\n    if (typeof radius === 'number' || radius instanceof Vector) {\n      this.radius = radius;\n    }\n\n    this._update();\n\n    if (typeof x === 'number') {\n      this.translation.x = x;\n    }\n    if (typeof y === 'number') {\n      this.translation.y = y;\n    }\n  }\n\n  /**\n   * @name Two.RoundedRectangle.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.RoundedRectangle}.\n   */\n  static Properties = ['width', 'height', 'radius'];\n\n  /**\n   * @name Two.RoundedRectangle.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.RoundedRectangle} to create a new instance\n   * @returns {Two.RoundedRectangle}\n   * @description Create a new {@link Two.RoundedRectangle} from an object notation of a {@link Two.RoundedRectangle}.\n   * @nota-bene Works in conjunction with {@link Two.RoundedRectangle#toObject}\n   */\n  static fromObject(obj) {\n    const rectangle = new RoundedRectangle().copy(obj);\n\n    if ('id' in obj) {\n      rectangle.id = obj.id;\n    }\n\n    return rectangle;\n  }\n\n  /**\n   * @name Two.RoundedRectangle#copy\n   * @function\n   * @param {Two.RoundedRectangle} roundedRectangle - The reference {@link Two.RoundedRectangle}\n   * @description Copy the properties of one {@link Two.RoundedRectangle} onto another.\n   */\n  copy(roundedRectangle) {\n    super.copy.call(this, roundedRectangle);\n\n    for (let i = 0; i < RoundedRectangle.Properties.length; i++) {\n      const k = RoundedRectangle.Properties[i];\n      if (k in roundedRectangle) {\n        const value = roundedRectangle[k];\n        if (/radius/i.test(k)) {\n          this[k] =\n            typeof value === 'number' || value instanceof Vector\n              ? value\n              : new Vector().copy(value);\n        } else if (typeof value === 'number') {\n          this[k] = value;\n        }\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.RoundedRectangle#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (\n      this._flagVertices ||\n      this._flagWidth ||\n      this._flagHeight ||\n      this._flagRadius\n    ) {\n      const width = this._width;\n      const height = this._height;\n\n      let rx, ry;\n\n      if (this._radius instanceof Vector) {\n        rx = this._radius.x;\n        ry = this._radius.y;\n      } else {\n        rx = this._radius;\n        ry = this._radius;\n      }\n\n      let v;\n      let w = width / 2;\n      let h = height / 2;\n\n      v = this.vertices[0];\n      v.x = -(w - rx);\n      v.y = -h;\n\n      // Upper Right Corner\n\n      v = this.vertices[1];\n      v.x = w - rx;\n      v.y = -h;\n      v.controls.left.clear();\n      v.controls.right.x = rx;\n      v.controls.right.y = 0;\n\n      v = this.vertices[2];\n      v.x = w;\n      v.y = -(h - ry);\n      v.controls.right.clear();\n      v.controls.left.clear();\n\n      // Bottom Right Corner\n\n      v = this.vertices[3];\n      v.x = w;\n      v.y = h - ry;\n      v.controls.left.clear();\n      v.controls.right.x = 0;\n      v.controls.right.y = ry;\n\n      v = this.vertices[4];\n      v.x = w - rx;\n      v.y = h;\n      v.controls.right.clear();\n      v.controls.left.clear();\n\n      // Bottom Left Corner\n\n      v = this.vertices[5];\n      v.x = -(w - rx);\n      v.y = h;\n      v.controls.left.clear();\n      v.controls.right.x = -rx;\n      v.controls.right.y = 0;\n\n      v = this.vertices[6];\n      v.x = -w;\n      v.y = h - ry;\n      v.controls.left.clear();\n      v.controls.right.clear();\n\n      // Upper Left Corner\n\n      v = this.vertices[7];\n      v.x = -w;\n      v.y = -(h - ry);\n      v.controls.left.clear();\n      v.controls.right.x = 0;\n      v.controls.right.y = -ry;\n\n      v = this.vertices[8];\n      v.x = -(w - rx);\n      v.y = -h;\n      v.controls.left.clear();\n      v.controls.right.clear();\n\n      v = this.vertices[9];\n      v.copy(this.vertices[8]);\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.RoundedRectangle#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagWidth = this._flagHeight = this._flagRadius = false;\n    super.flagReset.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.RoundedRectangle#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.RoundedRectangle}\n   * @description Create a new instance of {@link Two.RoundedRectangle} with the same properties of the current path.\n   */\n  clone(parent) {\n    const width = this.width;\n    const height = this.height;\n    const radius = this.radius;\n\n    const clone = new RoundedRectangle(0, 0, width, height, radius);\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.RoundedRectangle#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n\n    object.renderer.type = 'rounded-rectangle';\n\n    for (let i = 0; i < RoundedRectangle.Properties.length; i++) {\n      const k = RoundedRectangle.Properties[i];\n      object[k] = this[k];\n    }\n\n    object.radius =\n      typeof this.radius === 'number' ? this.radius : this.radius.toObject();\n\n    return object;\n  }\n}\n\nconst proto = {\n  width: {\n    enumerable: true,\n    get: function () {\n      return this._width;\n    },\n    set: function (v) {\n      this._width = v;\n      this._flagWidth = true;\n    },\n  },\n  height: {\n    enumerable: true,\n    get: function () {\n      return this._height;\n    },\n    set: function (v) {\n      this._height = v;\n      this._flagHeight = true;\n    },\n  },\n  radius: {\n    enumerable: true,\n    get: function () {\n      return this._radius;\n    },\n    set: function (v) {\n      if (this._radius instanceof Vector) {\n        this._radius.unbind(Events.Types.change, this._renderer.flagRadius);\n      }\n\n      this._radius = v;\n\n      if (this._radius instanceof Vector) {\n        this._radius.bind(Events.Types.change, this._renderer.flagRadius);\n      }\n\n      this._flagRadius = true;\n    },\n  },\n};\n\n/**\n * @name FlagRadius\n * @private\n * @property {Function} - A convenience function to trigger the flag for radius changing.\n */\nfunction FlagRadius() {\n  this._flagRadius = true;\n}\n"
  },
  {
    "path": "src/shapes/star.d.ts",
    "content": "declare module 'two.js/src/shapes/star' {\n  /**\n   * @name Two.Star\n   * @class\n   * @param {Number} [x=0] - The x position of the star.\n   * @param {Number} [y=0] - The y position of the star.\n   * @param {Number} [innerRadius=0] - The inner radius value of the star.\n   * @param {Number} [outerRadius=0] - The outer radius value of the star.\n   * @param {Number} [sides=5] - The number of sides used to construct the star.\n   */\n  export class Star extends Path {\n    /**\n     * @name Two.Star.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Star}.\n     */\n    static Properties: ('innerRadius' | 'outerRadius' | 'sides' | string)[];\n    /**\n     * @name Two.Star.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Star} to create a new instance\n     * @returns {Two.Star}\n     * @description Create a new {@link Two.Star} from an object notation of a {@link Two.Star}.\n     * @nota-bene Works in conjunction with {@link Two.Star#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Path.fromObject>[0] & {\n        innerRadius?: number;\n        outerRadius?: number;\n        sides?: number;\n      }\n    ): Star;\n    constructor(\n      ox?: number,\n      oy?: number,\n      ir?: number,\n      or?: number,\n      sides?: number\n    );\n    /**\n     * @name Two.Star#_flagInnerRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#innerRadius} needs updating.\n     */\n    private _flagInnerRadius;\n    /**\n     * @name Two.Star#_flagOuterRadius\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#outerRadius} needs updating.\n     */\n    private _flagOuterRadius;\n    /**\n     * @name Two.Star#_flagSides\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Star#sides} needs updating.\n     */\n    private _flagSides;\n    /**\n     * @name Two.Star#_innerRadius\n     * @private\n     * @see {@link Two.Star#innerRadius}\n     */\n    private _innerRadius;\n    /**\n     * @name Two.Star#_outerRadius\n     * @private\n     * @see {@link Two.Star#outerRadius}\n     */\n    private _outerRadius;\n    /**\n     * @name Two.Star#_sides\n     * @private\n     * @see {@link Two.Star#sides}\n     */\n    private _sides;\n    /**\n     * @name Two.Star#innerRadius\n     * @property {Number} - The size of the inner radius of the star.\n     */\n    innerRadius: number;\n    /**\n     * @name Two.Star#outerRadius\n     * @property {Number} - The size of the outer radius of the star.\n     */\n    outerRadius: number;\n    /**\n     * @name Two.Star#sides\n     * @property {Number} - The amount of sides the star has.\n     */\n    sides: number;\n    /**\n     * @name Two.Star#copy\n     * @function\n     * @param {Two.Star} star - The reference {@link Two.Star}\n     * @description Copy the properties of one {@link Two.Star} onto another.\n     */\n    copy(star: Star): Star;\n    /**\n     * @name Two.Star#clone\n     * @function\n     * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n     * @returns {Two.Star}\n     * @description Create a new instance of {@link Two.Star} with the same properties of the current path.\n     */\n    clone(parent: Group): Star;\n  }\n  import { Path } from 'two.js/src/path';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/shapes/star.js",
    "content": "import { Commands } from '../utils/path-commands.js';\nimport { TWO_PI } from '../utils/math.js';\n\nimport { Path } from '../path.js';\nimport { Anchor } from '../anchor.js';\n\nconst cos = Math.cos,\n  sin = Math.sin;\n\n/**\n * @name Two.Star\n * @class\n * @extends Two.Path\n * @param {Number} [x=0] - The x position of the star.\n * @param {Number} [y=0] - The y position of the star.\n * @param {Number} [innerRadius=0] - The inner radius value of the star.\n * @param {Number} [outerRadius=0] - The outer radius value of the star.\n * @param {Number} [sides=5] - The number of sides used to construct the star.\n */\nexport class Star extends Path {\n  /**\n   * @name Two.Star#_flagInnerRadius\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Star#innerRadius} needs updating.\n   */\n  _flagInnerRadius = false;\n  /**\n   * @name Two.Star#_flagOuterRadius\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Star#outerRadius} needs updating.\n   */\n  _flagOuterRadius = false;\n  /**\n   * @name Two.Star#_flagSides\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Star#sides} needs updating.\n   */\n  _flagSides = false;\n\n  /**\n   * @name Two.Star#_innerRadius\n   * @private\n   * @see {@link Two.Star#innerRadius}\n   */\n  _innerRadius = 0;\n  /**\n   * @name Two.Star#_outerRadius\n   * @private\n   * @see {@link Two.Star#outerRadius}\n   */\n  _outerRadius = 0;\n  /**\n   * @name Two.Star#_sides\n   * @private\n   * @see {@link Two.Star#sides}\n   */\n  _sides = 0;\n\n  constructor(x, y, innerRadius, outerRadius, sides) {\n    if (arguments.length <= 3) {\n      outerRadius = innerRadius;\n      innerRadius = outerRadius / 2;\n    }\n\n    if (typeof sides !== 'number' || sides <= 0) {\n      sides = 5;\n    }\n\n    super();\n\n    this._renderer.type = 'star';\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this.closed = true;\n    this.automatic = false;\n\n    /**\n     * @name Two.Star#innerRadius\n     * @property {Number} - The size of the inner radius of the star.\n     */\n    if (typeof innerRadius === 'number') {\n      this.innerRadius = innerRadius;\n    }\n\n    /**\n     * @name Two.Star#outerRadius\n     * @property {Number} - The size of the outer radius of the star.\n     */\n    if (typeof outerRadius === 'number') {\n      this.outerRadius = outerRadius;\n    }\n\n    /**\n     * @name Two.Star#sides\n     * @property {Number} - The amount of sides the star has.\n     */\n    if (typeof sides === 'number') {\n      this.sides = sides;\n    }\n\n    this._update();\n\n    if (typeof x === 'number') {\n      this.translation.x = x;\n    }\n    if (typeof y === 'number') {\n      this.translation.y = y;\n    }\n  }\n\n  /**\n   * @name Two.Star.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Star}.\n   */\n  static Properties = ['innerRadius', 'outerRadius', 'sides'];\n\n  /**\n   * @name Two.Star.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Star} to create a new instance\n   * @returns {Two.Star}\n   * @description Create a new {@link Two.Star} from an object notation of a {@link Two.Star}.\n   * @nota-bene Works in conjunction with {@link Two.Star#toObject}\n   */\n  static fromObject(obj) {\n    const star = new Star().copy(obj);\n\n    if ('id' in obj) {\n      star.id = obj.id;\n    }\n\n    return star;\n  }\n\n  /**\n   * @name Two.Star#copy\n   * @function\n   * @param {Two.Star} star - The reference {@link Two.Star}\n   * @description Copy the properties of one {@link Two.Star} onto another.\n   */\n  copy(star) {\n    super.copy.call(this, star);\n\n    for (let i = 0; i < Star.Properties.length; i++) {\n      const k = Star.Properties[i];\n      if (k in star && typeof star[k] === 'number') {\n        this[k] = star[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Star#_update\n   * @function\n   * @private\n   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.\n   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.\n   * @nota-bene Try not to call this method more than once a frame.\n   */\n  _update() {\n    if (\n      this._flagVertices ||\n      this._flagInnerRadius ||\n      this._flagOuterRadius ||\n      this._flagSides\n    ) {\n      const sides = this._sides * 2;\n      const amount = sides + 1;\n      let length = this.vertices.length;\n\n      if (length > sides) {\n        this.vertices.splice(sides - 1, length - sides);\n        length = sides;\n      }\n\n      for (let i = 0; i < amount; i++) {\n        const pct = (i + 0.5) / sides;\n        const theta = TWO_PI * pct;\n        const r = (!(i % 2) ? this._innerRadius : this._outerRadius) / 2;\n        const x = r * cos(theta);\n        const y = r * sin(theta);\n\n        if (i >= length) {\n          this.vertices.push(new Anchor(x, y));\n        } else {\n          this.vertices[i].set(x, y);\n        }\n\n        this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n      }\n    }\n\n    super._update.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Star#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false;\n    super.flagReset.call(this);\n\n    return this;\n  }\n\n  /**\n   * @name Two.Star#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Star}\n   * @description Create a new instance of {@link Two.Star} with the same properties of the current path.\n   */\n  clone(parent) {\n    const ir = this.innerRadius;\n    const or = this.outerRadius;\n    const sides = this.sides;\n\n    const clone = new Star(0, 0, ir, or, sides);\n\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n    clone.skewX = this.skewX;\n    clone.skewY = this.skewY;\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    for (let i = 0; i < Path.Properties.length; i++) {\n      const k = Path.Properties[i];\n      clone[k] = this[k];\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone;\n  }\n\n  /**\n   * @name Two.Star#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the path.\n   */\n  toObject() {\n    const object = super.toObject.call(this);\n\n    object.renderer.type = 'star';\n\n    for (let i = 0; i < Star.Properties.length; i++) {\n      const k = Star.Properties[i];\n      object[k] = this[k];\n    }\n\n    return object;\n  }\n}\n\nconst proto = {\n  innerRadius: {\n    enumerable: true,\n    get: function () {\n      return this._innerRadius;\n    },\n    set: function (v) {\n      this._innerRadius = v;\n      this._flagInnerRadius = true;\n    },\n  },\n  outerRadius: {\n    enumerable: true,\n    get: function () {\n      return this._outerRadius;\n    },\n    set: function (v) {\n      this._outerRadius = v;\n      this._flagOuterRadius = true;\n    },\n  },\n  sides: {\n    enumerable: true,\n    get: function () {\n      return this._sides;\n    },\n    set: function (v) {\n      this._sides = v;\n      this._flagSides = true;\n    },\n  },\n};\n"
  },
  {
    "path": "src/text.d.ts",
    "content": "declare module 'two.js/src/text' {\n  export type AlignmentProperties = 'left' | 'center' | 'right';\n  export type StyleProperties = 'normal' | 'italic';\n  export type DecorationProperties = 'underline' | 'strikethrough' | 'none';\n  export type DirectionProperties = 'ltr' | 'rtl';\n  export type BaselineProperties = 'top' | 'middle' | 'bottom' | 'baseline';\n  /**\n   * @name Two.Text\n   * @class\n   * @param {String} [message] - The String to be rendered to the scene.\n   * @param {Number} [x=0] - The position in the x direction for the object.\n   * @param {Number} [y=0] - The position in the y direction for the object.\n   * @param {Object} [styles] - An object where styles are applied. Attribute must exist in Two.Text.Properties.\n   * @description This is a primitive class for creating drawable text that can be added to the scenegraph.\n   * @returns {Text}\n   */\n  export class Text extends Shape {\n    /**\n     * @name Two.Text.Ratio\n     * @property {Number} - Approximate aspect ratio of a typeface's character width to height.\n     */\n    static Ratio: number;\n    /**\n     * @name Two.Text.Properties\n     * @property {String[]} - A list of properties that are on every {@link Two.Text}.\n     */\n    static Properties: string[];\n    /**\n     * @name Two.Measure\n     * @function\n     * @param {Two.Text} [text] - The instance of {@link Two.Text} to measure.\n     * @returns {Object} - The width and height of the {@link Two.Text} instance.\n     */\n    static Measure(text: Text): Dimensions;\n    /**\n     * @name Two.Text.fromObject\n     * @function\n     * @param {Object} obj - Object notation of a {@link Two.Text} to create a new instance\n     * @returns {Two.Text}\n     * @description Create a new {@link Two.Text} from an object notation of a {@link Two.Text}.\n     * @nota-bene Works in conjunction with {@link Two.Text#toObject}\n     */\n    static fromObject(\n      obj: Parameters<typeof Shape.fromObject>[0] & {\n        value?: string;\n        family?: string;\n        size?: number;\n        leading?: number;\n        alignment?: AlignmentProperties;\n        linewidth?: number;\n        style?: StyleProperties;\n        weight?: number | string;\n        decoration?: DecorationProperties;\n        direction?: DirectionProperties;\n        baseline?: BaselineProperties;\n        opacity?: number;\n        visible?: boolean;\n        fill?: string;\n        stroke?: string;\n        dashes?: number[] & {\n          offset?: number;\n        };\n      }\n    ): Text;\n\n    constructor(\n      message?: string,\n      x?: number,\n      y?: number,\n      styles?: {\n        value?: string;\n        family?: string;\n        size?: number;\n        leading?: number;\n        alignment?: AlignmentProperties;\n        linewidth?: number;\n        style?: StyleProperties;\n        weight?: number | string;\n        decoration?: DecorationProperties;\n        direction?: DirectionProperties;\n        baseline?: BaselineProperties;\n        opacity?: number;\n        visible?: boolean;\n        fill?: string | Gradient | Texture;\n        stroke?: string | Gradient | Texture;\n        dashes?: number[] & { offset?: number };\n      }\n    );\n    /**\n     * @name Two.Text#_flagValue\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#value} need updating.\n     */\n    private _flagValue;\n    /**\n     * @name Two.Text#_flagFamily\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#family} need updating.\n     */\n    private _flagFamily;\n    /**\n     * @name Two.Text#_flagSize\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#size} need updating.\n     */\n    private _flagSize;\n    /**\n     * @name Two.Text#_flagLeading\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#leading} need updating.\n     */\n    private _flagLeading;\n    /**\n     * @name Two.Text#_flagAlignment\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#alignment} need updating.\n     */\n    private _flagAlignment;\n    /**\n     * @name Two.Text#_flagBaseline\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#baseline} need updating.\n     */\n    private _flagBaseline;\n    /**\n     * @name Two.Text#_flagStyle\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#style} need updating.\n     */\n    private _flagStyle;\n    /**\n     * @name Two.Text#_flagWeight\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#weight} need updating.\n     */\n    private _flagWeight;\n    /**\n     * @name Two.Text#_flagDecoration\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#decoration} need updating.\n     */\n    private _flagDecoration;\n    /**\n     * @name Two.Text#_flagDirection\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#direction} needs updating.\n     */\n    private _flagDirection;\n    /**\n     * @name Two.Text#_flagFill\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#fill} need updating.\n     */\n    private _flagFill;\n    /**\n     * @name Two.Text#_flagStroke\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#stroke} need updating.\n     */\n    private _flagStroke;\n    /**\n     * @name Two.Text#_flagLinewidth\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#linewidth} need updating.\n     */\n    private _flagLinewidth;\n    /**\n     * @name Two.Text#_flagOpacity\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#opacity} need updating.\n     */\n    private _flagOpacity;\n    /**\n     * @name Two.Text#_flagVisible\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#visible} need updating.\n     */\n    private _flagVisible;\n    /**\n     * @name Two.Path#_flagMask\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Path#mask} needs updating.\n     */\n    private _flagMask;\n    /**\n     * @name Two.Text#_flagClip\n     * @private\n     * @property {Boolean} - Determines whether the {@link Two.Text#clip} need updating.\n     */\n    private _flagClip;\n    /**\n     * @name Two.Text#value\n     * @property {String} - The characters to be rendered to the the screen. Referred to in the documentation sometimes as the `message`.\n     */\n    /**\n     * @name Two.Text#_dashes\n     * @private\n     * @see {@link Two.Text#dashes}\n     */\n    private _dashes;\n    /**\n     * @name Two.Path#_strokeAttenuation\n     * @private\n     * @see {@link Two.Path#strokeAttenuation}\n     */\n    private _strokeAttenuation;\n    /**\n     * @name Two.Text#value\n     * @property {String} - The characters to be rendered to the the screen. Referred to in the documentation sometimes as the `message`.\n     */\n    value: string;\n    /**\n     * @name Two.Text#family\n     * @property {String} - The font family Two.js should attempt to regsiter for rendering. The default value is `'sans-serif'`. Comma separated font names can be supplied as a \"stack\", similar to the CSS implementation of `font-family`.\n     */\n    family: string;\n    /**\n     * @name Two.Text#size\n     * @property {Number} - The font size in Two.js point space. Defaults to `13`.\n     */\n    size: number;\n    /**\n     * @name Two.Text#leading\n     * @property {Number} - The height between lines measured from base to base in Two.js point space. Defaults to `17`.\n     */\n    leading: number;\n    /**\n     * @name Two.Text#alignment\n     * @property {String} - Alignment of text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'left'`, `'center'`, `'right'`. Defaults to `'center'`.\n     */\n    alignment: AlignmentProperties;\n    /**\n     * @name Two.Text#baseline\n     * @property {String} - The vertical aligment of the text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'top'`, `'middle'`, `'bottom'`, and `'baseline'`. Defaults to `'baseline'`.\n     */\n    baseline: BaselineProperties;\n    /**\n     * @name Two.Text#style\n     * @property {String} - The font's style. Possible values include '`normal`', `'italic'`. Defaults to `'normal'`.\n     */\n    style: StyleProperties;\n    /**\n     * @name Two.Text#weight\n     * @property {Number} - A number at intervals of 100 to describe the font's weight. This compatibility varies with the typeface's variant weights. Larger values are bolder. Smaller values are thinner. Defaults to `500`.\n     */\n    weight: number | string;\n    /**\n     * @name Two.Text#decoration\n     * @property {String} - String to delineate whether text should be decorated with for instance an `'underline'`. Defaults to `'none'`.\n     */\n    decoration: DecorationProperties;\n    /**\n     * @name Two.Text#direction\n     * @property {String} - String to determine what direction the text should run. Possibly values are `'ltr'` for left-to-right and `'rtl'` for right-to-left. Defaults to `'ltr'`.\n     */\n    direction: DirectionProperties;\n    /**\n     * @name Two.Text#fill\n     * @property {(String|Gradient|Texture)} - The value of what the text object should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    fill: string | Gradient | Texture;\n    /**\n     * @name Two.Text#stroke\n     * @property {(String|Gradient|Texture)} - The value of what the text object should be filled in with.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n     */\n    stroke: string | Gradient | Texture;\n    /**\n     * @name Two.Text#linewidth\n     * @property {Number} - The thickness in pixels of the stroke.\n     */\n    linewidth: number;\n    /**\n     * @name Two.Text#opacity\n     * @property {Number} - The opaqueness of the text object.\n     * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n     */\n    opacity: number;\n    /**\n     * @name Two.Text#visible\n     * @property {Boolean} - Display the text object or not.\n     * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n     */\n    visible: boolean;\n    /**\n     * @name Two.Text#mask\n     * @property {Shape} - The shape whose alpha property becomes a clipping area for the text.\n     * @nota-bene This property is currently not working becuase of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n     */\n    mask: Shape | null | undefined;\n    /**\n     * @name Two.Text#clip\n     * @property {Shape} - Object to define clipping area.\n     * @nota-bene This property is currently not working becuase of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n     */\n    clip: boolean;\n    /**\n     * @name Two.Text#dashes\n     * @type {number[] & { offset?: number }}\n     * @property {Number[]} - Array of numbers. Odd indices represent dash length. Even indices represent dash space.\n     * @description A list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray} for more information on the SVG stroke-dasharray attribute.\n     */\n    dashes: number[] & {\n      offset?: number;\n    };\n    /**\n     * @name Two.Text#strokeAttenuation\n     * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n     * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n     */\n    strokeAttenuation: boolean;\n    /**\n     * @name Two.Text#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the text object.\n     */\n    toObject(): object;\n    /**\n     * @name Two.Text#dispose\n     * @function\n     * @description Release the text's renderer resources and detach all events.\n     * This method disposes fill and stroke effects (calling dispose() on\n     * Gradients and Textures for thorough cleanup) while preserving the\n     * renderer type for potential re-attachment to a new renderer.\n     */\n    dispose(): Text;\n    /**\n     * @name Two.Text#noFill\n     * @function\n     * @description Short hand method to set fill to `none`.\n     */\n    noFill(): Text;\n    /**\n     * @name Two.Text#noStroke\n     * @function\n     * @description Short hand method to set stroke to `none`.\n     */\n    noStroke(): Text;\n    /**\n     * @name Two.Text#getBoundingClientRect\n     * @function\n     * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n     * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n     * @description Return an object with top, left, right, bottom, width, and height parameters of the text object.\n     */\n    getBoundingClientRect(shallow?: boolean): BoundingBox;\n    /**\n     * @name Two.Text#flagReset\n     * @function\n     * @private\n     * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n     */\n    flagReset(): Text;\n  }\n  import { Shape } from 'two.js/src/shape';\n  import { Gradient } from 'two.js/src/effects/gradient';\n  import { Texture } from 'two.js/src/effects/texture';\n  import { BoundingBox, Dimensions } from 'two.js';\n}\n"
  },
  {
    "path": "src/text.js",
    "content": "import { Events } from './events.js';\nimport { _ } from './utils/underscore.js';\n\nimport { Shape } from './shape.js';\n\nimport { Gradient } from './effects/gradient.js';\nimport { LinearGradient } from './effects/linear-gradient.js';\nimport { RadialGradient } from './effects/radial-gradient.js';\nimport { Texture } from './effects/texture.js';\nimport { root } from './utils/root.js';\nimport { getEffectFromObject } from './utils/shape.js';\n\nlet canvas;\nconst min = Math.min,\n  max = Math.max;\n\nif (root.document) {\n  canvas = document.createElement('canvas');\n}\n\n/**\n * @name Two.Text\n * @class\n * @extends Two.Shape\n * @param {String} [message] - The String to be rendered to the scene.\n * @param {Number} [x=0] - The position in the x direction for the object.\n * @param {Number} [y=0] - The position in the y direction for the object.\n * @param {Object} [styles] - An object where styles are applied. Attribute must exist in Two.Text.Properties.\n * @description This is a primitive class for creating drawable text that can be added to the scenegraph.\n * @returns {Two.Text}\n */\nexport class Text extends Shape {\n  /**\n   * @name Two.Text#_flagValue\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#value} need updating.\n   */\n  _flagValue = true;\n\n  /**\n   * @name Two.Text#_flagFamily\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#family} need updating.\n   */\n  _flagFamily = true;\n\n  /**\n   * @name Two.Text#_flagSize\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#size} need updating.\n   */\n  _flagSize = true;\n\n  /**\n   * @name Two.Text#_flagLeading\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#leading} need updating.\n   */\n  _flagLeading = true;\n\n  /**\n   * @name Two.Text#_flagAlignment\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#alignment} need updating.\n   */\n  _flagAlignment = true;\n\n  /**\n   * @name Two.Text#_flagBaseline\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#baseline} need updating.\n   */\n  _flagBaseline = true;\n\n  /**\n   * @name Two.Text#_flagStyle\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#style} need updating.\n   */\n  _flagStyle = true;\n\n  /**\n   * @name Two.Text#_flagWeight\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#weight} need updating.\n   */\n  _flagWeight = true;\n\n  /**\n   * @name Two.Text#_flagDecoration\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#decoration} need updating.\n   */\n  _flagDecoration = true;\n\n  /**\n   * @name Two.Text#_flagFill\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#fill} need updating.\n   */\n  _flagFill = true;\n\n  /**\n   * @name Two.Text#_flagStroke\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#stroke} need updating.\n   */\n  _flagStroke = true;\n\n  /**\n   * @name Two.Text#_flagLinewidth\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#linewidth} need updating.\n   */\n  _flagLinewidth = true;\n\n  /**\n   * @name Two.Text#_flagOpacity\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#opacity} need updating.\n   */\n  _flagOpacity = true;\n\n  /**\n   * @name Two.Text#_flagVisible\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#visible} need updating.\n   */\n  _flagVisible = true;\n\n  /**\n   * @name Two.Text#_flagMask\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#mask} needs updating.\n   */\n  _flagMask = false;\n\n  /**\n   * @name Two.Text#_flagClip\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#clip} needs updating.\n   */\n  _flagClip = false;\n\n  /**\n   * @name Two.Text#_flagDirection\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#direction} needs updating.\n   */\n  _flagDirection = true;\n\n  /**\n   * @name Two.Text#_flagStrokeAttenuation\n   * @private\n   * @property {Boolean} - Determines whether the {@link Two.Text#strokeAttenuation} needs updating.\n   */\n  _flagStrokeAttenuation = true;\n\n  // Underlying Properties\n\n  /**\n   * @name Two.Text#value\n   * @property {String} - The characters to be rendered to the the screen. Referred to in the documentation sometimes as the `message`.\n   */\n  _value = '';\n\n  /**\n   * @name Two.Text#family\n   * @property {String} - The font family Two.js should attempt to register for rendering. The default value is `'sans-serif'`. Comma separated font names can be supplied as a \"stack\", similar to the CSS implementation of `font-family`.\n   */\n  _family = 'sans-serif';\n\n  /**\n   * @name Two.Text#size\n   * @property {Number} - The font size in Two.js point space. Defaults to `13`.\n   */\n  _size = 13;\n\n  /**\n   * @name Two.Text#leading\n   * @property {Number} - The height between lines measured from base to base in Two.js point space. Defaults to `17`.\n   */\n  _leading = 17;\n\n  /**\n   * @name Two.Text#alignment\n   * @property {String} - Alignment of text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'left'`, `'center'`, `'right'`. Defaults to `'center'`.\n   */\n  _alignment = 'center';\n\n  /**\n   * @name Two.Text#baseline\n   * @property {String} - The vertical aligment of the text in relation to {@link Two.Text#translation}'s coordinates. Possible values include `'top'`, `'middle'`, `'bottom'`, and `'baseline'`. Defaults to `'baseline'`.\n   * @nota-bene In headless environments where the canvas is based on {@link https://github.com/Automattic/node-canvas}, `baseline` seems to be the only valid property.\n   */\n  _baseline = 'middle';\n\n  /**\n   * @name Two.Text#style\n   * @property {String} - The font's style. Possible values include '`normal`', `'italic'`. Defaults to `'normal'`.\n   */\n  _style = 'normal';\n\n  /**\n   * @name Two.Text#weight\n   * @property {Number} - A number at intervals of 100 to describe the font's weight. This compatibility varies with the typeface's variant weights. Larger values are bolder. Smaller values are thinner. Defaults to `'500'`.\n   */\n  _weight = 500;\n\n  /**\n   * @name Two.Text#decoration\n   * @property {String} - String to delineate whether text should be decorated with for instance an `'underline'`. Defaults to `'none'`.\n   */\n  _decoration = 'none';\n\n  /**\n   * @name Two.Text#direction\n   * @property {String} - String to determine what direction the text should run. Possibly values are `'ltr'` for left-to-right and `'rtl'` for right-to-left. Defaults to `'ltr'`.\n   */\n  _direction = 'ltr';\n\n  /**\n   * @name Two.Text#fill\n   * @property {(String|Two.Gradient|Two.Texture)} - The value of what the text object should be filled in with.\n   * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n   */\n  _fill = '#000';\n\n  /**\n   * @name Two.Text#stroke\n   * @property {(String|Two.Gradient|Two.Texture)} - The value of what the text object should be filled in with.\n   * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for more information on CSS's colors as `String`.\n   */\n  _stroke = 'none';\n\n  /**\n   * @name Two.Text#linewidth\n   * @property {Number} - The thickness in pixels of the stroke.\n   */\n  _linewidth = 1;\n\n  /**\n   * @name Two.Text#opacity\n   * @property {Number} - The opaqueness of the text object.\n   * @nota-bene Can be used in conjunction with CSS Colors that have an alpha value.\n   */\n  _opacity = 1;\n\n  /**\n   * @name Two.Text#visible\n   * @property {Boolean} - Display the text object or not.\n   * @nota-bene For {@link Two.CanvasRenderer} and {@link Two.WebGLRenderer} when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n   */\n  _visible = true;\n\n  /**\n   * @name Two.Text#mask\n   * @property {Two.Shape} - The shape whose alpha property becomes a clipping area for the text.\n   * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n   */\n  _mask = null;\n\n  /**\n   * @name Two.Text#clip\n   * @property {Two.Shape} - Object to define clipping area.\n   * @nota-bene This property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n   */\n  _clip = false;\n\n  /**\n   * @name Two.Text#_dashes\n   * @private\n   * @see {@link Two.Text#dashes}\n   */\n  _dashes = null;\n\n  /**\n   * @name Two.Text#_strokeAttenuation\n   * @private\n   * @see {@link Two.Text#strokeAttenuation}\n   */\n  _strokeAttenuation = true;\n\n  constructor(message, x, y, styles) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    this._renderer.type = 'text';\n    this._renderer.flagFill = FlagFill.bind(this);\n    this._renderer.flagStroke = FlagStroke.bind(this);\n\n    this.value = message;\n\n    if (typeof x === 'number') {\n      this.translation.x = x;\n    }\n    if (typeof y === 'number') {\n      this.translation.y = y;\n    }\n\n    /**\n     * @name Two.Text#dashes\n     * @property {Number[]} - Array of numbers. Odd indices represent dash length. Even indices represent dash space.\n     * @description A list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n     * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray} for more information on the SVG stroke-dasharray attribute.\n     */\n    this.dashes = [];\n\n    /**\n     * @name Two.Text#dashes#offset\n     * @property {Number} - A number in pixels to offset {@link Two.Text#dashes} display.\n     */\n    this.dashes.offset = 0;\n\n    if (!_.isObject(styles)) {\n      return this;\n    }\n\n    for (let i = 0; i < Text.Properties.length; i++) {\n      const property = Text.Properties[i];\n      if (property in styles) {\n        this[property] = styles[property];\n      }\n    }\n  }\n\n  /**\n   * @name Two.Text.Ratio\n   * @property {Number} - Approximate aspect ratio of a typeface's character width to height.\n   */\n  static Ratio = 0.6;\n\n  /**\n   * @name Two.Text.Properties\n   * @property {String[]} - A list of properties that are on every {@link Two.Text}.\n   */\n  static Properties = [\n    'value',\n    'family',\n    'size',\n    'leading',\n    'alignment',\n    'linewidth',\n    'style',\n    'weight',\n    'decoration',\n    'direction',\n    'baseline',\n    'opacity',\n    'visible',\n    'fill',\n    'stroke',\n    'dashes',\n    'strokeAttenuation',\n  ];\n\n  /**\n   *\n   * @name Two.Measure\n   * @function\n   * @param {Two.Text} [text] - The instance of {@link Two.Text} to measure.\n   * @returns {Object} - The width and height of the {@link Two.Text} instance.\n   */\n  static Measure(text) {\n    if (canvas) {\n      const ctx = canvas.getContext('2d');\n      ctx.font = [\n        text._style,\n        text._weight,\n        `${text._size}px/${text._leading}px`,\n        text._family,\n      ].join(' ');\n      const metrics = ctx.measureText(text.value, 0, 0);\n      const height =\n        metrics.actualBoundingBoxDescent + metrics.actualBoundingBoxAscent;\n      return {\n        width: metrics.width,\n        height,\n      };\n    } else {\n      const width = this.value.length * this.size * Text.Ratio;\n      const height = this.leading;\n      console.warn(\n        'Two.Text: unable to accurately measure text, so using an approximation.'\n      );\n      return {\n        width,\n        height,\n      };\n    }\n  }\n\n  /**\n   * @name Two.Text.fromObject\n   * @function\n   * @param {Object} obj - Object notation of a {@link Two.Text} to create a new instance\n   * @returns {Two.Text}\n   * @description Create a new {@link Two.Text} from an object notation of a {@link Two.Text}.\n   * @nota-bene Works in conjunction with {@link Two.Text#toObject}\n   */\n  static fromObject(obj) {\n    const fill =\n      typeof obj.fill === 'string' ? obj.fill : getEffectFromObject(obj.fill);\n    const stroke =\n      typeof obj.stroke === 'string'\n        ? obj.stroke\n        : getEffectFromObject(obj.stroke);\n    const text = new Text().copy({ ...obj, fill, stroke });\n\n    if ('id' in obj) {\n      text.id = obj.id;\n    }\n\n    return text;\n  }\n\n  /**\n   * @name Two.Text#copy\n   * @function\n   * @param {Two.Text} text\n   * @description Copy the properties of one {@link Two.Text} onto another.\n   */\n  copy(text) {\n    super.copy.call(this, text);\n\n    for (let i = 0; i < Text.Properties.length; i++) {\n      const k = Text.Properties[i];\n      if (k in text) {\n        this[k] = text[k];\n      }\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Text#clone\n   * @function\n   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.\n   * @returns {Two.Text}\n   * @description Create a new instance of {@link Two.Text} with the same properties of the current text object.\n   */\n  clone(parent) {\n    const clone = new Text(this.value);\n    clone.translation.copy(this.translation);\n    clone.rotation = this.rotation;\n    clone.scale = this.scale;\n\n    for (let i = 0; i < Text.Properties.length; i++) {\n      const prop = Text.Properties[i];\n      clone[prop] = this[prop];\n    }\n\n    if (this.matrix.manual) {\n      clone.matrix.copy(this.matrix);\n    }\n\n    if (parent) {\n      parent.add(clone);\n    }\n\n    return clone._update();\n  }\n\n  /**\n   * @name Two.Text#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the text object.\n   * @nota-bene Works in conjunction with {@link Two.Text.fromObject}\n   */\n  toObject() {\n    const result = super.toObject.call(this);\n    result.renderer.type = 'text';\n\n    for (let i = 0; i < Text.Properties.length; i++) {\n      const prop = Text.Properties[i];\n      result[prop] = this[prop];\n    }\n\n    return result;\n  }\n\n  /**\n   * @name Two.Text#dispose\n   * @function\n   * @returns {Two.Text}\n   * @description Release the text's renderer resources and detach all events.\n   * This method disposes fill and stroke effects (calling dispose() on\n   * Gradients and Textures for thorough cleanup) while preserving the\n   * renderer type for potential re-attachment to a new renderer.\n   */\n  dispose() {\n    // Call parent dispose to preserve renderer type and unbind events\n    super.dispose();\n\n    // Dispose fill effect (more thorough than unbind)\n    if (\n      typeof this.fill === 'object' &&\n      typeof this.fill.dispose === 'function'\n    ) {\n      this.fill.dispose();\n    } else if (\n      typeof this.fill === 'object' &&\n      typeof this.fill.unbind === 'function'\n    ) {\n      this.fill.unbind();\n    }\n\n    // Dispose stroke effect (more thorough than unbind)\n    if (\n      typeof this.stroke === 'object' &&\n      typeof this.stroke.dispose === 'function'\n    ) {\n      this.stroke.dispose();\n    } else if (\n      typeof this.stroke === 'object' &&\n      typeof this.stroke.unbind === 'function'\n    ) {\n      this.stroke.unbind();\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two.Text#noFill\n   * @function\n   * @description Short hand method to set fill to `none`.\n   */\n  noFill() {\n    this.fill = 'none';\n    return this;\n  }\n\n  /**\n   * @name Two.Text#noStroke\n   * @function\n   * @description Short hand method to set stroke to `none`.\n   */\n  noStroke() {\n    this.stroke = 'none';\n    this.linewidth = 0;\n    return this;\n  }\n\n  // A shim to not break `getBoundingClientRect` calls.\n  // TODO: Implement a way to calculate proper bounding\n  // boxes of `Two.Text`.\n\n  /**\n   * @name Two.Text#getBoundingClientRect\n   * @function\n   * @param {Boolean} [shallow=false] - Describes whether to calculate off local matrix or world matrix.\n   * @returns {Object} - Returns object with top, left, right, bottom, width, height attributes.\n   * @description Return an object with top, left, right, bottom, width, and height parameters of the text object.\n   */\n  getBoundingClientRect(shallow) {\n    let matrix;\n    let left, right, top, bottom;\n\n    // TODO: Update this to not __always__ update. Just when it needs to.\n    this._update(true);\n\n    matrix = shallow ? this.matrix : this.worldMatrix;\n\n    const { width, height } = Text.Measure(this);\n    const border = (this._linewidth || 0) / 2;\n\n    switch (this.alignment) {\n      case 'left':\n        left = -border;\n        right = width + border;\n        break;\n      case 'right':\n        left = -(width + border);\n        right = border;\n        break;\n      default:\n        left = -(width / 2 + border);\n        right = width / 2 + border;\n    }\n\n    switch (this.baseline) {\n      case 'middle':\n        top = -(height / 2 + border);\n        bottom = height / 2 + border;\n        break;\n      default:\n        top = -(height + border);\n        bottom = border;\n    }\n\n    const [ax, ay] = matrix.multiply(left, top);\n    const [bx, by] = matrix.multiply(left, bottom);\n    const [cx, cy] = matrix.multiply(right, top);\n    const [dx, dy] = matrix.multiply(right, bottom);\n\n    top = min(ay, by, cy, dy);\n    left = min(ax, bx, cx, dx);\n    right = max(ax, bx, cx, dx);\n    bottom = max(ay, by, cy, dy);\n\n    return {\n      top: top,\n      left: left,\n      right: right,\n      bottom: bottom,\n      width: right - left,\n      height: bottom - top,\n    };\n  }\n\n  /**\n   * @name Two.Text#flagReset\n   * @function\n   * @private\n   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n   */\n  flagReset() {\n    super.flagReset.call(this);\n\n    this._flagValue =\n      this._flagFamily =\n      this._flagSize =\n      this._flagLeading =\n      this._flagAlignment =\n      this._flagFill =\n      this._flagStroke =\n      this._flagLinewidth =\n      this._flagOpacity =\n      this._flagVisible =\n      this._flagClip =\n      this._flagDecoration =\n      this._flagClassName =\n      this._flagBaseline =\n      this._flagWeight =\n      this._flagStyle =\n      this._flagDirection =\n        false;\n\n    return this;\n  }\n}\n\nconst proto = {\n  value: {\n    enumerable: true,\n    get: function () {\n      return this._value;\n    },\n    set: function (v) {\n      this._value = v;\n      this._flagValue = true;\n    },\n  },\n  family: {\n    enumerable: true,\n    get: function () {\n      return this._family;\n    },\n    set: function (v) {\n      this._family = v;\n      this._flagFamily = true;\n    },\n  },\n  size: {\n    enumerable: true,\n    get: function () {\n      return this._size;\n    },\n    set: function (v) {\n      this._size = v;\n      this._flagSize = true;\n    },\n  },\n  leading: {\n    enumerable: true,\n    get: function () {\n      return this._leading;\n    },\n    set: function (v) {\n      this._leading = v;\n      this._flagLeading = true;\n    },\n  },\n  alignment: {\n    enumerable: true,\n    get: function () {\n      return this._alignment;\n    },\n    set: function (v) {\n      this._alignment = v;\n      this._flagAlignment = true;\n    },\n  },\n  linewidth: {\n    enumerable: true,\n    get: function () {\n      return this._linewidth;\n    },\n    set: function (v) {\n      this._linewidth = v;\n      this._flagLinewidth = true;\n    },\n  },\n  style: {\n    enumerable: true,\n    get: function () {\n      return this._style;\n    },\n    set: function (v) {\n      this._style = v;\n      this._flagStyle = true;\n    },\n  },\n  weight: {\n    enumerable: true,\n    get: function () {\n      return this._weight;\n    },\n    set: function (v) {\n      this._weight = v;\n      this._flagWeight = true;\n    },\n  },\n  decoration: {\n    enumerable: true,\n    get: function () {\n      return this._decoration;\n    },\n    set: function (v) {\n      this._decoration = v;\n      this._flagDecoration = true;\n    },\n  },\n  direction: {\n    enumerable: true,\n    get: function () {\n      return this._direction;\n    },\n    set: function (v) {\n      this._direction = v;\n      this._flagDirection = true;\n    },\n  },\n  baseline: {\n    enumerable: true,\n    get: function () {\n      return this._baseline;\n    },\n    set: function (v) {\n      this._baseline = v;\n      this._flagBaseline = true;\n    },\n  },\n  opacity: {\n    enumerable: true,\n    get: function () {\n      return this._opacity;\n    },\n    set: function (v) {\n      this._opacity = v;\n      this._flagOpacity = true;\n    },\n  },\n  visible: {\n    enumerable: true,\n    get: function () {\n      return this._visible;\n    },\n    set: function (v) {\n      this._visible = v;\n      this._flagVisible = true;\n    },\n  },\n  fill: {\n    enumerable: true,\n    get: function () {\n      return this._fill;\n    },\n    set: function (f) {\n      if (\n        this._fill instanceof Gradient ||\n        this._fill instanceof LinearGradient ||\n        this._fill instanceof RadialGradient ||\n        this._fill instanceof Texture\n      ) {\n        this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n      }\n\n      this._fill = f;\n      this._flagFill = true;\n\n      if (\n        this._fill instanceof Gradient ||\n        this._fill instanceof LinearGradient ||\n        this._fill instanceof RadialGradient ||\n        this._fill instanceof Texture\n      ) {\n        this._fill.bind(Events.Types.change, this._renderer.flagFill);\n      }\n    },\n  },\n  stroke: {\n    enumerable: true,\n    get: function () {\n      return this._stroke;\n    },\n    set: function (f) {\n      if (\n        this._stroke instanceof Gradient ||\n        this._stroke instanceof LinearGradient ||\n        this._stroke instanceof RadialGradient ||\n        this._stroke instanceof Texture\n      ) {\n        this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n      }\n\n      this._stroke = f;\n      this._flagStroke = true;\n\n      if (\n        this._stroke instanceof Gradient ||\n        this._stroke instanceof LinearGradient ||\n        this._stroke instanceof RadialGradient ||\n        this._stroke instanceof Texture\n      ) {\n        this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n      }\n    },\n  },\n  mask: {\n    enumerable: true,\n    get: function () {\n      return this._mask;\n    },\n    set: function (v) {\n      this._mask = v;\n      this._flagMask = true;\n      if (_.isObject(v) && !v.clip) {\n        v.clip = true;\n      }\n    },\n  },\n  clip: {\n    enumerable: true,\n    get: function () {\n      return this._clip;\n    },\n    set: function (v) {\n      this._clip = v;\n      this._flagClip = true;\n    },\n  },\n  dashes: {\n    enumerable: true,\n    get: function () {\n      return this._dashes;\n    },\n    set: function (v) {\n      if (typeof v.offset !== 'number') {\n        v.offset = (this.dashes && this._dashes.offset) || 0;\n      }\n      this._dashes = v;\n    },\n  },\n  /**\n   * @name Two.Text#strokeAttenuation\n   * @property {Boolean} - When set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n   * @description When `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n   */\n  strokeAttenuation: {\n    enumerable: true,\n    get: function () {\n      return this._strokeAttenuation;\n    },\n    set: function (v) {\n      this._strokeAttenuation = !!v;\n      this._flagStrokeAttenuation = true;\n      this._flagLinewidth = true;\n    },\n  },\n};\n\n/**\n * @name Two.Text.FlagFill\n * @function\n * @private\n * @description Cached method to let renderers know the fill property have been updated on a {@link Two.Text}.\n */\nfunction FlagFill() {\n  this._flagFill = true;\n}\n\n/**\n * @name Two.Text.FlagStroke\n * @function\n * @private\n * @description Cached method to let renderers know the stroke property have been updated on a {@link Two.Text}.\n */\nfunction FlagStroke() {\n  this._flagStroke = true;\n}\n"
  },
  {
    "path": "src/two.d.ts",
    "content": "declare module 'two.js' {\n  /**\n     * @name Two\n     * @class\n     * @global\n\n     * @param {Object} [options]\n     * @param {Boolean} [options.fullscreen=false] - Set to `true` to automatically make the stage adapt to the width and height of the parent document. This parameter overrides `width` and `height` parameters if set to `true`. This overrides `options.fitted` as well.\n     * @param {Boolean} [options.fitted=false] = Set to `true` to automatically make the stage adapt to the width and height of the parent element. This parameter overrides `width` and `height` parameters if set to `true`.\n     * @param {Number} [options.width=640] - The width of the stage on construction. This can be set at a later time.\n     * @param {Number} [options.height=480] - The height of the stage on construction. This can be set at a later time.\n     * @param {String} [options.type=Two.Types.svg] - The type of renderer to setup drawing with. See {@link Two.Types} for available options.\n     * @param {Boolean} [options.autostart=false] - Set to `true` to add the instance to draw on `requestAnimationFrame`. This is a convenient substitute for {@link Two#play}.\n     * @param {Element} [options.domElement] - The canvas or SVG element to draw into. This overrides the `options.type` argument.\n     * @description The entrypoint for Two.js. Instantiate a `new Two` in order to setup a scene to render to. `Two` is also the publicly accessible interface that all other sub-classes, functions, and utilities attach to.\n     */\n  export default class Two {\n    static NextFrameId: any;\n    /**\n     * @name Two.Types\n     * @property {Object} - The different rendering types available in the library.\n     */\n    static Types: {\n      webgl: 'WebGLRenderer';\n      svg: 'SVGRenderer';\n      canvas: 'CanvasRenderer';\n    };\n    /**\n     * @name Two.Version\n     * @property {String} - The current working version of the library, `$version`.\n     */\n    static Version: string;\n    /**\n     * @name Two.PublishDate\n     * @property {String} - The automatically generated publish date in the build process to verify version release candidates.\n     */\n    static PublishDate: string;\n    /**\n     * @name Two.Identifier\n     * @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.\n     */\n    static Identifier: string;\n    /**\n     * @name Two.Resolution\n     * @property {Number} - Default amount of vertices to be used for interpreting Arcs and ArcSegments.\n     */\n    static Resolution: number;\n    /**\n     * @name Two.AutoCalculateImportedMatrices\n     * @property {Boolean} - When importing SVGs through the {@link Two#interpret} and {@link Two#load}, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n     * @nota-bene `false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n     */\n    static AutoCalculateImportedMatrices: boolean;\n    /**\n     * @name Two.Instances\n     * @property {Two[]} - Registered list of all Two.js instances in the current session.\n     */\n    static Instances: Two[];\n    /**\n     * @function Two.uniqueId\n     * @description Simple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n     * @returns {Number} Ever increasing Number.\n     */\n    static uniqueId: () => number;\n    static Anchor: typeof Anchor;\n    static Collection: typeof Collection;\n    static Events: typeof Events;\n    static Group: typeof Group;\n    static Matrix: typeof Matrix;\n    static Path: typeof Path;\n    static Registry: typeof Registry;\n    static Element: typeof TwoElement;\n    static Shape: typeof Shape;\n    static Text: typeof Text;\n    static Vector: typeof Vector;\n    static Gradient: typeof Gradient;\n    static Image: typeof Image;\n    static ImageSequence: typeof ImageSequence;\n    static LinearGradient: typeof LinearGradient;\n    static RadialGradient: typeof RadialGradient;\n    static Sprite: typeof Sprite;\n    static Stop: typeof Stop;\n    static Texture: typeof Texture;\n    static ArcSegment: typeof ArcSegment;\n    static Circle: typeof Circle;\n    static Ellipse: typeof Ellipse;\n    static Line: typeof Line;\n    static Points: typeof Points;\n    static Polygon: typeof Polygon;\n    static Rectangle: typeof Rectangle;\n    static RoundedRectangle: typeof RoundedRectangle;\n    static Star: typeof Star;\n    static CanvasRenderer: typeof CanvasRenderer;\n    static SVGRenderer: typeof SVGRenderer;\n    static WebGLRenderer: typeof WebGLRenderer;\n    static Commands: {\n      move: 'M';\n      line: 'L';\n      curve: 'C';\n      arc: 'A';\n      close: 'Z';\n    };\n    /**\n     * @name Two.Utils\n     * @property {Object} - A massive object filled with utility functions and properties.\n     */\n    static Utils: any;\n    constructor(options?: {\n      fullscreen?: boolean;\n      fitted?: boolean;\n      autostart?: boolean;\n      width?: number;\n      height?: number;\n      type?: (typeof Two.Types)[keyof typeof Two.Types];\n      domElement?: SVGElement | HTMLCanvasElement;\n      overdraw?: boolean;\n      smoothing?: boolean;\n      ratio?: number;\n    });\n    /**\n     * @private\n     */\n    private _events;\n    set _bound(arg: boolean);\n    get _bound(): boolean;\n    addEventListener(...args: any[]): any;\n    on(...args: any[]): any;\n    bind(...args: any[]): any;\n    removeEventListener(...args: any[]): any;\n    off(...args: any[]): any;\n    unbind(...args: any[]): any;\n    dispatchEvent(...args: any[]): any;\n    trigger(...args: any[]): any;\n    listen(...args: any[]): any;\n    ignore(...args: any[]): any;\n    /**\n     * @name Two#type\n     * @property {String} - A string representing which type of renderer the instance has instantiated.\n     */\n    type: (typeof Two.Types)[keyof typeof Two.Types];\n    /**\n     * @name Two#renderer\n     * @property {(Two.SVGRenderer|CanvasRenderer|WebGLRenderer)} - The instantiated rendering class for the instance. For a list of possible rendering types check out Two.Types.\n     */\n    renderer: SVGRenderer | CanvasRenderer | WebGLRenderer;\n    /**\n     * @name Two#scene\n     * @property {Group} - The base level {@link Two.Group} which houses all objects for the instance. Because it is a {@link Two.Group} transformations can be applied to it that will affect all objects in the instance. This is handy as a makeshift inverted camera.\n     */\n    scene: Group;\n    /**\n     * @name Two#width\n     * @property {Number} - The width of the instance's dom element.\n     */\n    width: number;\n    /**\n     * @name Two#height\n     * @property {Number} - The height of the instance's dom element.\n     */\n    height: number;\n    /**\n     * @name Two#frameCount\n     * @property {Number} - An integer representing how many frames have elapsed.\n     */\n    frameCount: number;\n    /**\n     * @name Two#timeDelta\n     * @property {Number} - A number representing how much time has elapsed since the last frame in milliseconds.\n     */\n    timeDelta: number;\n    /**\n     * @name Two#playing\n     * @property {Boolean} - A boolean representing whether or not the instance is being updated through the automatic `requestAnimationFrame`.\n     */\n    playing: boolean;\n    fit(): void;\n    /**\n     * @name Two#appendTo\n     * @function\n     * @param {Element} elem - The DOM element to append the Two.js stage to.\n     * @description Shorthand method to append your instance of Two.js to the `document`.\n     */\n    appendTo(elem: HTMLElement): Two;\n    /**\n     * @name Two#play\n     * @function\n     * @fires Two.Events.Types.play event\n     * @description Call to start an internal animation loop.\n     * @nota-bene This function initiates a `requestAnimationFrame` loop.\n     */\n    play(): Two;\n    /**\n     * @name Two#pause\n     * @function\n     * @fires Two.Events.Types.pause event\n     * @description Call to stop the internal animation loop for a specific instance of Two.js.\n     */\n    pause(): Two;\n    setPlaying(p: boolean): Two;\n    /**\n     * @name Two#release\n     * @function\n     * @param {Two.Element} [obj] - Object to release from event listening. If none provided then the root {@link Two.Group} will be used.\n     * @returns {Two.Element} The object passed for event deallocation.\n     * @description Release a {@link Two.Element}’s events from memory and recurse through its children, effects, and/or vertices.\n     */\n    release<T>(obj?: TwoElement): T;\n    getShapesAtPoint(\n      x: number,\n      y: number,\n      options?: SceneHitTestOptions\n    ): Shape[];\n    /**\n     * @name Two#update\n     * @function\n     * @fires Two.Events.Types.update event\n     * @description Update positions and calculations in one pass before rendering. Then render to the canvas.\n     * @nota-bene This function is called automatically if using {@link Two#play} or the `autostart` parameter in construction.\n     */\n    update(): Two;\n    _lastFrame: number;\n    /**\n     * @name Two#render\n     * @function\n     * @fires render\n     * @description Render all drawable and visible objects of the scene.\n     */\n    render(): Two;\n    /**\n     * @name Two#add\n     * @function\n     * @param {Shape | Shape[]} [objects] - An array of Two.js objects. Alternatively can add objects as individual arguments.\n     * @description A shorthand method to add specific Two.js objects to the scene.\n     */\n    add(objects: Shape[]): Two;\n    /**\n     * @name Two#add\n     * @function\n     * @param {...Shape} [args] - Alternatively pass each shape as an argument\n     * @description A shorthand method to add specific Two.js objects to the scene.\n     */\n    add(...args: Shape[]): Two;\n    /**\n     * @name Two#remove\n     * @function\n     * @param {Shape | Shape[]} [objects] - An array of Two.js objects.\n     * @description A shorthand method to remove specific Two.js objects from the scene.\n     */\n    remove(objects: Shape[]): Two;\n    /**\n     * @name Two#remove\n     * @function\n     * @param {...Shape} [args] - Alternatively pass each shape as an argument\n     * @description A shorthand method to remove specific Two.js objects from the scene.\n     */\n    remove(...args: Shape[]): Two;\n    /**\n     * @name Two#clear\n     * @function\n     * @description Removes all objects from the instance's scene. If you intend to have the browser garbage collect this, don't forget to delete the references in your application as well.\n     */\n    clear(): Two;\n    /**\n     * @name Two#makeLine\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} x2\n     * @param {Number} y2\n     * @returns {Line}\n     * @description Creates a Two.js line and adds it to the scene.\n     */\n    makeLine(x1: number, y1: number, x2: number, y2: number): Line;\n    /**\n     * @name Two#makeArrow\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} x2\n     * @param {Number} y2\n     * @returns {Path}\n     * @description Creates a Two.js arrow and adds it to the scene.\n     */\n    makeArrow(\n      x1: number,\n      y1: number,\n      x2: number,\n      y2: number,\n      size?: number\n    ): Path;\n    /**\n     * @name Two#makeRectangle\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} width\n     * @param {Number} height\n     * @returns {Rectangle}\n     * @description Creates a Two.js rectangle and adds it to the scene.\n     */\n    makeRectangle(\n      x: number,\n      y: number,\n      width: number,\n      height: number\n    ): Rectangle;\n    /**\n     * @name Two#makeRoundedRectangle\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} width\n     * @param {Number} height\n     * @param {Number} radius\n     * @returns {RoundedRectangle}\n     * @description Creates a Two.js rounded rectangle and adds it to the scene.\n     */\n    makeRoundedRectangle(\n      x: number,\n      y: number,\n      width: number,\n      height: number,\n      radius: number | Vector\n    ): RoundedRectangle;\n    /**\n     * @name Two#makeCircle\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} radius\n     * @param {Number} [resolution=4]\n     * @returns {Circle}\n     * @description Creates a Two.js circle and adds it to the scene.\n     */\n    makeCircle(\n      x: number,\n      y: number,\n      radius: number,\n      resolution?: number\n    ): Circle;\n    /**\n     * @name Two#makeEllipse\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} rx\n     * @param {Number} ry\n     * @param {Number} [resolution=4]\n     * @returns {Ellipse}\n     * @description Creates a Two.js ellipse and adds it to the scene.\n     */\n    makeEllipse(\n      x: number,\n      y: number,\n      rx: number,\n      ry: number,\n      resolution?: number\n    ): Ellipse;\n    /**\n     * @name Two#makeStar\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} outerRadius\n     * @param {Number} innerRadius\n     * @param {Number} sides\n     * @returns {Star}\n     * @description Creates a Two.js star and adds it to the scene.\n     */\n    makeStar(\n      x: any,\n      y: any,\n      outerRadius: number,\n      innerRadius: number,\n      sides: number\n    ): Star;\n    /**\n     * @name Two#makeCurve\n     * @function\n     * @param {Anchor[]} [points] - An array of {@link Two.Anchor} points.\n     * @returns {Path} - Where `path.curved` is set to `true`.\n     * @description Creates a Two.js path that is curved and adds it to the scene.\n     * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n     */\n    makeCurve(points?: Anchor[]): Path;\n    /**\n     * @name Two#makeCurve\n     * @function\n     * @param {...Number} [args] - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n     * @returns {Path} - Where `path.curved` is set to `true`.\n     * @description Creates a Two.js path that is curved and adds it to the scene.\n     * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n     */\n    makeCurve(...args: number[]): Path;\n    /**\n     * @name Two#makePolygon\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} radius\n     * @param {Number} sides\n     * @returns {Polygon}\n     * @description Creates a Two.js polygon and adds it to the scene.\n     */\n    makePolygon(x: number, y: number, radius: number, sides: number): Polygon;\n    /**\n     * @name Two#makeArcSegment\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} innerRadius\n     * @param {Number} outerRadius\n     * @param {Number} startAngle\n     * @param {Number} endAngle\n     * @param {Number} [resolution=Two.Resolution] - The number of vertices that should comprise the arc segment.\n     * @returns {ArcSegment}\n     */\n    makeArcSegment(\n      x: number,\n      y: number,\n      innerRadius: number,\n      outerRadius: number,\n      startAngle: number,\n      endAngle: number,\n      resolution?: number\n    ): ArcSegment;\n    /**\n     * @name Two#makePoints\n     * @function\n     * @param {Vector[]} [points] - An array of {@link Two.Vector} points\n     * @returns {Points}\n     * @description Creates a Two.js points object and adds it to the current scene.\n     */\n    makePoints(points?: Vector[]): Points;\n    /**\n     * @name Two#makePoints\n     * @function\n     * @param {...Number} [args] - Alternatively you can pass alternating `x` / `y` coordinate values as individual agrguments. These will be combined into {@link Two.Vector}s for use in the points object.\n     * @returns {Points}\n     * @description Creates a Two.js points object and adds it to the current scene.\n     */\n    makePoints(...args: number[]): Points;\n    /**\n     * @name Two#makePath\n     * @function\n     * @param {Anchor[]} [points] - An array of {@link Two.Anchor} points\n     * @returns {Path}\n     * @description Creates a Two.js path and adds it to the scene.\n     * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n     */\n    makePath(points?: Anchor[]): Path;\n    /**\n     * @name Two#makePath\n     * @function\n     * @param {...Number} [args] - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n     * @returns {Path}\n     * @description Creates a Two.js path and adds it to the scene.\n     * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n     */\n    makePath(...args: number[]): Path;\n    /**\n     * @name Two#makeText\n     * @function\n     * @param {String} message\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Object} [styles] - An object to describe any of the {@link Two.Text.Properties} including `fill`, `stroke`, `linewidth`, `family`, `alignment`, `leading`, `opacity`, etc..\n     * @returns {Text}\n     * @description Creates a Two.js text object and adds it to the scene.\n     */\n    makeText(\n      message: string,\n      x: number,\n      y: number,\n      styles?: {\n        value?: string;\n        family?: string;\n        size?: number;\n        leading?: number;\n        alignment?: 'left' | 'center' | 'right';\n        linewidth?: number;\n        style?: 'normal' | 'italic';\n        weight?: number | string;\n        decoration?: 'underline' | 'strikethrough' | 'none';\n        direction?: 'ltr' | 'rtl';\n        baseline?: 'top' | 'middle' | 'bottom' | 'baseline';\n        opacity?: number;\n        visible?: boolean;\n        fill?: string | Gradient | Texture;\n        stroke?: string | Gradient | Texture;\n        dashes?: number[] & { offset?: number };\n      }\n    ): Text;\n    /**\n     * @name Two#makeLinearGradient\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} x2\n     * @param {Number} y2\n     * @param {...Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n     * @returns {LinearGradient}\n     * @description Creates a Two.js linear gradient and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n     */\n    makeLinearGradient(\n      x1: number,\n      y1: number,\n      x2: number,\n      y2: number,\n      ...args: Stop[]\n    ): LinearGradient;\n    /**\n     * @name Two#makeRadialGradient\n     * @function\n     * @param {Number} x1\n     * @param {Number} y1\n     * @param {Number} radius\n     * @param {...Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n     * @returns {RadialGradient}\n     * @description Creates a Two.js linear-gradient object and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n     */\n    makeRadialGradient(\n      x1: number,\n      y1: number,\n      radius: number,\n      ...args: Stop[]\n    ): RadialGradient;\n    /**\n     * @name Two#makeSprite\n     * @function\n     * @param {(String|Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} [columns=1]\n     * @param {Number} [rows=1]\n     * @param {Number} [frameRate=0]\n     * @param {Boolean} [autostart=false]\n     * @returns {Sprite}\n     * @description Creates a Two.js sprite object and adds it to the scene. Sprites can be used for still images as well as animations.\n     */\n    makeSprite(\n      src: any,\n      x: number,\n      y: number,\n      columns?: number,\n      rows?: number,\n      frameRate?: number,\n      autostart?: boolean\n    ): Sprite;\n    /**\n     * @name Two#makeImage\n     * @function\n     * @param {(String|Two.Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} [width]\n     * @param {Number} [height]\n     * @param {String} [mode=\"fill\"]\n     * @returns {Two.Image}\n     * @description Creates a Two.js image object and adds it to the scene. Images are scaled to fit the provided width and height.\n     */\n    makeImage(\n      src: any,\n      x: number,\n      y: number,\n      width?: number,\n      height?: number,\n      mode?: 'fit' | 'fill' | 'crop' | 'tile' | 'stretch'\n    ): Image;\n    /**\n     * @name Two#makeImageSequence\n     * @function\n     * @param {(String[]|Texture[])} src - An array of paths or of {@link Two.Textures}.\n     * @param {Number} x\n     * @param {Number} y\n     * @param {Number} [frameRate=0]\n     * @param {Boolean} [autostart=false]\n     * @returns {ImageSequence}\n     * @description Creates a Two.js image sequence object and adds it to the scene.\n     */\n    makeImageSequence(\n      src: string[] | Texture[] | string | Texture,\n      x: number,\n      y: number,\n      frameRate?: number,\n      autostart?: boolean\n    ): ImageSequence;\n    /**\n     * @name Two#makeTexture\n     * @function\n     * @param {(String|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement)} [src] - The URL path to an image or a DOM image-like element.\n     * @param {Function} [callback] - Function to be invoked when the image is loaded.\n     * @returns {Texture}\n     * @description Creates a Two.js texture object.\n     */\n    makeTexture(src: any, callback?: () => void): Texture;\n    /**\n     * @name Two#makeGroup\n     * @function\n     * @param {Shape[]} [objects] - Two.js objects to be added to the group in the form of an array or as individual arguments.\n     * @returns {Group}\n     * @description Creates a Two.js group object and adds it to the scene.\n     */\n    makeGroup(objects?: Shape[]): Group;\n    /**\n     * @name Two#makeGroup\n     * @function\n     * @param {...Shape} [args] - Alternatively pass each element as an argument\n     * @returns {Group}\n     * @description Creates a Two.js group object and adds it to the scene.\n     */\n    makeGroup(...args: Shape[]): Group;\n    /**\n     * @name Two#interpret\n     * @function\n     * @param {SVGElement} svg - The SVG node to be parsed.\n     * @param {Boolean} shallow - Don't create a top-most group but append all content directly.\n     * @param {Boolean} [add=true] � Automatically add the reconstructed SVG node to scene.\n     * @returns {Group}\n     * @description Interpret an SVG Node and add it to this instance's scene. The distinction should be made that this doesn't `import` svg's, it solely interprets them into something compatible for Two.js - this is slightly different than a direct transcription.\n     */\n    interpret(svg: SVGElement, shallow?: boolean, add?: boolean): Group;\n    /**\n     * @name Two#load\n     * @function\n     * @param {String|SVGElement} pathOrSVGContent - The URL path of an SVG file or an SVG document as text.\n     * @param {Function} [callback] - Function to call once loading has completed.\n     * @returns {Group}\n     * @description Load an SVG file or SVG text and interpret it into Two.js legible objects.\n     */\n    load(\n      pathOrSVGContent: any,\n      callback?: (\n        group: Group,\n        svg:\n          | SVGElement\n          | SVGGElement\n          | SVGPathElement\n          | SVGTextElement\n          | SVGPatternElement\n          | SVGDefsElement\n          | SVGGradientElement\n          | SVGLinearGradientElement\n          | SVGRadialGradientElement\n          | SVGImageElement\n          | SVGClipPathElement\n          | SVGStopElement\n          | (\n              | SVGElement\n              | SVGGElement\n              | SVGPathElement\n              | SVGTextElement\n              | SVGPatternElement\n              | SVGDefsElement\n              | SVGGradientElement\n              | SVGLinearGradientElement\n              | SVGRadialGradientElement\n              | SVGImageElement\n              | SVGClipPathElement\n              | SVGStopElement\n            )[]\n      ) => void\n    ): Group;\n  }\n  import { Line } from 'two.js/src/shapes/line';\n  import { Path } from 'two.js/src/path';\n  import { Rectangle } from 'two.js/src/shapes/rectangle';\n  import { Circle } from 'two.js/src/shapes/circle';\n  import { Ellipse } from 'two.js/src/shapes/ellipse';\n  import { Star } from 'two.js/src/shapes/star';\n  import { Polygon } from 'two.js/src/shapes/polygon';\n  import { ArcSegment } from 'two.js/src/shapes/arc-segment';\n  import { Points } from 'two.js/src/shapes/points';\n  import { Text } from 'two.js/src/text';\n  import { LinearGradient } from 'two.js/src/effects/linear-gradient';\n  import { RadialGradient } from 'two.js/src/effects/radial-gradient';\n  import { Sprite } from 'two.js/src/effects/sprite';\n  import { Image } from 'two.js/src/effects/image';\n  import { ImageSequence } from 'two.js/src/effects/image-sequence';\n  import { Texture } from 'two.js/src/effects/texture';\n  import { Group } from 'two.js/src/group';\n  import { Anchor } from 'two.js/src/anchor';\n  import { Collection } from 'two.js/src/collection';\n  import { Events } from 'two.js/src/events';\n  import { Matrix } from 'two.js/src/matrix';\n  import { Registry } from 'two.js/src/registry';\n  import { Element as TwoElement } from 'two.js/src/element';\n  import { Shape, type ShapeHitTestOptions } from 'two.js/src/shape';\n  import { Vector } from 'two.js/src/vector';\n  import { Gradient } from 'two.js/src/effects/gradient';\n  import { Stop } from 'two.js/src/effects/stop';\n  import { RoundedRectangle } from 'two.js/src/shapes/rounded-rectangle';\n  import { Renderer as CanvasRenderer } from 'two.js/src/renderers/canvas';\n  import { Renderer as SVGRenderer } from 'two.js/src/renderers/svg';\n  import { Renderer as WebGLRenderer } from 'two.js/src/renderers/webgl';\n\n  export type BoundingBox = {\n    top: number;\n    left: number;\n    right: number;\n    bottom: number;\n  } & Dimensions;\n\n  export type Dimensions = {\n    width: number;\n    height: number;\n  };\n\n  export interface SceneHitTestOptions extends ShapeHitTestOptions {\n    visibleOnly?: boolean;\n    includeGroups?: boolean;\n    mode?: 'all' | 'deepest';\n    deepest?: boolean;\n    filter?: (shape: Shape) => boolean;\n  }\n}\n"
  },
  {
    "path": "src/two.js",
    "content": "// Utils\n\nimport { CanvasPolyfill } from './utils/canvas-polyfill.js';\nimport * as Curves from './utils/curves.js';\nimport { dom } from './utils/dom.js';\nimport { TwoError } from './utils/error.js';\nimport { getRatio } from './utils/device-pixel-ratio.js';\nimport { read } from './utils/interpret-svg.js';\nimport * as math from './utils/math.js';\nimport { Commands } from './utils/path-commands.js';\nimport { _ } from './utils/underscore.js';\nimport { xhr } from './utils/xhr.js';\n\n// Core Classes\n\nimport { Anchor } from './anchor.js';\nimport { Collection } from './collection.js';\nimport { Events } from './events.js';\nimport { Group } from './group.js';\nimport { Matrix } from './matrix.js';\nimport { Path } from './path.js';\nimport { Registry } from './registry.js';\nimport { Element } from './element.js';\nimport { Shape } from './shape.js';\nimport { Text } from './text.js';\nimport { Vector } from './vector.js';\n\n// Effects\n\nimport { Stop } from './effects/stop.js';\nimport { Gradient } from './effects/gradient.js';\nimport { Image } from './effects/image.js';\nimport { ImageSequence } from './effects/image-sequence.js';\nimport { LinearGradient } from './effects/linear-gradient.js';\nimport { RadialGradient } from './effects/radial-gradient.js';\nimport { Sprite } from './effects/sprite.js';\nimport { Texture } from './effects/texture.js';\n\n// Secondary Classes\n\nimport { ArcSegment } from './shapes/arc-segment.js';\nimport { Circle } from './shapes/circle.js';\nimport { Ellipse } from './shapes/ellipse.js';\nimport { Line } from './shapes/line.js';\nimport { Points } from './shapes/points.js';\nimport { Polygon } from './shapes/polygon.js';\nimport { Rectangle } from './shapes/rectangle.js';\nimport { RoundedRectangle } from './shapes/rounded-rectangle.js';\nimport { Star } from './shapes/star.js';\n\n// Renderers\n\nimport { Renderer as CanvasRenderer } from './renderers/canvas.js';\nimport { Renderer as SVGRenderer } from './renderers/svg.js';\nimport { Renderer as WebGLRenderer } from './renderers/webgl.js';\n\nimport { Constants } from './constants.js';\n\nconst Utils = _.extend(\n  {\n    Error: TwoError,\n    getRatio,\n    read,\n    xhr,\n  },\n  _,\n  CanvasPolyfill,\n  Curves,\n  math\n);\n\n/**\n * @name Two\n * @class\n * @global\n * @extends Two.Events\n * @param {Object} [options]\n * @param {Boolean} [options.fullscreen=false] - Set to `true` to automatically make the stage adapt to the width and height of the parent document. This parameter overrides `width` and `height` parameters if set to `true`. This overrides `options.fitted` as well.\n * @param {Boolean} [options.fitted=false] - Set to `true` to automatically make the stage adapt to the width and height of the parent element. This parameter overrides `width` and `height` parameters if set to `true`.\n * @param {Number} [options.width=640] - The width of the stage on construction. This can be set at a later time.\n * @param {Number} [options.height=480] - The height of the stage on construction. This can be set at a later time.\n * @param {String} [options.type=Two.Types.svg] - The type of renderer to setup drawing with. See {@link Two.Types} for available options.\n * @param {Boolean} [options.autostart=false] - Set to `true` to add the instance to draw on `requestAnimationFrame`. This is a convenient substitute for {@link Two#play}.\n * @param {Element} [options.domElement] - The canvas or SVG element to draw into. This overrides the `options.type` argument.\n * @description The entrypoint for Two.js. Instantiate a `new Two` in order to setup a scene to render to. `Two` is also the publicly accessible namespace that all other sub-classes, functions, and utilities attach to.\n */\nexport default class Two {\n  // Warning: inherit events while overriding static properties\n  /**\n   * @private\n   */\n  _events = new Events();\n\n  // Getters and setters aren't enumerable\n  get _bound() {\n    return this._events._bound;\n  }\n  set _bound(v) {\n    this._events._bound = v;\n  }\n\n  addEventListener() {\n    return this._events.addEventListener?.apply(this, arguments);\n  }\n  on() {\n    return this._events.addEventListener?.apply(this, arguments);\n  }\n  bind() {\n    return this._events.addEventListener?.apply(this, arguments);\n  }\n  removeEventListener() {\n    return this._events.removeEventListener?.apply(this, arguments);\n  }\n  off() {\n    return this._events.removeEventListener?.apply(this, arguments);\n  }\n  unbind() {\n    return this._events.removeEventListener?.apply(this, arguments);\n  }\n  dispatchEvent() {\n    return this._events.dispatchEvent?.apply(this, arguments);\n  }\n  trigger() {\n    return this._events.dispatchEvent?.apply(this, arguments);\n  }\n  listen() {\n    return this._events.listen?.apply(this, arguments);\n  }\n  ignore() {\n    return this._events.ignore?.apply(this, arguments);\n  }\n\n  /**\n   * @name Two#type\n   * @property {String} - A string representing which type of renderer the instance has instantiated.\n   */\n  type = '';\n\n  /**\n   * @name Two#renderer\n   * @property {(Two.SVGRenderer|Two.CanvasRenderer|Two.WebGLRenderer)} - The instantiated rendering class for the instance. For a list of possible rendering types check out Two.Types.\n   */\n  renderer = null;\n\n  /**\n   * @name Two#scene\n   * @property {Two.Group} - The base level {@link Two.Group} which houses all objects for the instance. Because it is a {@link Two.Group} transformations can be applied to it that will affect all objects in the instance. This is handy as a makeshift inverted camera.\n   */\n  scene = null;\n\n  /**\n   * @name Two#width\n   * @property {Number} - The width of the instance's dom element.\n   */\n  width = 0;\n\n  /**\n   * @name Two#height\n   * @property {Number} - The height of the instance's dom element.\n   */\n  height = 0;\n\n  /**\n   * @name Two#frameCount\n   * @property {Number} - An integer representing how many frames have elapsed.\n   */\n  frameCount = 0;\n\n  /**\n   * @name Two#timeDelta\n   * @property {Number} - A number representing how much time has elapsed since the last frame in milliseconds.\n   */\n  timeDelta = 0;\n\n  /**\n   * @name Two#playing\n   * @property {Boolean} - A boolean representing whether or not the instance is being updated through the automatic `requestAnimationFrame`.\n   */\n  playing = false;\n\n  constructor(options) {\n    // Determine what Renderer to use and setup a scene.\n\n    const params = _.defaults(options || {}, {\n      fullscreen: false,\n      fitted: false,\n      width: 640,\n      height: 480,\n      type: Two.Types.svg,\n      autostart: false,\n    });\n\n    _.each(\n      params,\n      function (v, k) {\n        if (/fullscreen/i.test(k) || /autostart/i.test(k)) {\n          return;\n        }\n        this[k] = v;\n      },\n      this\n    );\n\n    // Specified domElement overrides type declaration only if the element does not support declared renderer type.\n    if (_.isElement(params.domElement)) {\n      const tagName = params.domElement.tagName.toLowerCase();\n      // TODO: Reconsider this if statement's logic.\n      if (\n        !/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test(\n          this.type + '-' + tagName\n        )\n      ) {\n        this.type = Two.Types[tagName];\n      }\n    }\n\n    this.renderer = new Two[this.type](this);\n    this.setPlaying(params.autostart);\n    this.frameCount = 0;\n\n    /**\n     * @name Two#fit\n     * @function\n     * @description If `options.fullscreen` or `options.fitted` in construction create this function. It sets the `width` and `height` of the instance to its respective parent `window` or `element` depending on the `options` passed.\n     */\n    if (params.fullscreen) {\n      this.fit = fitToWindow.bind(this);\n      this.fit.domElement = window;\n      this.fit.attached = true;\n      _.extend(document.body.style, {\n        overflow: 'hidden',\n        margin: 0,\n        padding: 0,\n        top: 0,\n        left: 0,\n        right: 0,\n        bottom: 0,\n        position: 'fixed',\n      });\n      _.extend(this.renderer.domElement.style, {\n        display: 'block',\n        top: 0,\n        left: 0,\n        right: 0,\n        bottom: 0,\n        position: 'fixed',\n      });\n      dom.bind(this.fit.domElement, 'resize', this.fit);\n      this.fit();\n    } else if (params.fitted) {\n      this.fit = fitToParent.bind(this);\n      _.extend(this.renderer.domElement.style, {\n        display: 'block',\n      });\n    } else if (\n      typeof params.width === 'number' &&\n      typeof params.height === 'number'\n    ) {\n      this.renderer.setSize(params.width, params.height, this.ratio);\n      this.width = params.width;\n      this.height = params.height;\n    }\n\n    this.renderer.bind(Events.Types.resize, updateDimensions.bind(this));\n    this.scene = this.renderer.scene;\n\n    Two.Instances.push(this);\n    if (params.autostart) {\n      raf.init();\n    }\n  }\n\n  static NextFrameId = Constants.NextFrameId;\n\n  // Primitive\n\n  /**\n   * @name Two.Types\n   * @property {Object} - The different rendering types available in the library.\n   */\n  static Types = Constants.Types;\n\n  /**\n   * @name Two.Version\n   * @property {String} - The current working version of the library, `$version`.\n   */\n  static Version = Constants.Version;\n\n  /**\n   * @name Two.PublishDate\n   * @property {String} - The automatically generated publish date in the build process to verify version release candidates.\n   */\n  static PublishDate = Constants.PublishDate;\n\n  /**\n   * @name Two.Identifier\n   * @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.\n   */\n  static Identifier = Constants.Identifier;\n\n  /**\n   * @name Two.Resolution\n   * @property {Number} - Default amount of vertices to be used for interpreting Arcs and ArcSegments.\n   */\n  static Resolution = Constants.Resolution;\n\n  /**\n   * @name Two.AutoCalculateImportedMatrices\n   * @property {Boolean} - When importing SVGs through the {@link Two#interpret} and {@link Two#load}, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n   * @nota-bene `false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n   */\n  static AutoCalculateImportedMatrices =\n    Constants.AutoCalculateImportedMatrices;\n\n  /**\n   * @name Two.Instances\n   * @property {Two[]} - Registered list of all Two.js instances in the current session.\n   */\n  static Instances = Constants.Instances;\n\n  /**\n   * @function Two.uniqueId\n   * @description Simple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n   * @returns {Number} Ever increasing Number.\n   */\n  static uniqueId = Constants.uniqueId;\n\n  static Anchor = Anchor;\n  static Collection = Collection;\n  static Events = Events;\n  static Group = Group;\n  static Matrix = Matrix;\n  static Path = Path;\n  static Registry = Registry;\n  static Element = Element;\n  static Shape = Shape;\n  static Text = Text;\n  static Vector = Vector;\n\n  static Gradient = Gradient;\n  static Image = Image;\n  static ImageSequence = ImageSequence;\n  static LinearGradient = LinearGradient;\n  static RadialGradient = RadialGradient;\n  static Sprite = Sprite;\n  static Stop = Stop;\n  static Texture = Texture;\n\n  static ArcSegment = ArcSegment;\n  static Circle = Circle;\n  static Ellipse = Ellipse;\n  static Line = Line;\n  static Points = Points;\n  static Polygon = Polygon;\n  static Rectangle = Rectangle;\n  static RoundedRectangle = RoundedRectangle;\n  static Star = Star;\n\n  static CanvasRenderer = CanvasRenderer;\n  static SVGRenderer = SVGRenderer;\n  static WebGLRenderer = WebGLRenderer;\n\n  /**\n   * @name Two.Commands\n   * @property {Object} - Map of possible path commands. Taken from the SVG specification. Commands include: `move`, `line`, `curve`, `arc`, and `close`.\n   */\n  static Commands = Commands;\n\n  /**\n   * @name Two.Utils\n   * @property {Object} Utils - A massive object filled with utility functions and properties.\n   * @property {Object} Two.Utils.read - A collection of SVG parsing functions indexed by element name.\n   * @property {Function} Two.Utils.read.path - Parse SVG path element or `d` attribute string.\n   */\n  static Utils = Utils;\n\n  /**\n   * @name Two#appendTo\n   * @function\n   * @param {Element} elem - The DOM element to append the Two.js stage to.\n   * @description Shorthand method to append your instance of Two.js to the `document`.\n   */\n  appendTo(elem) {\n    elem.appendChild(this.renderer.domElement);\n\n    if (this.fit) {\n      if (this.fit.domElement !== window) {\n        this.fit.domElement = elem;\n        this.fit.attached = false;\n      }\n      this.update();\n    }\n\n    return this;\n  }\n\n  /**\n   * @name Two#play\n   * @function\n   * @fires play\n   * @description Call to start an internal animation loop.\n   * @nota-bene This function initiates a `requestAnimationFrame` loop.\n   */\n  play() {\n    this.playing = true;\n    raf.init();\n    return this.trigger(Events.Types.play);\n  }\n\n  /**\n   * @name Two#pause\n   * @function\n   * @fires pause\n   * @description Call to stop the internal animation loop for a specific instance of Two.js.\n   */\n  pause() {\n    this.playing = false;\n    return this.trigger(Events.Types.pause);\n  }\n\n  setPlaying(p) {\n    this.playing = p;\n  }\n\n  /**\n   * @name Two#release\n   * @function\n   * @param {Two.Element} [obj] - Object to release from event listening. If none provided then the root {@link Two.Group} will be used.\n   * @returns {Two.Element} The object passed for event deallocation.\n   * @description Release a {@link Two.Element}’s events from memory and recurse through its children, effects, and/or vertices.\n   */\n  release(obj) {\n    let i, v, child;\n\n    // Release this instance of Two.js if nothing passed\n    if (typeof obj === 'undefined') {\n      return this.release(this.scene);\n    }\n\n    if (typeof obj.unbind === 'function') {\n      obj.unbind();\n    }\n\n    // Unbind effects applied to an object\n    if (typeof obj.fill === 'object' && typeof obj.fill.unbind === 'function') {\n      obj.fill.unbind();\n    }\n    if (\n      typeof obj.stroke === 'object' &&\n      typeof obj.stroke.unbind === 'function'\n    ) {\n      obj.stroke.unbind();\n    }\n\n    // Unbind vertices on an object\n    if (obj.vertices) {\n      if (typeof obj.vertices.unbind === 'function') {\n        try {\n          obj.vertices.unbind();\n        } catch (e) {\n          // Ignore unbind errors for incomplete Collection objects\n        }\n      }\n      for (i = 0; i < obj.vertices.length; i++) {\n        v = obj.vertices[i];\n        if (typeof v.unbind === 'function') {\n          v.unbind();\n        }\n        if (v.controls) {\n          if (v.controls.left && typeof v.controls.left.unbind === 'function') {\n            v.controls.left.unbind();\n          }\n          if (\n            v.controls.right &&\n            typeof v.controls.right.unbind === 'function'\n          ) {\n            v.controls.right.unbind();\n          }\n        }\n      }\n    }\n\n    // Unbind any children of the object\n    if (obj.children) {\n      for (i = 0; i < obj.children.length; i++) {\n        child = obj.children[i];\n        this.release(child);\n      }\n      if (typeof obj.children.unbind === 'function') {\n        try {\n          obj.children.unbind();\n        } catch (e) {\n          // Ignore unbind errors for incomplete Collection objects\n        }\n      }\n    }\n\n    // Clean up renderer-specific resources\n    if (obj._renderer) {\n      // SVG DOM element cleanup\n      if (obj._renderer.elem && obj._renderer.elem.parentNode) {\n        obj._renderer.elem.parentNode.removeChild(obj._renderer.elem);\n        delete obj._renderer.elem;\n      }\n\n      // WebGL resource cleanup\n      if (this.type === 'WebGLRenderer' && this.renderer.ctx) {\n        const gl = this.renderer.ctx;\n\n        // Clean up textures\n        if (obj._renderer.texture) {\n          gl.deleteTexture(obj._renderer.texture);\n          delete obj._renderer.texture;\n        }\n\n        // Clean up buffers\n        if (obj._renderer.positionBuffer) {\n          gl.deleteBuffer(obj._renderer.positionBuffer);\n          delete obj._renderer.positionBuffer;\n        }\n\n        // Clean up any other WebGL effects\n        if (obj._renderer.effect) {\n          obj._renderer.effect = null;\n        }\n      }\n\n      // Canvas renderer cleanup - clear cached contexts and data\n      if (this.type === 'CanvasRenderer' && obj._renderer.context) {\n        delete obj._renderer.context;\n      }\n    }\n\n    return obj;\n  }\n\n  /**\n   * @name Two#getShapesAtPoint\n   * @function\n   * @param {Number} x - X coordinate in world space.\n   * @param {Number} y - Y coordinate in world space.\n   * @param {Object} [options] - Hit test configuration.\n   * @param {Boolean} [options.visibleOnly=true] - Limit results to visible shapes.\n   * @param {Boolean} [options.includeGroups=false] - Include groups in the hit results.\n   * @param {('all'|'deepest')} [options.mode='all'] - Whether to return all intersecting shapes or only the top-most.\n   * @param {Boolean} [options.deepest] - Alias for `mode: 'deepest'`.\n   * @param {Number} [options.precision] - Segmentation precision for curved geometry.\n   * @param {Number} [options.tolerance=0] - Pixel tolerance applied to hit testing.\n   * @param {Boolean} [options.fill] - Override fill testing behaviour.\n   * @param {Boolean} [options.stroke] - Override stroke testing behaviour.\n   * @param {Function} [options.filter] - Predicate to filter shapes from the result set.\n   * @returns {Two.Shape[]} Ordered list of shapes under the specified point, front to back.\n   * @description Returns shapes underneath the provided coordinates. Coordinates are expected in world space (matching the renderer output).\n   * @nota-bene Delegates to {@link Two.Group#getShapesAtPoint} on the root scene.\n   */\n  getShapesAtPoint(x, y, options) {\n    if (this.scene && typeof this.scene.getShapesAtPoint === 'function') {\n      return this.scene.getShapesAtPoint(x, y, options);\n    }\n    return [];\n  }\n\n  /**\n   * @name Two#update\n   * @function\n   * @fires update\n   * @description Update positions and calculations in one pass before rendering. Then render to the canvas.\n   * @nota-bene This function is called automatically if using {@link Two#play} or the `autostart` parameter in construction.\n   */\n  update() {\n    const animated = !!this._lastFrame;\n    const now = _.performance.now();\n\n    if (animated) {\n      this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3));\n    }\n    this._lastFrame = now;\n\n    if (this.fit && this.fit.domElement && !this.fit.attached) {\n      dom.bind(this.fit.domElement, 'resize', this.fit);\n      this.fit.attached = true;\n      this.fit();\n    }\n\n    const width = this.width;\n    const height = this.height;\n    const renderer = this.renderer;\n\n    // Update width / height for the renderer\n    if (width !== renderer.width || height !== renderer.height) {\n      renderer.setSize(width, height, this.ratio);\n    }\n\n    this.trigger(Events.Types.update, this.frameCount, this.timeDelta);\n\n    return this.render();\n  }\n\n  /**\n   * @name Two#render\n   * @function\n   * @fires render\n   * @description Render all drawable and visible objects of the scene.\n   */\n  render() {\n    this.renderer.render();\n    return this.trigger(Events.Types.render, this.frameCount++);\n  }\n\n  // Convenience Methods\n\n  /**\n   * @name Two#add\n   * @function\n   * @param {(Two.Shape[]|...Two.Shape)} [objects] - An array of Two.js objects. Alternatively can add objects as individual arguments.\n   * @description A shorthand method to add specific Two.js objects to the scene.\n   */\n  add(objects) {\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    }\n\n    this.scene.add(objects);\n    return this;\n  }\n\n  /**\n   * @name Two#remove\n   * @function\n   * @param {(Two.Shape[]|...Two.Shape)} [objects] - An array of Two.js objects.\n   * @description A shorthand method to remove specific Two.js objects from the scene.\n   */\n  remove(objects) {\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    }\n\n    this.scene.remove(objects);\n\n    return this;\n  }\n\n  /**\n   * @name Two#clear\n   * @function\n   * @description Removes all objects from the instance's scene. If you intend to have the browser garbage collect this, don't forget to delete the references in your application as well.\n   */\n  clear() {\n    this.scene.remove(this.scene.children);\n    return this;\n  }\n\n  /**\n   * @name Two#makeLine\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} x2\n   * @param {Number} y2\n   * @returns {Two.Line}\n   * @description Creates a Two.js line and adds it to the scene.\n   */\n  makeLine(x1, y1, x2, y2) {\n    const line = new Line(x1, y1, x2, y2);\n    this.scene.add(line);\n\n    return line;\n  }\n\n  /**\n   * @name Two#makeArrow\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} x2\n   * @param {Number} y2\n   * @returns {Two.Path}\n   * @description Creates a Two.js arrow and adds it to the scene.\n   */\n  makeArrow(x1, y1, x2, y2, size) {\n    const headlen = typeof size === 'number' ? size : 10;\n\n    const angle = Math.atan2(y2 - y1, x2 - x1);\n\n    const vertices = [\n      new Anchor(\n        x1,\n        y1,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        Commands.move\n      ),\n      new Anchor(\n        x2,\n        y2,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        Commands.line\n      ),\n      new Anchor(\n        x2 - headlen * Math.cos(angle - Math.PI / 4),\n        y2 - headlen * Math.sin(angle - Math.PI / 4),\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        Commands.line\n      ),\n\n      new Anchor(\n        x2,\n        y2,\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        Commands.move\n      ),\n      new Anchor(\n        x2 - headlen * Math.cos(angle + Math.PI / 4),\n        y2 - headlen * Math.sin(angle + Math.PI / 4),\n        undefined,\n        undefined,\n        undefined,\n        undefined,\n        Commands.line\n      ),\n    ];\n\n    const path = new Path(vertices, false, false, true);\n    path.noFill();\n    path.cap = 'round';\n    path.join = 'round';\n\n    this.scene.add(path);\n\n    return path;\n  }\n\n  /**\n   * @name Two#makeRectangle\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} width\n   * @param {Number} height\n   * @returns {Two.Rectangle}\n   * @description Creates a Two.js rectangle and adds it to the scene.\n   */\n  makeRectangle(x, y, width, height) {\n    const rect = new Rectangle(x, y, width, height);\n    this.scene.add(rect);\n\n    return rect;\n  }\n\n  /**\n   * @name Two#makeRoundedRectangle\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} width\n   * @param {Number} height\n   * @param {Number} sides\n   * @returns {Two.RoundedRectangle}\n   * @description Creates a Two.js rounded rectangle and adds it to the scene.\n   */\n  makeRoundedRectangle(x, y, width, height, sides) {\n    const rect = new RoundedRectangle(x, y, width, height, sides);\n    this.scene.add(rect);\n\n    return rect;\n  }\n\n  /**\n   * @name Two#makeCircle\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} radius\n   * @param {Number} [resolution=4]\n   * @returns {Two.Circle}\n   * @description Creates a Two.js circle and adds it to the scene.\n   */\n  makeCircle(x, y, radius, resolution) {\n    const circle = new Circle(x, y, radius, resolution);\n    this.scene.add(circle);\n\n    return circle;\n  }\n\n  /**\n   * @name Two#makeEllipse\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} rx\n   * @param {Number} ry\n   * @param {Number} [resolution=4]\n   * @returns {Two.Ellipse}\n   * @description Creates a Two.js ellipse and adds it to the scene.\n   */\n  makeEllipse(x, y, rx, ry, resolution) {\n    const ellipse = new Ellipse(x, y, rx, ry, resolution);\n    this.scene.add(ellipse);\n\n    return ellipse;\n  }\n\n  /**\n   * @name Two#makeStar\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} outerRadius\n   * @param {Number} innerRadius\n   * @param {Number} sides\n   * @returns {Two.Star}\n   * @description Creates a Two.js star and adds it to the scene.\n   */\n  makeStar(x, y, outerRadius, innerRadius, sides) {\n    const star = new Star(x, y, outerRadius, innerRadius, sides);\n    this.scene.add(star);\n\n    return star;\n  }\n\n  /**\n   * @name Two#makeCurve\n   * @function\n   * @param {Two.Anchor[]} [points] - An array of {@link Two.Anchor} points.\n   * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n   * @returns {Two.Path} - Where `path.curved` is set to `true`.\n   * @description Creates a Two.js path that is curved and adds it to the scene.\n   * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n   */\n  makeCurve(points) {\n    const l = arguments.length;\n\n    if (!Array.isArray(points)) {\n      points = [];\n      for (let i = 0; i < l; i += 2) {\n        const x = arguments[i];\n        if (typeof x !== 'number') {\n          break;\n        }\n        const y = arguments[i + 1];\n        points.push(new Anchor(x, y));\n      }\n    }\n\n    const last = arguments[l - 1];\n    const curve = new Path(\n      points,\n      !(typeof last === 'boolean' ? last : undefined),\n      true\n    );\n    const rect = curve.getBoundingClientRect();\n    curve\n      .center()\n      .translation.set(rect.left + rect.width / 2, rect.top + rect.height / 2);\n\n    this.scene.add(curve);\n\n    return curve;\n  }\n\n  /**\n   * @name Two#makePolygon\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} radius\n   * @param {Number} sides\n   * @returns {Two.Polygon}\n   * @description Creates a Two.js polygon and adds it to the scene.\n   */\n  makePolygon(x, y, radius, sides) {\n    const poly = new Polygon(x, y, radius, sides);\n    this.scene.add(poly);\n\n    return poly;\n  }\n\n  /**\n   * @name Two#makeArcSegment\n   * @function\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} innerRadius\n   * @param {Number} outerRadius\n   * @param {Number} startAngle\n   * @param {Number} endAngle\n   * @param {Number} [resolution=Two.Resolution] - The number of vertices that should comprise the arc segment.\n   * @returns {Two.ArcSegment}\n   */\n  makeArcSegment(\n    x,\n    y,\n    innerRadius,\n    outerRadius,\n    startAngle,\n    endAngle,\n    resolution\n  ) {\n    const arcSegment = new ArcSegment(\n      x,\n      y,\n      innerRadius,\n      outerRadius,\n      startAngle,\n      endAngle,\n      resolution\n    );\n    this.scene.add(arcSegment);\n    return arcSegment;\n  }\n\n  /**\n   * @name Two#makePoints\n   * @function\n   * @param {Two.Vector[]} [points] - An array of {@link Two.Vector} points\n   * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual agrguments. These will be combined into {@link Two.Vector}s for use in the points object.\n   * @returns {Two.Points}\n   * @description Creates a Two.js points object and adds it to the current scene.\n   */\n  makePoints(p) {\n    const l = arguments.length;\n    let vertices = p;\n\n    if (!Array.isArray(p)) {\n      vertices = [];\n      for (let i = 0; i < l; i += 2) {\n        const x = arguments[i];\n        if (typeof x !== 'number') {\n          break;\n        }\n        const y = arguments[i + 1];\n        vertices.push(new Vector(x, y));\n      }\n    }\n\n    const points = new Points(vertices);\n\n    this.scene.add(points);\n\n    return points;\n  }\n\n  /**\n   * @name Two#makePath\n   * @function\n   * @param {Two.Anchor[]} [points] - An array of {@link Two.Anchor} points\n   * @param {...Number} - Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into {@link Two.Anchor}s for use in the path.\n   * @returns {Two.Path}\n   * @description Creates a Two.js path and adds it to the scene.\n   * @nota-bene In either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n   */\n  makePath(p) {\n    const l = arguments.length;\n    let points = p;\n\n    if (!Array.isArray(p)) {\n      points = [];\n      for (let i = 0; i < l; i += 2) {\n        const x = arguments[i];\n        if (typeof x !== 'number') {\n          break;\n        }\n        const y = arguments[i + 1];\n        points.push(new Anchor(x, y));\n      }\n    }\n\n    const last = arguments[l - 1];\n    const path = new Path(\n      points,\n      !(typeof last === 'boolean' ? last : undefined)\n    );\n    const rect = path.getBoundingClientRect();\n    if (\n      typeof rect.top === 'number' &&\n      typeof rect.left === 'number' &&\n      typeof rect.right === 'number' &&\n      typeof rect.bottom === 'number'\n    ) {\n      path\n        .center()\n        .translation.set(\n          rect.left + rect.width / 2,\n          rect.top + rect.height / 2\n        );\n    }\n\n    this.scene.add(path);\n\n    return path;\n  }\n\n  /**\n   * @name Two#makeText\n   * @function\n   * @param {String} message\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Object} [styles] - An object to describe any of the {@link Two.Text.Properties} including `fill`, `stroke`, `linewidth`, `family`, `alignment`, `leading`, `opacity`, etc..\n   * @returns {Two.Text}\n   * @description Creates a Two.js text object and adds it to the scene.\n   */\n  makeText(message, x, y, styles) {\n    const text = new Text(message, x, y, styles);\n    this.add(text);\n    return text;\n  }\n\n  /**\n   * @name Two#makeLinearGradient\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} x2\n   * @param {Number} y2\n   * @param {...Two.Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n   * @returns {Two.LinearGradient}\n   * @description Creates a Two.js linear gradient and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n   */\n  makeLinearGradient(x1, y1, x2, y2 /* stops */) {\n    const stops = Array.prototype.slice.call(arguments, 4);\n    const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n\n    this.add(gradient);\n\n    return gradient;\n  }\n\n  /**\n   * @name Two#makeRadialGradient\n   * @function\n   * @param {Number} x1\n   * @param {Number} y1\n   * @param {Number} radius\n   * @param {...Two.Stop} args - Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied.\n   * @returns {Two.RadialGradient}\n   * @description Creates a Two.js linear-gradient object and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n   */\n  makeRadialGradient(x1, y1, radius /* stops */) {\n    const stops = Array.prototype.slice.call(arguments, 3);\n    const gradient = new RadialGradient(x1, y1, radius, stops);\n\n    this.add(gradient);\n\n    return gradient;\n  }\n\n  /**\n   * @name Two#makeSprite\n   * @function\n   * @param {(String|Two.Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} [columns=1]\n   * @param {Number} [rows=1]\n   * @param {Number} [frameRate=0]\n   * @param {Boolean} [autostart=false]\n   * @returns {Two.Sprite}\n   * @description Creates a Two.js sprite object and adds it to the scene. Sprites can be used for still images as well as animations.\n   */\n  makeSprite(src, x, y, columns, rows, frameRate, autostart) {\n    const sprite = new Sprite(src, x, y, columns, rows, frameRate);\n    if (autostart) {\n      sprite.play();\n    }\n    this.add(sprite);\n\n    return sprite;\n  }\n\n  /**\n   * @name Two#makeImage\n   * @function\n   * @param {(String|Two.Texture)} src - The URL path to an image or an already created {@link Two.Texture}.\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} width\n   * @param {Number} height\n   * @param {String} [mode=\"fill\"]\n   * @returns {Two.Image}\n   * @description Creates a Two.js image object and adds it to the scene. Images are scaled to fit the provided width and height.\n   */\n  makeImage(src, x, y, width, height, mode) {\n    const image = new Image(src, x, y, width, height, mode);\n    this.add(image);\n\n    return image;\n  }\n\n  /**\n   * @name Two#makeImageSequence\n   * @function\n   * @param {(String[]|Two.Texture[])} src - An array of paths or of {@link Two.Textures}.\n   * @param {Number} x\n   * @param {Number} y\n   * @param {Number} [frameRate=0]\n   * @param {Boolean} [autostart=false]\n   * @returns {Two.ImageSequence}\n   * @description Creates a Two.js image sequence object and adds it to the scene.\n   */\n  makeImageSequence(src, x, y, frameRate, autostart) {\n    const imageSequence = new ImageSequence(src, x, y, frameRate);\n    if (autostart) {\n      imageSequence.play();\n    }\n    this.add(imageSequence);\n\n    return imageSequence;\n  }\n\n  /**\n   * @name Two#makeTexture\n   * @function\n   * @param {(String|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement)} [src] - The URL path to an image or a DOM image-like element.\n   * @param {Function} [callback] - Function to be invoked when the image is loaded.\n   * @returns {Two.Texture}\n   * @description Creates a Two.js texture object.\n   */\n  makeTexture(src, callback) {\n    const texture = new Texture(src, callback);\n    return texture;\n  }\n\n  /**\n   * @name Two#makeGroup\n   * @function\n   * @param {(Two.Shape[]|...Two.Shape)} [objects] - Two.js objects to be added to the group in the form of an array or as individual arguments.\n   * @returns {Two.Group}\n   * @description Creates a Two.js group object and adds it to the scene.\n   */\n  makeGroup(objects) {\n    if (!(objects instanceof Array)) {\n      objects = Array.prototype.slice.call(arguments);\n    }\n\n    const group = new Group();\n    this.scene.add(group);\n    group.add(objects);\n\n    return group;\n  }\n\n  /**\n   * @name Two#interpret\n   * @function\n   * @param {SVGElement} svg - The SVG node to be parsed.\n   * @param {Boolean} shallow - Don't create a top-most group but append all content directly.\n   * @param {Boolean} [add=true] – Automatically add the reconstructed SVG node to scene.\n   * @returns {Two.Group}\n   * @description Interpret an SVG Node and add it to this instance's scene. The distinction should be made that this doesn't `import` svg's, it solely interprets them into something compatible for Two.js - this is slightly different than a direct transcription.\n   */\n  interpret(svg, shallow, add) {\n    const tag = svg.tagName.toLowerCase();\n\n    add = typeof add !== 'undefined' ? add : true;\n\n    if (!(tag in read)) {\n      return null;\n    }\n\n    const node = read[tag].call(this, svg);\n\n    if (add) {\n      this.add(shallow && node instanceof Group ? node.children : node);\n    } else if (node.parent) {\n      // Remove `g` tags that have been added to scenegraph / DOM\n      // in order to be compatible with `getById` methods.\n      node.remove();\n    }\n\n    return node;\n  }\n\n  /**\n   * @name Two#load\n   * @function\n   * @param {String|SVGElement} pathOrSVGContent - The URL path of an SVG file or an SVG document as text.\n   * @param {Function} [callback] - Function to call once loading has completed.\n   * @returns {Two.Group}\n   * @description Load an SVG file or SVG text and interpret it into Two.js legible objects.\n   */\n  load(pathOrSVGContent, callback) {\n    const group = new Group();\n    let elem, i, child;\n\n    const attach = function (data) {\n      dom.temp.innerHTML = data;\n\n      for (i = 0; i < dom.temp.children.length; i++) {\n        elem = dom.temp.children[i];\n        child = this.interpret(elem, false, false);\n        if (child !== null) {\n          group.add(child);\n        }\n      }\n\n      if (typeof callback === 'function') {\n        const svg =\n          dom.temp.children.length <= 1\n            ? dom.temp.children[0]\n            : dom.temp.children;\n        callback(group, svg);\n      }\n    }.bind(this);\n\n    if (/\\.svg$/i.test(pathOrSVGContent)) {\n      xhr(pathOrSVGContent, attach);\n\n      return group;\n    }\n\n    attach(pathOrSVGContent);\n\n    return group;\n  }\n}\n\nfunction fitToWindow() {\n  const wr = document.body.getBoundingClientRect();\n\n  const width = (this.width = wr.width);\n  const height = (this.height = wr.height);\n\n  this.renderer.setSize(width, height, this.ratio);\n}\n\nfunction fitToParent() {\n  const parent = this.renderer.domElement.parentElement;\n  if (!parent) {\n    console.warn('Two.js: Attempting to fit to parent, but no parent found.');\n    return;\n  }\n  const wr = parent.getBoundingClientRect();\n\n  const width = (this.width = wr.width);\n  const height = (this.height = wr.height);\n\n  this.renderer.setSize(width, height, this.ratio);\n}\n\nfunction updateDimensions(width, height) {\n  this.width = width;\n  this.height = height;\n  this.trigger(Events.Types.resize, width, height);\n}\n\n// Request Animation Frame\n\nconst raf = dom.getRequestAnimationFrame();\n\nfunction loop() {\n  for (let i = 0; i < Two.Instances.length; i++) {\n    const t = Two.Instances[i];\n    if (t.playing) {\n      t.update();\n    }\n  }\n\n  Two.NextFrameId = raf(loop);\n}\n\nraf.init = function () {\n  loop();\n  raf.init = function () {};\n};\n"
  },
  {
    "path": "src/utils/canvas-polyfill.d.ts",
    "content": "declare module 'two.js/src/utils/canvas-polyfill' {\n  export interface CanvasPolyfill {\n    Image: any;\n    isHeadless: boolean;\n    shim(canvas: any, name?: string): any;\n    polyfill(\n      canvas: any,\n      Image?: new (width?: number, height?: number) => HTMLImageElement\n    ): any;\n  }\n}\n"
  },
  {
    "path": "src/utils/canvas-polyfill.js",
    "content": "export const CanvasPolyfill = {\n  /**\n   * @param {Image}\n   */\n  Image: null,\n\n  /**\n   * @param {Boolean}\n   */\n  isHeadless: false,\n\n  /**\n   *\n   * @param {canvas} elem - An element to spoof as a `<canvas />`.\n   * @param {String} [name] - An optional tag and node name to spoof. Defaults to `'canvas'`.\n   * @returns {canvas} - The same `elem` passed in the first argument with updated attributes needed to be used by Two.js.\n   * @description Adds attributes invoked by Two.js in order to execute and run correctly. This is used by headless environments.\n   */\n  shim: function (elem, name) {\n    elem.tagName = elem.nodeName = name || 'canvas';\n    elem.nodeType = 1;\n    elem.getAttribute = function (prop) {\n      return this[prop];\n    };\n    elem.setAttribute = function (prop, val) {\n      this[prop] = val;\n      return this;\n    };\n    return elem;\n  },\n\n  /**\n   * @name Two.Utils.polyfill\n   * @function\n   * @param {canvas} canvas - The instanced `Canvas` object provided by `node-canvas`.\n   * @param {Image} [Image] - The prototypical `Image` object provided by `node-canvas`. This is only necessary to pass if you're going to load bitmap imagery.\n   * @returns {canvas} Returns the instanced canvas object you passed from with additional attributes needed for Two.js.\n   * @description Convenience method for defining all the dependencies from the npm package `node-canvas`. See [node-canvas](https://github.com/Automattic/node-canvas) for additional information on setting up HTML5 `<canvas />` drawing in a node.js environment.\n   */\n  polyfill: function (canvas, Image) {\n    CanvasPolyfill.shim(canvas);\n    if (typeof Image !== 'undefined') {\n      CanvasPolyfill.Image = Image;\n    }\n    CanvasPolyfill.isHeadless = true;\n    return canvas;\n  },\n};\n"
  },
  {
    "path": "src/utils/curves.d.ts",
    "content": "declare module 'two.js/src/utils/curves' {\n  export interface Curve {\n    CollinearityEpsilon: number;\n    RecursionLimit: number;\n    CuspLimit: number;\n    Tolerance: {\n      distance: number;\n      angle: number;\n      epsilon: number;\n    };\n    abscissas: number[][];\n    weights: number[][];\n  }\n  /**\n   * @name Two.Utils.getComponentOnCubicBezier\n   * @function\n   * @param {Number} t - Zero-to-one value describing what percentage to calculate.\n   * @param {Number} a - The firt point's component value.\n   * @param {Number} b - The first point's bezier component value.\n   * @param {Number} c - The second point's bezier component value.\n   * @param {Number} d - The second point's component value.\n   * @returns {Number} The coordinate value for a specific component along a cubic bezier curve by `t`.\n   */\n  export function getComponentOnCubicBezier(\n    t: number,\n    a: number,\n    b: number,\n    c: number,\n    d: number\n  ): number;\n  /**\n   * @name Two.Utils.subdivide\n   * @function\n   * @param {Number} x1 - x position of first anchor point.\n   * @param {Number} y1 - y position of first anchor point.\n   * @param {Number} x2 - x position of first anchor point's \"right\" bezier handle.\n   * @param {Number} y2 - y position of first anchor point's \"right\" bezier handle.\n   * @param {Number} x3 - x position of second anchor point's \"left\" bezier handle.\n   * @param {Number} y3 - y position of second anchor point's \"left\" bezier handle.\n   * @param {Number} x4 - x position of second anchor point.\n   * @param {Number} y4 - y position of second anchor point.\n   * @param {Number} [limit=Two.Utils.Curve.RecursionLimit] - The amount of vertices to create by subdividing.\n   * @returns {Anchor[]} A list of anchor points ordered in between `x1`, `y1` and `x4`, `y4`\n   * @description Given 2 points (a, b) and corresponding control point for each return an array of points that represent points plotted along the curve. The number of returned points is determined by `limit`.\n   */\n  export function subdivide(\n    x1: number,\n    y1: number,\n    x2: number,\n    y2: number,\n    x3: number,\n    y3: number,\n    x4: number,\n    y4: number,\n    limit?: number\n  ): Anchor[];\n  /**\n   * @name Two.Utils.getCurveLength\n   * @function\n   * @param {Number} x1 - x position of first anchor point.\n   * @param {Number} y1 - y position of first anchor point.\n   * @param {Number} x2 - x position of first anchor point's \"right\" bezier handle.\n   * @param {Number} y2 - y position of first anchor point's \"right\" bezier handle.\n   * @param {Number} x3 - x position of second anchor point's \"left\" bezier handle.\n   * @param {Number} y3 - y position of second anchor point's \"left\" bezier handle.\n   * @param {Number} x4 - x position of second anchor point.\n   * @param {Number} y4 - y position of second anchor point.\n   * @param {Number} [limit=Two.Utils.Curve.RecursionLimit] - The amount of vertices to create by subdividing.\n   * @returns {Number} The length of a curve.\n   * @description Given 2 points (a, b) and corresponding control point for each, return a float that represents the length of the curve using Gauss-Legendre algorithm. Limit iterations of calculation by `limit`.\n   */\n  export function getCurveLength(\n    x1: number,\n    y1: number,\n    x2: number,\n    y2: number,\n    x3: number,\n    y3: number,\n    x4: number,\n    y4: number,\n    limit?: number\n  ): number;\n  /**\n   * @name Two.Utils.getCurveBoundingBox\n   * @function\n   * @param {Number} x1 - x position of first anchor point.\n   * @param {Number} y1 - y position of first anchor point.\n   * @param {Number} x2 - x position of first anchor point's \"right\" bezier handle.\n   * @param {Number} y2 - y position of first anchor point's \"right\" bezier handle.\n   * @param {Number} x3 - x position of second anchor point's \"left\" bezier handle.\n   * @param {Number} y3 - y position of second anchor point's \"left\" bezier handle.\n   * @param {Number} x4 - x position of second anchor point.\n   * @param {Number} y4 - y position of second anchor point.\n   * @returns {Object} Object contains min and max `x` / `y` bounds.\n   * @see {@link https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js#L856}\n   */\n  export function getCurveBoundingBox(\n    x1: number,\n    y1: number,\n    x2: number,\n    y2: number,\n    x3: number,\n    y3: number,\n    x4: number,\n    y4: number\n  ): any;\n  /**\n   * @name Two.Utils.integrate\n   * @function\n   * @param {Function} f\n   * @param {Number} a\n   * @param {Number} b\n   * @param {Number} n\n   * @description Integration for `getCurveLength` calculations.\n   * @see [Paper.js](@link https://github.com/paperjs/paper.js/blob/master/src/util/Numerical.js#L101)\n   */\n  export function integrate(\n    f: Function,\n    a: number,\n    b: number,\n    n: number\n  ): number;\n  /**\n   * @name Two.Utils.getCurveFromPoints\n   * @function\n   * @param {Anchor[]} points\n   * @param {Boolean} closed\n   * @description Sets the bezier handles on {@link Anchor}s in the `points` list with estimated values to create a catmull-rom like curve. Used by {@link Two.Path#plot}.\n   */\n  export function getCurveFromPoints(points: Anchor[], closed: boolean): void;\n  /**\n   * @name Two.Utils.getControlPoints\n   * @function\n   * @param {Anchor} a\n   * @param {Anchor} b\n   * @param {Anchor} c\n   * @returns {Anchor} Returns the passed middle point `b`.\n   * @description Given three coordinates set the control points for the middle, b, vertex based on its position with the adjacent points.\n   */\n  export function getControlPoints(a: Anchor, b: Anchor, c: Anchor): Anchor;\n  /**\n   * @name Two.Utils.getReflection\n   * @function\n   * @param {Vector} a\n   * @param {Vector} b\n   * @param {Boolean} [relative=false]\n   * @returns {Vector} New {@link Vector} that represents the reflection point.\n   * @description Get the reflection of a point `b` about point `a`. Where `a` is in absolute space and `b` is relative to `a`.\n   * @see {@link http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes}\n   */\n  export function getReflection(\n    a: Vector,\n    b: Vector,\n    relative?: boolean\n  ): Vector;\n  /**\n   * @name Two.Utils.getAnchorsFromArcData\n   * @function\n   * @param {Vector} center\n   * @param {Number} xAxisRotation\n   * @param {Number} rx - x radius\n   * @param {Number} ry - y radius\n   * @param {Number} ts\n   * @param {Number} td\n   * @param {Boolean} [ccw=false] - Set path traversal to counter-clockwise\n   */\n  export function getAnchorsFromArcData(\n    center: Vector,\n    xAxisRotation: number,\n    rx: number,\n    ry: number,\n    ts: number,\n    td: number,\n    ccw?: boolean\n  ): void;\n  import { Anchor } from 'two.js/src/anchor';\n  import { Vector } from 'two.js/src/vector';\n}\n"
  },
  {
    "path": "src/utils/curves.js",
    "content": "import { mod, HALF_PI } from './math.js';\nimport { Commands } from './path-commands.js';\n\nimport { Anchor } from '../anchor.js';\nimport { Vector } from '../vector.js';\nimport { Constants } from '../constants.js';\n\n/**\n * @name Two.Utils.Curve\n * @property {Object} - Additional utility constant variables related to curve math and calculations.\n */\nconst Curve = {\n  CollinearityEpsilon: Math.pow(10, -30),\n\n  RecursionLimit: 16,\n\n  CuspLimit: 0,\n\n  Tolerance: {\n    distance: 0.25,\n    angle: 0,\n    epsilon: Number.EPSILON,\n  },\n\n  // Lookup tables for abscissas and weights with values for n = 2 .. 16.\n  // As values are symmetric, only store half of them and adapt algorithm\n  // to factor in symmetry.\n  abscissas: [\n    [0.5773502691896],\n    [0, 0.7745966692415],\n    [0.3399810435849, 0.8611363115941],\n    [0, 0.5384693101057, 0.9061798459387],\n    [0.2386191860832, 0.6612093864663, 0.9324695142032],\n    [0, 0.4058451513774, 0.7415311855994, 0.9491079123428],\n    [0.1834346424956, 0.5255324099163, 0.7966664774136, 0.9602898564975],\n    [0, 0.3242534234038, 0.6133714327006, 0.8360311073266, 0.9681602395076],\n    [\n      0.1488743389816, 0.4333953941292, 0.679409568299, 0.865063366689,\n      0.9739065285172,\n    ],\n    [\n      0, 0.2695431559523, 0.5190961292068, 0.730152005574, 0.8870625997681,\n      0.9782286581461,\n    ],\n    [\n      0.1252334085115, 0.3678314989982, 0.5873179542866, 0.7699026741943,\n      0.9041172563705, 0.9815606342467,\n    ],\n    [\n      0, 0.2304583159551, 0.4484927510364, 0.6423493394403, 0.8015780907333,\n      0.917598399223, 0.9841830547186,\n    ],\n    [\n      0.1080549487073, 0.3191123689279, 0.5152486363582, 0.6872929048117,\n      0.8272013150698, 0.9284348836636, 0.9862838086968,\n    ],\n    [\n      0, 0.2011940939974, 0.3941513470776, 0.5709721726085, 0.7244177313602,\n      0.8482065834104, 0.9372733924007, 0.9879925180205,\n    ],\n    [\n      0.0950125098376, 0.2816035507793, 0.4580167776572, 0.6178762444026,\n      0.755404408355, 0.8656312023878, 0.9445750230732, 0.9894009349916,\n    ],\n  ],\n\n  weights: [\n    [1],\n    [0.8888888888889, 0.5555555555556],\n    [0.6521451548625, 0.3478548451375],\n    [0.5688888888889, 0.4786286704994, 0.2369268850562],\n    [0.4679139345727, 0.3607615730481, 0.1713244923792],\n    [0.4179591836735, 0.3818300505051, 0.2797053914893, 0.1294849661689],\n    [0.3626837833784, 0.3137066458779, 0.2223810344534, 0.1012285362904],\n    [\n      0.3302393550013, 0.31234707704, 0.2606106964029, 0.1806481606949,\n      0.0812743883616,\n    ],\n    [\n      0.2955242247148, 0.26926671931, 0.219086362516, 0.1494513491506,\n      0.0666713443087,\n    ],\n    [\n      0.2729250867779, 0.2628045445102, 0.233193764592, 0.1862902109277,\n      0.1255803694649, 0.0556685671162,\n    ],\n    [\n      0.2491470458134, 0.2334925365384, 0.2031674267231, 0.1600783285433,\n      0.1069393259953, 0.0471753363865,\n    ],\n    [\n      0.2325515532309, 0.2262831802629, 0.2078160475369, 0.1781459807619,\n      0.1388735102198, 0.0921214998377, 0.0404840047653,\n    ],\n    [\n      0.2152638534632, 0.2051984637213, 0.1855383974779, 0.1572031671582,\n      0.1215185706879, 0.0801580871598, 0.0351194603318,\n    ],\n    [\n      0.2025782419256, 0.1984314853271, 0.1861610000156, 0.166269205817,\n      0.1395706779262, 0.1071592204672, 0.0703660474881, 0.0307532419961,\n    ],\n    [\n      0.1894506104551, 0.1826034150449, 0.169156519395, 0.1495959888166,\n      0.1246289712555, 0.0951585116825, 0.0622535239386, 0.0271524594118,\n    ],\n  ],\n};\n\n/**\n * @name Two.Utils.getComponentOnCubicBezier\n * @function\n * @param {Number} t - Zero-to-one value describing what percentage to calculate.\n * @param {Number} a - The firt point's component value.\n * @param {Number} b - The first point's bezier component value.\n * @param {Number} c - The second point's bezier component value.\n * @param {Number} d - The second point's component value.\n * @returns {Number} The coordinate value for a specific component along a cubic bezier curve by `t`.\n */\nfunction getComponentOnCubicBezier(t, a, b, c, d) {\n  const k = 1 - t;\n  return k * k * k * a + 3 * k * k * t * b + 3 * k * t * t * c + t * t * t * d;\n}\n\n/**\n * @name Two.Utils.subdivide\n * @function\n * @param {Number} x1 - x position of first anchor point.\n * @param {Number} y1 - y position of first anchor point.\n * @param {Number} x2 - x position of first anchor point's \"right\" bezier handle.\n * @param {Number} y2 - y position of first anchor point's \"right\" bezier handle.\n * @param {Number} x3 - x position of second anchor point's \"left\" bezier handle.\n * @param {Number} y3 - y position of second anchor point's \"left\" bezier handle.\n * @param {Number} x4 - x position of second anchor point.\n * @param {Number} y4 - y position of second anchor point.\n * @param {Number} [limit=Two.Utils.Curve.RecursionLimit] - The amount of vertices to create by subdividing.\n * @returns {Anchor[]} A list of anchor points ordered in between `x1`, `y1` and `x4`, `y4`\n * @description Given 2 points (a, b) and corresponding control point for each return an array of points that represent points plotted along the curve. The number of returned points is determined by `limit`.\n */\nfunction subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n  limit = limit || Curve.RecursionLimit;\n  const amount = limit + 1;\n\n  // TODO: Abstract 0.001 to a limiting variable\n  // Don't recurse if the end points are identical\n  if (Math.abs(x1 - x4) < 0.001 && Math.abs(y1 - y4) < 0.001) {\n    return [new Anchor(x4, y4)];\n  }\n\n  const result = [];\n\n  for (let i = 0; i < amount; i++) {\n    const t = i / amount;\n    const x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n    const y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n    result.push(new Anchor(x, y));\n  }\n\n  return result;\n}\n\n/**\n * @name Two.Utils.getCurveLength\n * @function\n * @param {Number} x1 - x position of first anchor point.\n * @param {Number} y1 - y position of first anchor point.\n * @param {Number} x2 - x position of first anchor point's \"right\" bezier handle.\n * @param {Number} y2 - y position of first anchor point's \"right\" bezier handle.\n * @param {Number} x3 - x position of second anchor point's \"left\" bezier handle.\n * @param {Number} y3 - y position of second anchor point's \"left\" bezier handle.\n * @param {Number} x4 - x position of second anchor point.\n * @param {Number} y4 - y position of second anchor point.\n * @param {Number} [limit=Two.Utils.Curve.RecursionLimit] - The amount of vertices to create by subdividing.\n * @returns {Number} The length of a curve.\n * @description Given 2 points (a, b) and corresponding control point for each, return a float that represents the length of the curve using Gauss-Legendre algorithm. Limit iterations of calculation by `limit`.\n */\nfunction getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n  // TODO: Better / fuzzier equality check\n  // Linear calculation\n  if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) {\n    const dx = x4 - x1;\n    const dy = y4 - y1;\n    return Math.sqrt(dx * dx + dy * dy);\n  }\n\n  // Calculate the coefficients of a Bezier derivative.\n  const ax = 9 * (x2 - x3) + 3 * (x4 - x1),\n    bx = 6 * (x1 + x3) - 12 * x2,\n    cx = 3 * (x2 - x1),\n    ay = 9 * (y2 - y3) + 3 * (y4 - y1),\n    by = 6 * (y1 + y3) - 12 * y2,\n    cy = 3 * (y2 - y1);\n\n  function integrand(t) {\n    // Calculate quadratic equations of derivatives for x and y\n    const dx = (ax * t + bx) * t + cx,\n      dy = (ay * t + by) * t + cy;\n    return Math.sqrt(dx * dx + dy * dy);\n  }\n\n  return integrate(integrand, 0, 1, limit || Curve.RecursionLimit);\n}\n\n/**\n * @name Two.Utils.getCurveBoundingBox\n * @function\n * @param {Number} x1 - x position of first anchor point.\n * @param {Number} y1 - y position of first anchor point.\n * @param {Number} x2 - x position of first anchor point's \"right\" bezier handle.\n * @param {Number} y2 - y position of first anchor point's \"right\" bezier handle.\n * @param {Number} x3 - x position of second anchor point's \"left\" bezier handle.\n * @param {Number} y3 - y position of second anchor point's \"left\" bezier handle.\n * @param {Number} x4 - x position of second anchor point.\n * @param {Number} y4 - y position of second anchor point.\n * @returns {Object} Object contains min and max `x` / `y` bounds.\n * @see {@link https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js#L856}\n */\nfunction getCurveBoundingBox(x1, y1, x2, y2, x3, y3, x4, y4) {\n  const tvalues = [];\n  const bounds = [[], []];\n  let a, b, c, t, t1, t2, b2ac, sqrtb2ac;\n\n  for (let i = 0; i < 2; ++i) {\n    if (i === 0) {\n      b = 6 * x1 - 12 * x2 + 6 * x3;\n      a = -3 * x1 + 9 * x2 - 9 * x3 + 3 * x4;\n      c = 3 * x2 - 3 * x1;\n    } else {\n      b = 6 * y1 - 12 * y2 + 6 * y3;\n      a = -3 * y1 + 9 * y2 - 9 * y3 + 3 * y4;\n      c = 3 * y2 - 3 * y1;\n    }\n    if (Math.abs(a) < 0.001) {\n      if (Math.abs(b) < 0.001) {\n        continue;\n      }\n      t = -c / b;\n      if (0 < t && t < 1) {\n        tvalues.push(t);\n      }\n      continue;\n    }\n    b2ac = b * b - 4 * c * a;\n    sqrtb2ac = Math.sqrt(b2ac);\n    if (b2ac < 0) {\n      continue;\n    }\n    t1 = (-b + sqrtb2ac) / (2 * a);\n    if (0 < t1 && t1 < 1) {\n      tvalues.push(t1);\n    }\n    t2 = (-b - sqrtb2ac) / (2 * a);\n    if (0 < t2 && t2 < 1) {\n      tvalues.push(t2);\n    }\n  }\n\n  let j = tvalues.length;\n  let jlen = j;\n  let mt;\n\n  while (j--) {\n    t = tvalues[j];\n    mt = 1 - t;\n    bounds[0][j] =\n      mt * mt * mt * x1 +\n      3 * mt * mt * t * x2 +\n      3 * mt * t * t * x3 +\n      t * t * t * x4;\n    bounds[1][j] =\n      mt * mt * mt * y1 +\n      3 * mt * mt * t * y2 +\n      3 * mt * t * t * y3 +\n      t * t * t * y4;\n  }\n\n  bounds[0][jlen] = x1;\n  bounds[1][jlen] = y1;\n  bounds[0][jlen + 1] = x4;\n  bounds[1][jlen + 1] = y4;\n  bounds[0].length = bounds[1].length = jlen + 2;\n\n  return {\n    min: { x: Math.min.apply(0, bounds[0]), y: Math.min.apply(0, bounds[1]) },\n    max: { x: Math.max.apply(0, bounds[0]), y: Math.max.apply(0, bounds[1]) },\n  };\n}\n\n/**\n * @name Two.Utils.integrate\n * @function\n * @param {Function} f\n * @param {Number} a\n * @param {Number} b\n * @param {Number} n\n * @description Integration for `getCurveLength` calculations.\n * @see [Paper.js](@link https://github.com/paperjs/paper.js/blob/master/src/util/Numerical.js#L101)\n */\nfunction integrate(f, a, b, n) {\n  let x = Curve.abscissas[n - 2],\n    w = Curve.weights[n - 2],\n    A = 0.5 * (b - a),\n    B = A + a,\n    i = 0,\n    m = (n + 1) >> 1,\n    sum = n & 1 ? w[i++] * f(B) : 0; // Handle odd n\n  while (i < m) {\n    const Ax = A * x[i];\n    sum += w[i++] * (f(B + Ax) + f(B - Ax));\n  }\n  return A * sum;\n}\n\n/**\n * @name Two.Utils.getCurveFromPoints\n * @function\n * @param {Anchor[]} points\n * @param {Boolean} closed\n * @description Sets the bezier handles on {@link Anchor}s in the `points` list with estimated values to create a catmull-rom like curve. Used by {@link Two.Path#plot}.\n */\nfunction getCurveFromPoints(points, closed) {\n  const l = points.length,\n    last = l - 1;\n\n  for (let i = 0; i < l; i++) {\n    const point = points[i];\n\n    const prev = closed ? mod(i - 1, l) : Math.max(i - 1, 0);\n    const next = closed ? mod(i + 1, l) : Math.min(i + 1, last);\n\n    const a = points[prev];\n    const b = point;\n    const c = points[next];\n    getControlPoints(a, b, c);\n\n    b.command = i === 0 ? Commands.move : Commands.curve;\n  }\n}\n\n/**\n * @name Two.Utils.getControlPoints\n * @function\n * @param {Anchor} a\n * @param {Anchor} b\n * @param {Anchor} c\n * @returns {Anchor} Returns the passed middle point `b`.\n * @description Given three coordinates set the control points for the middle, b, vertex based on its position with the adjacent points.\n */\nfunction getControlPoints(a, b, c) {\n  const a1 = Vector.angleBetween(a, b);\n  const a2 = Vector.angleBetween(c, b);\n\n  let d1 = Vector.distanceBetween(a, b);\n  let d2 = Vector.distanceBetween(c, b);\n\n  let mid = (a1 + a2) / 2;\n\n  // TODO: Issue 73\n  if (d1 < 0.001 || d2 < 0.001) {\n    if (typeof b.relative === 'boolean' && !b.relative) {\n      b.controls.left.copy(b);\n      b.controls.right.copy(b);\n    }\n    return b;\n  }\n\n  d1 *= 0.33; // Why 0.33?\n  d2 *= 0.33;\n\n  if (a2 < a1) {\n    mid += HALF_PI;\n  } else {\n    mid -= HALF_PI;\n  }\n\n  b.controls.left.x = Math.cos(mid) * d1;\n  b.controls.left.y = Math.sin(mid) * d1;\n\n  mid -= Math.PI;\n\n  b.controls.right.x = Math.cos(mid) * d2;\n  b.controls.right.y = Math.sin(mid) * d2;\n\n  if (typeof b.relative === 'boolean' && !b.relative) {\n    b.controls.left.x += b.x;\n    b.controls.left.y += b.y;\n    b.controls.right.x += b.x;\n    b.controls.right.y += b.y;\n  }\n\n  return b;\n}\n\n/**\n * @name Two.Utils.getReflection\n * @function\n * @param {Vector} a\n * @param {Vector} b\n * @param {Boolean} [relative=false]\n * @returns {Vector} New {@link Vector} that represents the reflection point.\n * @description Get the reflection of a point `b` about point `a`. Where `a` is in absolute space and `b` is relative to `a`.\n * @see {@link http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes}\n */\nfunction getReflection(a, b, relative) {\n  return new Vector(\n    2 * a.x - (b.x + a.x) - (relative ? a.x : 0),\n    2 * a.y - (b.y + a.y) - (relative ? a.y : 0)\n  );\n}\n\n/**\n * @name Two.Utils.getAnchorsFromArcData\n * @function\n * @param {Vector} center\n * @param {Number} xAxisRotation\n * @param {Number} rx - x radius\n * @param {Number} ry - y radius\n * @param {Number} ts\n * @param {Number} td\n * @param {Boolean} [ccw=false] - Set path traversal to counter-clockwise\n */\nfunction getAnchorsFromArcData(center, xAxisRotation, rx, ry, ts, td, ccw) {\n  const resolution = Constants.Resolution;\n  const anchors = [];\n\n  for (let i = 0; i < resolution; i++) {\n    let pct = (i + 1) / resolution;\n    if (ccw) {\n      pct = 1 - pct;\n    }\n\n    const theta = pct * td + ts;\n    const x = rx * Math.cos(theta);\n    const y = ry * Math.sin(theta);\n\n    // x += center.x;\n    // y += center.y;\n\n    const anchor = new Anchor(x, y);\n    anchor.command = Commands.line;\n\n    // TODO: Calculate control points here...\n\n    anchors.push(anchor);\n  }\n}\n\nexport {\n  Curve,\n  getComponentOnCubicBezier,\n  subdivide,\n  getCurveLength,\n  getCurveBoundingBox,\n  integrate,\n  getCurveFromPoints,\n  getControlPoints,\n  getReflection,\n  getAnchorsFromArcData,\n};\n"
  },
  {
    "path": "src/utils/device-pixel-ratio.d.ts",
    "content": "declare module 'two.js/src/utils/device-pixel-ratio' {\n  /**\n   * @name Two.Utils.getRatio\n   * @function\n   * @param {CanvasRenderingContext2D} ctx\n   * @returns {Number} The ratio of a unit in Two.js to the pixel density of a session's screen.\n   * @see [High DPI Rendering](http://www.html5rocks.com/en/tutorials/canvas/hidpi/)\n   */\n  export function getRatio(ctx: CanvasRenderingContext2D): number;\n}\n"
  },
  {
    "path": "src/utils/device-pixel-ratio.js",
    "content": "import { root } from './root.js';\n\nconst devicePixelRatio = root.devicePixelRatio || 1;\n\nfunction getBackingStoreRatio(ctx) {\n  return ctx.webkitBackingStorePixelRatio ||\n    ctx.mozBackingStorePixelRatio ||\n    ctx.msBackingStorePixelRatio ||\n    ctx.oBackingStorePixelRatio ||\n    ctx.backingStorePixelRatio || 1;\n}\n\n/**\n * @name Two.Utils.getRatio\n * @function\n * @param {CanvasRenderingContext2D} ctx\n * @returns {Number} The ratio of a unit in Two.js to the pixel density of a session's screen.\n * @see [High DPI Rendering](http://www.html5rocks.com/en/tutorials/canvas/hidpi/)\n */\nfunction getRatio(ctx) {\n  return devicePixelRatio / getBackingStoreRatio(ctx);\n}\n\nexport { getRatio };\n"
  },
  {
    "path": "src/utils/dom.d.ts",
    "content": "declare module 'two.js/src/utils/dom' {\n  export interface dom {\n    temp: HTMLDivElement;\n  }\n}\n"
  },
  {
    "path": "src/utils/dom.js",
    "content": "import { root } from './root.js';\nimport { _ } from './underscore.js';\n\nexport const dom = {\n\n  hasEventListeners: typeof root.addEventListener === 'function',\n\n  bind: function(elem, event, func, bool) {\n    if (this.hasEventListeners) {\n      elem.addEventListener(event, func, !!bool);\n    } else {\n      elem.attachEvent('on' + event, func);\n    }\n    return dom;\n  },\n\n  unbind: function(elem, event, func, bool) {\n    if (dom.hasEventListeners) {\n      elem.removeEventListeners(event, func, !!bool);\n    } else {\n      elem.detachEvent('on' + event, func);\n    }\n    return dom;\n  },\n\n  getRequestAnimationFrame: function() {\n\n    const vendors = ['ms', 'moz', 'webkit', 'o'];\n\n    let lastTime = 0;\n    let request = root.requestAnimationFrame;\n\n    if (!request) {\n\n      for (let i = 0; i < vendors.length; i++) {\n        request = root[vendors[i] + 'RequestAnimationFrame'] || request;\n      }\n\n      request = request || fallbackRequest;\n\n    }\n\n    function fallbackRequest(callback, element) {\n\n      const currTime = new Date().getTime();\n      const timeToCall = Math.max(0, 16 - (currTime - lastTime));\n      const id = root.setTimeout(nextRequest, timeToCall);\n      lastTime = currTime + timeToCall;\n\n      function nextRequest() {\n        callback(currTime + timeToCall);\n      }\n\n      return id;\n\n    }\n\n    return request;\n\n  }\n\n};\n\nconst temp = (root.document ? root.document.createElement('div') : {});\ntemp.id = 'help-two-load';\n\nObject.defineProperty(dom, 'temp', {\n  enumerable: true,\n  get: function() {\n    if (_.isElement(temp) && !root.document.head.contains(temp)) {\n      temp.style.display = 'none';\n      root.document.head.appendChild(temp);\n    }\n    return temp;\n  }\n});\n"
  },
  {
    "path": "src/utils/error.d.ts",
    "content": "declare module 'two.js/src/utils/error' {\n  /**\n   * @name Two.Utils.Error\n   * @class\n   * @description Custom error throwing for Two.js specific identification.\n   */\n  export class TwoError extends Error {\n    constructor(message: string);\n  }\n}\n"
  },
  {
    "path": "src/utils/error.js",
    "content": "/**\n * @name Two.Utils.Error\n * @class\n * @description Custom error throwing for Two.js specific identification.\n */\nexport class TwoError extends Error {\n\n  name = 'Two.js';\n  message;\n\n  constructor(message) {\n    super();\n    this.message = message;\n  }\n\n}\n"
  },
  {
    "path": "src/utils/hit-test.js",
    "content": "import { Commands } from './path-commands.js';\nimport { getSubdivisions } from './shape.js';\nimport { Constants } from '../constants.js';\nimport { mod, TWO_PI } from './math.js';\n\nconst TRANSPARENT_REGEX = /^(?:none|transparent)$/i;\nconst DEFAULT_PRECISION = 8;\nconst EPSILON = Number.EPSILON;\n\nfunction createPoint(x, y) {\n  return { x, y };\n}\n\nfunction pointsEqual(a, b, epsilon = EPSILON) {\n  return Math.abs(a.x - b.x) <= epsilon && Math.abs(a.y - b.y) <= epsilon;\n}\n\nfunction svgAngle(ux, uy, vx, vy) {\n  const dot = ux * vx + uy * vy;\n  const len =\n    Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy) || 1e-12;\n  let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));\n  if (ux * vy - uy * vx < 0) {\n    ang = -ang;\n  }\n  return ang;\n}\n\nfunction sampleArcPoints(prev, anchor, precision) {\n  if (!prev) {\n    return [createPoint(anchor.x, anchor.y)];\n  }\n\n  let rx = anchor.rx;\n  let ry = anchor.ry;\n\n  if (!(rx && ry)) {\n    return [createPoint(anchor.x, anchor.y)];\n  }\n\n  const xAxisRotation = ((anchor.xAxisRotation || 0) * Math.PI) / 180;\n  const largeArcFlag = anchor.largeArcFlag ? 1 : 0;\n  const sweepFlag = anchor.sweepFlag ? 1 : 0;\n\n  rx = Math.abs(rx);\n  ry = Math.abs(ry);\n\n  const ax = prev.x;\n  const ay = prev.y;\n  const x = anchor.x;\n  const y = anchor.y;\n\n  const dx2 = (ax - x) / 2;\n  const dy2 = (ay - y) / 2;\n\n  const cosRot = Math.cos(xAxisRotation);\n  const sinRot = Math.sin(xAxisRotation);\n\n  let x1p = cosRot * dx2 + sinRot * dy2;\n  let y1p = -sinRot * dx2 + cosRot * dy2;\n\n  let rxs = rx * rx;\n  let rys = ry * ry;\n\n  const cr = (x1p * x1p) / rxs + (y1p * y1p) / rys;\n\n  if (cr > 1) {\n    const s = Math.sqrt(cr);\n    rx *= s;\n    ry *= s;\n    rxs = rx * rx;\n    rys = ry * ry;\n  }\n\n  const dq = rxs * y1p * y1p + rys * x1p * x1p;\n  const pq = dq === 0 ? 0 : (rxs * rys - dq) / dq;\n  let q = Math.sqrt(Math.max(0, pq));\n\n  if (largeArcFlag === sweepFlag) {\n    q = -q;\n  }\n\n  const cxp = (q * rx * y1p) / ry;\n  const cyp = (-q * ry * x1p) / rx;\n\n  const cx = cosRot * cxp - sinRot * cyp + (ax + x) / 2;\n  const cy = sinRot * cxp + cosRot * cyp + (ay + y) / 2;\n\n  const startAngle = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n  const delta =\n    svgAngle(\n      (x1p - cxp) / rx,\n      (y1p - cyp) / ry,\n      (-x1p - cxp) / rx,\n      (-y1p - cyp) / ry\n    ) % TWO_PI;\n\n  const endAngle = startAngle + delta;\n  const clockwise = sweepFlag === 0;\n\n  const angleDelta = (() => {\n    const raw = endAngle - startAngle;\n    const samePoints = Math.abs(raw) < Number.EPSILON;\n\n    let deltaAngle = mod(raw, TWO_PI);\n\n    if (deltaAngle < Number.EPSILON) {\n      deltaAngle = samePoints ? 0 : TWO_PI;\n    }\n\n    if (clockwise && !samePoints) {\n      deltaAngle = deltaAngle === TWO_PI ? -TWO_PI : deltaAngle - TWO_PI;\n    }\n\n    return deltaAngle;\n  })();\n\n  const steps = Math.max(Constants.Resolution, Math.max(precision * 2, 1));\n\n  const points = [];\n\n  for (let i = 1; i <= steps; i++) {\n    const t = i / steps;\n    const angle = startAngle + t * angleDelta;\n\n    let px = cx + rx * Math.cos(angle);\n    let py = cy + ry * Math.sin(angle);\n\n    if (xAxisRotation !== 0) {\n      const tx = px - cx;\n      const ty = py - cy;\n      const cosR = Math.cos(xAxisRotation);\n      const sinR = Math.sin(xAxisRotation);\n      px = tx * cosR - ty * sinR + cx;\n      py = tx * sinR + ty * cosR + cy;\n    }\n\n    points.push(createPoint(px, py));\n  }\n\n  return points;\n}\n\nfunction buildPathHitParts(path, precision = DEFAULT_PRECISION) {\n  const polygons = [];\n  const segments = [];\n\n  const vertices =\n    path._renderer &&\n    path._renderer.vertices &&\n    path._renderer.vertices.length > 0\n      ? path._renderer.vertices\n      : path.vertices;\n\n  if (!vertices || vertices.length === 0) {\n    return { polygons, segments };\n  }\n\n  const limit = Math.max(1, Math.floor(precision));\n\n  let currentPolygon = null;\n  let firstPoint = null;\n  let lastPoint = null;\n  let prevVertex = null;\n\n  const closePolygon = (forceClose = false) => {\n    if (!currentPolygon) {\n      return;\n    }\n\n    if (\n      forceClose &&\n      firstPoint &&\n      lastPoint &&\n      !pointsEqual(firstPoint, lastPoint)\n    ) {\n      const closingPoint = createPoint(firstPoint.x, firstPoint.y);\n      segments.push({ a: lastPoint, b: closingPoint });\n      currentPolygon.push(closingPoint);\n      lastPoint = closingPoint;\n    }\n\n    if (\n      currentPolygon.length >= 3 &&\n      firstPoint &&\n      lastPoint &&\n      pointsEqual(firstPoint, lastPoint)\n    ) {\n      polygons.push(currentPolygon);\n    }\n\n    currentPolygon = null;\n    firstPoint = null;\n    lastPoint = null;\n  };\n\n  const appendPoint = (pt) => {\n    if (!lastPoint) {\n      lastPoint = pt;\n      if (currentPolygon) {\n        currentPolygon.push(pt);\n      }\n      return;\n    }\n\n    if (pointsEqual(lastPoint, pt)) {\n      return;\n    }\n\n    segments.push({ a: lastPoint, b: pt });\n    if (currentPolygon) {\n      currentPolygon.push(pt);\n    }\n    lastPoint = pt;\n  };\n\n  for (let i = 0; i < vertices.length; i++) {\n    const vertex = vertices[i];\n    const command = vertex.command || (i === 0 ? Commands.move : Commands.line);\n\n    if (command === Commands.move) {\n      closePolygon(false);\n      const pt = createPoint(vertex.x, vertex.y);\n      currentPolygon = [pt];\n      firstPoint = pt;\n      lastPoint = pt;\n      prevVertex = vertex;\n      continue;\n    }\n\n    if (!prevVertex) {\n      prevVertex = vertices[Math.max(i - 1, 0)];\n    }\n\n    if (command === Commands.line) {\n      appendPoint(createPoint(vertex.x, vertex.y));\n    } else if (command === Commands.curve) {\n      const subdivisions = getSubdivisions(vertex, prevVertex, limit);\n      for (let j = 1; j < subdivisions.length; j++) {\n        const sv = subdivisions[j];\n        appendPoint(createPoint(sv.x, sv.y));\n      }\n      appendPoint(createPoint(vertex.x, vertex.y));\n    } else if (command === Commands.arc) {\n      const arcPoints = sampleArcPoints(prevVertex, vertex, limit);\n      for (let j = 0; j < arcPoints.length; j++) {\n        appendPoint(arcPoints[j]);\n      }\n    } else if (command === Commands.close) {\n      closePolygon(true);\n      prevVertex = vertex;\n      continue;\n    } else {\n      appendPoint(createPoint(vertex.x, vertex.y));\n    }\n\n    prevVertex = vertex;\n  }\n\n  if (currentPolygon) {\n    const shouldForceClose =\n      !!path._closed ||\n      !!path.closed ||\n      (firstPoint && lastPoint && !pointsEqual(firstPoint, lastPoint));\n    closePolygon(shouldForceClose);\n  }\n\n  return { polygons, segments };\n}\n\nfunction pointInPolygons(polygons, x, y) {\n  let inside = false;\n\n  for (let i = 0; i < polygons.length; i++) {\n    const polygon = polygons[i];\n    if (!polygon || polygon.length < 3) {\n      continue;\n    }\n\n    let lastIndex = polygon.length - 1;\n\n    for (let j = 0; j < polygon.length; j++) {\n      const v0 = polygon[lastIndex];\n      const v1 = polygon[j];\n\n      const intersects =\n        v1.y > y !== v0.y > y &&\n        x < ((v0.x - v1.x) * (y - v1.y)) / (v0.y - v1.y || 1e-12) + v1.x;\n\n      if (intersects) {\n        inside = !inside;\n      }\n\n      lastIndex = j;\n    }\n  }\n\n  return inside;\n}\n\nfunction distanceToSegmentSquared(x, y, a, b) {\n  const dx = b.x - a.x;\n  const dy = b.y - a.y;\n\n  if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {\n    const ddx = x - a.x;\n    const ddy = y - a.y;\n    return ddx * ddx + ddy * ddy;\n  }\n\n  const t = ((x - a.x) * dx + (y - a.y) * dy) / (dx * dx + dy * dy);\n  const clamped = Math.max(0, Math.min(1, t));\n\n  const cx = a.x + clamped * dx;\n  const cy = a.y + clamped * dy;\n\n  const ddx = x - cx;\n  const ddy = y - cy;\n  return ddx * ddx + ddy * ddy;\n}\n\nfunction distanceToSegments(segments, x, y) {\n  if (!segments || segments.length === 0) {\n    return Infinity;\n  }\n\n  let minDistance = Infinity;\n\n  for (let i = 0; i < segments.length; i++) {\n    const segment = segments[i];\n    const distance = distanceToSegmentSquared(x, y, segment.a, segment.b);\n    if (distance < minDistance) {\n      minDistance = distance;\n    }\n  }\n\n  return Math.sqrt(minDistance);\n}\n\nfunction hasVisibleFill(shape, override) {\n  if (typeof override === 'boolean') {\n    return override;\n  }\n\n  const fill = shape.fill;\n\n  if (!fill && fill !== 0) {\n    return false;\n  }\n\n  if (typeof fill === 'string') {\n    return !TRANSPARENT_REGEX.test(fill);\n  }\n\n  return true;\n}\n\nfunction hasVisibleStroke(shape, override) {\n  const linewidth =\n    typeof shape.linewidth === 'number'\n      ? shape.linewidth\n      : shape._linewidth || 0;\n\n  if (typeof override === 'boolean') {\n    return override && linewidth > 0;\n  }\n\n  if (!(linewidth > 0)) {\n    return false;\n  }\n\n  const stroke = shape.stroke;\n\n  if (!stroke && stroke !== 0) {\n    return false;\n  }\n\n  if (typeof stroke === 'string') {\n    return !TRANSPARENT_REGEX.test(stroke);\n  }\n\n  return true;\n}\n\nfunction boundsContains(rect, x, y, tolerance = 0) {\n  if (!rect) {\n    return false;\n  }\n\n  const left = rect.left - tolerance;\n  const right = rect.right + tolerance;\n  const top = rect.top - tolerance;\n  const bottom = rect.bottom + tolerance;\n\n  return x >= left && x <= right && y >= top && y <= bottom;\n}\n\nexport {\n  buildPathHitParts,\n  pointInPolygons,\n  distanceToSegments,\n  hasVisibleFill,\n  hasVisibleStroke,\n  boundsContains,\n};\n"
  },
  {
    "path": "src/utils/interpret-svg.d.ts",
    "content": "declare module 'two.js/src/utils/interpret-svg' {\n  /**\n   * @name Two.Utils.read\n   * @property {Object} read - A map of functions to read any number of SVG node types and create Two.js equivalents of them. Primarily used by the {@link Two#interpret} method.\n   */\n  export const read: {\n    svg: (node: any) => any;\n    defs: (node: any) => any;\n    use: (node: any, styles: any) => any;\n    g: (node: any, parentStyles: any) => Group;\n    polygon: (node: any, parentStyles: any) => Path;\n    polyline: (node: any, parentStyles: any) => any;\n    path: (node: any, parentStyles: any) => Path;\n    circle: (node: any, parentStyles: any) => Circle;\n    ellipse: (node: any, parentStyles: any) => Ellipse;\n    rect: (node: any, parentStyles: any) => Rectangle | RoundedRectangle;\n    'rounded-rect': (node: any, parentStyles: any) => RoundedRectangle;\n    line: (node: any, parentStyles: any) => Line;\n    lineargradient: (node: any, parentStyles: any) => LinearGradient;\n    radialgradient: (node: any, parentStyles: any) => RadialGradient;\n    text: (node: any, parentStyles: any) => Text;\n    clippath: (node: any, parentStyles: any) => any;\n    image: (node: any, parentStyles: any) => Sprite;\n  };\n  import { Group } from 'two.js/src/group';\n  import { Path } from 'two.js/src/path';\n  import { Circle } from 'two.js/src/shapes/circle';\n  import { Ellipse } from 'two.js/src/shapes/ellipse';\n  import { Rectangle } from 'two.js/src/shapes/rectangle';\n  import { RoundedRectangle } from 'two.js/src/shapes/rounded-rectangle';\n  import { Line } from 'two.js/src/shapes/line';\n  import { LinearGradient } from 'two.js/src/effects/linear-gradient';\n  import { RadialGradient } from 'two.js/src/effects/radial-gradient';\n  import { Text } from 'two.js/src/text';\n  import { Sprite } from 'two.js/src/effects/sprite';\n}\n"
  },
  {
    "path": "src/utils/interpret-svg.js",
    "content": "import { root } from './root.js';\nimport { Commands } from './path-commands.js';\nimport { decomposeMatrix } from './math.js';\nimport { getReflection } from './curves.js';\nimport { _ } from './underscore.js';\nimport { TwoError } from './error.js';\nimport { Registry } from '../registry.js';\n\nimport { Anchor } from '../anchor.js';\nimport { Vector } from '../vector.js';\nimport { Path } from '../path.js';\nimport { Sprite } from '../effects/sprite.js';\nimport { Group } from '../group.js';\n\nimport { Circle } from '../shapes/circle.js';\nimport { Ellipse } from '../shapes/ellipse.js';\nimport { Line } from '../shapes/line.js';\nimport { Rectangle } from '../shapes/rectangle.js';\nimport { RoundedRectangle } from '../shapes/rounded-rectangle.js';\n\nimport { Stop } from '../effects/stop.js';\nimport { Gradient } from '../effects/gradient.js';\nimport { LinearGradient } from '../effects/linear-gradient.js';\nimport { RadialGradient } from '../effects/radial-gradient.js';\nimport { Text } from '../text.js';\n\nimport { Constants } from '../constants.js';\n\n// https://github.com/jonobr1/two.js/issues/507#issuecomment-777159213\nconst regex = {\n  path: /[+-]?(?:\\d*\\.\\d+|\\d+)(?:[eE][+-]\\d+)?/g,\n  cssBackgroundImage: /url\\(['\"]?#([\\w\\d-_]*)['\"]?\\)/i,\n  unitSuffix: /[a-zA-Z%]*/i,\n};\n\nconst alignments = {\n  start: 'left',\n  middle: 'center',\n  end: 'right',\n};\n\n// Reserved attributes to remove\nconst reservedAttributesToRemove = [\n  'id',\n  'class',\n  'transform',\n  'xmlns',\n  'viewBox',\n];\n\nconst overwriteAttrs = ['x', 'y', 'width', 'height', 'href', 'xlink:href'];\n\n/**\n * @name Two.Utils.getAlignment\n * @function\n * @param {AlignmentString}\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor}\n */\nfunction getAlignment(anchor) {\n  return alignments[anchor];\n}\n\nfunction getBaseline(node) {\n  const a = node.getAttribute('dominant-baseline');\n  const b = node.getAttribute('alignment-baseline');\n  return a || b;\n}\n\nfunction getTagName(tag) {\n  return tag.replace(/svg:/gi, '').toLowerCase();\n}\n\nfunction applyTransformsToVector(transforms, vector) {\n  vector.x += transforms.translateX;\n  vector.y += transforms.translateY;\n\n  vector.x *= transforms.scaleX;\n  vector.y *= transforms.scaleY;\n\n  if (transforms.rotation !== 0) {\n    // TODO: Test further\n    const l = vector.length();\n    vector.x = l * Math.cos(transforms.rotation);\n    vector.y = l * Math.sin(transforms.rotation);\n  }\n}\n\n/**\n * @name Two.Utils.extractCSSText\n * @function\n * @param {String} text - The CSS text body to be parsed and extracted.\n * @param {Object} [styles] - The styles object to apply CSS key values to.\n * @returns {Object} styles\n * @description Parse CSS text body and apply them as key value pairs to a JavaScript object.\n */\nfunction extractCSSText(text, styles) {\n  if (!styles) {\n    styles = {};\n  }\n\n  const commands = text.split(';');\n\n  for (let i = 0; i < commands.length; i++) {\n    const command = commands[i].split(':');\n    const name = command[0];\n    const value = command[1];\n    if (typeof name === 'undefined' || typeof value === 'undefined') {\n      continue;\n    }\n\n    //Delete whitespace and line breaks from name and value\n    const trimmedName = name.replace(/\\s/g, '');\n    const trimmedValue = value.replace(/\\s/g, '');\n\n    styles[trimmedName] = trimmedValue;\n  }\n  return styles;\n}\n\n/**\n * @name Two.Utils.getSvgStyles\n * @function\n * @param {SVGElement} node - The SVG node to parse.\n * @returns {Object} styles\n * @description Get the CSS comands from the `style` attribute of an SVG node and apply them as key value pairs to a JavaScript object.\n */\nfunction getSvgStyles(node) {\n  const styles = {};\n  const attributes = getSvgAttributes(node);\n  const length = Math.max(attributes.length, node.style.length);\n\n  for (let i = 0; i < length; i++) {\n    const command = node.style[i];\n    const attribute = attributes[i];\n\n    if (command) {\n      styles[command] = node.style[command];\n    }\n    if (attribute) {\n      styles[attribute] = node.getAttribute(attribute);\n    }\n  }\n\n  return styles;\n}\n\nfunction getSvgAttributes(node) {\n  const attributes = node.getAttributeNames();\n\n  for (let i = 0; i < reservedAttributesToRemove.length; i++) {\n    const keyword = reservedAttributesToRemove[i];\n    const index = Array.prototype.indexOf.call(attributes, keyword);\n    if (index >= 0) {\n      attributes.splice(index, 1);\n    }\n  }\n\n  return attributes;\n}\n\n/**\n * @name Two.Utils.applySvgViewBox\n * @function\n * @param {Two.Shape} node - The Two.js object to apply viewbox matrix to\n * @param {String} value - The viewBox value from the SVG attribute\n * @returns {Two.Shape} node\n * @description Applies the transform of the SVG Viewbox on a given node.\n */\nfunction applySvgViewBox(node, value) {\n  const elements = value.split(/[\\s,]/);\n\n  const x = -parseFloat(elements[0]);\n  const y = -parseFloat(elements[1]);\n  const width = parseFloat(elements[2]);\n  const height = parseFloat(elements[3]);\n\n  if (x && y) {\n    for (let i = 0; i < node.children.length; i++) {\n      const child = node.children[i];\n      if ('translation' in child) {\n        child.translation.add(x, y);\n      } else if ('x' in child) {\n        child.x = x;\n      } else if ('y' in child) {\n        child.y = y;\n      }\n    }\n  }\n\n  const xExists = typeof node.x === 'number';\n  const yExists = typeof node.y === 'number';\n  const widthExists = typeof node.width === 'number';\n  const heightExists = typeof node.height === 'number';\n\n  if (xExists) {\n    node.translation.x += node.x;\n  }\n  if (yExists) {\n    node.translation.y += node.y;\n  }\n  if (widthExists || heightExists) {\n    node.scale = new Vector(1, 1);\n  }\n  if (widthExists) {\n    node.scale.x = node.width / width;\n  }\n  if (heightExists) {\n    node.scale.y = node.height / height;\n  }\n\n  node.mask = new Rectangle(0, 0, width, height);\n  node.mask.origin.set(-width / 2, -height / 2);\n\n  return node;\n}\n\n/**\n * @name Two.Utils.applySvgAttributes\n * @function\n * @param {SVGElement} node - An SVG Node to extrapolate attributes from.\n * @param {Two.Shape} elem - The Two.js object to apply extrapolated attributes to.\n * @returns {Two.Shape} The Two.js object passed now with applied attributes.\n * @description This function iterates through an SVG Node's properties and stores ones of interest. It tries to resolve styles applied via CSS as well.\n * @TODO Reverse calculate {@link Two.Gradient}s for fill / stroke of any given path.\n */\nfunction applySvgAttributes(node, elem, parentStyles) {\n  const styles = {},\n    attributes = {},\n    extracted = {};\n  let i, m, key, value, prop, attr;\n  let transforms, x, y;\n  let id, scene, ref, tagName;\n  let ca, cb, cc, error;\n\n  if (node === null) {\n    return styles;\n  }\n\n  // Not available in non browser environments\n  if (root.getComputedStyle) {\n    // Convert CSSStyleDeclaration to a normal object\n    const computedStyles = root.getComputedStyle(node);\n    i = computedStyles.length;\n\n    while (i--) {\n      key = computedStyles[i];\n      value = computedStyles[key];\n      // Gecko returns undefined for unset properties\n      // Webkit returns the default value\n      if (typeof value !== 'undefined') {\n        styles[key] = value;\n      }\n    }\n  }\n\n  // Convert NodeMap to a normal object\n  for (i = 0; i < node.attributes.length; i++) {\n    attr = node.attributes[i];\n    if (/style/i.test(attr.nodeName)) {\n      extractCSSText(attr.value, extracted);\n    } else {\n      attributes[attr.nodeName] = attr.value;\n    }\n  }\n\n  // Getting the correct opacity is a bit tricky, since SVG path elements don't\n  // support opacity as an attribute, but you can apply it via CSS.\n  // So we take the opacity and set (stroke/fill)-opacity to the same value.\n  if (typeof styles.opacity !== 'undefined') {\n    styles['stroke-opacity'] = styles.opacity;\n    styles['fill-opacity'] = styles.opacity;\n    delete styles.opacity;\n  }\n\n  // Merge attributes and applied styles (attributes take precedence)\n  if (parentStyles) {\n    _.defaults(styles, parentStyles);\n  }\n  _.extend(styles, extracted, attributes);\n\n  // Similarly visibility is influenced by the value of both display and visibility.\n  // Calculate a unified value here which defaults to `true`.\n  styles.visible =\n    !(typeof styles.display === 'undefined' && /none/i.test(styles.display)) ||\n    (typeof styles.visibility === 'undefined' &&\n      /hidden/i.test(styles.visibility));\n\n  // Now iterate the whole thing\n  for (key in styles) {\n    value = styles[key];\n\n    switch (key) {\n      case 'gradientTransform':\n        // TODO: Check this out https://github.com/paperjs/paper.js/blob/develop/src/svg/SvgImport.js#L315\n        if (/none/i.test(value)) break;\n        m =\n          node.gradientTransform &&\n          node.gradientTransform.baseVal &&\n          node.gradientTransform.baseVal.length > 0\n            ? node.gradientTransform.baseVal[0].matrix\n            : node.getCTM\n            ? node.getCTM()\n            : null;\n\n        if (m === null) break;\n\n        transforms = decomposeMatrix(m);\n\n        switch (elem._renderer.type) {\n          case 'linear-gradient':\n            applyTransformsToVector(transforms, elem.left);\n            applyTransformsToVector(transforms, elem.right);\n            break;\n          case 'radial-gradient':\n            elem.center.x += transforms.translateX;\n            elem.center.y += transforms.translateY;\n\n            elem.focal.x += transforms.translateX;\n            elem.focal.y += transforms.translateY;\n\n            elem.radius *= Math.max(transforms.scaleX, transforms.scaleY);\n            break;\n        }\n\n        break;\n      case 'transform':\n        // TODO: Check this out https://github.com/paperjs/paper.js/blob/develop/src/svg/SvgImport.js#L315\n        if (/none/i.test(value)) break;\n        m =\n          node.transform &&\n          node.transform.baseVal &&\n          node.transform.baseVal.length > 0\n            ? node.transform.baseVal[0].matrix\n            : node.getCTM\n            ? node.getCTM()\n            : null;\n\n        // Might happen when transform string is empty or not valid.\n        if (m === null) break;\n\n        if (Constants.AutoCalculateImportedMatrices) {\n          // Decompose and infer Two.js related properties.\n          transforms = decomposeMatrix(m);\n\n          elem.translation.set(transforms.translateX, transforms.translateY);\n          elem.rotation = Math.PI * (transforms.rotation / 180);\n          elem.scale = new Vector(transforms.scaleX, transforms.scaleY);\n\n          x = parseFloat((styles.x + '').replace('px'));\n          y = parseFloat((styles.y + '').replace('px'));\n\n          // Override based on attributes.\n          if (x) {\n            elem.translation.x = x;\n          }\n\n          if (y) {\n            elem.translation.y = y;\n          }\n        } else {\n          // Edit the underlying matrix and don't force an auto calc.\n          m = node.getCTM();\n          elem._matrix.manual = true;\n          elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f);\n        }\n\n        break;\n      case 'visible':\n        if (elem instanceof Group) {\n          elem._visible = value;\n          break;\n        }\n        elem.visible = value;\n        break;\n      case 'stroke-linecap':\n        if (elem instanceof Group) {\n          elem._cap = value;\n          break;\n        }\n        elem.cap = value;\n        break;\n      case 'stroke-linejoin':\n        if (elem instanceof Group) {\n          elem._join = value;\n          break;\n        }\n        elem.join = value;\n        break;\n      case 'stroke-miterlimit':\n        if (elem instanceof Group) {\n          elem._miter = value;\n          break;\n        }\n        elem.miter = value;\n        break;\n      case 'stroke-width':\n        if (elem instanceof Group) {\n          elem._linewidth = parseFloat(value);\n          break;\n        }\n        elem.linewidth = parseFloat(value);\n        break;\n      case 'opacity':\n      case 'stroke-opacity':\n      case 'fill-opacity':\n        // Only apply styles to rendered shapes\n        // in the scene.\n        if (elem instanceof Group) {\n          elem._opacity = parseFloat(value);\n          break;\n        }\n        elem.opacity = parseFloat(value);\n        break;\n      case 'clip-path':\n        if (regex.cssBackgroundImage.test(value)) {\n          id = value.replace(regex.cssBackgroundImage, '$1');\n          if (read.defs.current && read.defs.current.contains(id)) {\n            ref = read.defs.current.get(id);\n            if (ref && ref.childNodes.length > 0) {\n              ref = ref.childNodes[0];\n              tagName = getTagName(ref.nodeName);\n              elem.mask = read[tagName].call(this, ref, {});\n              switch (elem._renderer.type) {\n                case 'text':\n                case 'path':\n                  // The matrix here needs to change to insure that the object\n                  // clipping is in the same coordinate space as the `elem`.\n                  elem.position.add(elem.mask.position);\n                  elem.mask.position.clear();\n                  break;\n              }\n            }\n          }\n        }\n        break;\n      case 'fill':\n      case 'stroke':\n        prop = (elem instanceof Group ? '_' : '') + key;\n        if (regex.cssBackgroundImage.test(value)) {\n          id = value.replace(regex.cssBackgroundImage, '$1');\n          // Overwritten id for non-conflicts on same page SVG documents\n          // TODO: Make this non-descructive\n          // node.setAttribute('two-' + key, value.replace(/\\)/i, '-' + Constants.Identifier + 'applied)'));\n          if (read.defs.current && read.defs.current.contains(id)) {\n            ref = read.defs.current.get(id);\n            if (!ref.object) {\n              tagName = getTagName(ref.nodeName);\n              ref.object = read[tagName].call(this, ref, {});\n            }\n            ref = ref.object;\n          } else {\n            scene = getScene(this);\n            ref = scene.getById(id);\n          }\n          elem[prop] = ref;\n        } else {\n          elem[prop] = value;\n        }\n        break;\n      case 'id':\n        elem.id = value;\n        // Overwritten id for non-conflicts on same page SVG documents\n        // TODO: Make this non-descructive\n        // node.id = value + '-' + Constants.Identifier + 'applied';\n        break;\n      case 'class':\n      case 'className':\n        elem.classList = value.split(' ');\n        elem._flagClassName = true;\n        break;\n      case 'x':\n      case 'y':\n        ca = elem instanceof Gradient;\n        cb = elem instanceof LinearGradient;\n        cc = elem instanceof RadialGradient;\n        if (ca || cb || cc) {\n          break;\n        }\n        if (value.match('[a-z%]$') && !value.endsWith('px')) {\n          error = new TwoError(\n            'only pixel values are supported with the ' + key + ' attribute.'\n          );\n          console.warn(error.name, error.message);\n        }\n        elem.translation[key] = parseFloat(value);\n        break;\n      case 'font-family':\n        if (elem instanceof Text) {\n          elem.family = value;\n        }\n        break;\n      case 'font-size':\n        if (elem instanceof Text) {\n          if (value.match('[a-z%]$') && !value.endsWith('px')) {\n            error = new TwoError(\n              'only pixel values are supported with the ' + key + ' attribute.'\n            );\n            console.warn(error.name, error.message);\n          }\n          elem.size = parseFloat(value);\n        }\n        break;\n      case 'font-weight':\n        if (elem instanceof Text) {\n          elem.weight = value;\n        }\n        break;\n      case 'font-style':\n        if (elem instanceof Text) {\n          elem.style = value;\n        }\n        break;\n      case 'text-decoration':\n        if (elem instanceof Text) {\n          elem.decoration = value;\n        }\n        break;\n      case 'line-height':\n        if (elem instanceof Text) {\n          elem.leading = value;\n        }\n        break;\n    }\n  }\n\n  if (Object.keys(node.dataset).length) elem.dataset = node.dataset;\n\n  return styles;\n}\n\n/**\n * @name Two.Utils.updateDefsCache\n * @function\n * @param {SVGElement} node - The SVG Node with which to update the defs cache.\n * @param {Object} Object - The defs cache to be updated.\n * @description Update the cache of children of <defs /> tags.\n */\nfunction updateDefsCache(node, defsCache) {\n  for (let i = 0, l = node.childNodes.length; i < l; i++) {\n    const n = node.childNodes[i];\n    if (!n.id) continue;\n\n    const tagName = getTagName(node.nodeName);\n    if (tagName === '#text') continue;\n\n    defsCache.add(n.id, n);\n  }\n}\n\n/**\n * @name Two.Utils.getScene\n * @param {Two.Shape} node - The currently available object in the scenegraph.\n * @returns {Group} - The highest order {@link Two.Group} in the scenegraph.\n * @property {Function}\n */\nfunction getScene(node) {\n  while (node.parent) {\n    node = node.parent;\n  }\n\n  return node.scene;\n}\n\n/**\n * @name Two.Utils.read\n * @property {Object} read - A map of functions to read any number of SVG node types and create Two.js equivalents of them. Primarily used by the {@link Two#interpret} method.\n */\nexport const read = {\n  svg: function (node) {\n    const defs = (read.defs.current = new Registry());\n    const elements = node.getElementsByTagName('defs');\n\n    for (let i = 0; i < elements.length; i++) {\n      updateDefsCache(elements[i], defs);\n    }\n\n    const svg = read.g.call(this, node);\n    const viewBox = node.getAttribute('viewBox');\n    const x = node.getAttribute('x');\n    const y = node.getAttribute('y');\n    const width = node.getAttribute('width');\n    const height = node.getAttribute('height');\n\n    svg.defs = defs; // Export out the <defs /> for later use\n\n    const viewBoxExists = viewBox !== null;\n    const xExists = x !== null;\n    const yExists = y !== null;\n    const widthExists = width !== null;\n    const heightExists = height !== null;\n\n    if (xExists) {\n      svg.x = parseFloat(x.replace(regex.unitSuffix, ''));\n    }\n    if (yExists) {\n      svg.y = parseFloat(y.replace(regex.unitSuffix, ''));\n    }\n    if (widthExists) {\n      svg.width = parseFloat(width.replace(regex.unitSuffix, ''));\n    }\n    if (heightExists) {\n      svg.height = parseFloat(height.replace(regex.unitSuffix, ''));\n    }\n    if (viewBoxExists) {\n      applySvgViewBox(svg, viewBox);\n    }\n\n    delete read.defs.current;\n\n    return svg;\n  },\n\n  defs: function (node) {\n    return null;\n  },\n\n  use: function (node, styles) {\n    let error;\n\n    const href = node.getAttribute('href') || node.getAttribute('xlink:href');\n    if (!href) {\n      error = new TwoError('encountered <use /> with no href.');\n      console.warn(error.name, error.message);\n      return null;\n    }\n\n    const id = href.slice(1);\n    if (!read.defs.current.contains(id)) {\n      error = new TwoError(\n        'unable to find element for reference ' + href + '.'\n      );\n      console.warn(error.name, error.message);\n      return null;\n    }\n\n    const template = read.defs.current.get(id);\n    const fullNode = template.cloneNode(true);\n\n    for (let i = 0; i < node.attributes.length; i++) {\n      const attr = node.attributes[i];\n      const ca = overwriteAttrs.includes(attr.nodeName);\n      const cb = !fullNode.hasAttribute(attr.nodeName);\n      if (ca || cb) {\n        fullNode.setAttribute(attr.nodeName, attr.value);\n      }\n    }\n\n    const tagName = getTagName(fullNode.nodeName);\n    return read[tagName].call(this, fullNode, styles);\n  },\n\n  g: function (node, parentStyles) {\n    const group = new Group();\n\n    applySvgAttributes.call(this, node, group, parentStyles);\n\n    this.add(group);\n\n    // Switched up order to inherit more specific styles\n    const styles = getSvgStyles.call(this, node);\n\n    for (let i = 0, l = node.childNodes.length; i < l; i++) {\n      const n = node.childNodes[i];\n      const tag = n.nodeName;\n      if (!tag) return;\n\n      const tagName = getTagName(tag);\n\n      if (tagName in read) {\n        const o = read[tagName].call(group, n, styles);\n        if (!!o && !o.parent) {\n          group.add(o);\n        }\n      }\n    }\n\n    return group;\n  },\n\n  polygon: function (node, parentStyles) {\n    let points;\n\n    if (typeof node === 'string') {\n      points = node;\n    } else {\n      points = node.getAttribute('points');\n    }\n\n    const verts = [];\n    points.replace(\n      /(-?[\\d.eE-]+)[,|\\s](-?[\\d.eE-]+)/g,\n      function (match, p1, p2) {\n        verts.push(new Anchor(parseFloat(p1), parseFloat(p2)));\n      }\n    );\n\n    const poly = new Path(verts, true);\n    poly.stroke = 'none';\n    poly.fill = 'black';\n\n    applySvgAttributes.call(this, node, poly, parentStyles);\n\n    return poly;\n  },\n\n  polyline: function (node, parentStyles) {\n    const poly = read.polygon.call(this, node, parentStyles);\n    poly.closed = false;\n    return poly;\n  },\n\n  path: function (node, parentStyles) {\n    let path;\n\n    if (typeof node === 'string') {\n      path = node;\n      node = null;\n    } else {\n      path = node.getAttribute('d');\n    }\n\n    let points = [];\n    let closed = false,\n      relative = false;\n\n    if (path) {\n      // Create a Two.Path from the paths.\n\n      let coord = new Anchor();\n      let control, coords;\n      let commands = path.match(/[a-df-z][^a-df-z]*/gi);\n      const last = commands.length - 1;\n\n      // Split up polybeziers\n\n      _.each(commands.slice(0), function (command, i) {\n        const items = command.slice(1).trim().match(regex.path);\n        const type = command[0];\n        const lower = type.toLowerCase();\n        let bin, j, l, ct, times;\n        const result = [];\n\n        if (i === 0) {\n          commands = [];\n        }\n\n        switch (lower) {\n          case 'h':\n          case 'v':\n            if (items.length > 1) {\n              bin = 1;\n            }\n            break;\n          case 'm':\n          case 'l':\n          case 't':\n            if (items.length > 2) {\n              bin = 2;\n            }\n            break;\n          case 's':\n          case 'q':\n            if (items.length > 4) {\n              bin = 4;\n            }\n            break;\n          case 'c':\n            if (items.length > 6) {\n              bin = 6;\n            }\n            break;\n          case 'a':\n            if (items.length > 7) {\n              bin = 7;\n            }\n            break;\n        }\n\n        // This means we have a polybezier.\n        if (bin) {\n          for (j = 0, l = items.length, times = 0; j < l; j += bin) {\n            ct = type;\n            if (times > 0) {\n              switch (type) {\n                case 'm':\n                  ct = 'l';\n                  break;\n                case 'M':\n                  ct = 'L';\n                  break;\n              }\n            }\n\n            result.push(ct + items.slice(j, j + bin).join(' '));\n            times++;\n          }\n\n          commands = Array.prototype.concat.apply(commands, result);\n        } else {\n          commands.push(command);\n        }\n      });\n\n      // Create the vertices for our Two.Path\n\n      _.each(commands, function (command, i) {\n        let result, x, y;\n        const type = command[0];\n        const lower = type.toLowerCase();\n\n        coords = command.slice(1).trim().match(regex.path);\n        relative = type === lower;\n\n        let x1, y1, x2, y2, x3, y3, x4, y4, reflection;\n        let a, b;\n        let anchor, rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n\n        switch (lower) {\n          case 'z':\n            if (i >= last) {\n              closed = true;\n            } else {\n              x = coord.x;\n              y = coord.y;\n              result = new Anchor(\n                x,\n                y,\n                undefined,\n                undefined,\n                undefined,\n                undefined,\n                Commands.close\n              );\n              // Make coord be the last `m` command\n              for (let j = points.length - 1; j >= 0; j--) {\n                const point = points[j];\n                if (/m/i.test(point.command)) {\n                  coord = point;\n                  break;\n                }\n              }\n            }\n            break;\n\n          case 'm':\n          case 'l':\n            control = undefined;\n\n            x = parseFloat(coords[0]);\n            y = parseFloat(coords[1]);\n\n            result = new Anchor(\n              x,\n              y,\n              undefined,\n              undefined,\n              undefined,\n              undefined,\n              /m/i.test(lower) ? Commands.move : Commands.line\n            );\n\n            if (relative) {\n              result.addSelf(coord);\n            }\n\n            // result.controls.left.copy(result);\n            // result.controls.right.copy(result);\n\n            coord = result;\n            break;\n\n          case 'h':\n          case 'v':\n            a = /h/i.test(lower) ? 'x' : 'y';\n            b = /x/i.test(a) ? 'y' : 'x';\n\n            result = new Anchor(\n              undefined,\n              undefined,\n              undefined,\n              undefined,\n              undefined,\n              undefined,\n              Commands.line\n            );\n            result[a] = parseFloat(coords[0]);\n            result[b] = coord[b];\n\n            if (relative) {\n              result[a] += coord[a];\n            }\n\n            // result.controls.left.copy(result);\n            // result.controls.right.copy(result);\n\n            coord = result;\n            break;\n\n          case 'c':\n          case 's':\n            x1 = coord.x;\n            y1 = coord.y;\n\n            if (!control) {\n              control = new Vector(); //.copy(coord);\n            }\n\n            if (/c/i.test(lower)) {\n              x2 = parseFloat(coords[0]);\n              y2 = parseFloat(coords[1]);\n              x3 = parseFloat(coords[2]);\n              y3 = parseFloat(coords[3]);\n              x4 = parseFloat(coords[4]);\n              y4 = parseFloat(coords[5]);\n            } else {\n              // Calculate reflection control point for proper x2, y2\n              // inclusion.\n\n              reflection = getReflection(coord, control, relative);\n\n              x2 = reflection.x;\n              y2 = reflection.y;\n              x3 = parseFloat(coords[0]);\n              y3 = parseFloat(coords[1]);\n              x4 = parseFloat(coords[2]);\n              y4 = parseFloat(coords[3]);\n            }\n\n            if (relative) {\n              x2 += x1;\n              y2 += y1;\n              x3 += x1;\n              y3 += y1;\n              x4 += x1;\n              y4 += y1;\n            }\n\n            coord.controls.right.set(x2 - coord.x, y2 - coord.y);\n            result = new Anchor(\n              x4,\n              y4,\n              x3 - x4,\n              y3 - y4,\n              undefined,\n              undefined,\n              Commands.curve\n            );\n\n            coord = result;\n            control = result.controls.left;\n\n            break;\n\n          case 't':\n          case 'q':\n            x1 = coord.x;\n            y1 = coord.y;\n\n            if (!control) {\n              control = new Vector();\n            }\n\n            if (/q/i.test(lower)) {\n              x2 = parseFloat(coords[0]);\n              y2 = parseFloat(coords[1]);\n              x3 = parseFloat(coords[0]);\n              y3 = parseFloat(coords[1]);\n              x4 = parseFloat(coords[2]);\n              y4 = parseFloat(coords[3]);\n            } else {\n              reflection = getReflection(coord, control, relative);\n\n              x2 = reflection.x;\n              y2 = reflection.y;\n              x3 = reflection.x;\n              y3 = reflection.y;\n              x4 = parseFloat(coords[0]);\n              y4 = parseFloat(coords[1]);\n            }\n\n            if (relative) {\n              x2 += x1;\n              y2 += y1;\n              x3 += x1;\n              y3 += y1;\n              x4 += x1;\n              y4 += y1;\n            }\n\n            coord.controls.right.set(\n              (x2 - coord.x) * 0.33,\n              (y2 - coord.y) * 0.33\n            );\n            result = new Anchor(\n              x4,\n              y4,\n              x3 - x4,\n              y3 - y4,\n              undefined,\n              undefined,\n              Commands.curve\n            );\n\n            coord = result;\n            control = result.controls.left;\n\n            break;\n\n          case 'a':\n            x1 = coord.x;\n            y1 = coord.y;\n\n            rx = parseFloat(coords[0]);\n            ry = parseFloat(coords[1]);\n            xAxisRotation = parseFloat(coords[2]); // * PI / 180;\n            largeArcFlag = parseFloat(coords[3]);\n            sweepFlag = parseFloat(coords[4]);\n\n            x4 = parseFloat(coords[5]);\n            y4 = parseFloat(coords[6]);\n\n            if (relative) {\n              x4 += x1;\n              y4 += y1;\n            }\n\n            anchor = new Anchor(x4, y4);\n            anchor.command = Commands.arc;\n            anchor.rx = rx;\n            anchor.ry = ry;\n            anchor.xAxisRotation = xAxisRotation;\n            anchor.largeArcFlag = largeArcFlag;\n            anchor.sweepFlag = sweepFlag;\n\n            result = anchor;\n\n            coord = anchor;\n            control = undefined;\n\n            break;\n        }\n\n        if (result) {\n          if (Array.isArray(result)) {\n            points = points.concat(result);\n          } else {\n            points.push(result);\n          }\n        }\n      });\n    }\n\n    path = new Path(points, closed, undefined, true);\n    path.stroke = 'none';\n    path.fill = 'black';\n\n    const rect = path.getBoundingClientRect(true);\n\n    // Center objects to stay consistent\n    // with the rest of the Two.js API.\n    rect.centroid = {\n      x: rect.left + rect.width / 2,\n      y: rect.top + rect.height / 2,\n    };\n\n    _.each(path.vertices, function (v) {\n      v.subSelf(rect.centroid);\n    });\n\n    applySvgAttributes.call(this, node, path, parentStyles);\n\n    path.translation.addSelf(rect.centroid);\n\n    return path;\n  },\n\n  circle: function (node, parentStyles) {\n    const x = parseFloat(node.getAttribute('cx'));\n    const y = parseFloat(node.getAttribute('cy'));\n    const r = parseFloat(node.getAttribute('r'));\n\n    const circle = new Circle(0, 0, r);\n    circle.stroke = 'none';\n    circle.fill = 'black';\n\n    applySvgAttributes.call(this, node, circle, parentStyles);\n\n    circle.translation.x = x;\n    circle.translation.y = y;\n\n    return circle;\n  },\n\n  ellipse: function (node, parentStyles) {\n    const x = parseFloat(node.getAttribute('cx'));\n    const y = parseFloat(node.getAttribute('cy'));\n    const width = parseFloat(node.getAttribute('rx'));\n    const height = parseFloat(node.getAttribute('ry'));\n\n    const ellipse = new Ellipse(0, 0, width, height);\n    ellipse.stroke = 'none';\n    ellipse.fill = 'black';\n\n    applySvgAttributes.call(this, node, ellipse, parentStyles);\n\n    ellipse.translation.x = x;\n    ellipse.translation.y = y;\n\n    return ellipse;\n  },\n\n  rect: function (node, parentStyles) {\n    const rx = parseFloat(node.getAttribute('rx'));\n    const ry = parseFloat(node.getAttribute('ry'));\n\n    if (!_.isNaN(rx) || !_.isNaN(ry)) {\n      return read['rounded-rect'](node);\n    }\n\n    const width = parseFloat(node.getAttribute('width'));\n    const height = parseFloat(node.getAttribute('height'));\n\n    const w2 = width / 2;\n    const h2 = height / 2;\n\n    const rect = new Rectangle(0, 0, width, height);\n    rect.stroke = 'none';\n    rect.fill = 'black';\n\n    applySvgAttributes.call(this, node, rect, parentStyles);\n\n    // For rectangles, (x, y) is the center of the shape rather than the top\n    // left corner.\n    rect.translation.x += w2;\n    rect.translation.y += h2;\n\n    return rect;\n  },\n\n  'rounded-rect': function (node, parentStyles) {\n    const rx = parseFloat(node.getAttribute('rx')) || 0;\n    const ry = parseFloat(node.getAttribute('ry')) || 0;\n\n    const width = parseFloat(node.getAttribute('width'));\n    const height = parseFloat(node.getAttribute('height'));\n\n    const w2 = width / 2;\n    const h2 = height / 2;\n    const radius = new Vector(rx, ry);\n\n    const rect = new RoundedRectangle(0, 0, width, height, radius);\n    rect.stroke = 'none';\n    rect.fill = 'black';\n\n    applySvgAttributes.call(this, node, rect, parentStyles);\n\n    // For rectangles, (x, y) is the center of the shape rather than the top\n    // left corner.\n    rect.translation.x += w2;\n    rect.translation.y += h2;\n\n    return rect;\n  },\n\n  line: function (node, parentStyles) {\n    const x1 = parseFloat(node.getAttribute('x1'));\n    const y1 = parseFloat(node.getAttribute('y1'));\n    const x2 = parseFloat(node.getAttribute('x2'));\n    const y2 = parseFloat(node.getAttribute('y2'));\n\n    const line = new Line(x1, y1, x2, y2).noFill();\n\n    applySvgAttributes.call(this, node, line, parentStyles);\n\n    return line;\n  },\n\n  lineargradient: function (node, parentStyles) {\n    let units = node.getAttribute('gradientUnits');\n    let spread = node.getAttribute('spreadMethod');\n\n    if (!units) {\n      units = 'objectBoundingBox';\n    }\n    if (!spread) {\n      spread = 'pad';\n    }\n\n    let x1 = parseFloat(node.getAttribute('x1') || 0);\n    let y1 = parseFloat(node.getAttribute('y1') || 0);\n    let x2 = parseFloat(node.getAttribute('x2') || 0);\n    let y2 = parseFloat(node.getAttribute('y2') || 0);\n\n    const ox = (x2 + x1) / 2;\n    const oy = (y2 + y1) / 2;\n\n    if (/userSpaceOnUse/i.test(units)) {\n      x1 -= ox;\n      y1 -= oy;\n      x2 -= ox;\n      y2 -= oy;\n    }\n\n    const stops = [];\n    for (let i = 0; i < node.children.length; i++) {\n      const child = node.children[i];\n\n      let offset = child.getAttribute('offset');\n      if (/%/gi.test(offset)) {\n        offset = parseFloat(offset.replace(/%/gi, '')) / 100;\n      }\n      offset = parseFloat(offset);\n\n      let color = child.getAttribute('stop-color');\n      let opacity = child.getAttribute('stop-opacity');\n      let style = child.getAttribute('style');\n\n      let matches;\n      if (color === null) {\n        matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n        color = matches && matches.length > 1 ? matches[1] : undefined;\n      }\n\n      if (opacity === null) {\n        matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n        opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n      } else {\n        opacity = parseFloat(opacity);\n      }\n\n      stops.push(new Stop(offset, color, opacity));\n    }\n\n    const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n\n    gradient.spread = spread;\n    gradient.units = units;\n\n    applySvgAttributes.call(this, node, gradient, parentStyles);\n\n    return gradient;\n  },\n\n  radialgradient: function (node, parentStyles) {\n    let units = node.getAttribute('gradientUnits');\n    let spread = node.getAttribute('spreadMethod');\n\n    if (!units) {\n      units = 'objectBoundingBox';\n    }\n    if (!spread) {\n      spread = 'pad';\n    }\n\n    let cx = parseFloat(node.getAttribute('cx')) || 0;\n    let cy = parseFloat(node.getAttribute('cy')) || 0;\n    let r = parseFloat(node.getAttribute('r'));\n\n    let fx = parseFloat(node.getAttribute('fx'));\n    let fy = parseFloat(node.getAttribute('fy'));\n\n    if (_.isNaN(fx)) {\n      fx = cx;\n    }\n\n    if (_.isNaN(fy)) {\n      fy = cy;\n    }\n\n    const ox = Math.abs(cx + fx) / 2;\n    const oy = Math.abs(cy + fy) / 2;\n\n    if (/userSpaceOnUse/i.test(units)) {\n      cx -= ox;\n      cy -= oy;\n      fx -= ox;\n      fy -= oy;\n    }\n\n    const stops = [];\n    for (let i = 0; i < node.children.length; i++) {\n      const child = node.children[i];\n\n      let offset = child.getAttribute('offset');\n      if (/%/gi.test(offset)) {\n        offset = parseFloat(offset.replace(/%/gi, '')) / 100;\n      }\n      offset = parseFloat(offset);\n\n      let color = child.getAttribute('stop-color');\n      let opacity = child.getAttribute('stop-opacity');\n      let style = child.getAttribute('style');\n\n      let matches;\n      if (color === null) {\n        matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n        color = matches && matches.length > 1 ? matches[1] : undefined;\n      }\n\n      if (opacity === null) {\n        matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n        opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n      } else {\n        opacity = parseFloat(opacity);\n      }\n\n      stops.push(new Stop(offset, color, opacity));\n    }\n\n    const gradient = new RadialGradient(cx, cy, r, stops, fx, fy);\n\n    gradient.spread = spread;\n    gradient.units = units;\n\n    applySvgAttributes.call(this, node, gradient, parentStyles);\n\n    return gradient;\n  },\n\n  text: function (node, parentStyles) {\n    const alignment = getAlignment(node.getAttribute('text-anchor')) || 'left';\n    const baseline = getBaseline(node) || 'baseline';\n    let message = '';\n\n    // Detect tspan for getting text content.\n    // If not, svg indentation apears in text content\n    if (node.childNodes.length > 0 && node.childNodes[0].tagName === 'TSPAN') {\n      message = node.childNodes[0].textContent;\n    } else {\n      message = node.textContent;\n    }\n\n    const text = new Text(message);\n\n    applySvgAttributes.call(this, node, text, parentStyles);\n\n    text.alignment = alignment;\n    text.baseline = baseline;\n\n    return text;\n  },\n\n  clippath: function (node, parentStyles) {\n    if (read.defs.current && !read.defs.current.contains(node.id)) {\n      read.defs.current.add(node.id, node);\n    }\n    return null;\n  },\n\n  image: function (node, parentStyles) {\n    let error;\n\n    const href = node.getAttribute('href') || node.getAttribute('xlink:href');\n    if (!href) {\n      error = new TwoError('encountered <image /> with no href.');\n      console.warn(error.name, error.message);\n      return null;\n    }\n\n    const x = parseFloat(node.getAttribute('x')) || 0;\n    const y = parseFloat(node.getAttribute('y')) || 0;\n    const width = parseFloat(node.getAttribute('width'));\n    const height = parseFloat(node.getAttribute('height'));\n\n    const sprite = new Sprite(href, x, y);\n\n    if (!_.isNaN(width)) {\n      sprite.width = width;\n    }\n    if (!_.isNaN(height)) {\n      sprite.height = height;\n    }\n\n    applySvgAttributes.call(this, node, sprite, parentStyles);\n\n    return sprite;\n  },\n};\n"
  },
  {
    "path": "src/utils/math.d.ts",
    "content": "declare module 'two.js/src/utils/math' {\n  export interface DecomposedMatrix {\n    translateX: number;\n    translateY: number;\n    scaleX: number;\n    scaleY: number;\n    skewX: number;\n    skewY: number;\n    rotation: number;\n  }\n  /**\n   * @name Two.Utils.decomposeMatrix\n   * @function\n   * @param {Matrix} matrix - The matrix to decompose.\n   * @returns {Object} An object containing relevant skew values.\n   * @description Decompose a 2D 3x3 Matrix to find the skew.\n   */\n  export function decomposeMatrix(matrix: Matrix): DecomposedMatrix;\n  export function decomposeMatrix(\n    a: number,\n    b: number,\n    c: number,\n    d: number,\n    e: number,\n    f: number\n  ): DecomposedMatrix;\n  /**\n   * @name Two.Utils.getComputedMatrix\n   * @function\n   * @param {Shape} object - The Two.js object that has a matrix property to calculate from.\n   * @param {Matrix} [matrix] - The matrix to apply calculated transformations to if available.\n   * @returns {Matrix} The computed matrix of a nested object. If no `matrix` was passed in arguments then a `new Two.Matrix` is returned.\n   * @description Method to get the world space transformation of a given object in a Two.js scene.\n   */\n  export function getComputedMatrix(object: Shape, matrix?: Matrix): Matrix;\n  export function getPoT(value: number | string): number;\n  export function setMatrix(matrix: Matrix): void;\n  /**\n   * @name Two.Utils.lerp\n   * @function\n   * @param {Number} a - Start value.\n   * @param {Number} b - End value.\n   * @param {Number} t - Zero-to-one value describing percentage between a and b.\n   * @returns {Number}\n   * @description Linear interpolation between two values `a` and `b` by an amount `t`.\n   */\n  export function lerp(a: number, b: number, t: number): number;\n  /**\n   * @name Two.Utils.mod\n   * @function\n   * @param {Number} v - The value to modulo\n   * @param {Number} l - The value to modulo by\n   * @returns {Number}\n   * @description Modulo with added functionality to handle negative values in a positive manner.\n   */\n  export function mod(v: number, l: number): number;\n  export const NumArray: Float32Array | number[];\n  /**\n   * @name Two.Utils.toFixed\n   * @function\n   * @param {Number} v - Any float\n   * @returns {Number} That float trimmed to the third decimal place.\n   * @description A pretty fast toFixed(3) alternative.\n   * @see {@link http://jsperf.com/parsefloat-tofixed-vs-math-round/18}\n   */\n  export function toFixed(v: number): number;\n  export const TWO_PI: number;\n  export const HALF_PI: number;\n  /**\n   * @name Two.Utils.getEffectiveStrokeWidth\n   * @function\n   * @param {Path|Group} object - The object to calculate effective stroke width for\n   * @param {Matrix} [worldMatrix] - The world transformation matrix. If not provided, will be calculated.\n   * @returns {Number} The effective stroke width adjusted for strokeAttenuation setting\n   * @description Calculate effective stroke width, compensating for world scale if strokeAttenuation is false\n   */\n  export function getEffectiveStrokeWidth(\n    object: Path | Group,\n    worldMatrix?: Matrix\n  ): number;\n  import { Matrix } from 'two.js/src/matrix';\n  import { Shape, ShapeHitTestOptions } from 'two.js/src/shape';\n  import { Path } from 'two.js/src/path';\n  import { Group } from 'two.js/src/group';\n}\n"
  },
  {
    "path": "src/utils/math.js",
    "content": "import { root } from './root.js';\n\nlet Matrix;\nconst TWO_PI = Math.PI * 2;\nconst HALF_PI = Math.PI * 0.5;\n\n/**\n * @name Two.Utils.decomposeMatrix\n * @function\n * @param {Two.Matrix} matrix - The matrix to decompose.\n * @returns {Object} An object containing relevant skew values.\n * @description Decompose a 2D 3x3 Matrix to find the skew.\n */\nfunction decomposeMatrix(matrix, b, c, d, e, f) {\n\n  // TODO: Include skewX, skewY\n  // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati/417813\n  // https://stackoverflow.com/questions/45159314/decompose-2d-transformation-matrix\n\n  let a;\n\n  if (arguments.length <= 1) {\n    a = matrix.a;\n    b = matrix.b;\n    c = matrix.c;\n    d = matrix.d;\n    e = matrix.e;\n    f = matrix.f;\n  } else {\n    a = matrix;\n  }\n\n  return {\n    translateX: e,\n    translateY: f,\n    scaleX: Math.sqrt(a * a + b * b),\n    scaleY: Math.sqrt(c * c + d * d),\n    rotation: 180 * Math.atan2(b, a) / Math.PI\n  };\n\n}\n\nfunction setMatrix(matrix) {\n  Matrix = matrix;\n}\n\n/**\n * @name Two.Utils.getComputedMatrix\n * @function\n * @param {Two.Shape} object - The Two.js object that has a matrix property to calculate from.\n * @param {Two.Matrix} [matrix] - The matrix to apply calculated transformations to if available.\n * @returns {Two.Matrix} The computed matrix of a nested object. If no `matrix` was passed in arguments then a `new Two.Matrix` is returned.\n * @description Method to get the world space transformation of a given object in a Two.js scene.\n */\nfunction getComputedMatrix(object, matrix) {\n\n  matrix = (matrix && matrix.identity()) || new Matrix();\n  let parent = object;\n  const matrices = [];\n\n  while (parent && parent._matrix) {\n    matrices.push(parent._matrix);\n    parent = parent.parent;\n  }\n\n  matrices.reverse();\n\n  for (let i = 0; i < matrices.length; i++) {\n\n    const m = matrices[i];\n    const e = m.elements;\n    matrix.multiply(\n      e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8]);\n\n  }\n\n  return matrix;\n\n}\n\n/**\n * @name Two.Utils.lerp\n * @function\n * @param {Number} a - Start value.\n * @param {Number} b - End value.\n * @param {Number} t - Zero-to-one value describing percentage between a and b.\n * @returns {Number}\n * @description Linear interpolation between two values `a` and `b` by an amount `t`.\n */\nfunction lerp(a, b, t) {\n  return t * (b - a) + a;\n}\n\n/**\n * @name Two.Utils.getPoT\n * @param {Number} value - The number to find the nearest power-of-two value\n * @returns {Number}\n * @description Rounds a number up to the nearest power-of-two value.\n * @see {@link https://en.wikipedia.org/wiki/Power_of_two}\n */\nconst pots = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096];\nfunction getPoT(value) {\n  let i = 0;\n  while (pots[i] && pots[i] < value) {\n    i++;\n  }\n  return pots[i];\n}\n\n/**\n * @name Two.Utils.mod\n * @function\n * @param {Number} v - The value to modulo\n * @param {Number} l - The value to modulo by\n * @returns {Number}\n * @description Modulo with added functionality to handle negative values in a positive manner.\n */\nfunction mod(v, l) {\n\n  while (v < 0) {\n    v += l;\n  }\n\n  return v % l;\n\n}\n\nconst NumArray = root.Float32Array || Array;\nconst floor = Math.floor;\n\n/**\n* @name Two.Utils.toFixed\n* @function\n* @param {Number} v - Any float\n* @returns {Number} That float trimmed to the third decimal place.\n* @description A pretty fast toFixed(3) alternative.\n* @see {@link http://jsperf.com/parsefloat-tofixed-vs-math-round/18}\n*/\nfunction toFixed(v) {\n  return floor(v * 1000000) / 1000000;\n}\n\n/**\n * @name Two.Utils.getEffectiveStrokeWidth\n * @function\n * @param {Two.Path|Two.Group} object - The object to calculate effective stroke width for.\n * @param {Two.Matrix} [worldMatrix] - The world transformation matrix. If not provided, will be calculated.\n * @returns {Number} The effective stroke width. If `object.strokeAttenuation` is true, returns the original linewidth (scales with transforms). If false, returns the linewidth compensated for world scale to maintain constant screen-space width.\n * @description Calculates the effective stroke width for an object. If `strokeAttenuation` is true, returns the original linewidth (which scales with transforms). If `strokeAttenuation` is false, compensates for world scale so the stroke width remains constant in screen space.\n */\nfunction getEffectiveStrokeWidth(object, worldMatrix) {\n  const linewidth = object._linewidth;\n  \n  // If strokeAttenuation is true (default), return original linewidth (scales with transforms)\n  if (object.strokeAttenuation) {\n    return linewidth;\n  }\n  \n  // Calculate world matrix if not provided\n  if (!worldMatrix) {\n    worldMatrix = object.worldMatrix || getComputedMatrix(object);\n  }\n  \n  // Decompose matrix to get scale\n  const decomposed = decomposeMatrix(\n    worldMatrix.elements[0],\n    worldMatrix.elements[3], \n    worldMatrix.elements[1],\n    worldMatrix.elements[4],\n    worldMatrix.elements[2],\n    worldMatrix.elements[5]\n  );\n  \n  // Use the larger of the two scale factors to maintain uniform appearance\n  const scale = Math.max(Math.abs(decomposed.scaleX), Math.abs(decomposed.scaleY));\n  \n  // Compensate for scale to maintain constant screen-space width\n  return scale > 0 ? linewidth / scale : linewidth;\n}\n\n\nexport {\n  decomposeMatrix, getComputedMatrix, getPoT, setMatrix, lerp, mod, NumArray,\n  toFixed, getEffectiveStrokeWidth, TWO_PI, HALF_PI\n};\n"
  },
  {
    "path": "src/utils/path-commands.d.ts",
    "content": "declare module 'two.js/src/utils/path-commands' {\n  export interface Commands {\n    move: 'M';\n    line: 'L';\n    curve: 'C';\n    arc: 'A';\n    close: 'Z';\n  }\n}\n"
  },
  {
    "path": "src/utils/path-commands.js",
    "content": "export const Commands = {\n  move: 'M',\n  line: 'L',\n  curve: 'C',\n  arc: 'A',\n  close: 'Z'\n};\n"
  },
  {
    "path": "src/utils/path.js",
    "content": "import { lerp, mod } from './math.js';\nimport { Commands } from './path-commands.js';\n\nimport { Vector } from '../vector.js';\nimport { Anchor } from '../anchor.js';\n\nconst EPSILON = Number.EPSILON;\n\nfunction isRelativeAnchor(anchor) {\n  return !(typeof anchor.relative === 'boolean') || !!anchor.relative;\n}\n\nfunction setHandleComponent(anchor, side, dx, dy) {\n  const controls = anchor.controls;\n  if (!controls || !controls[side]) {\n    return;\n  }\n\n  if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {\n    if (isRelativeAnchor(anchor)) {\n      controls[side].clear();\n    } else {\n      controls[side].set(anchor.x, anchor.y);\n    }\n    return;\n  }\n\n  if (isRelativeAnchor(anchor)) {\n    controls[side].set(dx, dy);\n  } else {\n    controls[side].set(anchor.x + dx, anchor.y + dy);\n  }\n}\n\nfunction clearHandleComponent(anchor, side) {\n  setHandleComponent(anchor, side, 0, 0);\n}\n\nfunction getHandleOffset(anchor, side) {\n  const controls = anchor.controls;\n  if (!controls || !controls[side]) {\n    return { x: 0, y: 0 };\n  }\n\n  if (isRelativeAnchor(anchor)) {\n    return { x: controls[side].x, y: controls[side].y };\n  }\n\n  return {\n    x: controls[side].x - anchor.x,\n    y: controls[side].y - anchor.y,\n  };\n}\n\nfunction hasNonZeroHandle(anchor, side) {\n  const offset = getHandleOffset(anchor, side);\n  return Math.abs(offset.x) > EPSILON || Math.abs(offset.y) > EPSILON;\n}\n\nfunction updateAnchorCommand(anchor) {\n  if (anchor.command === Commands.move || anchor.command === Commands.close) {\n    return;\n  }\n\n  anchor.command =\n    hasNonZeroHandle(anchor, 'left') || hasNonZeroHandle(anchor, 'right')\n      ? Commands.curve\n      : Commands.line;\n}\n\nfunction inheritRelative(anchor, reference) {\n  if (typeof reference.relative === 'boolean') {\n    anchor.relative = reference.relative;\n  }\n}\n\nfunction isSegmentCurved(a, b) {\n  return (\n    hasNonZeroHandle(b, 'right') ||\n    hasNonZeroHandle(a, 'left') ||\n    hasNonZeroHandle(a, 'right') ||\n    hasNonZeroHandle(b, 'left') ||\n    a.command === Commands.curve ||\n    b.command === Commands.curve\n  );\n}\n\nfunction lerpPoint(a, b, t) {\n  return {\n    x: lerp(a.x, b.x, t),\n    y: lerp(a.y, b.y, t),\n  };\n}\n\nfunction getAbsoluteHandle(anchor, side) {\n  const controls = anchor.controls && anchor.controls[side];\n  if (!controls) {\n    return { x: anchor.x, y: anchor.y };\n  }\n  if (isRelativeAnchor(anchor)) {\n    return { x: anchor.x + controls.x, y: anchor.y + controls.y };\n  }\n  return { x: controls.x, y: controls.y };\n}\n\nfunction splitSubdivisionSegment(start, end, t) {\n  const right = start.controls && start.controls.right;\n  const left = end.controls && end.controls.left;\n\n  const p0 = { x: start.x, y: start.y };\n  const p1 = right ? getAbsoluteHandle(start, 'right') : { ...p0 };\n  const p3 = { x: end.x, y: end.y };\n  const p2 = left ? getAbsoluteHandle(end, 'left') : { ...p3 };\n\n  const q0 = lerpPoint(p0, p1, t);\n  const q1 = lerpPoint(p1, p2, t);\n  const q2 = lerpPoint(p2, p3, t);\n\n  const r0 = lerpPoint(q0, q1, t);\n  const r1 = lerpPoint(q1, q2, t);\n\n  const point = lerpPoint(r0, r1, t);\n\n  const anchor = new Anchor(point.x, point.y);\n  inheritRelative(anchor, start);\n  setHandleComponent(anchor, 'left', r0.x - point.x, r0.y - point.y);\n  setHandleComponent(anchor, 'right', r1.x - point.x, r1.y - point.y);\n  anchor.command = Commands.curve;\n\n  return {\n    anchor,\n    startOut: q0,\n    endIn: q2,\n  };\n}\n\nfunction applyGlobalSmooth(vertices, from, to, closed, loop, asymmetric) {\n  const length = vertices.length;\n  const amount = to - from + 1;\n  let n = amount - 1;\n  let padding = loop ? Math.min(amount, 4) : 1;\n  let paddingLeft = padding;\n  let paddingRight = padding;\n\n  if (!closed) {\n    paddingLeft = Math.min(1, from);\n    paddingRight = Math.min(1, length - to - 1);\n  }\n\n  n += paddingLeft + paddingRight;\n  if (n <= 1) {\n    return;\n  }\n\n  const knots = new Array(n + 1);\n  for (let i = 0, j = from - paddingLeft; i <= n; i += 1, j += 1) {\n    const index = mod(j, length);\n    knots[i] = vertices[index];\n  }\n\n  let x = knots[0].x + 2 * knots[1].x;\n  let y = knots[0].y + 2 * knots[1].y;\n  let f = 2;\n  const n1 = n - 1;\n  const rx = [x];\n  const ry = [y];\n  const rf = [f];\n  const px = new Array(n + 1);\n  const py = new Array(n + 1);\n\n  for (let i = 1; i < n; i += 1) {\n    const internal = i < n1;\n    const a = internal ? 1 : asymmetric ? 1 : 2;\n    const b = internal ? 4 : asymmetric ? 2 : 7;\n    const u = internal ? 4 : asymmetric ? 3 : 8;\n    const v = internal ? 2 : asymmetric ? 0 : 1;\n    const m = a / f;\n    f = rf[i] = b - m;\n    x = rx[i] = u * knots[i].x + v * knots[i + 1].x - m * x;\n    y = ry[i] = u * knots[i].y + v * knots[i + 1].y - m * y;\n  }\n\n  px[n1] = rx[n1] / rf[n1];\n  py[n1] = ry[n1] / rf[n1];\n\n  for (let i = n - 2; i >= 0; i -= 1) {\n    px[i] = (rx[i] - px[i + 1]) / rf[i];\n    py[i] = (ry[i] - py[i + 1]) / rf[i];\n  }\n\n  px[n] = (3 * knots[n].x - px[n1]) / 2;\n  py[n] = (3 * knots[n].y - py[n1]) / 2;\n\n  const max = n - paddingRight;\n\n  for (let i = paddingLeft, j = from; i <= max; i += 1, j += 1) {\n    const index = mod(j, length);\n    const anchor = vertices[index];\n    const hx = px[i] - anchor.x;\n    const hy = py[i] - anchor.y;\n\n    if (loop || i < max) {\n      setHandleComponent(anchor, 'right', hx, hy);\n    } else {\n      clearHandleComponent(anchor, 'right');\n    }\n\n    if (loop || i > paddingLeft) {\n      setHandleComponent(anchor, 'left', -hx, -hy);\n    } else {\n      clearHandleComponent(anchor, 'left');\n    }\n\n    updateAnchorCommand(anchor);\n  }\n}\n\nfunction applyCatmullRom(anchor, prev, next, factor, clampIn, clampOut) {\n  const p0 = prev || anchor;\n  const p1 = anchor;\n  const p2 = next || anchor;\n  const d1 = Vector.distanceBetween(p0, p1);\n  const d2 = Vector.distanceBetween(p1, p2);\n  const a = factor === undefined ? 0.5 : factor;\n  const d1a = Math.pow(d1, a);\n  const d2a = Math.pow(d2, a);\n  const d1_2a = d1a * d1a;\n  const d2_2a = d2a * d2a;\n\n  if (!clampIn && prev) {\n    const A = 2 * d2_2a + 3 * d2a * d1a + d1_2a;\n    const N = 3 * d2a * (d2a + d1a);\n    if (N !== 0) {\n      const hx = (d2_2a * p0.x + A * p1.x - d1_2a * p2.x) / N - p1.x;\n      const hy = (d2_2a * p0.y + A * p1.y - d1_2a * p2.y) / N - p1.y;\n      setHandleComponent(anchor, 'left', hx, hy);\n    } else {\n      clearHandleComponent(anchor, 'left');\n    }\n  } else {\n    clearHandleComponent(anchor, 'left');\n  }\n\n  if (!clampOut && next) {\n    const A = 2 * d1_2a + 3 * d1a * d2a + d2_2a;\n    const N = 3 * d1a * (d1a + d2a);\n    if (N !== 0) {\n      const hx = (d1_2a * p2.x + A * p1.x - d2_2a * p0.x) / N - p1.x;\n      const hy = (d1_2a * p2.y + A * p1.y - d2_2a * p0.y) / N - p1.y;\n      setHandleComponent(anchor, 'right', hx, hy);\n    } else {\n      clearHandleComponent(anchor, 'right');\n    }\n  } else {\n    clearHandleComponent(anchor, 'right');\n  }\n\n  updateAnchorCommand(anchor);\n}\n\nfunction applyGeometric(anchor, prev, next, factor, clampIn, clampOut) {\n  if (!(prev && next)) {\n    if (!prev) {\n      clearHandleComponent(anchor, 'left');\n    }\n    if (!next) {\n      clearHandleComponent(anchor, 'right');\n    }\n    updateAnchorCommand(anchor);\n    return;\n  }\n\n  const p0 = prev;\n  const p1 = anchor;\n  const p2 = next;\n  const d1 = Vector.distanceBetween(p0, p1);\n  const d2 = Vector.distanceBetween(p1, p2);\n  const total = d1 + d2;\n  const tension = factor === undefined ? 0.4 : factor;\n  const vector = { x: p0.x - p2.x, y: p0.y - p2.y };\n\n  if (!clampIn && total !== 0) {\n    const k = (tension * d1) / total;\n    setHandleComponent(anchor, 'left', vector.x * k, vector.y * k);\n  } else {\n    clearHandleComponent(anchor, 'left');\n  }\n\n  if (!clampOut && total !== 0) {\n    const k = (tension * d1) / total - tension;\n    setHandleComponent(anchor, 'right', vector.x * k, vector.y * k);\n  } else {\n    clearHandleComponent(anchor, 'right');\n  }\n\n  updateAnchorCommand(anchor);\n}\n\nfunction applyLocalSmooth(vertices, from, to, closed, loop, options) {\n  const type = options.type || 'catmull-rom';\n  const factor = options.factor;\n  const length = vertices.length;\n\n  for (let i = from; i <= to; i += 1) {\n    const index = mod(i, length);\n    const anchor = vertices[index];\n\n    if (anchor.command === Commands.move) {\n      clearHandleComponent(anchor, 'left');\n      clearHandleComponent(anchor, 'right');\n      continue;\n    }\n\n    const prevIndex = i === from && !loop ? null : i - 1;\n    const nextIndex = i === to && !loop ? null : i + 1;\n\n    const prev = prevIndex === null ? null : vertices[mod(prevIndex, length)];\n    const next = nextIndex === null ? null : vertices[mod(nextIndex, length)];\n\n    const clampIn = prevIndex === null;\n    const clampOut = nextIndex === null;\n\n    if (type === 'geometric') {\n      applyGeometric(anchor, prev, next, factor, clampIn, clampOut);\n    } else {\n      applyCatmullRom(anchor, prev, next, factor, clampIn, clampOut);\n    }\n  }\n}\n\nexport {\n  isRelativeAnchor,\n  setHandleComponent,\n  clearHandleComponent,\n  hasNonZeroHandle,\n  updateAnchorCommand,\n  inheritRelative,\n  isSegmentCurved,\n  splitSubdivisionSegment,\n  applyGlobalSmooth,\n  applyLocalSmooth,\n};\n"
  },
  {
    "path": "src/utils/root.d.ts",
    "content": "declare module 'two.js/src/utils/root' {\n  export let root: typeof globalThis;\n}\n"
  },
  {
    "path": "src/utils/root.js",
    "content": "\nlet root;\n\nif (typeof window !== 'undefined') {\n  root = window;\n} else if (typeof global !== 'undefined') {\n  root = global;\n} else if (typeof self !== 'undefined') {\n  root = self;\n}\n\nexport { root };\n"
  },
  {
    "path": "src/utils/shaders.d.ts",
    "content": "declare module 'two.js/src/utils/shaders' {\n  export interface shaders {\n    create(gl: any, source: any, type: any): any;\n    types: {\n      vertex: string;\n      fragment: string;\n    };\n    path: {\n      vertex: string;\n      fragment: string;\n    };\n    points: {\n      vertex: string;\n      fragment: string;\n    };\n  }\n}\n"
  },
  {
    "path": "src/utils/shaders.js",
    "content": "import { TwoError } from './error.js';\n\nexport const shaders = {\n\n  create: function(gl, source, type) {\n\n    const shader = gl.createShader(gl[type]);\n    gl.shaderSource(shader, source);\n    gl.compileShader(shader);\n\n    const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);\n    if (!compiled) {\n      const error = gl.getShaderInfoLog(shader);\n      gl.deleteShader(shader);\n      throw new TwoError('unable to compile shader ' + shader + ': ' + error);\n    }\n\n    return shader;\n\n  },\n\n  types: {\n    vertex: 'VERTEX_SHADER',\n    fragment: 'FRAGMENT_SHADER'\n  },\n\n  path : {\n\n    vertex:`\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n      uniform vec4 u_rect;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 rectCoords = (a_position * (u_rect.zw - u_rect.xy)) + u_rect.xy;\n        vec2 projected = (u_matrix * vec3(rectCoords, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n\n    fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec4 texel = texture2D(u_image, v_textureCoords);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `,\n\n  },\n\n  points: {\n\n    vertex: `\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform float u_size;\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_PointSize = u_size;\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n\n    fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n\n      void main() {\n        vec4 texel = texture2D(u_image, gl_PointCoord);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `\n\n  }\n\n};\n"
  },
  {
    "path": "src/utils/shape.d.ts",
    "content": "declare module 'two.js/src/utils/shape' {\n  /**\n   * @private\n   * @param {Path} path - The path to analyze against.\n   * @param {Number} t -\n   * @returns {Number}\n   * @description\n   */\n  export function contains(path: Path, t: number): number;\n  /**\n   * @private\n   * @param {Path} path - The path to analyze against.\n   * @param {Number} target - The target length at which to find an anchor.\n   * @returns {Number}\n   * @description Return the id of an anchor based on a target length.\n   */\n  export function getIdByLength(path: Path, target: number): number;\n  export function getCurveLength(a: any, b: any, limit: any): number;\n  export function getSubdivisions(\n    a: any,\n    b: any,\n    limit: any\n  ): import('two.js/src/anchor').Anchor[];\n  import { Path } from 'two.js/src/path';\n}\n"
  },
  {
    "path": "src/utils/shape.js",
    "content": "import { Texture } from '../effects/texture.js';\nimport { subdivide, getCurveLength as gcl } from './curves.js';\nimport { Gradient } from '../effects/gradient.js';\nimport { LinearGradient } from '../effects/linear-gradient.js';\nimport { RadialGradient } from '../effects/radial-gradient.js';\n\n/**\n * @private\n * @param {Two.Path} path - The path to analyze against.\n * @param {Number} t -\n * @returns {Number}\n * @description\n */\nfunction contains(path, t) {\n  if (t === 0 || t === 1) {\n    return true;\n  }\n\n  const length = path._length;\n  const target = length * t;\n  let elapsed = 0;\n\n  for (let i = 0; i < path._lengths.length; i++) {\n    const dist = path._lengths[i];\n    if (elapsed >= target) {\n      return target - elapsed >= 0;\n    }\n    elapsed += dist;\n  }\n\n  return false;\n}\n\n/**\n * @private\n * @param {Two.Path} path - The path to analyze against.\n * @param {Number} target - The target length at which to find an anchor.\n * @returns {Number}\n * @description Return the id of an anchor based on a target length.\n */\nfunction getIdByLength(path, target) {\n  const total = path._length;\n\n  if (target <= 0) {\n    return 0;\n  } else if (target >= total) {\n    return path._lengths.length - 1;\n  }\n\n  for (let i = 0, sum = 0; i < path._lengths.length; i++) {\n    if (sum + path._lengths[i] >= target) {\n      target -= sum;\n      return Math.max(i - 1, 0) + target / path._lengths[i];\n    }\n\n    sum += path._lengths[i];\n  }\n\n  return -1;\n}\n\nfunction getCurveLength(a, b, limit) {\n  // TODO: DRYness\n  let x1, x2, x3, x4, y1, y2, y3, y4;\n\n  const right = b.controls && b.controls.right;\n  const left = a.controls && a.controls.left;\n\n  x1 = b.x;\n  y1 = b.y;\n  x2 = (right || b).x;\n  y2 = (right || b).y;\n  x3 = (left || a).x;\n  y3 = (left || a).y;\n  x4 = a.x;\n  y4 = a.y;\n\n  if (right && b._relative) {\n    x2 += b.x;\n    y2 += b.y;\n  }\n\n  if (left && a._relative) {\n    x3 += a.x;\n    y3 += a.y;\n  }\n\n  return gcl(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n}\n\nfunction getSubdivisions(a, b, limit) {\n  // TODO: DRYness\n  let x1, x2, x3, x4, y1, y2, y3, y4;\n\n  const right = b.controls && b.controls.right;\n  const left = a.controls && a.controls.left;\n\n  x1 = b.x;\n  y1 = b.y;\n  x2 = (right || b).x;\n  y2 = (right || b).y;\n  x3 = (left || a).x;\n  y3 = (left || a).y;\n  x4 = a.x;\n  y4 = a.y;\n\n  if (right && b._relative) {\n    x2 += b.x;\n    y2 += b.y;\n  }\n\n  if (left && a._relative) {\n    x3 += a.x;\n    y3 += a.y;\n  }\n\n  return subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n}\n\nfunction getEffectFromObject(obj) {\n  switch (obj.renderer.type) {\n    case 'texture':\n      return Texture.fromObject(obj);\n    case 'gradient':\n      return Gradient.fromObject(obj);\n    case 'linear-gradient':\n      return LinearGradient.fromObject(obj);\n    case 'radial-gradient':\n      return RadialGradient.fromObject(obj);\n  }\n  return obj;\n}\n\nexport {\n  contains,\n  getEffectFromObject,\n  getIdByLength,\n  getCurveLength,\n  getSubdivisions,\n};\n"
  },
  {
    "path": "src/utils/underscore.d.ts",
    "content": "declare module 'two.js/src/utils/underscore' {\n  export interface _ {\n    isNaN(obj: any): boolean;\n    isElement(obj: any): boolean;\n    isObject(obj: any): boolean;\n    extend(base: any, ...args: any[]): any;\n    defaults(base: any, ...args: any[]): any;\n    each(obj: any, iteratee: any, context: any): any;\n    performance: { now: () => number };\n  }\n}\n"
  },
  {
    "path": "src/utils/underscore.js",
    "content": "import { root } from './root.js';\n\nconst slice = Array.prototype.slice;\n\nfunction isArrayLike(collection) {\n  if (collection === null || collection === undefined) return false;\n  const length = collection.length;\n  // Arrays cannot hold more than 2^32 - 1 items\n  return typeof length == 'number' && length >= 0 && length < 4294967296;\n}\n\nexport const _ = {\n  isNaN: function (obj) {\n    return typeof obj === 'number' && obj !== +obj;\n  },\n  isElement: function (obj) {\n    return !!(obj && obj.nodeType === 1);\n  },\n  isObject: function (obj) {\n    const type = typeof obj;\n    return type === 'function' || (type === 'object' && !!obj);\n  },\n  isFunction: function (obj) {\n    return typeof obj === 'function';\n  },\n  extend: function (base) {\n    const sources = slice.call(arguments, 1);\n    for (let i = 0; i < sources.length; i++) {\n      const obj = sources[i];\n      for (let k in obj) {\n        base[k] = obj[k];\n      }\n    }\n    return base;\n  },\n  defaults: function (base) {\n    const sources = slice.call(arguments, 1);\n    for (let i = 0; i < sources.length; i++) {\n      const obj = sources[i];\n      for (let k in obj) {\n        if (base[k] === void 0) {\n          base[k] = obj[k];\n        }\n      }\n    }\n    return base;\n  },\n  each: function (obj, iteratee, context) {\n    const ctx = context || this;\n    const keys = !isArrayLike(obj) && Object.keys(obj);\n    const length = (keys || obj).length;\n    for (let i = 0; i < length; i++) {\n      const k = keys ? keys[i] : i;\n      iteratee.call(ctx, obj[k], k, obj);\n    }\n    return obj;\n  },\n  /**\n   * @name Two.Utils.performance\n   * @property {Date} - A special `Date` like object to get the current millis of the session. Used internally to calculate time between frames.\n   * e.g: `Utils.performance.now() // milliseconds since epoch`\n   */\n  performance:\n    root.performance && root.performance.now ? root.performance : Date,\n};\n"
  },
  {
    "path": "src/utils/xhr.d.ts",
    "content": "declare module 'two.js/src/utils/xhr' {\n  /**\n   * @name Two.Utils.xhr\n   * @function\n   * @param {String} path\n   * @param {Function} callback\n   * @returns {XMLHttpRequest} The constructed and called XHR request.\n   * @description Canonical method to initiate `GET` requests in the browser. Mainly used by {@link Two#load} method.\n   */\n  export function xhr(\n    path: string,\n    callback: (resp: XMLHttpRequestResponseType) => void\n  ): XMLHttpRequest;\n}\n"
  },
  {
    "path": "src/utils/xhr.js",
    "content": "/**\n * @name Two.Utils.xhr\n * @function\n * @param {String} path\n * @param {Function} callback\n * @returns {XMLHttpRequest} The constructed and called XHR request.\n * @description Canonical method to initiate `GET` requests in the browser. Mainly used by {@link Two#load} method.\n */\nexport function xhr(path, callback) {\n\n  const xhr = new XMLHttpRequest();\n  xhr.open('GET', path);\n\n  xhr.onreadystatechange = function() {\n    if (xhr.readyState === 4 && xhr.status === 200) {\n      callback(xhr.responseText);\n    }\n  };\n\n  xhr.send();\n  return xhr;\n\n}\n"
  },
  {
    "path": "src/vector.d.ts",
    "content": "declare module 'two.js/src/vector' {\n  /**\n     * @name Two.Vector\n     * @class\n\n     * @param {Number} [x=0] - Any number to represent the horizontal x-component of the vector.\n     * @param {Number} [y=0] - Any number to represent the vertical y-component of the vector.\n     * @description A class to store x / y component vector data. In addition to storing data `Two.Vector` has suped up methods for commonplace mathematical operations.\n     */\n  export class Vector extends Events {\n    /**\n     * @name Two.Vector.zero\n     * @readonly\n     * @property {Vector} - Handy reference to a vector with component values 0, 0 at all times.\n     */\n    static readonly zero: Vector;\n    /**\n     * @name Two.Vector.add\n     * @function\n     * @param {Vector} v1\n     * @param {Vector} v2\n     * @returns {Vector}\n     * @description Add two vectors together.\n     */\n    static add(v1: Vector, v2: Vector): Vector;\n    /**\n     * @name Two.Vector.sub\n     * @function\n     * @param {Vector} v1\n     * @param {Vector} v2\n     * @returns {Vector}\n     * @description Subtract two vectors: `v2` from `v1`.\n     */\n    static sub(v1: Vector, v2: Vector): Vector;\n    /**\n     * @name Two.Vector.subtract\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    static subtract(v1: Vector, v2: Vector): Vector;\n    /**\n     * @name Two.Vector.ratioBetween\n     * @function\n     * @param {Vector} v1\n     * @param {Vector} v2\n     * @returns {Number} The ratio betwen two points `v1` and `v2`.\n     */\n    static ratioBetween(v1: Vector, v2: Vector): number;\n    /**\n     * @name Two.Vector.angleBetween\n     * @function\n     * @param {Vector} v1\n     * @param {Vector} v2\n     * @returns {Number} The angle between points `v1` and `v2`.\n     */\n    static angleBetween(v1: Vector, v2: Vector): number;\n    static angleBetween(x1: number, y1: number, x2: number, y2: number): number;\n    /**\n     * @name Two.Vector.distanceBetween\n     * @function\n     * @param {Vector} v1\n     * @param {Vector} v2\n     * @returns {Number} The distance between points `v1` and `v2`. Distance is always positive.\n     */\n    static distanceBetween(v1: Vector, v2: Vector): number;\n    /**\n     * @name Two.Vector.distanceBetweenSquared\n     * @function\n     * @param {Vector} v1\n     * @param {Vector} v2\n     * @returns {Number} The squared distance between points `v1` and `v2`.\n     */\n    static distanceBetweenSquared(v1: Vector, v2: Vector): number;\n    constructor(x?: number, y?: number);\n    /**\n     * @name Two.Vector#_x\n     * @private\n     */\n    private _x;\n    /**\n     * @name Two.Vector#_y\n     * @private\n     */\n    private _y;\n    /**\n     * @name Two.Vector#x\n     * @property {Number} - The horizontal x-component of the vector.\n     * @type {Number}\n     */\n    x: number;\n    /**\n     * @name Two.Vector#y\n     * @property {Number} - The vertical y-component of the vector.\n     * @type {Number}\n     */\n    y: number;\n    set(x: number, y: number): Vector;\n    /**\n     * @name Two.Vector#copy\n     * @function\n     * @param {Vector} v\n     * @description Copy the x / y components of another object `v`.\n     */\n    copy(v: Vector): Vector;\n    /**\n     * @name Two.Vector#clear\n     * @function\n     * @description Set the x / y component values of the vector to zero.\n     */\n    clear(): Vector;\n    /**\n     * @name Two.Vector#clone\n     * @function\n     * @description Create a new vector and copy the existing values onto the newly created instance.\n     */\n    clone(): Vector;\n    /**\n     * @name Two.Vector#add\n     * @function\n     * @param {Vector} v\n     * @description Add an object with x / y component values to the instance.\n     * @overloaded\n     */\n    add(v: Vector): Vector;\n    /**\n     * @name Two.Vector#add\n     * @function\n     * @param {Number} v\n     * @description Add the **same** number to both x / y component values of the instance.\n     * @overloaded\n     */\n    add(v: number): Vector;\n    /**\n     * @name Two.Vector#add\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @description Add `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    add(x: number, y: number): Vector;\n    /**\n     * @name Two.Vector#addSelf\n     * @function\n     * @description Alias for {@link Two.Vector.add}.\n     */\n    addSelf(x: number, y: number): Vector;\n    addSelf(v: Vector): Vector;\n    addSelf(v: number): Vector;\n    /**\n     * @name Two.Vector#sub\n     * @function\n     * @param {Vector} v\n     * @description Subtract an object with x / y component values to the instance.\n     * @overloaded\n     */\n    sub(v: Vector): Vector;\n    /**\n     * @name Two.Vector#sub\n     * @function\n     * @param {Number} v\n     * @description Subtract the **same** number to both x / y component values of the instance.\n     * @overloaded\n     */\n    sub(v: number): Vector;\n    /**\n     * @name Two.Vector#sub\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @description Subtract `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    sub(x: number, y: number): Vector;\n    /**\n     * @name Two.Vector#subtract\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    subtract(x: number, y: number): Vector;\n    subtract(v: number): Vector;\n    subtract(v: Vector): Vector;\n    /**\n     * @name Two.Vector#subSelf\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    subSelf(x: number, y: number): Vector;\n    subSelf(v: number): Vector;\n    subSelf(v: Vector): Vector;\n    /**\n     * @name Two.Vector#subtractSelf\n     * @function\n     * @description Alias for {@link Two.Vector.sub}.\n     */\n    subtractSelft(x: number, y: number): Vector;\n    subtractSelft(v: number): Vector;\n    subtractSelft(v: Vector): Vector;\n    /**\n     * @name Two.Vector#multiply\n     * @function\n     * @param {Vector} v\n     * @description Multiply an object with x / y component values to the instance.\n     * @overloaded\n     */\n    multiply(v: number): Vector;\n    /**\n     * @name Two.Vector#multiply\n     * @function\n     * @param {Number} v\n     * @description Multiply the **same** number to both x / y component values of the instance.\n     * @overloaded\n     */\n    multiply(v: Vector): Vector;\n    /**\n     * @name Two.Vector#multiply\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @description Multiply `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    multiply(x: number, y: number): Vector;\n    /**\n     * @name Two.Vector#multiplySelf\n     * @function\n     * @description Alias for {@link Two.Vector.multiply}.\n     */\n    multiplySelf(v: Vector, ...args: Vector[]): Vector;\n    /**\n     * @name Two.Vector#multiplyScalar\n     * @function\n     * @param {Number} s - The scalar to multiply by.\n     * @description Mulitiply the vector by a single number. Shorthand to call {@link Two.Vector#multiply} directly.\n     */\n    multiplyScalar(s: number): Vector;\n    /**\n     * @name Two.Vector#divide\n     * @function\n     * @param {Vector} v\n     * @description Divide an object with x / y component values to the instance.\n     * @overloaded\n     */\n    divide(v: Vector): Vector;\n    /**\n     * @name Two.Vector#divide\n     * @function\n     * @param {Number} v\n     * @description Divide the **same** number to both x / y component values of the instance.\n     * @overloaded\n     */\n    divide(v: number): Vector;\n    /**\n     * @name Two.Vector#divide\n     * @function\n     * @param {Number} x\n     * @param {Number} y\n     * @description Divide `x` / `y` values to their respective component value on the instance.\n     * @overloaded\n     */\n    divide(x: number, y: number): Vector;\n    /**\n     * @name Two.Vector#divideSelf\n     * @function\n     * @description Alias for {@link Two.Vector.divide}.\n     */\n    divideSelf(x: number, y: number): Vector;\n    divideSelf(v: number): Vector;\n    divideSelf(v: Vector): Vector;\n    /**\n     * @name Two.Vector#divideScalar\n     * @function\n     * @param {Number} s - The scalar to divide by.\n     * @description Divide the vector by a single number. Shorthand to call {@link Two.Vector#divide} directly.\n     */\n    divideScalar(s: number): Vector;\n    /**\n     * @name Two.Vector#negate\n     * @function\n     * @description Invert each component's sign value.\n     */\n    negate(): Vector;\n    /**\n     * @name Two.Vector#negate\n     * @function\n     * @returns {Number}\n     * @description Get the [dot product](https://en.wikipedia.org/wiki/Dot_product) of the vector.\n     */\n    dot(v: Vector): number;\n    /**\n     * @name Two.Vector#length\n     * @function\n     * @returns {Number}\n     * @description Get the length of a vector.\n     */\n    length(): number;\n    /**\n     * @name Two.Vector#lengthSquared\n     * @function\n     * @returns {Number}\n     * @description Get the length of the vector to the power of two. Widely used as less expensive than {@link Two.Vector#length} because it isn't square-rooting any numbers.\n     */\n    lengthSquared(): number;\n    /**\n     * @name Two.Vector#normalize\n     * @function\n     * @description Normalize the vector from negative one to one.\n     */\n    normalize(): Vector;\n    /**\n     * @name Two.Vector#distanceTo\n     * @function\n     * @returns {Number}\n     * @description Get the distance between two vectors.\n     */\n    distanceTo(v: Vector): number;\n    /**\n     * @name Two.Vector#distanceToSquared\n     * @function\n     * @returns {Number}\n     * @description Get the distance between two vectors to the power of two. Widely used as less expensive than {@link Two.Vector#distanceTo} because it isn't square-rooting any numbers.\n     */\n    distanceToSquared(v: Vector): number;\n    /**\n     * @name Two.Vector#setLength\n     * @function\n     * @param {Number} l - length to set vector to.\n     * @description Set the length of a vector.\n     */\n    setLength(l: number): Vector;\n    /**\n     * @name Two.Vector#equals\n     * @function\n     * @param {Vector} v - The vector to compare against.\n     * @param {Number} [eps=0.0001] - An options epsilon for precision.\n     * @returns {Boolean}\n     * @description Qualify if one vector roughly equal another. With a margin of error defined by epsilon.\n     */\n    equals(v: Vector, eps?: number): boolean;\n    /**\n     * @name Two.Vector#lerp\n     * @function\n     * @param {Vector} v - The destination vector to step towards.\n     * @param {Number} t - The zero to one value of how close the current vector gets to the destination vector.\n     * @description Linear interpolate one vector to another by an amount `t` defined as a zero to one number.\n     * @see [Matt DesLauriers](https://twitter.com/mattdesl/status/1031305279227478016) has a good thread about this.\n     */\n    lerp(v: Vector, t: number): Vector;\n    /**\n     * @name Two.Vector#isZero\n     * @function\n     * @param {Number} [eps=0.0001] - Optional precision amount to check against.\n     * @returns {Boolean}\n     * @description Check to see if vector is roughly zero, based on the `epsilon` precision value.\n     */\n    isZero(eps?: number): boolean;\n    /**\n     * @name Two.Vector#toObject\n     * @function\n     * @returns {Object}\n     * @description Return a JSON compatible plain object that represents the vector.\n     */\n    toObject(): object;\n    /**\n     * @name Two.Vector#rotate\n     * @function\n     * @param {Number} radians - The amount to rotate the vector by in radians.\n     * @description Rotate a vector.\n     */\n    rotate(radians: number): Vector;\n  }\n  import { Events } from 'two.js/src/events';\n}\n"
  },
  {
    "path": "src/vector.js",
    "content": "import { Events } from './events.js';\nimport { toFixed } from './utils/math.js';\n\nconst proto = {\n  x: {\n    enumerable: true,\n    get: function () {\n      return this._x;\n    },\n    set: function (v) {\n      if (this._x !== v) {\n        this._x = v;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n  y: {\n    enumerable: true,\n    get: function () {\n      return this._y;\n    },\n    set: function (v) {\n      if (this._y !== v) {\n        this._y = v;\n        if (this._bound) {\n          this.dispatchEvent(Events.Types.change);\n        }\n      }\n    },\n  },\n};\n\n/**\n * @name Two.Vector\n * @class\n * @extends Two.Events\n * @param {Number} [x=0] - Any number to represent the horizontal `x` component of the vector.\n * @param {Number} [y=0] - Any number to represent the vertical `y` component of the vector.\n * @description A class to store `x` / `y` component vector data. In addition to storing data `Two.Vector` has suped up methods for commonplace mathematical operations.\n */\nexport class Vector extends Events {\n  /**\n   * @name Two.Vector#_x\n   * @private\n   */\n  _x = 0;\n  /**\n   * @name Two.Vector#_y\n   * @private\n   */\n  _y = 0;\n\n  constructor(x = 0, y = 0) {\n    super();\n\n    for (let prop in proto) {\n      Object.defineProperty(this, prop, proto[prop]);\n    }\n\n    /**\n     * @name Two.Vector#x\n     * @property {Number} - The horizontal x-component of the vector.\n     * @type {Number}\n     */\n    this.x = x;\n\n    /**\n     * @name Two.Vector#y\n     * @property {Number} - The vertical y-component of the vector.\n     * @type {Number}\n     */\n    this.y = y;\n  }\n\n  /**\n   * @name Two.Vector.zero\n   * @readonly\n   * @property {Two.Vector} - Handy reference to a vector with component values 0, 0 at all times.\n   */\n  static zero = new Vector();\n\n  /**\n   * @name Two.Vector.left\n   * @readonly\n   * @property {Two.Vector} - Handy reference to a vector with component values -1, 0 at all times.\n   */\n  static left = new Vector(-1, 0);\n\n  /**\n   * @name Two.Vector.right\n   * @readonly\n   * @property {Two.Vector} - Handy reference to a vector with component values 1, 0 at all times.\n   */\n  static right = new Vector(1, 0);\n\n  /**\n   * @name Two.Vector.up\n   * @readonly\n   * @property {Two.Vector} - Handy reference to a vector with component values 0, -1 at all times.\n   */\n  static up = new Vector(0, -1);\n\n  /**\n   * @name Two.Vector.down\n   * @readonly\n   * @property {Two.Vector} - Handy reference to a vector with component values 0, 1 at all times.\n   */\n  static down = new Vector(0, 1);\n\n  /**\n   * @name Two.Vector.add\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Two.Vector}\n   * @description Add two vectors together.\n   */\n  static add(v1, v2) {\n    return new Vector(v1.x + v2.x, v1.y + v2.y);\n  }\n\n  /**\n   * @name Two.Vector.sub\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Two.Vector}\n   * @description Subtract two vectors: `v2` from `v1`.\n   */\n  static sub(v1, v2) {\n    return new Vector(v1.x - v2.x, v1.y - v2.y);\n  }\n\n  /**\n   * @name Two.Vector.subtract\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  static subtract(v1, v2) {\n    return Vector.sub(v1, v2);\n  }\n\n  /**\n   * @name Two.Vector.ratioBetween\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The ratio betwen two points `v1` and `v2`.\n   */\n  static ratioBetween(v1, v2) {\n    return (v1.x * v2.x + v1.y * v2.y) / (v1.length() * v2.length());\n  }\n\n  /**\n   * @name Two.Vector.angleBetween\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The angle between points `v1` and `v2`.\n   */\n  static angleBetween(v1, v2) {\n    if (arguments.length >= 4) {\n      const dx = arguments[0] - arguments[2];\n      const dy = arguments[1] - arguments[3];\n\n      return Math.atan2(dy, dx);\n    }\n\n    const dx = v1.x - v2.x;\n    const dy = v1.y - v2.y;\n\n    return Math.atan2(dy, dx);\n  }\n\n  /**\n   * @name Two.Vector.distanceBetween\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The distance between points `v1` and `v2`. Distance is always positive.\n   */\n  static distanceBetween(v1, v2) {\n    return Math.sqrt(Vector.distanceBetweenSquared(v1, v2));\n  }\n\n  /**\n   * @name Two.Vector.distanceBetweenSquared\n   * @function\n   * @param {Two.Vector} v1 - First {@link Two.Vector}\n   * @param {Two.Vector} v2 - Second {@link Two.Vector}\n   * @returns {Number} The squared distance between points `v1` and `v2`.\n   */\n  static distanceBetweenSquared(v1, v2) {\n    const dx = v1.x - v2.x;\n    const dy = v1.y - v2.y;\n\n    return dx * dx + dy * dy;\n  }\n\n  //\n\n  /**\n   * @name Two.Vector#set\n   * @function\n   * @param {number} x - Value of `x` component\n   * @param {number} y - Value of `y` component\n   */\n  set(x, y) {\n    this.x = x;\n    this.y = y;\n    return this;\n  }\n\n  /**\n   * @name Two.Vector#copy\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to copy\n   * @description Copy the `x` / `y` components of another object {@link Two.Vector}.\n   */\n  copy(v) {\n    this.x = v.x;\n    this.y = v.y;\n    return this;\n  }\n\n  /**\n   * @name Two.Vector#clear\n   * @function\n   * @description Set the `x` / `y` component values of the vector to zero.\n   */\n  clear() {\n    this.x = 0;\n    this.y = 0;\n    return this;\n  }\n\n  /**\n   * @name Two.Vector#clone\n   * @function\n   * @description Create a new vector and copy the existing values onto the newly created instance.\n   * @return {Two.Vector}\n   */\n  clone() {\n    return new Vector(this.x, this.y);\n  }\n\n  /**\n   * @name Two.Vector#add\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to add\n   * @description Add an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#add\n   * @function\n   * @param {Number} n - Number to add\n   * @description Add the **same** number to both `x` / `y` component values of the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#add\n   * @function\n   * @param {Number} x - Number to add to `x` component\n   * @param {Number} y - Number to add to `y` component\n   * @description Add `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  add(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === 'number') {\n        this.x += x;\n        this.y += x;\n      } else if (x && typeof x.x === 'number' && typeof x.y === 'number') {\n        this.x += x.x;\n        this.y += x.y;\n      }\n    } else {\n      this.x += x;\n      this.y += y;\n    }\n    return this;\n  }\n\n  /**\n   * @name Two.Vector#addSelf\n   * @function\n   * @description Alias for {@link Two.Vector.add}.\n   */\n  addSelf(v) {\n    return this.add.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Vector#sub\n   * @function\n   * @param {Two.Vector} v - The amount as a {@link Two.Vector} to subtract\n   * @description Subtract an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#sub\n   * @function\n   * @param {Number} n - Number to subtract\n   * @description Subtract the **same** number to both `x` / `y` component values of the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#sub\n   * @function\n   * @param {Number} x - Number to subtract from `x` component\n   * @param {Number} y - Number to subtract from `y` component\n   * @description Subtract `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  sub(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === 'number') {\n        this.x -= x;\n        this.y -= x;\n      } else if (x && typeof x.x === 'number' && typeof x.y === 'number') {\n        this.x -= x.x;\n        this.y -= x.y;\n      }\n    } else {\n      this.x -= x;\n      this.y -= y;\n    }\n    return this;\n  }\n\n  /**\n   * @name Two.Vector#subtract\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  subtract() {\n    return this.sub.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Vector#subSelf\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  subSelf(v) {\n    return this.sub.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Vector#subtractSelf\n   * @function\n   * @description Alias for {@link Two.Vector.sub}.\n   */\n  subtractSelf(v) {\n    return this.sub.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Vector#multiply\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to multiply\n   * @description Multiply an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#multiply\n   * @function\n   * @param {Number} n - The number to multiply\n   * @description Multiply the **same** number to both x / y component values of the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#multiply\n   * @function\n   * @param {Number} x - The number to multiply to `x` component\n   * @param {Number} y - The number to multiply to `y` component\n   * @description Multiply `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  multiply(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === 'number') {\n        this.x *= x;\n        this.y *= x;\n      } else if (x && typeof x.x === 'number' && typeof x.y === 'number') {\n        this.x *= x.x;\n        this.y *= x.y;\n      }\n    } else {\n      this.x *= x;\n      this.y *= y;\n    }\n    return this;\n  }\n\n  /**\n   * @name Two.Vector#multiplySelf\n   * @function\n   * @description Alias for {@link Two.Vector.multiply}.\n   */\n  multiplySelf(v) {\n    return this.multiply.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Vector#multiplyScalar\n   * @function\n   * @param {Number} s - The scalar to multiply by.\n   * @description Mulitiply the vector by a single number. Shorthand to call {@link Two.Vector#multiply} directly.\n   */\n  multiplyScalar(s) {\n    return this.multiply(s);\n  }\n\n  /**\n   * @name Two.Vector#divide\n   * @function\n   * @param {Two.Vector} v - The {@link Two.Vector} to divide\n   * @description Divide an object with `x` / `y` component values to the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#divide\n   * @function\n   * @param {Number} n - The number to divide\n   * @description Divide the **same** number to both x / y component values of the instance.\n   * @overloaded\n   */\n\n  /**\n   * @name Two.Vector#divide\n   * @function\n   * @param {Number} x - The number to divide on the `x` component\n   * @param {Number} y - The number to divide on the `y` component\n   * @description Divide `x` / `y` values to their respective component value on the instance.\n   * @overloaded\n   */\n  divide(x, y) {\n    if (arguments.length <= 0) {\n      return this;\n    } else if (arguments.length <= 1) {\n      if (typeof x === 'number') {\n        this.x /= x;\n        this.y /= x;\n      } else if (x && typeof x.x === 'number' && typeof x.y === 'number') {\n        this.x /= x.x;\n        this.y /= x.y;\n      }\n    } else {\n      this.x /= x;\n      this.y /= y;\n    }\n    if (isNaN(this.x)) {\n      this.x = 0;\n    }\n    if (isNaN(this.y)) {\n      this.y = 0;\n    }\n    return this;\n  }\n\n  /**\n   * @name Two.Vector#divideSelf\n   * @function\n   * @description Alias for {@link Two.Vector.divide}.\n   */\n  divideSelf(v) {\n    return this.divide.apply(this, arguments);\n  }\n\n  /**\n   * @name Two.Vector#divideScalar\n   * @function\n   * @param {Number} s - The scalar to divide by.\n   * @description Divide the vector by a single number. Shorthand to call {@link Two.Vector#divide} directly.\n   */\n  divideScalar(s) {\n    return this.divide(s);\n  }\n\n  /**\n   * @name Two.Vector#negate\n   * @function\n   * @description Invert each component's sign value.\n   */\n  negate() {\n    return this.multiply(-1);\n  }\n\n  /**\n   * @name Two.Vector#dot\n   * @function\n   * @returns {Number}\n   * @description Get the [dot product](https://en.wikipedia.org/wiki/Dot_product) of the vector.\n   */\n  dot(v) {\n    return this.x * v.x + this.y * v.y;\n  }\n\n  /**\n   * @name Two.Vector#length\n   * @function\n   * @returns {Number}\n   * @description Get the length of a vector.\n   */\n  length() {\n    return Math.sqrt(this.lengthSquared());\n  }\n\n  /**\n   * @name Two.Vector#lengthSquared\n   * @function\n   * @returns {Number}\n   * @description Get the length of the vector to the power of two. Widely used as less expensive than {@link Two.Vector#length} because it isn't square-rooting any numbers.\n   */\n  lengthSquared() {\n    return this.x * this.x + this.y * this.y;\n  }\n\n  /**\n   * @name Two.Vector#normalize\n   * @function\n   * @description Normalize the vector from negative one to one.\n   */\n  normalize() {\n    return this.divideScalar(this.length());\n  }\n\n  /**\n   * @name Two.Vector#distanceTo\n   * @function\n   * @returns {Number}\n   * @description Get the distance between two vectors.\n   */\n  distanceTo(v) {\n    return Math.sqrt(this.distanceToSquared(v));\n  }\n\n  /**\n   * @name Two.Vector#distanceToSquared\n   * @function\n   * @returns {Number}\n   * @description Get the distance between two vectors to the power of two. Widely used as less expensive than {@link Two.Vector#distanceTo} because it isn't square-rooting any numbers.\n   */\n  distanceToSquared(v) {\n    const dx = this.x - v.x;\n    const dy = this.y - v.y;\n    return dx * dx + dy * dy;\n  }\n\n  /**\n   * @name Two.Vector#setLength\n   * @function\n   * @param {Number} l - length to set vector to.\n   * @description Set the length of a vector.\n   */\n  setLength(l) {\n    return this.normalize().multiplyScalar(l);\n  }\n\n  /**\n   * @name Two.Vector#equals\n   * @function\n   * @param {Two.Vector} v - The vector to compare against.\n   * @param {Number} [eps=0.0001] - An options epsilon for precision.\n   * @returns {Boolean}\n   * @description Qualify if one vector roughly equal another. With a margin of error defined by epsilon.\n   */\n  equals(v, eps) {\n    eps = typeof eps === 'undefined' ? 0.0001 : eps;\n    return this.distanceTo(v) < eps;\n  }\n\n  /**\n   * @name Two.Vector#lerp\n   * @function\n   * @param {Two.Vector} v - The destination vector to step towards.\n   * @param {Number} t - The zero to one value of how close the current vector gets to the destination vector.\n   * @description Linear interpolate one vector to another by an amount `t` defined as a zero to one number.\n   * @see [Matt DesLauriers](https://twitter.com/mattdesl/status/1031305279227478016) has a good thread about this.\n   */\n  lerp(v, t) {\n    const x = (v.x - this.x) * t + this.x;\n    const y = (v.y - this.y) * t + this.y;\n    return this.set(x, y);\n  }\n\n  /**\n   * @name Two.Vector#isZero\n   * @function\n   * @param {Number} [eps=0.0001] - Optional precision amount to check against.\n   * @returns {Boolean}\n   * @description Check to see if vector is roughly zero, based on the `epsilon` precision value.\n   */\n  isZero(eps) {\n    eps = typeof eps === 'undefined' ? 0.0001 : eps;\n    return this.length() < eps;\n  }\n\n  /**\n   * @name Two.Vector#toString\n   * @function\n   * @returns {String}\n   * @description Return a comma-separated string of x, y value. Great for storing in a database.\n   */\n  toString() {\n    return this.x + ', ' + this.y;\n  }\n\n  /**\n   * @name Two.Vector#toObject\n   * @function\n   * @returns {Object}\n   * @description Return a JSON compatible plain object that represents the vector.\n   */\n  toObject() {\n    return { x: toFixed(this.x), y: toFixed(this.y) };\n  }\n\n  /**\n   * @name Two.Vector#rotate\n   * @function\n   * @param {Number} radians - The amount to rotate the vector by in radians.\n   * @description Rotate a vector.\n   */\n  rotate(radians) {\n    const x = this.x;\n    const y = this.y;\n    const cos = Math.cos(radians);\n    const sin = Math.sin(radians);\n    this.x = x * cos - y * sin;\n    this.y = x * sin + y * cos;\n    return this;\n  }\n}\n"
  },
  {
    "path": "tests/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Two.js Tests</title>\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdn.jsdelivr.net/npm/qunit@latest/qunit/qunit.css\"\n    />\n  </head>\n  <body>\n    <div id=\"qunit\"></div>\n    <div id=\"qunit-fixture\"></div>\n    <div class=\"scripts\">\n      <script src=\"https://cdn.jsdelivr.net/npm/resemblejs@latest\"></script>\n      <script src=\"https://cdn.jsdelivr.net/npm/qunit@latest\"></script>\n      <script src=\"https://cdn.jsdelivr.net/npm/underscore@latest\"></script>\n\n      <script src=\"../build/two.js\"></script>\n\n      <script src=\"./src/utils.js\"></script>\n      <script src=\"./suite/core.js\"></script>\n      <script src=\"./suite/shapes.js\"></script>\n      <script src=\"./suite/svg.js\"></script>\n      <script src=\"./suite/canvas.js\"></script>\n      <script src=\"./suite/webgl.js\"></script>\n      <script src=\"./suite/svg-interpreter.js\"></script>\n      <script src=\"./suite/bounding-box.js\"></script>\n      <script src=\"./suite/release.js\"></script>\n      <script src=\"./suite/dispose.js\"></script>\n      <script src=\"./suite/hit-test.js\"></script>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/noWebGL.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Two.js Tests sans WebGL</title>\n    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/qunit@latest/qunit/qunit.css\">\n  </head>\n  <body>\n    <div id=\"qunit\"></div>\n    <div id=\"qunit-fixture\"></div>\n    <div class=\"scripts\">\n\n      <script src=\"https://cdn.jsdelivr.net/npm/resemblejs@latest\"></script>\n      <script src=\"https://cdn.jsdelivr.net/npm/qunit@latest\"></script>\n      <script src=\"https://cdn.jsdelivr.net/npm/underscore@latest\"></script>\n\n      <script src=\"../build/two.js\"></script>\n\n      <script src=\"./src/utils.js\"></script>\n      <script src=\"./suite/core.js\"></script>\n      <script src=\"./suite/svg.js\"></script>\n      <script src=\"./suite/canvas.js\"></script>\n      <script src=\"./suite/svg-interpreter.js\"></script>\n      <script src=\"./suite/bounding-box.js\"></script>\n      <script src=\"./suite/release.js\"></script>\n      <script src=\"./suite/dispose.js\"></script>\n\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/src/utils.js",
    "content": "/**\n * Convenience properties and methods for QUnit testing within two.js\n */\n\n(function () {\n  var root = this;\n  var QU = root.QUnit || {};\n  var TEMP = document.createElement('div');\n  var Tolerance = 0.001;\n\n  var Utils = (QU.Utils = {\n    digits: function (v, d) {\n      var r = '';\n      var s = v + '';\n      var diff = Math.max(d - s.length, 0);\n      var i = 0;\n\n      while (i < diff) {\n        r += '0';\n        i++;\n      }\n\n      return r + v;\n    },\n\n    getSelector: function (test) {\n      return '#qunit-test-output-' + test.testId + ' ol';\n    },\n\n    /**\n     * Add a DOM Element to your current unit test.\n     */\n    addElemToTest: function (test, elem) {\n      // // Skip for headless\n      // if (window.URL) return;\n\n      var domElement = document.createElement('li');\n\n      if (Array.isArray(elem)) {\n        _.each(elem, function (el) {\n          domElement.appendChild(el);\n        });\n      } else {\n        domElement.appendChild(elem);\n      }\n\n      _.delay(function () {\n        var selector = Utils.getSelector(test);\n        document.querySelector(selector).appendChild(domElement);\n      }, 100);\n    },\n\n    /**\n     * Add an instance of Two.js to your current unit test.\n     */\n    addInstanceToTest: function (test, two) {\n      var elem;\n\n      if (Array.isArray(two)) {\n        elem = two.map(function (t) {\n          var el = t.renderer.domElement;\n          switch (el.tagName.toLowerCase()) {\n            case 'svg':\n              break;\n            default:\n              el.style.width = 200 + 'px';\n              el.style.height = (200 * el.height) / el.width + 'px';\n          }\n          el.style.border = '1px solid #ccc';\n          return el;\n        });\n      } else {\n        elem = two.renderer.domElement;\n        switch (elem.tagName.toLowerCase()) {\n          case 'svg':\n            break;\n          default:\n            elem.style.width = 200 + 'px';\n            elem.style.height = (200 * elem.height) / elem.width + 'px';\n        }\n        elem.style.border = '1px solid #ccc';\n      }\n\n      Utils.addElemToTest(test, elem);\n    },\n\n    get: function (path, callback) {\n      var xhr = new XMLHttpRequest();\n      xhr.open('GET', path, true);\n\n      xhr.onreadystatechange = function (e) {\n        if (xhr.readyState !== 4 || xhr.status !== 200) {\n          return;\n        }\n\n        callback(xhr.response);\n      };\n\n      xhr.send();\n    },\n\n    /**\n     * Ajax get request to get blob.\n     */\n    getImageBlob: function (path, callback) {\n      var xhr = new XMLHttpRequest();\n      xhr.open('GET', path, true);\n\n      if (window.URL) {\n        xhr.responseType = 'blob';\n      } else {\n        xhr.responseType = 'arraybuffer';\n      }\n\n      xhr.onreadystatechange = function (e) {\n        if (xhr.readyState !== 4 || xhr.status !== 200) {\n          return;\n        }\n\n        if (window.URL) {\n          callback(this.response);\n        } else {\n          var blob;\n          var mimeString = 'image/png';\n\n          // Some older Webkits don't support responseType blob,\n          // So create a blob from arraybuffer\n\n          try {\n            blob = new Blob([this.response], { type: mimeString });\n          } catch (e) {\n            // The BlobBuilder API has been deprecated in favour of Blob, but older\n            // browsers don't know about the Blob constructor\n            // IE10 also supports BlobBuilder, but since the `Blob` constructor\n            // also works, there's no need to add `MSBlobBuilder`.\n            var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder;\n            var bb = new BlobBuilder();\n            bb.append(this.response);\n            blob = bb.getBlob(mimeString);\n          }\n\n          callback(blob);\n        }\n      };\n\n      xhr.send();\n    },\n\n    /**\n     * Compare a specific instance of two to an image in the context of a\n     * specific test.\n     */\n    compare: function (path, renderer, message, callback) {\n      var assert = this;\n\n      QUnit.Utils.getImageBlob(path, function (reference) {\n        var data = renderer.domElement.toDataURL('image/png');\n        resemble(reference)\n          .compareTo(data)\n          .onComplete(function (data) {\n            var pct = parseFloat(data.misMatchPercentage);\n\n            // Can differ a bit due to antialiasing etc.\n            assert.ok(pct <= 3, message);\n            if (assert.done) {\n              assert.done();\n            }\n\n            var img = document.createElement('img');\n            img.src = path;\n            img.title = 'Reference Image';\n            img.width = 200;\n            img.style.border = '1px solid #ccc';\n\n            var domElement = document.createElement('li');\n            renderer.domElement.title = 'Computed Image';\n            renderer.domElement.style.border = '1px solid #000';\n            renderer.domElement.style.width = 200 + 'px';\n            renderer.domElement.style.height =\n              200 * (renderer.height / renderer.width) + 'px';\n            renderer.domElement.style.marginLeft = 10 + 'px';\n\n            domElement.appendChild(img);\n            domElement.appendChild(renderer.domElement);\n\n            _.delay(function () {\n              var selector = Utils.getSelector(assert.test);\n              document.querySelector(selector).appendChild(domElement);\n            }, 100);\n\n            if (typeof callback === 'function') {\n              callback();\n            }\n          });\n      });\n    },\n\n    textToDOM: function (str) {\n      TEMP.innerHTML = str;\n      return Array.prototype.map.call(TEMP.children, function (child) {\n        return child;\n      });\n    },\n\n    /**\n     * Deep equality between an answer, a, and an object in question, q.\n     */\n    shapeEquals: function (a, q) {\n      for (var i in a) {\n        var check;\n\n        if (Array.isArray(a[i])) {\n          check = Utils.shapeEquals(a[i], Array.prototype.slice.call(q[i]));\n        } else if (_.isObject(a[i])) {\n          check = Utils.shapeEquals(a[i], q[i]);\n        } else if (typeof a[i] === 'number') {\n          check = Math.abs(a[i] - q[i]) <= Tolerance; // Fuzzy checking\n        } else {\n          check = q[i] === a[i];\n        }\n\n        if (!check) {\n          return false;\n        }\n      }\n\n      return true;\n    },\n  });\n})();\n"
  },
  {
    "path": "tests/suite/bounding-box.js",
    "content": "/**\n * Tests Two.js Utilities related to getBoundingClientRect methods:\n * + polygon._matrix transformations\n * + Two.getComputedMatrix\n */\n\n(function () {\n  QUnit.module('getBoundingClientRect');\n\n  QUnit.test('Two.Path.getBoundingClientRect', function (assert) {\n    assert.expect(5);\n    // assert.done = assert.async(4);\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      var answer = {\n        top: 134.671853,\n        left: 134.671853,\n        right: 265.328146,\n        bottom: 265.328146,\n        width: 130.656293,\n        height: 130.656293,\n      };\n      var shape = two.makeRectangle(200, 200, 100, 100);\n      shape.rotation = Math.PI / 8;\n      shape.noStroke().fill = 'rgb(60, 209, 201)';\n\n      var box = two.makeRectangle(\n        answer.left + answer.width / 2,\n        answer.top + answer.height / 2,\n        answer.width,\n        answer.height\n      );\n      box.noFill();\n\n      two.update();\n\n      var rect = shape.getBoundingClientRect();\n      for (let prop in rect) {\n        var value = rect[prop];\n        rect[prop] = Two.Utils.toFixed(value);\n      }\n      var a1 = JSON.stringify(answer);\n      var a2 = JSON.stringify(rect);\n\n      assert.equal(\n        a1,\n        a2,\n        true,\n        'Two.Path.getBoundingClientRect properly calculates rotated shapes.'\n      );\n\n      QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement]);\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      var answer = {\n        top: 129.671853,\n        left: 129.671853,\n        right: 270.328146,\n        bottom: 270.328146,\n        width: 140.656293,\n        height: 140.656293,\n      };\n      var shape = two.makeRectangle(200, 200, 50, 50);\n      shape.rotation = Math.PI / 8;\n      shape.fill = 'rgb(60, 209, 201)';\n      shape.stroke = '#ccc';\n      shape.linewidth = 5;\n      shape.scale = 2;\n\n      var box = two.makeRectangle(\n        answer.left + answer.width / 2,\n        answer.top + answer.height / 2,\n        answer.width,\n        answer.height\n      );\n      box.noFill();\n\n      two.update();\n\n      var rect = shape.getBoundingClientRect();\n      for (let prop in rect) {\n        var value = rect[prop];\n        rect[prop] = Two.Utils.toFixed(value);\n      }\n      var a1 = JSON.stringify(answer);\n      var a2 = JSON.stringify(rect);\n\n      assert.equal(\n        a1,\n        a2,\n        true,\n        'Two.Path.getBoundingClientRect properly calculates scaled shapes.'\n      );\n\n      QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement]);\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      var answer = {\n        top: 150,\n        left: 150,\n        right: 250,\n        bottom: 250,\n        width: 100,\n        height: 100,\n      };\n      var shape = two.makeCircle(200, 200, 50);\n      shape.fill = 'rgb(60, 209, 201)';\n      shape.linewidth = 0;\n\n      var box = two.makeRectangle(\n        answer.left + answer.width / 2,\n        answer.top + answer.height / 2,\n        answer.width,\n        answer.height\n      );\n      box.noFill();\n\n      two.update();\n\n      var rect = shape.getBoundingClientRect();\n      for (let prop in rect) {\n        var value = rect[prop];\n        rect[prop] = Two.Utils.toFixed(value);\n      }\n      var a1 = JSON.stringify(answer);\n      var a2 = JSON.stringify(rect);\n\n      assert.equal(\n        a1,\n        a2,\n        true,\n        'Two.Path.getBoundingClientRect properly calculates circles.'\n      );\n\n      QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement]);\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      var answer = {\n        top: 150,\n        left: 150,\n        right: 249.999999,\n        bottom: 249.999999,\n        width: 99.999998,\n        height: 99.999998,\n      };\n      var shape = two.makeCircle(200, 200, 50);\n      shape.fill = 'rgb(60, 209, 201)';\n      shape.linewidth = 0;\n      shape.rotation = Math.PI / 4;\n\n      var box = two.makeRectangle(\n        answer.left + answer.width / 2,\n        answer.top + answer.height / 2,\n        answer.width,\n        answer.height\n      );\n      box.noFill();\n\n      two.update();\n\n      var rect = shape.getBoundingClientRect();\n      for (let prop in rect) {\n        var value = rect[prop];\n        rect[prop] = Two.Utils.toFixed(value);\n      }\n      var a1 = JSON.stringify(answer);\n      var a2 = JSON.stringify(rect);\n\n      assert.equal(\n        a1,\n        a2,\n        true,\n        'Two.Path.getBoundingClientRect properly calculates rotated circles (projected).'\n      );\n\n      QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement]);\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      var answer = {\n        top: 182.752229,\n        left: 167.233141,\n        right: 232.766858,\n        bottom: 217.24777,\n        width: 65.533717,\n        height: 34.495541,\n      };\n      var shape = two.makeText('Hello World', 200, 200);\n      shape.rotation = Math.PI / 8;\n      shape.noStroke().fill = 'rgb(60, 209, 201)';\n\n      var box = two.makeRectangle(\n        answer.left + answer.width / 2,\n        answer.top + answer.height / 2,\n        answer.width,\n        answer.height\n      );\n      box.noFill();\n\n      two.update();\n\n      var rect = shape.getBoundingClientRect();\n      for (let prop in rect) {\n        var value = rect[prop];\n        rect[prop] = Two.Utils.toFixed(value);\n      }\n      var a1 = JSON.stringify(answer);\n      var a2 = JSON.stringify(rect);\n\n      assert.equal(\n        a1,\n        a2,\n        true,\n        'Two.Text.getBoundingClientRect properly calculates rotated shapes.'\n      );\n\n      QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement]);\n    })();\n  });\n\n  QUnit.test('Two.getComputedMatrix', function (assert) {\n    assert.expect(2);\n    // assert.done = assert.async(1);\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      two.renderer.domElement.style.border = '1px solid blue';\n\n      var parentGroup = two.makeGroup();\n      parentGroup.scale = 2;\n      parentGroup.translation.set(two.width / 2, two.height / 2);\n      parentGroup.rotation = Math.PI / 8;\n\n      var group = two.makeGroup();\n      group.scale = 2;\n      group.translation.set(50, 0);\n      group.rotation = Math.PI / 8;\n\n      var shape = two.makeRectangle(0, 0, 50, 50);\n      shape.fill = 'black';\n      shape.stroke = 'yellow';\n      shape.linewidth = 2;\n\n      parentGroup.add(group);\n      group.add(shape);\n\n      var answer = {\n        top: 95.846993,\n        left: 149.966591,\n        right: 434.809287,\n        bottom: 380.689688,\n        width: 284.842695,\n        height: 284.842695,\n      };\n\n      var bBox = group.getBoundingClientRect();\n      var rect = two.makeRectangle(\n        bBox.left + bBox.width / 2,\n        bBox.top + bBox.height / 2,\n        bBox.width,\n        bBox.height\n      );\n      rect.noFill().stroke = 'orangered';\n\n      var bBoxClose = shape.getBoundingClientRect();\n      var rectClose = two.makeRectangle(\n        bBoxClose.left + bBoxClose.width / 2,\n        bBoxClose.top + bBoxClose.height / 2,\n        bBoxClose.width,\n        bBoxClose.height\n      );\n      rectClose.noFill().stroke = 'green';\n\n      two.update();\n\n      for (var prop in bBox) {\n        bBox[prop] = Two.Utils.toFixed(bBox[prop]);\n        bBoxClose[prop] = Two.Utils.toFixed(bBoxClose[prop]);\n      }\n\n      var a1 = JSON.stringify(answer);\n      var a2 = JSON.stringify(bBox);\n      var a3 = JSON.stringify(bBoxClose);\n\n      assert.equal(\n        a1,\n        a2,\n        true,\n        'Two.Path.getBoundingClientRect properly calculates parent’s Two.Group.getBoundingClientRect.'\n      );\n      assert.equal(\n        a1,\n        a3,\n        true,\n        'Two.Path.getBoundingClientRect properly calculates child’s Two.Group.getBoundingClientRect.'\n      );\n\n      QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement]);\n    })();\n  });\n})();\n"
  },
  {
    "path": "tests/suite/canvas.js",
    "content": "/**\n * Tests Two.js Canvas Rendering Functionality:\n */\n\n(function () {\n  QUnit.module('CanvasRenderer');\n\n  var getRatio = function (v) {\n    return Math.round(window.devicePixelRatio);\n  };\n  var deviceRatio = getRatio(document.createElement('canvas').getContext('2d'));\n  var suffix = '@' + deviceRatio + 'x.png';\n\n  QUnit.test('Two.makeLine', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    two.makeLine(0, 0, two.width, two.height);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/line' + suffix,\n      two.renderer,\n      'Two.makeLine renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeRectangle', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    two.makeRectangle(two.width / 2, two.height / 2, 100, 100);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/rectangle' + suffix,\n      two.renderer,\n      'Two.makeRectangle renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeEllipse', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    two.makeEllipse(two.width / 2, two.height / 2, 100, 100);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/ellipse' + suffix,\n      two.renderer,\n      'Two.makeEllipse renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeCircle', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    two.makeCircle(two.width / 2, two.height / 2, 50);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/circle' + suffix,\n      two.renderer,\n      'Two.makeCircle renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makePoints', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: 1,\n    });\n\n    var points = two.makePoints(200, 200, 220, 200, 180, 200);\n    points.size = 10;\n    points.noStroke();\n    points.fill = '#00AEFF';\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/points' + suffix,\n      two.renderer,\n      'Two.makePoints renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makePath', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var amount = 20;\n    var points = [];\n    for (var i = 0; i < amount; i++) {\n      var pct = i / amount;\n      var x = pct * 300 + 50;\n      var y = i % 2 ? 25 : 75;\n      points.push(new Two.Anchor(x, y));\n    }\n    two.makePath(points, true);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/polygon' + suffix,\n      two.renderer,\n      'Two.makePath renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeCurve', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var amount = 20;\n    var points = [];\n    for (var i = 0; i < amount; i++) {\n      var pct = i / amount;\n      var x = pct * 300 + 50;\n      var y = i % 2 ? 25 : 75;\n      points.push(new Two.Anchor(x, y));\n    }\n    two.makeCurve(points, true);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/curve' + suffix,\n      two.renderer,\n      'Two.makeCurve renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeLinearGradient', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var gradient = two.makeLinearGradient(\n      0,\n      -two.height / 2,\n      0,\n      two.height / 2,\n      new Two.Gradient.Stop(0, 'rgb(255, 100, 100)'),\n      new Two.Gradient.Stop(1, 'rgb(100, 100, 255)')\n    );\n\n    var rect = two.makeRectangle(\n      two.width / 2,\n      two.height / 2,\n      two.width / 4,\n      two.height / 4\n    );\n    rect.fill = gradient;\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/linear-gradient' + suffix,\n      two.renderer,\n      'Two.makeLinearGradient renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeRadialGradient', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var gradient = two.makeRadialGradient(\n      0.5,\n      0.5,\n      2,\n      new Two.Gradient.Stop(0, 'rgb(255, 100, 100)'),\n      new Two.Gradient.Stop(1, 'rgb(100, 100, 255)')\n    );\n\n    var rect = two.makeRectangle(\n      two.width / 2,\n      two.height / 2,\n      two.width / 4,\n      two.height / 4\n    );\n    rect.fill = gradient;\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/radial-gradient' + suffix,\n      two.renderer,\n      'Two.makeLinearGradient renders properly.'\n    );\n  });\n\n  QUnit.test('two.makeImage (Simple)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(path, two.width / 2, two.height / 2, 200, 100);\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fill' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly with default fill mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: fit)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'fit'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fit' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in fit mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: fill)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'fill'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fill' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in fill mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: crop)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'crop'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-crop' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in crop mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: tile)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'tile'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-tile' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in tile mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: stretch)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'stretch'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-stretch' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in stretch mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode switching)', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(2);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'fit'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fit' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in initial fit mode.',\n        function () {\n          image.mode = 'fill';\n          two.update();\n\n          QUnit.Utils.compare.call(\n            assert,\n            './images/canvas/image-fill' + suffix,\n            two.renderer,\n            'Two.Image changes mode properly from fit to fill.'\n          );\n        }\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeSprite (Simple)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/sequence/00000.png';\n    var sprite = two.makeSprite(path, two.width / 2, two.height / 2);\n    var texture = sprite.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/sprite-simple' + suffix,\n        two.renderer,\n        'Two.makeSprite renders properly.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n  });\n\n  QUnit.test('two.makeImageSequence', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(2);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var paths = [];\n    for (var i = 0; i < 30; i++) {\n      paths.push('/tests/images/sequence/' + QUnit.Utils.digits(i, 5) + '.png');\n    }\n    var sequence = two.makeImageSequence(\n      paths,\n      two.width / 2,\n      two.height / 2,\n      2\n    );\n    sequence.index = 3;\n    var texture = sequence.textures[sequence.index];\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-sequence-1' + suffix,\n        two.renderer,\n        'Two.ImageSequence applied the correct texture properly.',\n        function () {\n          sequence.index = 7;\n          texture = sequence.textures[sequence.index];\n          texture._flagImage = true;\n\n          texture.bind(Two.Events.Types.load, function () {\n            texture.unbind(Two.Events.Types.load);\n\n            two.update();\n\n            QUnit.Utils.compare.call(\n              assert,\n              './images/canvas/image-sequence-2' + suffix,\n              two.renderer,\n              'Two.ImageSequence can change index properly.'\n            );\n          });\n\n          texture._update();\n        }\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    two.renderer.domElement.style.cursor = 'pointer';\n    two.renderer.domElement.addEventListener(\n      'click',\n      function () {\n        if (two.playing) {\n          two.pause();\n        } else {\n          sequence.loop = true;\n          sequence.play();\n          two.play();\n        }\n      },\n      false\n    );\n  });\n\n  QUnit.test('two.makeSprite', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(2);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var sprite = two.makeSprite(\n      path,\n      two.width / 2,\n      two.height / 2,\n      4,\n      4,\n      2,\n      false\n    );\n    var texture = sprite.texture;\n    sprite.index = 3;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-sequence-1' + suffix,\n        two.renderer,\n        'Two.makeSprite renders properly.',\n        function () {\n          sprite.index = 7;\n          two.update();\n\n          QUnit.Utils.compare.call(\n            assert,\n            './images/canvas/image-sequence-2' + suffix,\n            two.renderer,\n            'Two.Sprite changed index properly.'\n          );\n        }\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    two.renderer.domElement.style.cursor = 'pointer';\n    two.renderer.domElement.addEventListener(\n      'click',\n      function () {\n        if (two.playing) {\n          two.pause();\n        } else {\n          sprite.loop = true;\n          sprite.play();\n          two.play();\n        }\n      },\n      false\n    );\n  });\n\n  QUnit.test('Two.makeText', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var text = two.makeText('Hello World', two.width / 2, two.height / 2);\n    text.fill = '#00aeff';\n    text.noStroke();\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/text' + suffix,\n      two.renderer,\n      'Two.makeText renders properly.'\n    );\n  });\n\n  QUnit.test('Styles', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var shape = two.makeRectangle(two.width / 2, two.height / 2, 50, 50);\n\n    shape.rotation = Math.PI / 2;\n    shape.scale = 0.5;\n\n    shape.fill = 'lightcoral';\n    shape.stroke = '#333';\n    shape.linewidth = 10;\n    shape.opacity = 0.5;\n    shape.join = 'miter';\n    shape.cap = 'butt';\n    shape.miter = 10;\n\n    shape.closed = false;\n    shape.curved = true;\n\n    shape.visible = false;\n    shape.visible = true;\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/styles' + suffix,\n      two.renderer,\n      'Styles render properly.'\n    );\n  });\n})();\n"
  },
  {
    "path": "tests/suite/core.js",
    "content": "/**\n * Tests Two.js Core Classes Functionality:\n * + Two.Utils ( Underscore Methods )\n * + Two.Events\n * + Two.Vector\n * + Two.Matrix\n * + Two.Collection\n * + Two.Shape\n * + Two.Registry\n * + Two.Texture\n * + Two\n */\n\nQUnit.module('Core');\n\nQUnit.test('Two.Events', function (assert) {\n  assert.expect(1);\n\n  var item = new Two.Events();\n\n  item.bind('change', function (message) {\n    assert.equal(message, 'hello', 'Bound Two.Events successfully.');\n  });\n  item.trigger('change', 'hello');\n  item.unbind('change');\n  item.trigger('change');\n});\n\nQUnit.test('Two.Vector', function (assert) {\n  assert.expect(48);\n\n  var vector = new Two.Anchor();\n\n  assert.equal(vector.x, 0, 'x property defaults to 0.');\n  assert.equal(vector.y, 0, 'y property defaults to 0.');\n\n  vector.x = 5;\n  vector.y = 5;\n\n  assert.equal(vector.x, 5, 'x property can be set properly.');\n  assert.equal(vector.y, 5, 'y property can be set properly.');\n\n  vector.set(10, 10);\n  assert.equal(vector.x, 10, 'Two.Vector.set applies x value properly.');\n  assert.equal(vector.y, 10, 'Two.Vector.set applies y value properly.');\n\n  vector.copy({ x: 20, y: 20 });\n  assert.equal(vector.x, 20, 'Two.Vector.copy applies x value properly.');\n  assert.equal(vector.y, 20, 'Two.Vector.copy applies y value properly.');\n\n  vector.clear();\n  assert.equal(vector.x, 0, 'Two.Vector.clear applies x value properly.');\n  assert.equal(vector.y, 0, 'Two.Vector.clear applies y value properly.');\n\n  vector.set(10, 10);\n  var clone = vector.clone();\n  assert.ok(\n    clone instanceof Two.Vector,\n    'Two.Vector.clone returns an instance of Two.Vector.'\n  );\n  assert.equal(clone.x, vector.x, 'Two.Vector.clone applies x value properly.');\n  assert.equal(clone.y, vector.y, 'Two.Vector.clone applies y value properly.');\n\n  vector.add({ x: 5, y: 5 });\n  assert.equal(vector.x, 15, 'Two.Vector.add applies x value properly.');\n  assert.equal(vector.y, 15, 'Two.Vector.add applies y value properly.');\n\n  vector.addSelf({ x: 5, y: 5 });\n  assert.equal(vector.x, 20, 'Two.Vector.addSelf applies x value properly.');\n  assert.equal(vector.y, 20, 'Two.Vector.addSelf applies y value properly.');\n\n  vector.sub({ x: 15, y: 15 });\n  assert.equal(vector.x, 5, 'Two.Vector.sub applies x value properly.');\n  assert.equal(vector.y, 5, 'Two.Vector.sub applies y value properly.');\n\n  vector.subSelf({ x: 5, y: 5 });\n  assert.equal(vector.x, 0, 'Two.Vector.subSelf applies x value properly.');\n  assert.equal(vector.y, 0, 'Two.Vector.subSelf applies y value properly.');\n\n  vector.set(2.5, 2.5);\n  vector.multiplySelf({ x: 2, y: 2 });\n  assert.equal(\n    vector.x,\n    5,\n    'Two.Vector.multiplySelf applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5,\n    'Two.Vector.multiplySelf applies y value properly.'\n  );\n\n  vector.multiplyScalar(2);\n  assert.equal(\n    vector.x,\n    10,\n    'Two.Vector.multiplyScalar applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    10,\n    'Two.Vector.multiplyScalar applies y value properly.'\n  );\n\n  vector.divideScalar(2);\n  assert.equal(\n    vector.x,\n    5,\n    'Two.Vector.divideScalar applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5,\n    'Two.Vector.divideScalar applies y value properly.'\n  );\n\n  vector.divideScalar();\n  assert.equal(\n    vector.x,\n    5,\n    'Two.Vector.divideScalar applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5,\n    'Two.Vector.divideScalar applies y value properly.'\n  );\n\n  vector.set(1, -1);\n  vector.negate();\n  assert.equal(vector.x, -1, 'Two.Vector.negate applies x value properly.');\n  assert.equal(vector.y, 1, 'Two.Vector.negate applies y value properly.');\n\n  assert.equal(\n    vector.dot({ x: 5, y: 5 }),\n    0,\n    'Two.Vector.dot returns correct result.'\n  );\n\n  vector.set(5, 5);\n  assert.equal(\n    vector.lengthSquared(),\n    50,\n    'Two.Vector.lengthSquared returns correct result.'\n  );\n  assert.equal(\n    vector.length(),\n    Math.sqrt(50),\n    'Two.Vector.length returns correct result.'\n  );\n\n  vector.normalize();\n  assert.equal(\n    vector.x,\n    5 / Math.sqrt(50),\n    'Two.Vector.normalize applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5 / Math.sqrt(50),\n    'Two.Vector.normalize applies y value properly.'\n  );\n\n  vector.set(0, 0);\n  clone.set(5, 5);\n  assert.equal(\n    vector.distanceToSquared(clone),\n    50,\n    'Two.Vector.distanceToSquared returns correct result.'\n  );\n  assert.equal(\n    vector.distanceTo(clone),\n    Math.sqrt(50),\n    'Two.Vector.distanceTo, returns correct result.'\n  );\n\n  vector.set(1, 1).setLength(5);\n  assert.equal(\n    vector.x,\n    5 / Math.sqrt(2),\n    'Two.Vector.setLength applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5 / Math.sqrt(2),\n    'Two.Vector.setLength applies x value properly.'\n  );\n\n  vector.set(1, 1);\n  assert.equal(\n    vector.equals({ x: 1, y: 1 }),\n    true,\n    'Two.Vector.equals returns correct result.'\n  );\n\n  vector.lerp({ x: 5, y: 5 }, 0.5);\n  assert.equal(\n    vector.x,\n    (5 - 1) * 0.5 + 1,\n    'Two.Vector.lerp applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    (5 - 1) * 0.5 + 1,\n    'Two.Vector.lerp applies y value properly.'\n  );\n\n  vector.clear();\n  assert.equal(\n    vector.isZero(),\n    true,\n    'Two.Vector.isZero returns correct result.'\n  );\n\n  vector.set(9, 3);\n  vector.rotate(Math.PI / 2);\n  assert.equal(\n    vector.equals({ x: -2.9999999999999996, y: 9 }),\n    true,\n    'Two.Vector.rotate applies x, y properly.'\n  );\n  vector.rotate(Math.PI / 2);\n  assert.equal(\n    vector.equals({ x: -9, y: -2.999999999999999 }),\n    true,\n    'Two.Vector.rotate applies x, y properly.'\n  );\n  vector.rotate(Math.PI / 2);\n  assert.equal(\n    vector.equals({ x: 2.9999999999999987, y: -9 }),\n    true,\n    'Two.Vector.rotate applies x, y properly.'\n  );\n  vector.rotate(Math.PI / 2);\n  assert.equal(\n    vector.equals({ x: 9, y: 2.9999999999999982 }),\n    true,\n    'Two.Vector.rotate applies x, y properly.'\n  );\n});\n\nQUnit.test('Bound Two.Vector', function (assert) {\n  assert.expect(45);\n\n  var vector = new Two.Anchor();\n  vector.bind(Two.Events.Types.change, function () {});\n\n  assert.equal(vector._bound, true, 'Vector is bound.');\n  assert.equal(vector.x, 0, 'x property defaults to 0.');\n  assert.equal(vector.y, 0, 'y property defaults to 0.');\n\n  vector.x = 5;\n  vector.y = 5;\n\n  assert.equal(vector.x, 5, 'x property can be set properly.');\n  assert.equal(vector.y, 5, 'y property can be set properly.');\n\n  vector.set(10, 10);\n  assert.equal(vector.x, 10, 'Two.Vector.set applies x value properly.');\n  assert.equal(vector.y, 10, 'Two.Vector.set applies y value properly.');\n\n  vector.copy({ x: 20, y: 20 });\n  assert.equal(vector.x, 20, 'Two.Vector.copy applies x value properly.');\n  assert.equal(vector.y, 20, 'Two.Vector.copy applies y value properly.');\n\n  vector.clear();\n  assert.equal(vector.x, 0, 'Two.Vector.clear applies x value properly.');\n  assert.equal(vector.y, 0, 'Two.Vector.clear applies y value properly.');\n\n  vector.set(10, 10);\n  var clone = vector.clone();\n  assert.ok(\n    clone instanceof Two.Vector,\n    'Two.Vector.clone returns an instance of Two.Vector.'\n  );\n  assert.equal(clone.x, vector.x, 'Two.Vector.clone applies x value properly.');\n  assert.equal(clone.y, vector.y, 'Two.Vector.clone applies y value properly.');\n\n  vector.add({ x: 5, y: 5 });\n  assert.equal(vector.x, 15, 'Two.Vector.add applies x value properly.');\n  assert.equal(vector.y, 15, 'Two.Vector.add applies y value properly.');\n\n  vector.addSelf({ x: 5, y: 5 });\n  assert.equal(vector.x, 20, 'Two.Vector.addSelf applies x value properly.');\n  assert.equal(vector.y, 20, 'Two.Vector.addSelf applies y value properly.');\n\n  vector.sub({ x: 15, y: 15 });\n  assert.equal(vector.x, 5, 'Two.Vector.sub applies x value properly.');\n  assert.equal(vector.y, 5, 'Two.Vector.sub applies y value properly.');\n\n  vector.subSelf({ x: 5, y: 5 });\n  assert.equal(vector.x, 0, 'Two.Vector.subSelf applies x value properly.');\n  assert.equal(vector.y, 0, 'Two.Vector.subSelf applies y value properly.');\n\n  vector.set(2.5, 2.5);\n  vector.multiplySelf({ x: 2, y: 2 });\n  assert.equal(\n    vector.x,\n    5,\n    'Two.Vector.multiplySelf applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5,\n    'Two.Vector.multiplySelf applies y value properly.'\n  );\n\n  vector.multiplyScalar(2);\n  assert.equal(\n    vector.x,\n    10,\n    'Two.Vector.multiplyScalar applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    10,\n    'Two.Vector.multiplyScalar applies y value properly.'\n  );\n\n  vector.divideScalar(2);\n  assert.equal(\n    vector.x,\n    5,\n    'Two.Vector.divideScalar applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5,\n    'Two.Vector.divideScalar applies y value properly.'\n  );\n\n  vector.divideScalar();\n  assert.equal(\n    vector.x,\n    5,\n    'Two.Vector.divideScalar applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5,\n    'Two.Vector.divideScalar applies y value properly.'\n  );\n\n  vector.set(1, -1);\n  vector.negate();\n  assert.equal(vector.x, -1, 'Two.Vector.negate applies x value properly.');\n  assert.equal(vector.y, 1, 'Two.Vector.negate applies y value properly.');\n\n  assert.equal(\n    vector.dot({ x: 5, y: 5 }),\n    0,\n    'Two.Vector.dot returns correct result.'\n  );\n\n  vector.set(5, 5);\n  assert.equal(\n    vector.lengthSquared(),\n    50,\n    'Two.Vector.lengthSquared returns correct result.'\n  );\n  assert.equal(\n    vector.length(),\n    Math.sqrt(50),\n    'Two.Vector.length returns correct result.'\n  );\n\n  vector.normalize();\n  assert.equal(\n    vector.x,\n    5 / Math.sqrt(50),\n    'Two.Vector.normalize applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5 / Math.sqrt(50),\n    'Two.Vector.normalize applies y value properly.'\n  );\n\n  vector.set(0, 0);\n  clone.set(5, 5);\n  assert.equal(\n    vector.distanceToSquared(clone),\n    50,\n    'Two.Vector.distanceToSquared returns correct result.'\n  );\n  assert.equal(\n    vector.distanceTo(clone),\n    Math.sqrt(50),\n    'Two.Vector.distanceTo, returns correct result.'\n  );\n\n  vector.set(1, 1).setLength(5);\n  assert.equal(\n    vector.x,\n    5 / Math.sqrt(2),\n    'Two.Vector.setLength applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    5 / Math.sqrt(2),\n    'Two.Vector.setLength applies x value properly.'\n  );\n\n  vector.set(1, 1);\n  assert.equal(\n    vector.equals({ x: 1, y: 1 }),\n    true,\n    'Two.Vector.equals returns correct result.'\n  );\n\n  vector.lerp({ x: 5, y: 5 }, 0.5);\n  assert.equal(\n    vector.x,\n    (5 - 1) * 0.5 + 1,\n    'Two.Vector.lerp applies x value properly.'\n  );\n  assert.equal(\n    vector.y,\n    (5 - 1) * 0.5 + 1,\n    'Two.Vector.lerp applies y value properly.'\n  );\n\n  vector.clear();\n  assert.equal(\n    vector.isZero(),\n    true,\n    'Two.Vector.isZero returns correct result.'\n  );\n});\n\nQUnit.test('Two.Matrix', function (assert) {\n  assert.expect(12);\n\n  var matrix = new Two.Matrix();\n  var check = true;\n\n  matrix.set(0, 0, 0, 0, 0, 0, 0, 0, 0);\n\n  for (var i = 0; i < matrix.elements.length; i++) {\n    var el = matrix.elements[i];\n    if (el !== 0) {\n      check = false;\n      break;\n    }\n  }\n\n  assert.equal(check, true, 'Two.Matrix.set applies elements properly.');\n\n  matrix.identity();\n  var identity = [1, 0, 0, 0, 1, 0, 0, 0, 1];\n\n  check = true;\n\n  for (var i = 0; i < matrix.elements.length; i++) {\n    var a = matrix.elements[i];\n    var b = identity[i];\n    if (a !== b) {\n      check = false;\n      break;\n    }\n  }\n\n  assert.equal(check, true, 'Two.Matrix.identity applies elements properly.');\n\n  matrix.set(1, 2, 3, 4, 5, 6, 7, 8, 9);\n  matrix.multiply(9, 8, 7, 6, 5, 4, 3, 2, 1);\n\n  var result = [30, 24, 18, 84, 69, 54, 138, 114, 90];\n\n  check = true;\n\n  for (var i = 0; i < matrix.elements.length; i++) {\n    var a = matrix.elements[i];\n    var b = result[i];\n    if (a !== b) {\n      check = false;\n      break;\n    }\n  }\n\n  assert.equal(\n    check,\n    true,\n    'Two.Matrix.multiply applies elements properly when multiplied by another matrix.'\n  );\n\n  matrix.set(1, 2, 3, 4, 5, 6, 7, 8, 9);\n  var vector = matrix.multiply(9, 8, 7);\n\n  result = [46, 118, 190];\n\n  assert.deepEqual(\n    vector,\n    result,\n    'Two.Matrix.multiply applies elements properly when multipled by a vector.'\n  );\n\n  matrix.set(1, 1, 1, 1, 1, 1, 1, 1, 1);\n  matrix.multiply(9);\n\n  check = true;\n\n  for (var i = 0; i < matrix.elements.length; i++) {\n    var el = matrix.elements[i];\n    if (el !== 9) {\n      check = false;\n      break;\n    }\n  }\n\n  assert.equal(\n    check,\n    true,\n    'Two.Matrix.multiply applies elements properly when multiplied by a scalar.'\n  );\n\n  matrix.set(1, 2, 3, 4, 5, 6, 7, 8, 9);\n\n  assert.deepEqual(\n    matrix.toTransformArray(),\n    [1, 4, 2, 5, 3, 6],\n    'Two.Matrix.toTransformArray returns correct result for 6 digit transformation.'\n  );\n  assert.deepEqual(\n    matrix.toTransformArray(true),\n    [1, 4, 7, 2, 5, 8, 3, 6, 9],\n    'Two.Matrix.toTransformArray returns correct result for 9 digit transformation.'\n  );\n  assert.deepEqual(\n    matrix.toArray(),\n    [1, 2, 3, 4, 5, 6],\n    'Two.Matrix.toArray returns correct result for 6 digit transformation.'\n  );\n  assert.deepEqual(\n    matrix.toArray(true),\n    [1, 2, 3, 4, 5, 6, 7, 8, 9],\n    'Two.Matrix.toArray returns correct result for 9 digit transformation.'\n  );\n  assert.equal(\n    matrix.toString(),\n    '1 4 2 5 3 6',\n    'Two.Matrix.toString returns correct result for 6 digit transformation.'\n  );\n\n  var clone = matrix.clone();\n  assert.ok(\n    clone instanceof Two.Matrix,\n    'Two.Matrix.clone returns instance of Two.Matrix.'\n  );\n  assert.deepEqual(\n    clone.elements,\n    matrix.elements,\n    'Two.Matrix.clone applies elements properly.'\n  );\n});\n\nQUnit.test('Two.Collection', function (assert) {\n  assert.expect(19);\n\n  var poly = new Two.Path([new Two.Anchor(0, 0)]);\n  var vector = new Two.Anchor(150, 150);\n  var vertices = poly.vertices;\n  var removed;\n\n  assert.equal(\n    vertices instanceof Two.Collection,\n    true,\n    'Polyon.vertices is an instance of Two.Collection'\n  );\n\n  assert.equal(\n    vertices[0].equals(new Two.Anchor(0, 0)),\n    true,\n    'Two.Collection created with correct items'\n  );\n\n  vertices.push(vector);\n  assert.equal(\n    vertices.length,\n    2,\n    'Two.Collection.push added one item to the end of vertices collection'\n  );\n\n  removed = vertices.pop();\n  assert.equal(\n    vertices.length,\n    1,\n    'Two.Collection.pop removed one item from the end of the vertices collection'\n  );\n  assert.equal(\n    removed.equals(vector),\n    true,\n    'Two.Collection.push removed the correct item'\n  );\n\n  // Clear removed to reuse\n  removed = null;\n\n  vertices.unshift(vector);\n  assert.equal(\n    vertices.length,\n    2,\n    'Two.Collection.unshift added one item to the front of the vertices collection'\n  );\n\n  removed = vertices.shift();\n  assert.equal(\n    vertices.length,\n    1,\n    'Two.Collection.shift removed one item from the front of the vertices collection'\n  );\n  assert.equal(\n    removed.equals(vector),\n    true,\n    'Two.Collection.shift removed the correct item'\n  );\n\n  // Clear removed to reuse\n  removed = null;\n\n  vertices.push(\n    new Two.Anchor(1, 1),\n    new Two.Anchor(2, 2),\n    new Two.Anchor(3, 3),\n    new Two.Anchor(4, 4)\n  );\n\n  assert.equal(\n    vertices.length,\n    5,\n    'Two.Collection.push adds several items to the end of vertices collection'\n  );\n\n  removed = vertices.splice(2, 1, vector);\n  assert.equal(\n    vertices.length,\n    5,\n    'Two.Collection.splice adds and removes items from the vertices collection'\n  );\n  assert.equal(\n    removed[0].equals(new Two.Anchor(2, 2)),\n    true,\n    'Two.Collection.splice remove the correct items from the vertices collection'\n  );\n  assert.equal(\n    vertices[2].equals(vector),\n    true,\n    'Two.Collection.splice inserts correct item to the middle of the vertices collection'\n  );\n\n  var a = new Two.Collection('a', 'b', 'c', 'd', 'e');\n  assert.equal(\n    a.slice(1, 2)[0],\n    'b',\n    'Two.Collection.slice does correct beginning / end index selection.'\n  );\n\n  a.splice(0, 0, 'z');\n\n  assert.equal(\n    a[0],\n    'z',\n    'Two.Collection.splice correctly inserts properties.'\n  );\n\n  var children = new Two.Group.Children('a', 'b', 'c', 'd', 'e');\n\n  var result = children.map(function (v) {\n    return v;\n  });\n\n  assert.equal(\n    result.length,\n    5,\n    'Two.Collection.map correctly iterates through the necessary items.'\n  );\n\n  const emptyCollection = new Two.Collection();\n\n  assert.equal(\n    emptyCollection.length,\n    0,\n    'Two.Collection correctly enumerates properties.'\n  );\n\n  assert.equal(\n    Object.keys(emptyCollection).length,\n    0,\n    'Two.Collection correctly Object.keys an empty collection.'\n  );\n\n  const included = emptyCollection.filter(() => true);\n  console.log(\n    included.length,\n    0,\n    'Two.Collection.filter correctly iterates over items.'\n  );\n\n  emptyCollection.push(5, 10, 432, 90);\n\n  assert.equal(\n    Object.keys(emptyCollection).length,\n    4,\n    'Two.Collection correctly Object.keys a populated list collection.'\n  );\n\n  assert.equal(\n    emptyCollection.find((v) => v === 5),\n    5,\n    'Two.Collection.find correctly iterates over items.'\n  );\n});\n\nQUnit.test('Two.Children', function (assert) {\n  assert.expect(4);\n\n  var group = new Two.Group();\n  var shape = new Two.Shape();\n\n  group.add(shape);\n  shape._update(true);\n\n  assert.equal(\n    shape.id in group.children.ids,\n    true,\n    'Two.Children properly adds child elements to list.'\n  );\n\n  group.remove(shape);\n  group._update(true);\n  assert.equal(\n    shape.id in group.children.ids,\n    false,\n    'Two.Children properly removes child element from list.'\n  );\n\n  group.add(shape);\n  shape.id = 'custom';\n  shape._update(true);\n  assert.equal(\n    shape.id in group.children.ids,\n    true,\n    'Two.Children properly updates ids map when child id changes.'\n  );\n\n  try {\n    // Check to be able to add and remove masks freely without errors\n    group.mask = new Two.Rectangle(0, 0, 10, 10);\n    group.mask = null;\n    assert.equal(\n      group.mask,\n      null,\n      'Two.Group properly assigns and unassigns masks.'\n    );\n  } catch (e) {\n    console.error(e);\n  }\n});\n\nQUnit.test('Two.Shape', function (assert) {\n  assert.expect(13);\n\n  var shape = new Two.Shape();\n  assert.equal(\n    shape.translation.toString(),\n    '0, 0',\n    'Two.Shape.translation constructed properly.'\n  );\n  assert.equal(shape.scale, 1, 'Two.Shape.scale constructed properly.');\n  assert.equal(shape.rotation, 0, 'Two.Shape.rotation constructed properly.');\n\n  shape.translation.x = 50;\n  shape.translation.y = 25;\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '1 0 0 1 50 25',\n    'Two.Shape.translation binds properly.'\n  );\n\n  shape.translation = new Two.Anchor(25, 50);\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '1 0 0 1 25 50',\n    'Two.Shape.translation binds properly.'\n  );\n\n  shape.translation.x = 0;\n  shape.translation.y = 0;\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '1 0 0 1 0 0',\n    'Two.Shape.translation binds properly.'\n  );\n\n  shape.scale = 3;\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '3 0 0 3 0 0',\n    'Two.Shape.scale uniform scale works properly.'\n  );\n\n  shape.scale = new Two.Anchor(1, 2);\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '1 0 0 2 0 0',\n    'Two.Shape.scale 2 dimension scale works properly.'\n  );\n\n  shape.scale.x = 2;\n  shape.scale.y = 1;\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '2 0 0 1 0 0',\n    'Two.Shape.scale 2 dimension scale binds properly for event listening.'\n  );\n\n  var s = shape.scale;\n  shape.scale = 10;\n  s.x = 5;\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '10 0 0 10 0 0',\n    'Two.Shape.scale 2 dimension scale unbinds properly.'\n  );\n\n  shape.rotation = 3.14;\n  shape._update();\n\n  assert.equal(\n    shape._matrix.toString(),\n    '-9.999988 0.015926 -0.015927 -9.999988 0 0',\n    'Two.Shape.rotation works properly.'\n  );\n\n  assert.equal(\n    shape.worldMatrix.toString(),\n    '-9.999988 0.015926 -0.015927 -9.999988 0 0',\n    'Two.Shape.worldMatrix locally updates properly.'\n  );\n\n  var scene = new Two.Group();\n\n  scene.add(shape);\n\n  scene.position.x = 100;\n\n  scene._update();\n\n  assert.equal(\n    shape.worldMatrix.toString(),\n    '-9.999988 0.015926 -0.015927 -9.999988 100 0',\n    'Two.Shape.worldMatrix properly calculates world matrix.'\n  );\n});\n\nQUnit.test('Children adding and removing', function (assert) {\n  assert.expect(28);\n\n  var group1 = new Two.Group();\n  var group2 = new Two.Group();\n  var group3 = new Two.Group();\n\n  var poly1 = new Two.Path([new Two.Anchor(0, 0)]);\n  var poly2 = new Two.Path([new Two.Anchor(0, 0)]);\n  var poly3 = new Two.Path([new Two.Anchor(0, 0)]);\n  var poly4 = new Two.Path([new Two.Anchor(0, 0)]);\n  var poly5 = new Two.Path([new Two.Anchor(0, 0)]);\n\n  poly1.addTo(group1);\n  assert.equal(\n    poly1,\n    group1.children[0],\n    'Can add objects to group (via object)'\n  );\n  assert.equal(group1, poly1.parent, 'Can add objects to group (via object)');\n  assert.ok(\n    ~poly1.parent.additions.indexOf(poly1),\n    'Can add objects to group (via object)'\n  );\n  assert.equal(group1.children.length, 1, 'Correct childrens length');\n\n  group2.add(poly2);\n  assert.equal(\n    poly2,\n    group2.children[0],\n    'Can add objects to group (via group)'\n  );\n  assert.equal(group2, poly2.parent, 'Can add objects to group (via group)');\n  assert.ok(\n    ~poly2.parent.additions.indexOf(poly2),\n    'Can add objects to group (via group)'\n  );\n  assert.equal(group2.children.length, 1, 'Correct childrens length');\n\n  group1.add(poly2);\n  assert.equal(poly2, group1.children[1], 'Can reassign objects to group');\n  assert.equal(group1, poly2.parent, 'Can reassign objects to group');\n  assert.ok(\n    ~poly2.parent.additions.indexOf(poly2),\n    'Can reassign objects to group'\n  );\n  assert.ok(!~group2.additions.indexOf(poly2), 'Can reassign objects to group');\n  assert.equal(group1.children.length, 2, 'Correct childrens length');\n  assert.equal(group2.children.length, 0, 'Correct childrens length');\n\n  group3.add(group1);\n  assert.equal(group1, group3.children[0], 'Can add groups to group');\n  assert.equal(group3, group1.parent, 'Can add groups to group');\n  assert.ok(~group3.additions.indexOf(group1), 'Can add groups to group');\n  assert.equal(group3.children.length, 1, 'Correct childrens length');\n\n  group1.add(poly3);\n  group1.add(poly4);\n  group1.add(poly5);\n  assert.equal(group1.children.length, 5, 'Correct childrens length');\n\n  group3.add(group1.children);\n  assert.equal(\n    group3.children.length,\n    6,\n    'Can reassign children to another group'\n  );\n  assert.equal(\n    group1.children.length,\n    0,\n    'Can reassign children to another group'\n  );\n  assert.equal(\n    group1.additions.length,\n    0,\n    'Can reassign children to another group'\n  );\n  assert.equal(poly5.parent, group3, 'Can reassign children to another group');\n  assert.ok(\n    ~poly5.parent.additions.indexOf(poly5),\n    'Can reassign children to another group'\n  );\n\n  group3.remove(poly4);\n  assert.equal(group3.children.length, 5, 'Can remove objects from group');\n  assert.equal(group3.additions.length, 5, 'Can remove objects from group');\n  assert.equal(poly4.parent, null, 'Can remove objects from group');\n\n  group3.add(void 0);\n  assert.ok(true, 'Can safely add undefined stuff to group');\n});\n\nQUnit.test('Two.Registry', function (assert) {\n  assert.expect(4);\n\n  var registry = new Two.Registry();\n  var id = 'foo';\n  var val = 'bar';\n\n  registry.add(id, val);\n  assert.equal(\n    registry.get(id),\n    val,\n    'Two.Registry registers key values properly.'\n  );\n  assert.equal(\n    registry.contains(id),\n    true,\n    'Two.Registry.contains found registered value properly.'\n  );\n\n  registry.remove(id);\n  assert.equal(\n    registry.map[id],\n    undefined,\n    'Two.Registry removes key values properly.'\n  );\n  assert.equal(\n    registry.contains(id),\n    false,\n    'Two.Registry.contains did not find removed id.'\n  );\n});\n\nQUnit.test('Two.Texture', function (assert) {\n  assert.expect(7);\n  assert.done = assert.async(2);\n\n  // Test Image Loading to Texture\n  var root = '/tests/images/sequence/';\n  var path = [root, '00000.png'].join('');\n\n  var ta = new Two.Texture(path, function () {\n    assert.ok(true, 'Two.Texture callback on load triggered properly.');\n    tc._update();\n    assert.equal(\n      ta.image,\n      tc.image,\n      'Two.Texture applies image properties properly after undefined.'\n    );\n    assert.ok(\n      tc.loaded,\n      'Two.Texture applies loaded property correctly on undefined source images.'\n    );\n    assert.done();\n  });\n  ta.image.id = 'first-original-image';\n\n  var image = document.createElement('img');\n  var onload = function () {\n    var tb = new Two.Texture(image);\n    var absolutePath = [\n      window.location.protocol,\n      '//',\n      window.location.host,\n      path,\n    ].join('');\n\n    assert.equal(\n      tb.src,\n      absolutePath,\n      'Two.Texture takes in image and applies path properly.'\n    );\n    assert.equal(\n      tb.image,\n      ta.image,\n      'Two.Texture takes in image and applies registered image tag properly.'\n    );\n    assert.equal(\n      tb.loaded,\n      true,\n      'Two.Texture takes in image and applies loaded property properly.'\n    );\n    assert.done();\n\n    image.onload = null;\n  };\n  image.onload = onload;\n  image.src = path;\n  image.id = 'second-same-image-as-first';\n\n  var tc = new Two.Texture();\n  assert.ok(\n    true,\n    'Two.Texture able to be constructed without any parameters properly.'\n  );\n\n  tc.src = path;\n});\n\nQUnit.test('Two', function (assert) {\n  assert.expect(8);\n\n  var selector = QUnit.Utils.getSelector(assert.test);\n  var elem = document.querySelector(selector);\n  elem.id = assert.test.id + '-container';\n\n  var two = new Two().appendTo(elem);\n  assert.equal(\n    two.renderer.domElement.parentElement.id,\n    elem.id,\n    'Two appends to the correct element when `appendTo` invoked.'\n  );\n\n  two.update();\n  assert.equal(two.frameCount, 1, 'Two increments frameCount correctly.');\n\n  two.play();\n  assert.ok(two.playing, 'Two.Utils.setPlaying applied correctly.');\n  assert.ok(\n    typeof Two.NextFrameId === 'number',\n    'requestAnimationFrame runs correctly.'\n  );\n\n  two.pause();\n  assert.ok(!two.playing, 'Two.pause correctly stops updating.');\n\n  var rectangle = two.makeRectangle(two.width / 2, two.height / 2, 10, 10);\n\n  assert.equal(two.scene.children.length, 1, 'Adds children correctly.');\n\n  two.remove(rectangle);\n  assert.equal(two.scene.children.length, 0, 'Removes children correctly');\n\n  two.add(rectangle);\n  two.clear();\n  assert.equal(two.scene.children.length, 0, 'Clears children correctly');\n});\n\nQUnit.test('Two.Path Object Conversion', function (assert) {\n  assert.expect(9);\n\n  // Create an original path with some properties\n  var path = new Two.Path([new Two.Anchor(0, 0), new Two.Anchor(100, 100)]);\n  path.fill = '#ff0000';\n  path.stroke = '#00ff00';\n  path.linewidth = 5;\n  path.opacity = 0.5;\n  path.visible = true;\n  path.cap = 'round';\n  path.join = 'miter';\n  path.miter = 10;\n  path.id = 'my-path';\n\n  // Convert to object\n  var obj = path.toObject();\n\n  // Test object has expected properties\n  assert.equal(typeof obj, 'object', 'Two.Path.toObject creates an object');\n  assert.equal(obj.vertices.length, 2, 'Two.Path.toObject preserves vertices');\n  assert.equal(obj.fill, '#ff0000', 'Two.Path.toObject preserves fill');\n  assert.equal(obj.id, 'my-path', 'Two.Path.toObject preserves id');\n\n  // Create new path from object\n  var newPath = Two.Path.fromObject(obj);\n\n  // Test new path matches original\n  assert.equal(newPath.fill, path.fill, 'Two.Path.fromObject preserves fill');\n  assert.equal(\n    newPath.stroke,\n    path.stroke,\n    'Two.Path.fromObject preserves stroke'\n  );\n  assert.equal(\n    newPath.vertices.length,\n    path.vertices.length,\n    'Two.Path.fromObject preserves vertices length'\n  );\n  assert.equal(newPath.id, path.id, 'Two.Path.fromObject preserves id');\n\n  // Test copy method\n  var copiedPath = new Two.Path().copy(path);\n  assert.deepEqual(\n    { ...copiedPath.toObject(), id: path.id },\n    obj,\n    'Two.Path.copy creates identical path'\n  );\n});\n\nQUnit.test('Two.Path strokeAttenuation', function (assert) {\n  assert.expect(4);\n\n  var path = new Two.Path([new Two.Anchor(0, 0), new Two.Anchor(100, 100)]);\n\n  path.strokeAttenuation = true;\n  assert.equal(\n    path.strokeAttenuation,\n    true,\n    'Can get property strokeAttenuation correctly.'\n  );\n  assert.equal(\n    path._strokeAttenuation,\n    true,\n    'Can set property strokeAttenuation correctly.'\n  );\n\n  path.strokeAttenuation = false;\n  assert.equal(\n    path.strokeAttenuation,\n    false,\n    'Can set property strokeAttenuation to false correctly.'\n  );\n  assert.equal(\n    path._strokeAttenuation,\n    false,\n    'Can get property strokeAttenuation false correctly.'\n  );\n});\n\nQUnit.test('Two.Group Object Conversion', function (assert) {\n  assert.expect(6);\n\n  // Create original group with children\n  var group = new Two.Group();\n  var circle = new Two.Circle(0, 0, 50);\n  var rect = new Two.Rectangle(100, 100, 50, 50);\n  group.add(circle, rect);\n  group.id = 'my-group';\n\n  // Convert to object\n  var obj = group.toObject();\n\n  // Test object has expected properties\n  assert.equal(typeof obj, 'object', 'Two.Group.toObject creates an object');\n  assert.equal(obj.children.length, 2, 'Two.Group.toObject preserves children');\n  assert.equal(obj.id, 'my-group', 'Two.Group.toObject preserves id');\n\n  // Create new group from object\n  var newGroup = Two.Group.fromObject(obj);\n\n  // Test new group matches original\n  assert.equal(\n    newGroup.children.length,\n    group.children.length,\n    'Two.Group.fromObject preserves children length'\n  );\n  assert.equal(newGroup.id, group.id, 'Two.Group.fromObject preserves id');\n\n  // Test child types are preserved\n  assert.ok(\n    newGroup.children[0] instanceof Two.Circle,\n    'Two.Group.fromObject preserves child types'\n  );\n\n  // TODO: Implement\n  // // Test copy method\n  // var copiedGroup = new Two.Group().copy(group);\n  // copiedGroup._update();\n  // assert.deepEqual(\n  //   copiedGroup.toObject(),\n  //   group.toObject(),\n  //   'Two.Group.copy creates identical group'\n  // );\n});\n\nQUnit.test('Two.Group strokeAttenuation', function (assert) {\n  assert.expect(4);\n\n  var group = new Two.Group();\n\n  group.strokeAttenuation = true;\n  assert.equal(\n    group.strokeAttenuation,\n    true,\n    'Can get property strokeAttenuation correctly.'\n  );\n  assert.equal(\n    group._strokeAttenuation,\n    true,\n    'Can set property strokeAttenuation correctly.'\n  );\n\n  group.strokeAttenuation = false;\n  assert.equal(\n    group.strokeAttenuation,\n    false,\n    'Can set property strokeAttenuation to false correctly.'\n  );\n  assert.equal(\n    group._strokeAttenuation,\n    false,\n    'Can get property strokeAttenuation false correctly.'\n  );\n});\n\nQUnit.test('Two.Group.getBoundingClientRect(shallow)', function (assert) {\n  assert.expect(6);\n\n  const group = new Two.Group();\n\n  for (let i = 0; i < 3; i++) {\n    const x = i * 100;\n    const width = 100;\n    const height = 50;\n    const child = new Two.Rectangle(x, 0, width, height);\n    group.add(child);\n  }\n\n  const rect = group.getBoundingClientRect(true);\n  assert.equal(\n    rect.top,\n    -25.5,\n    'Two.Group.getBoundingClientRect(shallow) correctly calculates top property.'\n  );\n  assert.equal(\n    rect.bottom,\n    25.5,\n    'Two.Group.getBoundingClientRect(shallow) correctly calculates bottom property.'\n  );\n  assert.equal(\n    rect.left,\n    -50.5,\n    'Two.Group.getBoundingClientRect(shallow) correctly calculates left property.'\n  );\n  assert.equal(\n    rect.right,\n    250.5,\n    'Two.Group.getBoundingClientRect(shallow) correctly calculates right property.'\n  );\n  assert.equal(\n    rect.width,\n    301,\n    'Two.Group.getBoundingClientRect(shallow) correctly calculates width property.'\n  );\n  assert.equal(\n    rect.height,\n    51,\n    'Two.Group.getBoundingClientRect(shallow) correctly calculates height property.'\n  );\n});\n\nQUnit.test('Two.Text Object Conversion', function (assert) {\n  assert.expect(9);\n\n  var text = new Two.Text('Hello, World!', 100, 100);\n  text.fill = '#ff0000';\n  text.stroke = '#00ff00';\n  text.size = 20;\n  text.id = 'my-text';\n\n  var obj = text.toObject();\n\n  assert.equal(typeof obj, 'object', 'Two.Text.toObject creates an object');\n  assert.equal(obj.value, 'Hello, World!', 'Two.Text.toObject preserves value');\n  assert.equal(obj.fill, '#ff0000', 'Two.Text.toObject preserves fill');\n  assert.equal(obj.stroke, '#00ff00', 'Two.Text.toObject preserves stroke');\n  assert.equal(obj.size, 20, 'Two.Text.toObject preserves size');\n  assert.equal(obj.id, 'my-text', 'Two.Text.toObject preserves id');\n\n  var newText = Two.Text.fromObject(obj);\n\n  assert.equal(\n    newText.value,\n    text.value,\n    'Two.Text.fromObject preserves value'\n  );\n  assert.equal(newText.fill, text.fill, 'Two.Text.fromObject preserves fill');\n\n  var copiedText = new Two.Text().copy(text);\n  assert.deepEqual(\n    { ...copiedText.toObject(), id: text.id },\n    text.toObject(),\n    'Two.Text.copy creates identical text'\n  );\n});\n\nQUnit.test('Two.Text strokeAttenuation', function (assert) {\n  assert.expect(4);\n\n  var text = new Two.Text('Hello, World!', 100, 100);\n\n  text.strokeAttenuation = true;\n  assert.equal(\n    text.strokeAttenuation,\n    true,\n    'Can get property strokeAttenuation correctly.'\n  );\n  assert.equal(\n    text._strokeAttenuation,\n    true,\n    'Can set property strokeAttenuation correctly.'\n  );\n\n  text.strokeAttenuation = false;\n  assert.equal(\n    text.strokeAttenuation,\n    false,\n    'Can set property strokeAttenuation to false correctly.'\n  );\n  assert.equal(\n    text._strokeAttenuation,\n    false,\n    'Can get property strokeAttenuation false correctly.'\n  );\n});\n"
  },
  {
    "path": "tests/suite/dispose.js",
    "content": "/**\n * Tests Two.js dispose() Methods:\n * Comprehensive testing of dispose() functionality across the class hierarchy.\n * Tests memory cleanup, event unbinding, renderer resource cleanup, and DOM element removal.\n */\n\n(function () {\n  QUnit.module('Dispose Method');\n\n  // ===========================\n  // Element Tests (Base Class)\n  // ===========================\n\n  QUnit.test('Element.dispose - Event Unbinding', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    var eventFired = false;\n\n    rect.bind('change', function () {\n      eventFired = true;\n    });\n\n    // Verify event fires before dispose\n    rect.trigger('change');\n    assert.ok(eventFired, 'Event fires before dispose');\n\n    // Reset\n    eventFired = false;\n\n    // Dispose\n    rect.dispose();\n\n    // Verify event doesn't fire after dispose\n    rect.trigger('change');\n    assert.ok(!eventFired, 'Event does not fire after dispose');\n\n    // Verify _bound flag is false\n    assert.ok(!rect._bound, '_bound flag is false after dispose');\n  });\n\n  QUnit.test('Element.dispose - SVG DOM Cleanup', function (assert) {\n    assert.expect(5);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    var initialType = rect._renderer.type;\n    two.update();\n\n    // Verify element exists in DOM before dispose\n    var elemBefore = two.renderer.domElement.querySelector('#' + rect.id);\n    assert.ok(elemBefore, 'Element exists in DOM before dispose');\n    assert.ok(rect._renderer.elem, '_renderer.elem exists before dispose');\n\n    // Dispose\n    rect.dispose();\n\n    // Verify element removed from DOM after dispose\n    var elemAfter = two.renderer.domElement.querySelector('#' + rect.id);\n    assert.ok(!elemAfter, 'Element removed from DOM after dispose');\n    assert.ok(!rect._renderer.elem, '_renderer.elem deleted after dispose');\n    assert.equal(rect._renderer.type, initialType, 'Renderer type preserved');\n  });\n\n  QUnit.test('Element.dispose - Renderer Type Preservation', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var circle = two.makeCircle(100, 100, 25);\n    var initialType = circle._renderer.type;\n\n    circle.dispose();\n\n    assert.equal(\n      circle._renderer.type,\n      initialType,\n      'Renderer type preserved after dispose'\n    );\n    assert.ok(circle._renderer.type, 'Renderer type exists after dispose');\n  });\n\n  QUnit.test('Element.dispose - Returns Instance', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var line = two.makeLine(0, 0, 100, 100);\n    var result = line.dispose();\n\n    assert.equal(result, line, 'dispose() returns the instance for chaining');\n  });\n\n  QUnit.test('Element.dispose - Multiple Dispose Calls', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var star = two.makeStar(100, 100, 20, 50, 5);\n    two.update();\n\n    // First dispose\n    try {\n      star.dispose();\n      assert.ok(true, 'First dispose() succeeds');\n    } catch (e) {\n      assert.ok(false, 'First dispose() threw error: ' + e.message);\n    }\n\n    // Second dispose\n    try {\n      star.dispose();\n      assert.ok(true, 'Second dispose() succeeds without error');\n    } catch (e) {\n      assert.ok(false, 'Second dispose() threw error: ' + e.message);\n    }\n  });\n\n  // ===========================\n  // Shape Tests\n  // ===========================\n\n  QUnit.test('Shape.dispose - Translation Vector Unbinding', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    var translationEventFired = false;\n\n    rect.translation.bind('change', function () {\n      translationEventFired = true;\n    });\n\n    // Verify event fires before dispose\n    rect.translation.trigger('change');\n    assert.ok(translationEventFired, 'Translation event fires before dispose');\n\n    // Reset\n    translationEventFired = false;\n\n    // Dispose\n    rect.dispose();\n\n    // Verify event doesn't fire after dispose\n    rect.translation.trigger('change');\n    assert.ok(\n      !translationEventFired,\n      'Translation event does not fire after dispose'\n    );\n    assert.ok(\n      !rect.translation._bound,\n      'Translation Vector _bound flag is false'\n    );\n  });\n\n  QUnit.test('Shape.dispose - Scale Vector Unbinding', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var circle = two.makeCircle(100, 100, 50);\n\n    // Set scale to a Vector (non-uniform scaling)\n    circle.scale = new Two.Vector(2, 3);\n\n    var scaleEventFired = false;\n\n    circle.scale.bind('change', function () {\n      scaleEventFired = true;\n    });\n\n    // Verify event fires before dispose\n    circle.scale.trigger('change');\n    assert.ok(scaleEventFired, 'Scale event fires before dispose');\n\n    // Reset\n    scaleEventFired = false;\n\n    // Dispose\n    circle.dispose();\n\n    // Verify event doesn't fire after dispose\n    circle.scale.trigger('change');\n    assert.ok(!scaleEventFired, 'Scale event does not fire after dispose');\n    assert.ok(!circle.scale._bound, 'Scale Vector _bound flag is false');\n  });\n\n  QUnit.test('Shape.dispose - Inheritance Chain', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var polygon = two.makePolygon(100, 100, 50, 6);\n    var eventFired = false;\n\n    polygon.bind('change', function () {\n      eventFired = true;\n    });\n\n    two.update();\n\n    // Verify Element dispose behavior (event unbinding)\n    polygon.trigger('change');\n    assert.ok(eventFired, 'Event fires before dispose');\n\n    polygon.dispose();\n\n    eventFired = false;\n    polygon.trigger('change');\n    assert.ok(!eventFired, 'Element.dispose() was called (events unbound)');\n\n    // Verify renderer cleanup (Element dispose behavior)\n    var elemAfter = two.renderer.domElement.querySelector('#' + polygon.id);\n    assert.ok(!elemAfter, 'Element.dispose() was called (DOM cleaned)');\n  });\n\n  // ===========================\n  // Path Tests\n  // ===========================\n\n  QUnit.test('Path.dispose - Vertices Collection Unbinding', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var path = two.makePath(0, 0, 100, 0, 100, 100, 0, 100);\n\n    // Dispose should unbind vertices collection without error\n    try {\n      path.dispose();\n      assert.ok(\n        true,\n        'Path disposes and unbinds vertices collection without error'\n      );\n    } catch (e) {\n      assert.ok(false, 'Path dispose threw error: ' + e.message);\n    }\n  });\n\n  QUnit.test('Path.dispose - Individual Anchor Unbinding', function (assert) {\n    assert.expect(9);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var path = two.makePath(0, 0, 100, 0, 100, 100);\n    var anchor0EventFired = false;\n    var anchor1EventFired = false;\n    var anchor2EventFired = false;\n\n    path.vertices[0].bind('change', function () {\n      anchor0EventFired = true;\n    });\n\n    path.vertices[1].bind('change', function () {\n      anchor1EventFired = true;\n    });\n\n    path.vertices[2].bind('change', function () {\n      anchor2EventFired = true;\n    });\n\n    // Verify events fire before dispose\n    path.vertices[0].trigger('change');\n    path.vertices[1].trigger('change');\n    path.vertices[2].trigger('change');\n    assert.ok(anchor0EventFired, 'Anchor 0 event fires before dispose');\n    assert.ok(anchor1EventFired, 'Anchor 1 event fires before dispose');\n    assert.ok(anchor2EventFired, 'Anchor 2 event fires before dispose');\n\n    // Reset\n    anchor0EventFired = false;\n    anchor1EventFired = false;\n    anchor2EventFired = false;\n\n    // Dispose\n    path.dispose();\n\n    // Verify events don't fire after dispose\n    path.vertices[0].trigger('change');\n    path.vertices[1].trigger('change');\n    path.vertices[2].trigger('change');\n    assert.ok(!anchor0EventFired, 'Anchor 0 event does not fire after dispose');\n    assert.ok(!anchor1EventFired, 'Anchor 1 event does not fire after dispose');\n    assert.ok(!anchor2EventFired, 'Anchor 2 event does not fire after dispose');\n\n    // Verify _bound flags\n    assert.ok(!path.vertices[0]._bound, 'Anchor 0 _bound flag is false');\n    assert.ok(!path.vertices[1]._bound, 'Anchor 1 _bound flag is false');\n    assert.ok(!path.vertices[2]._bound, 'Anchor 2 _bound flag is false');\n  });\n\n  QUnit.test('Path.dispose - Control Point Unbinding', function (assert) {\n    assert.expect(7);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var path = two.makePath(0, 0, 100, 0, 100, 100);\n    path.curved = true;\n\n    var leftEventFired = false;\n    var rightEventFired = false;\n\n    // Bind to first vertex control points\n    path.vertices[0].controls.left.bind('change', function () {\n      leftEventFired = true;\n    });\n\n    path.vertices[0].controls.right.bind('change', function () {\n      rightEventFired = true;\n    });\n\n    // Verify events fire before dispose\n    path.vertices[0].controls.left.trigger('change');\n    path.vertices[0].controls.right.trigger('change');\n    assert.ok(leftEventFired, 'Left control point event fires before dispose');\n    assert.ok(\n      rightEventFired,\n      'Right control point event fires before dispose'\n    );\n\n    // Reset\n    leftEventFired = false;\n    rightEventFired = false;\n\n    // Dispose\n    path.dispose();\n\n    // Verify events don't fire after dispose\n    path.vertices[0].controls.left.trigger('change');\n    path.vertices[0].controls.right.trigger('change');\n    assert.ok(\n      !leftEventFired,\n      'Left control point event does not fire after dispose'\n    );\n    assert.ok(\n      !rightEventFired,\n      'Right control point event does not fire after dispose'\n    );\n\n    // Verify _bound flags\n    assert.ok(\n      !path.vertices[0].controls.left._bound,\n      'Left control point _bound flag is false'\n    );\n    assert.ok(\n      !path.vertices[0].controls.right._bound,\n      'Right control point _bound flag is false'\n    );\n\n    // Verify parent anchor also unbound\n    assert.ok(!path.vertices[0]._bound, 'Parent anchor _bound flag is false');\n  });\n\n  QUnit.test(\n    'Path.dispose - Fill Effect Disposal (Gradient)',\n    function (assert) {\n      assert.expect(2);\n\n      var two = new Two({\n        type: Two.Types.svg,\n        width: 400,\n        height: 400,\n      }).appendTo(document.body);\n\n      var gradient = two.makeLinearGradient(\n        0,\n        0,\n        1,\n        1,\n        new Two.Stop(0, 'red'),\n        new Two.Stop(1, 'blue')\n      );\n\n      var rect = two.makeRectangle(100, 100, 100, 100);\n      rect.fill = gradient;\n\n      two.update();\n\n      // Verify gradient has DOM element before dispose\n      var gradientElemBefore = two.renderer.domElement.querySelector(\n        '#' + gradient.id\n      );\n      assert.ok(\n        gradientElemBefore,\n        'Gradient element exists in DOM before path dispose'\n      );\n\n      // Dispose path (should call dispose on gradient)\n      try {\n        rect.dispose();\n        assert.ok(true, 'Path disposes with gradient fill without error');\n      } catch (e) {\n        assert.ok(false, 'Path dispose threw error: ' + e.message);\n      }\n    }\n  );\n\n  QUnit.test(\n    'Path.dispose - Stroke Effect Disposal (Texture)',\n    function (assert) {\n      assert.expect(4);\n\n      var two = new Two({\n        type: Two.Types.svg,\n        width: 400,\n        height: 400,\n      }).appendTo(document.body);\n\n      var texture = new Two.Texture('./images/canvas/line@2x.png');\n      var circle = two.makeCircle(100, 100, 50);\n      circle.stroke = texture;\n      circle.noFill();\n\n      two.update();\n\n      // Bind event to texture\n      var textureEventFired = false;\n      texture.bind('change', function () {\n        textureEventFired = true;\n      });\n\n      texture.trigger('change');\n      assert.ok(textureEventFired, 'Texture event fires before path dispose');\n\n      textureEventFired = false;\n\n      // Verify texture has renderer\n      assert.ok(texture._renderer, 'Texture has _renderer before dispose');\n\n      // Dispose path (should dispose texture)\n      circle.dispose();\n\n      // Verify texture disposed\n      texture.trigger('change');\n      assert.ok(\n        !textureEventFired,\n        'Texture event does not fire after path dispose'\n      );\n      assert.ok(\n        texture._renderer.type,\n        'Texture renderer type preserved after dispose'\n      );\n    }\n  );\n\n  QUnit.test('Path.dispose - String Fill/Stroke', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var path = two.makePath(0, 0, 100, 0, 100, 100);\n    path.fill = 'red';\n    path.stroke = 'blue';\n\n    // Dispose path with string fill/stroke (no effect objects)\n    try {\n      path.dispose();\n      assert.ok(true, 'Path with string fill/stroke disposes without error');\n    } catch (e) {\n      assert.ok(\n        false,\n        'Path with string fill/stroke threw error: ' + e.message\n      );\n    }\n  });\n\n  QUnit.test('Path.dispose - Incomplete Collection', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var path = two.makePath(0, 0, 100, 0, 100, 100);\n\n    // Simulate incomplete collection by removing unbind method\n    var originalUnbind = path.vertices.unbind;\n    delete path.vertices.unbind;\n\n    // Dispose should handle incomplete collection gracefully\n    try {\n      path.dispose();\n      assert.ok(\n        true,\n        'Path disposes without error when vertices collection has no unbind method'\n      );\n    } catch (e) {\n      assert.ok(\n        false,\n        'Path dispose threw error with incomplete collection: ' + e.message\n      );\n    }\n\n    // Restore for cleanup\n    path.vertices.unbind = originalUnbind;\n  });\n\n  // ===========================\n  // Group Tests\n  // ===========================\n\n  QUnit.test('Group.dispose - Recursive Child Disposal', function (assert) {\n    assert.expect(7);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var group = two.makeGroup();\n    var child1 = two.makeCircle(50, 50, 25);\n    var child2 = two.makeRectangle(150, 150, 50, 50);\n\n    group.add(child1, child2);\n\n    var child1EventFired = false;\n    var child2EventFired = false;\n\n    child1.bind('change', function () {\n      child1EventFired = true;\n    });\n\n    child2.bind('change', function () {\n      child2EventFired = true;\n    });\n\n    // Verify events fire before dispose\n    child1.trigger('change');\n    child2.trigger('change');\n    assert.ok(child1EventFired, 'Child1 event fires before group dispose');\n    assert.ok(child2EventFired, 'Child2 event fires before group dispose');\n\n    child1EventFired = false;\n    child2EventFired = false;\n\n    two.update();\n\n    // Dispose group (should recursively dispose children)\n    group.dispose();\n\n    // Verify children disposed\n    child1.trigger('change');\n    child2.trigger('change');\n    assert.ok(\n      !child1EventFired,\n      'Child1 event does not fire after group dispose'\n    );\n    assert.ok(\n      !child2EventFired,\n      'Child2 event does not fire after group dispose'\n    );\n\n    // Verify children DOM elements removed\n    var child1ElemAfter = two.renderer.domElement.querySelector(\n      '#' + child1.id\n    );\n    var child2ElemAfter = two.renderer.domElement.querySelector(\n      '#' + child2.id\n    );\n    assert.ok(!child1ElemAfter, 'Child1 DOM element removed');\n    assert.ok(!child2ElemAfter, 'Child2 DOM element removed');\n\n    // Verify group DOM element removed\n    var groupElemAfter = two.renderer.domElement.querySelector('#' + group.id);\n    assert.ok(!groupElemAfter, 'Group DOM element removed');\n  });\n\n  QUnit.test('Group.dispose - Nested Group Disposal', function (assert) {\n    assert.expect(9);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var rootGroup = two.makeGroup();\n    var subGroup1 = two.makeGroup();\n    var subGroup2 = two.makeGroup();\n    var leaf1 = two.makeCircle(50, 50, 20);\n    var leaf2 = two.makeRectangle(150, 50, 40, 40);\n    var leaf3 = two.makeLine(200, 50, 250, 100);\n\n    subGroup1.add(leaf1, leaf2);\n    subGroup2.add(leaf3);\n    rootGroup.add(subGroup1, subGroup2);\n\n    // Bind events at all levels\n    var leaf1EventFired = false;\n    var leaf2EventFired = false;\n    var leaf3EventFired = false;\n\n    leaf1.bind('change', function () {\n      leaf1EventFired = true;\n    });\n    leaf2.bind('change', function () {\n      leaf2EventFired = true;\n    });\n    leaf3.bind('change', function () {\n      leaf3EventFired = true;\n    });\n\n    // Verify events fire before dispose\n    leaf1.trigger('change');\n    leaf2.trigger('change');\n    leaf3.trigger('change');\n    assert.ok(leaf1EventFired, 'Leaf1 event fires before dispose');\n    assert.ok(leaf2EventFired, 'Leaf2 event fires before dispose');\n    assert.ok(leaf3EventFired, 'Leaf3 event fires before dispose');\n\n    leaf1EventFired = false;\n    leaf2EventFired = false;\n    leaf3EventFired = false;\n\n    two.update();\n\n    // Dispose root group (should recursively dispose entire tree)\n    rootGroup.dispose();\n\n    // Verify all events unbound\n    leaf1.trigger('change');\n    leaf2.trigger('change');\n    leaf3.trigger('change');\n    assert.ok(!leaf1EventFired, 'Leaf1 event does not fire after root dispose');\n    assert.ok(!leaf2EventFired, 'Leaf2 event does not fire after root dispose');\n    assert.ok(!leaf3EventFired, 'Leaf3 event does not fire after root dispose');\n\n    // Verify all DOM elements removed\n    var leaf1ElemAfter = two.renderer.domElement.querySelector('#' + leaf1.id);\n    var leaf2ElemAfter = two.renderer.domElement.querySelector('#' + leaf2.id);\n    var leaf3ElemAfter = two.renderer.domElement.querySelector('#' + leaf3.id);\n    assert.ok(!leaf1ElemAfter, 'Leaf1 DOM element removed');\n    assert.ok(!leaf2ElemAfter, 'Leaf2 DOM element removed');\n    assert.ok(!leaf3ElemAfter, 'Leaf3 DOM element removed');\n  });\n\n  QUnit.test(\n    'Group.dispose - Children Collection Unbinding',\n    function (assert) {\n      assert.expect(1);\n\n      var two = new Two({\n        type: Two.Types.svg,\n        width: 400,\n        height: 400,\n      }).appendTo(document.body);\n\n      var group = two.makeGroup();\n      var child = two.makeCircle(50, 50, 25);\n      group.add(child);\n\n      // Dispose should unbind children collection without error\n      try {\n        group.dispose();\n        assert.ok(\n          true,\n          'Group disposes and unbinds children collection without error'\n        );\n      } catch (e) {\n        assert.ok(false, 'Group dispose threw error: ' + e.message);\n      }\n    }\n  );\n\n  QUnit.test('Group.dispose - Empty Group', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var emptyGroup = two.makeGroup();\n\n    // Dispose empty group\n    try {\n      emptyGroup.dispose();\n      assert.ok(true, 'Empty group disposes without error');\n    } catch (e) {\n      assert.ok(false, 'Empty group dispose threw error: ' + e.message);\n    }\n  });\n\n  // ===========================\n  // Text Tests\n  // ===========================\n\n  QUnit.test('Text.dispose - Fill Effect Disposal', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var gradient = two.makeLinearGradient(\n      0,\n      0,\n      1,\n      1,\n      new Two.Stop(0, '#ff0000'),\n      new Two.Stop(1, '#0000ff')\n    );\n\n    var text = two.makeText('Hello', 100, 100);\n    text.fill = gradient;\n\n    two.update();\n\n    // Dispose text (should dispose gradient)\n    try {\n      text.dispose();\n      assert.ok(true, 'Text disposes with gradient fill without error');\n    } catch (e) {\n      assert.ok(false, 'Text dispose threw error: ' + e.message);\n    }\n  });\n\n  QUnit.test('Text.dispose - Stroke Effect Disposal', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var gradient = two.makeRadialGradient(\n      0.5,\n      0.5,\n      1,\n      new Two.Stop(0, 'white'),\n      new Two.Stop(1, 'black')\n    );\n\n    var text = two.makeText('World', 200, 100);\n    text.stroke = gradient;\n    text.linewidth = 2;\n\n    two.update();\n\n    // Dispose text (should dispose gradient)\n    try {\n      text.dispose();\n      assert.ok(true, 'Text disposes with gradient stroke without error');\n    } catch (e) {\n      assert.ok(false, 'Text dispose threw error: ' + e.message);\n    }\n  });\n\n  // ===========================\n  // Points Tests\n  // ===========================\n\n  QUnit.test(\n    'Points.dispose - Vertices Collection Unbinding',\n    function (assert) {\n      assert.expect(1);\n\n      var two = new Two({\n        type: Two.Types.svg,\n        width: 400,\n        height: 400,\n      }).appendTo(document.body);\n\n      var points = two.makePoints([\n        new Two.Vector(0, 0),\n        new Two.Vector(100, 50),\n        new Two.Vector(50, 100),\n      ]);\n\n      // Dispose should unbind vertices collection without error\n      try {\n        points.dispose();\n        assert.ok(\n          true,\n          'Points disposes and unbinds vertices collection without error'\n        );\n      } catch (e) {\n        assert.ok(false, 'Points dispose threw error: ' + e.message);\n      }\n    }\n  );\n\n  QUnit.test('Points.dispose - Individual Vector Unbinding', function (assert) {\n    assert.expect(7);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var points = two.makePoints([\n      new Two.Vector(0, 0),\n      new Two.Vector(100, 50),\n      new Two.Vector(50, 100),\n    ]);\n\n    var vector0EventFired = false;\n    var vector1EventFired = false;\n    var vector2EventFired = false;\n\n    points.vertices[0].bind('change', function () {\n      vector0EventFired = true;\n    });\n\n    points.vertices[1].bind('change', function () {\n      vector1EventFired = true;\n    });\n\n    points.vertices[2].bind('change', function () {\n      vector2EventFired = true;\n    });\n\n    // Verify events fire before dispose\n    points.vertices[0].trigger('change');\n    points.vertices[1].trigger('change');\n    points.vertices[2].trigger('change');\n    assert.ok(vector0EventFired, 'Vector 0 event fires before dispose');\n    assert.ok(vector1EventFired, 'Vector 1 event fires before dispose');\n    assert.ok(vector2EventFired, 'Vector 2 event fires before dispose');\n\n    // Reset\n    vector0EventFired = false;\n    vector1EventFired = false;\n    vector2EventFired = false;\n\n    // Dispose\n    points.dispose();\n\n    // Verify events don't fire after dispose\n    points.vertices[0].trigger('change');\n    points.vertices[1].trigger('change');\n    points.vertices[2].trigger('change');\n    assert.ok(!vector0EventFired, 'Vector 0 event does not fire after dispose');\n    assert.ok(!vector1EventFired, 'Vector 1 event does not fire after dispose');\n    assert.ok(!vector2EventFired, 'Vector 2 event does not fire after dispose');\n\n    // Verify _bound flag\n    assert.ok(!points.vertices[0]._bound, 'Vector 0 _bound flag is false');\n  });\n\n  QUnit.test('Points.dispose - Fill/Stroke Effect Disposal', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var fillGradient = two.makeLinearGradient(\n      0,\n      0,\n      1,\n      1,\n      new Two.Stop(0, 'red'),\n      new Two.Stop(1, 'yellow')\n    );\n\n    var strokeGradient = two.makeLinearGradient(\n      0,\n      0,\n      1,\n      0,\n      new Two.Stop(0, 'blue'),\n      new Two.Stop(1, 'green')\n    );\n\n    var points = two.makePoints([\n      new Two.Vector(50, 50),\n      new Two.Vector(150, 50),\n      new Two.Vector(100, 150),\n    ]);\n\n    points.fill = fillGradient;\n    points.stroke = strokeGradient;\n\n    two.update();\n\n    // Dispose points (should dispose both gradients)\n    try {\n      points.dispose();\n      assert.ok(\n        true,\n        'Points disposes with gradient fill/stroke without error'\n      );\n    } catch (e) {\n      assert.ok(false, 'Points dispose threw error: ' + e.message);\n    }\n  });\n\n  // ===========================\n  // Gradient Tests\n  // ===========================\n\n  QUnit.test('Gradient.dispose - SVG Element Removal', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var gradient = two.makeLinearGradient(\n      0,\n      0,\n      1,\n      1,\n      new Two.Stop(0, '#ff0000'),\n      new Two.Stop(1, '#0000ff')\n    );\n\n    var rect = two.makeRectangle(100, 100, 100, 100);\n    rect.fill = gradient;\n\n    two.update();\n\n    // Verify gradient has DOM element\n    var gradientElemBefore = two.renderer.domElement.querySelector(\n      '#' + gradient.id\n    );\n    assert.ok(\n      gradientElemBefore,\n      'Gradient element exists in DOM before dispose'\n    );\n    assert.ok(\n      gradient._renderer.elem,\n      'Gradient has _renderer.elem before dispose'\n    );\n\n    // Dispose gradient directly\n    gradient.dispose();\n\n    // Verify gradient DOM element removed\n    var gradientElemAfter = two.renderer.domElement.querySelector(\n      '#' + gradient.id\n    );\n    assert.ok(\n      !gradientElemAfter,\n      'Gradient element removed from DOM after dispose'\n    );\n  });\n\n  QUnit.test('Gradient.dispose - Direct Disposal', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var gradient = two.makeRadialGradient(\n      0.5,\n      0.5,\n      1,\n      new Two.Stop(0, 'white'),\n      new Two.Stop(1, 'black')\n    );\n\n    // Dispose gradient not attached to any shape\n    try {\n      gradient.dispose();\n      assert.ok(\n        true,\n        'Gradient disposes without error when not attached to shape'\n      );\n    } catch (e) {\n      assert.ok(false, 'Gradient dispose threw error: ' + e.message);\n    }\n  });\n\n  QUnit.test('Gradient.dispose - Event Unbinding', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var gradient = two.makeLinearGradient(\n      0,\n      0,\n      1,\n      1,\n      new Two.Stop(0, 'red'),\n      new Two.Stop(1, 'blue')\n    );\n\n    // Dispose gradient directly\n    try {\n      gradient.dispose();\n      assert.ok(true, 'Gradient disposes without error');\n    } catch (e) {\n      assert.ok(false, 'Gradient dispose threw error: ' + e.message);\n    }\n  });\n\n  // ===========================\n  // Texture Tests\n  // ===========================\n\n  QUnit.test('Texture.dispose - SVG Element Removal', function (assert) {\n    assert.expect(4);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var texture = new Two.Texture('./images/canvas/line@2x.png');\n    var rect = two.makeRectangle(100, 100, 100, 100);\n    rect.fill = texture;\n\n    two.update();\n\n    // Wait for texture to render\n    var textureElemBefore = two.renderer.domElement.querySelector(\n      '#' + texture.id\n    );\n    var hasElemBefore = !!texture._renderer.elem;\n    assert.ok(\n      hasElemBefore || !textureElemBefore,\n      'Texture rendering state tracked'\n    );\n\n    // Dispose texture directly\n    texture.dispose();\n\n    // Verify texture DOM element removed\n    var textureElemAfter = two.renderer.domElement.querySelector(\n      '#' + texture.id\n    );\n    assert.ok(\n      !textureElemAfter,\n      'Texture element removed from DOM after dispose'\n    );\n    assert.ok(\n      !texture._renderer.elem,\n      'Texture _renderer.elem deleted after dispose'\n    );\n    assert.ok(texture._renderer.type, 'Texture renderer type preserved');\n  });\n\n  QUnit.test('Texture.dispose - Parent Dispose Called', function (assert) {\n    assert.expect(3);\n\n    // eslint-disable-next-line no-unused-vars\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var texture = new Two.Texture('./images/canvas/line@2x.png');\n\n    // Bind event to texture\n    var eventFired = false;\n    texture.bind('change', function () {\n      eventFired = true;\n    });\n\n    texture.trigger('change');\n    assert.ok(eventFired, 'Texture event fires before dispose');\n\n    eventFired = false;\n\n    // Dispose (should call Element.dispose via inheritance)\n    texture.dispose();\n\n    // Verify Element.dispose was called (events unbound)\n    texture.trigger('change');\n    assert.ok(\n      !eventFired,\n      'Texture event does not fire after dispose (Element.dispose called)'\n    );\n    assert.ok(!texture._bound, 'Texture _bound flag is false');\n  });\n\n  QUnit.test('Texture.dispose - Event Unbinding', function (assert) {\n    assert.expect(3);\n\n    // eslint-disable-next-line no-unused-vars\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var texture = new Two.Texture('./images/canvas/line@2x.png');\n    var eventFired = false;\n\n    texture.bind('change', function () {\n      eventFired = true;\n    });\n\n    // Verify event fires before dispose\n    texture.trigger('change');\n    assert.ok(eventFired, 'Texture event fires before dispose');\n\n    // Reset\n    eventFired = false;\n\n    // Dispose\n    texture.dispose();\n\n    // Verify event doesn't fire after dispose\n    texture.trigger('change');\n    assert.ok(!eventFired, 'Texture event does not fire after dispose');\n    assert.ok(!texture._bound, 'Texture _bound flag is false');\n  });\n\n  // ===========================\n  // Sprite Tests\n  // ===========================\n\n  QUnit.test('Sprite.dispose - Animation Stop', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var texture = new Two.Texture('./images/canvas/line@2x.png');\n    var sprite = two.makeSprite(texture, 100, 100, 4, 1);\n    sprite.play();\n\n    assert.ok(sprite._playing, 'Sprite is playing before dispose');\n\n    // Dispose\n    sprite.dispose();\n\n    // Verify animation stopped\n    assert.ok(!sprite._playing, 'Sprite is not playing after dispose');\n  });\n\n  QUnit.test('Sprite.dispose - Callback Cleanup', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var texture = new Two.Texture('./images/canvas/line@2x.png');\n    var sprite = two.makeSprite(texture, 100, 100, 4, 1);\n\n    // eslint-disable-next-line no-unused-vars\n    var callbackFired = false;\n    sprite._onLastFrame = function () {\n      callbackFired = true;\n    };\n\n    assert.ok(\n      sprite._onLastFrame !== null,\n      'Sprite has _onLastFrame callback before dispose'\n    );\n\n    // Dispose\n    sprite.dispose();\n\n    // Verify callback cleared\n    assert.ok(\n      sprite._onLastFrame === null,\n      'Sprite _onLastFrame callback is null after dispose'\n    );\n  });\n\n  QUnit.test('Sprite.dispose - Timing Reset', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var texture = new Two.Texture('./images/canvas/line@2x.png');\n    var sprite = two.makeSprite(texture, 100, 100, 4, 1);\n    sprite.play();\n\n    // Manually set start time to simulate animation running\n    sprite._startTime = Date.now();\n\n    assert.ok(sprite._startTime > 0, 'Sprite has _startTime before dispose');\n\n    // Dispose\n    sprite.dispose();\n\n    // Verify timing reset\n    assert.equal(sprite._startTime, 0, 'Sprite _startTime is 0 after dispose');\n  });\n\n  QUnit.test('Sprite.dispose - Texture Disposal', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var texture = new Two.Texture('./images/canvas/line@2x.png');\n    var sprite = two.makeSprite(texture, 100, 100, 4, 1);\n\n    // Bind event to texture\n    var textureEventFired = false;\n    texture.bind('change', function () {\n      textureEventFired = true;\n    });\n\n    texture.trigger('change');\n    assert.ok(textureEventFired, 'Texture event fires before sprite dispose');\n\n    textureEventFired = false;\n\n    // Dispose sprite (should dispose texture)\n    sprite.dispose();\n\n    // Verify texture disposed\n    texture.trigger('change');\n    assert.ok(\n      !textureEventFired,\n      'Texture event does not fire after sprite dispose'\n    );\n    assert.ok(!texture._bound, 'Texture _bound flag is false');\n  });\n\n  // ===========================\n  // Image Tests\n  // ===========================\n\n  QUnit.test('Image.dispose - Texture Disposal', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var image = two.makeImage('./images/canvas/line@2x.png', 100, 100);\n\n    // Get the texture\n    var texture = image.texture;\n\n    // Bind event to texture\n    var textureEventFired = false;\n    texture.bind('change', function () {\n      textureEventFired = true;\n    });\n\n    texture.trigger('change');\n    assert.ok(textureEventFired, 'Texture event fires before image dispose');\n\n    textureEventFired = false;\n\n    // Dispose image (should dispose texture)\n    image.dispose();\n\n    // Verify texture disposed\n    texture.trigger('change');\n    assert.ok(\n      !textureEventFired,\n      'Texture event does not fire after image dispose'\n    );\n    assert.ok(!texture._bound, 'Texture _bound flag is false');\n  });\n\n  QUnit.test('Image.dispose - Inheritance Chain', function (assert) {\n    assert.expect(4);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var image = two.makeImage('./images/canvas/line@2x.png', 100, 100);\n\n    // Bind event to image\n    var eventFired = false;\n    image.bind('change', function () {\n      eventFired = true;\n    });\n\n    image.trigger('change');\n    assert.ok(eventFired, 'Image event fires before dispose');\n\n    eventFired = false;\n\n    two.update();\n\n    // Dispose image\n    image.dispose();\n\n    // Verify Element/Shape/Path dispose called (events unbound)\n    image.trigger('change');\n    assert.ok(!eventFired, 'Image event does not fire after dispose');\n\n    // Verify Rectangle/Path dispose called (vertices unbound)\n    assert.ok(\n      !image.vertices._bound,\n      'Image vertices collection _bound flag is false'\n    );\n\n    // Verify DOM cleanup\n    var imageElemAfter = two.renderer.domElement.querySelector('#' + image.id);\n    assert.ok(!imageElemAfter, 'Image DOM element removed');\n  });\n\n  // ===========================\n  // ImageSequence Tests\n  // ===========================\n\n  QUnit.test('ImageSequence.dispose - Animation Stop', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400,\n    }).appendTo(document.body);\n\n    var sequence = two.makeImageSequence(\n      ['./images/canvas/line@2x.png', './images/canvas/line@2x.png'],\n      100,\n      100\n    );\n\n    sequence.play();\n\n    assert.ok(sequence._playing, 'ImageSequence is playing before dispose');\n\n    // Dispose\n    sequence.dispose();\n\n    // Verify animation stopped\n    assert.ok(!sequence._playing, 'ImageSequence is not playing after dispose');\n  });\n\n  QUnit.test(\n    'ImageSequence.dispose - Textures Collection Unbinding',\n    function (assert) {\n      assert.expect(1);\n\n      var two = new Two({\n        type: Two.Types.svg,\n        width: 400,\n        height: 400,\n      }).appendTo(document.body);\n\n      var sequence = two.makeImageSequence(\n        ['./images/canvas/line@2x.png', './images/canvas/line@2x.png'],\n        100,\n        100\n      );\n\n      // Dispose should unbind textures collection without error\n      try {\n        sequence.dispose();\n        assert.ok(\n          true,\n          'ImageSequence disposes and unbinds textures collection without error'\n        );\n      } catch (e) {\n        assert.ok(false, 'ImageSequence dispose threw error: ' + e.message);\n      }\n    }\n  );\n\n  QUnit.test(\n    'ImageSequence.dispose - Individual Texture Disposal',\n    function (assert) {\n      assert.expect(5);\n\n      var two = new Two({\n        type: Two.Types.svg,\n        width: 400,\n        height: 400,\n      }).appendTo(document.body);\n\n      var sequence = two.makeImageSequence(\n        ['./images/canvas/line@2x.png', './images/canvas/line@2x.png'],\n        100,\n        100\n      );\n\n      var texture0EventFired = false;\n      var texture1EventFired = false;\n\n      sequence.textures[0].bind('change', function () {\n        texture0EventFired = true;\n      });\n\n      sequence.textures[1].bind('change', function () {\n        texture1EventFired = true;\n      });\n\n      // Verify events fire before dispose\n      sequence.textures[0].trigger('change');\n      sequence.textures[1].trigger('change');\n      assert.ok(texture0EventFired, 'Texture 0 event fires before dispose');\n      assert.ok(texture1EventFired, 'Texture 1 event fires before dispose');\n\n      // Reset\n      texture0EventFired = false;\n      texture1EventFired = false;\n\n      // Dispose sequence (should dispose all textures)\n      sequence.dispose();\n\n      // Verify textures disposed\n      sequence.textures[0].trigger('change');\n      sequence.textures[1].trigger('change');\n      assert.ok(\n        !texture0EventFired,\n        'Texture 0 event does not fire after sequence dispose'\n      );\n      assert.ok(\n        !texture1EventFired,\n        'Texture 1 event does not fire after sequence dispose'\n      );\n      assert.ok(!sequence.textures[0]._bound, 'Texture 0 _bound flag is false');\n    }\n  );\n\n  QUnit.test(\n    'ImageSequence.dispose - Incomplete Collection',\n    function (assert) {\n      assert.expect(1);\n\n      var two = new Two({\n        type: Two.Types.svg,\n        width: 400,\n        height: 400,\n      }).appendTo(document.body);\n\n      var sequence = two.makeImageSequence(\n        ['./images/canvas/line@2x.png', './images/canvas/line@2x.png'],\n        100,\n        100\n      );\n\n      // Simulate incomplete collection by removing unbind method\n      var originalUnbind = sequence.textures.unbind;\n      delete sequence.textures.unbind;\n\n      // Dispose should handle incomplete collection gracefully\n      try {\n        sequence.dispose();\n        assert.ok(\n          true,\n          'ImageSequence disposes without error when textures collection has no unbind method'\n        );\n      } catch (e) {\n        assert.ok(\n          false,\n          'ImageSequence dispose threw error with incomplete collection: ' +\n            e.message\n        );\n      }\n\n      // Restore for cleanup\n      sequence.textures.unbind = originalUnbind;\n    }\n  );\n})();\n"
  },
  {
    "path": "tests/suite/hit-test.js",
    "content": "QUnit.module('Hit Testing');\n\nQUnit.test('Shape.contains evaluates fill and stroke geometry', function (assert) {\n  assert.expect(8);\n\n  var two = new Two({ width: 400, height: 400, autostart: false });\n\n  var circle = new Two.Circle(100, 100, 20);\n  two.add(circle);\n  two.update();\n\n  assert.ok(circle.contains(100, 100), 'Circle contains its center point.');\n  assert.notOk(circle.contains(140, 100), 'Circle excludes distant point.');\n\n  var line = new Two.Line(0, 0, 100, 0);\n  line.linewidth = 12;\n  two.add(line);\n  two.update();\n\n  assert.ok(line.contains(50, 4), 'Line stroke hit succeeds within tolerance.');\n  assert.notOk(line.contains(50, 40), 'Line stroke rejects far point.');\n\n  var rect = new Two.Rectangle(150, 150, 40, 40);\n  rect.rotation = Math.PI / 4;\n  two.add(rect);\n  two.update();\n\n  assert.ok(rect.contains(150, 150), 'Rotated rectangle contains its center.');\n  assert.notOk(rect.contains(200, 150), 'Rotated rectangle excludes outside point.');\n\n  var invisible = new Two.Circle(200, 200, 10);\n  invisible.visible = false;\n  two.add(invisible);\n  two.update();\n\n  assert.notOk(\n    invisible.contains(200, 200),\n    'Invisible shapes bypass contains unless ignoreVisibility is set.'\n  );\n  assert.ok(\n    invisible.contains(200, 200, { ignoreVisibility: true }),\n    'ignoreVisibility option bypasses visibility check.'\n  );\n});\n\nQUnit.test('Two#getShapesAtPoint respects options', function (assert) {\n  assert.expect(6);\n\n  var two = new Two({ width: 400, height: 400, autostart: false });\n\n  var circle = new Two.Circle(100, 100, 20);\n  two.add(circle);\n\n  var line = new Two.Line(0, 0, 100, 0);\n  line.linewidth = 12;\n  two.add(line);\n\n  var rect = new Two.Rectangle(150, 150, 40, 40);\n  rect.rotation = Math.PI / 4;\n  two.add(rect);\n\n  var hidden = new Two.Circle(200, 200, 10);\n  hidden.visible = false;\n  two.add(hidden);\n\n  two.update();\n\n  var circleHits = two.getShapesAtPoint(100, 100);\n  assert.ok(circleHits.indexOf(circle) > -1, 'Circle reported at its center.');\n\n  var sceneHits = two.scene.getShapesAtPoint(100, 100);\n  assert.ok(\n    sceneHits.indexOf(circle) > -1,\n    'Scene group reports circle at its center.'\n  );\n\n  var deepest = two.getShapesAtPoint(50, 4, { mode: 'deepest' });\n  assert.deepEqual(deepest, [line], 'Deepest mode returns top-most shape only.');\n\n  var visibleHits = two.getShapesAtPoint(200, 200);\n  assert.strictEqual(\n    visibleHits.indexOf(hidden),\n    -1,\n    'Hidden shapes excluded when visibleOnly is true.'\n  );\n\n  var allHits = two.getShapesAtPoint(200, 200, { visibleOnly: false });\n  assert.ok(\n    allHits.indexOf(hidden) > -1,\n    'Hidden shapes included when visibleOnly is false.'\n  );\n\n  var sceneAll = two.scene.getShapesAtPoint(200, 200, {\n    visibleOnly: false,\n  });\n  assert.ok(\n    sceneAll.indexOf(hidden) > -1,\n    'Scene group honours visibleOnly option.'\n  );\n});\n"
  },
  {
    "path": "tests/suite/release.js",
    "content": "/**\n * Tests Two.js release() Method Enhanced Functionality:\n * + Basic release functionality\n * + SVG DOM element cleanup\n * + Canvas renderer cleanup  \n * + WebGL resource cleanup\n * + Memory leak prevention\n * + Recursive cleanup of nested objects\n */\n\n(function () {\n  QUnit.module('Release Method');\n\n  QUnit.test('Two.release - Basic Functionality', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      width: 400,\n      height: 400\n    });\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    \n    // Test release method exists\n    assert.ok(typeof two.release === 'function', 'Two.release method exists');\n    \n    // Test release returns the object\n    var result = two.release(rect);\n    assert.equal(result, rect, 'Two.release returns the released object');\n    \n    // Test release works with undefined (the default case)\n    var undefinedResult = two.release();\n    assert.ok(undefinedResult !== null, 'Two.release handles undefined gracefully by releasing scene');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - SVG DOM Element Cleanup', function (assert) {\n    assert.expect(6);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400\n    }).appendTo(document.body);\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    var circle = two.makeCircle(200, 200, 25);\n    \n    two.update();\n    \n    // Verify elements exist in DOM\n    var rectElem = two.renderer.domElement.querySelector('#' + rect.id);\n    var circleElem = two.renderer.domElement.querySelector('#' + circle.id);\n    \n    assert.ok(rectElem, 'Rectangle SVG element exists in DOM before release');\n    assert.ok(circleElem, 'Circle SVG element exists in DOM before release');\n    assert.ok(rect._renderer.elem, 'Rectangle has _renderer.elem before release');\n    assert.ok(circle._renderer.elem, 'Circle has _renderer.elem before release');\n    \n    // Release objects\n    two.release(rect);\n    two.release(circle);\n    \n    // Verify elements are removed from DOM\n    var rectElemAfter = two.renderer.domElement.querySelector('#' + rect.id);\n    var circleElemAfter = two.renderer.domElement.querySelector('#' + circle.id);\n    \n    assert.ok(!rectElemAfter, 'Rectangle SVG element removed from DOM after release');\n    assert.ok(!circleElemAfter, 'Circle SVG element removed from DOM after release');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - SVG Gradient Cleanup', function (assert) {\n    assert.expect(4);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400\n    }).appendTo(document.body);\n\n    var gradient = two.makeLinearGradient(0, 0, 1, 1,\n      new Two.Stop(0, 'red'),\n      new Two.Stop(1, 'blue')\n    );\n    \n    var rect = two.makeRectangle(100, 100, 50, 50);\n    rect.fill = gradient;\n    \n    two.update();\n    \n    // Verify gradient exists in DOM\n    var gradientElem = two.renderer.domElement.querySelector('#' + gradient.id);\n    assert.ok(gradientElem, 'Gradient SVG element exists in DOM before release');\n    assert.ok(gradient._renderer.elem, 'Gradient has _renderer.elem before release');\n    \n    // Release gradient\n    two.release(gradient);\n    \n    // Verify gradient is removed from DOM\n    var gradientElemAfter = two.renderer.domElement.querySelector('#' + gradient.id);\n    assert.ok(!gradientElemAfter, 'Gradient SVG element removed from DOM after release');\n    assert.ok(!gradient._renderer.elem, 'Gradient _renderer.elem is cleared after release');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - Canvas Renderer Cleanup', function (assert) {\n    assert.expect(3);\n\n    var two = new Two({\n      type: Two.Types.canvas,\n      width: 400,\n      height: 400\n    }).appendTo(document.body);\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    \n    two.update();\n    \n    // Verify renderer context exists\n    assert.ok(two.renderer.ctx, 'Canvas renderer has context');\n    assert.ok(rect._renderer, 'Rectangle has _renderer object');\n    \n    // Release object\n    two.release(rect);\n    \n    // Verify cleanup (context references should be cleared if they existed)\n    // Canvas cleanup mainly involves clearing cached references\n    assert.ok(true, 'Canvas release completed without errors');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - WebGL Resource Cleanup', function (assert) {\n    assert.expect(5);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400\n    }).appendTo(document.body);\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    \n    two.update();\n    \n    // Verify WebGL context and basic setup\n    var gl = two.renderer.ctx;\n    assert.ok(gl, 'WebGL context exists');\n    assert.ok(rect._renderer, 'Rectangle has _renderer object');\n    \n    // Test that we can release WebGL objects without errors\n    var releaseResult = two.release(rect);\n    assert.equal(releaseResult, rect, 'WebGL object release returns the object');\n    \n    // Test effect cleanup - manually set an effect and verify it gets cleared\n    var rect2 = two.makeRectangle(150, 150, 50, 50);\n    rect2._renderer.effect = 'test-effect';\n    two.release(rect2);\n    assert.ok(!rect2._renderer.effect, 'WebGL effect cleared after release');\n    \n    assert.ok(true, 'WebGL release completed without errors');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - Recursive Group Cleanup', function (assert) {\n    assert.expect(8);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400\n    }).appendTo(document.body);\n\n    var group = two.makeGroup();\n    var rect1 = two.makeRectangle(100, 100, 50, 50);\n    var rect2 = two.makeRectangle(200, 200, 50, 50);\n    var subGroup = two.makeGroup();\n    var circle = two.makeCircle(150, 150, 25);\n    \n    // Build hierarchy\n    group.add(rect1, rect2, subGroup);\n    subGroup.add(circle);\n    \n    two.update();\n    \n    // Verify all elements exist in DOM\n    assert.ok(two.renderer.domElement.querySelector('#' + group.id), 'Group exists in DOM');\n    assert.ok(two.renderer.domElement.querySelector('#' + rect1.id), 'Rect1 exists in DOM');\n    assert.ok(two.renderer.domElement.querySelector('#' + rect2.id), 'Rect2 exists in DOM');\n    assert.ok(two.renderer.domElement.querySelector('#' + subGroup.id), 'SubGroup exists in DOM');\n    assert.ok(two.renderer.domElement.querySelector('#' + circle.id), 'Circle exists in DOM');\n    \n    // Release the group (should recursively clean up children)\n    two.release(group);\n    \n    // Verify all elements are removed from DOM\n    assert.ok(!two.renderer.domElement.querySelector('#' + rect1.id), 'Rect1 removed from DOM');\n    assert.ok(!two.renderer.domElement.querySelector('#' + rect2.id), 'Rect2 removed from DOM');\n    assert.ok(!two.renderer.domElement.querySelector('#' + circle.id), 'Circle removed from DOM');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - Event Unbinding', function (assert) {\n    assert.expect(4);\n\n    var two = new Two({\n      width: 400,\n      height: 400\n    });\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    var eventFired = false;\n    \n    // Bind event listener\n    rect.bind('change', function() {\n      eventFired = true;\n    });\n    \n    // Test event fires before release\n    rect.trigger('change');\n    assert.ok(eventFired, 'Event fires before release');\n    \n    // Reset flag\n    eventFired = false;\n    \n    // Release object\n    two.release(rect);\n    \n    // Test event doesn't fire after release\n    rect.trigger('change');\n    assert.ok(!eventFired, 'Event does not fire after release');\n    \n    // Test vertex event unbinding\n    var path = two.makePath(0, 0, 100, 0, 100, 100, 0, 100);\n    var vertexEventFired = false;\n    \n    if (path.vertices && path.vertices[0]) {\n      path.vertices[0].bind('change', function() {\n        vertexEventFired = true;\n      });\n      \n      path.vertices[0].trigger('change');\n      assert.ok(vertexEventFired, 'Vertex event fires before release');\n      \n      vertexEventFired = false;\n      two.release(path);\n      \n      path.vertices[0].trigger('change');\n      assert.ok(!vertexEventFired, 'Vertex event does not fire after release');\n    } else {\n      assert.ok(true, 'Path has no vertices to test');\n      assert.ok(true, 'Skipping vertex event test');\n    }\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - Effects Cleanup', function (assert) {\n    assert.expect(6);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400\n    }).appendTo(document.body);\n\n    var gradient = two.makeLinearGradient(0, 0, 1, 1,\n      new Two.Stop(0, 'red'),\n      new Two.Stop(1, 'blue')\n    );\n    \n    var rect = two.makeRectangle(100, 100, 50, 50);\n    rect.fill = gradient;\n    rect.stroke = gradient;\n    \n    var eventFired = false;\n    gradient.bind('change', function() {\n      eventFired = true;\n    });\n    \n    two.update();\n    \n    // Test effects are bound\n    gradient.trigger('change');\n    assert.ok(eventFired, 'Gradient event fires before release');\n    \n    eventFired = false;\n    \n    // Release object (should clean up effects)\n    two.release(rect);\n    \n    // Test that gradient events are unbound for fill\n    if (rect.fill && typeof rect.fill.unbind === 'function') {\n      gradient.trigger('change');\n      assert.ok(!eventFired, 'Fill gradient event does not fire after release');\n    } else {\n      assert.ok(true, 'Fill gradient already unbound or not applicable');\n    }\n    \n    // Test that gradient events are unbound for stroke  \n    if (rect.stroke && typeof rect.stroke.unbind === 'function') {\n      gradient.trigger('change');\n      assert.ok(!eventFired, 'Stroke gradient event does not fire after release');\n    } else {\n      assert.ok(true, 'Stroke gradient already unbound or not applicable');\n    }\n    \n    // Test gradient disposal\n    var gradientElem = two.renderer.domElement.querySelector('#' + gradient.id);\n    if (gradientElem) {\n      assert.ok(gradientElem, 'Gradient still exists in DOM (expected for shared effects)');\n    } else {\n      assert.ok(true, 'Gradient removed from DOM');\n    }\n    \n    // Release the gradient itself\n    two.release(gradient);\n    var gradientElemAfter = two.renderer.domElement.querySelector('#' + gradient.id);\n    assert.ok(!gradientElemAfter, 'Gradient removed from DOM after direct release');\n    \n    assert.ok(true, 'Effects cleanup completed without errors');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.release - Scene Release', function (assert) {\n    assert.expect(4);\n\n    var two = new Two({\n      type: Two.Types.svg,\n      width: 400,\n      height: 400\n    }).appendTo(document.body);\n\n    var rect = two.makeRectangle(100, 100, 50, 50);\n    var circle = two.makeCircle(200, 200, 25);\n    \n    two.update();\n    \n    // Verify elements exist\n    assert.ok(two.renderer.domElement.querySelector('#' + rect.id), 'Rectangle exists before scene release');\n    assert.ok(two.renderer.domElement.querySelector('#' + circle.id), 'Circle exists before scene release');\n    \n    // Release entire scene\n    two.release();\n    \n    // Verify all elements are cleaned up\n    assert.ok(!two.renderer.domElement.querySelector('#' + rect.id), 'Rectangle removed after scene release');\n    assert.ok(!two.renderer.domElement.querySelector('#' + circle.id), 'Circle removed after scene release');\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n})();"
  },
  {
    "path": "tests/suite/shapes.js",
    "content": "QUnit.module('Primitives');\n\nQUnit.test('Two.Points', function (assert) {\n  var props = Two.Points.Properties.slice(0, 7);\n\n  assert.expect(props.length + 15);\n\n  var two = new Two();\n  var points = new Two.Points();\n  two.add(points);\n  two.update();\n\n  points = new Two.Points([\n    new Two.Anchor(0, 0),\n    new Two.Anchor(100, 100),\n    new Two.Anchor(200, 200),\n    new Two.Anchor(300, 300),\n  ]);\n\n  assert.equal(points.vertices.length, 4, 'Amount of vertices set correctly.');\n\n  points.fill = 'red';\n  assert.equal(points.fill, 'red', 'Can get property fill correctly.');\n  assert.equal(points._fill, 'red', 'Can set property fill correctly.');\n\n  points.stroke = 'red';\n  assert.equal(points.stroke, 'red', 'Can get property stroke correctly.');\n  assert.equal(points._stroke, 'red', 'Can set property stroke correctly.');\n\n  points.size = 6;\n  assert.equal(points.size, 6, 'Can get property size correctly.');\n  assert.equal(points._size, 6, 'Can set property size correctly.');\n\n  points.sizeAttenuation = true;\n  assert.equal(\n    points.sizeAttenuation,\n    true,\n    'Can get property sizeAttenuation correctly.'\n  );\n  assert.equal(\n    points._sizeAttenuation,\n    true,\n    'Can set property sizeAttenuation correctly.'\n  );\n\n  points.strokeAttenuation = true;\n  assert.equal(\n    points.strokeAttenuation,\n    true,\n    'Can get property strokeAttenuation correctly.'\n  );\n  assert.equal(\n    points._strokeAttenuation,\n    true,\n    'Can set property strokeAttenuation correctly.'\n  );\n\n  points.dashes = [2, 2];\n  assert.equal(\n    points.dashes.length === 2 &&\n      points.dashes[0] === 2 &&\n      points.dashes[1] === 2,\n    true,\n    'Can get property dashes correctly.'\n  );\n  assert.equal(\n    points._dashes.length === 2 &&\n      points._dashes[0] === 2 &&\n      points._dashes[1] === 2,\n    true,\n    'Can set property dashes correctly.'\n  );\n\n  points.className = 'ui-points';\n  assert.equal(\n    points.className,\n    'ui-points',\n    'Can get property className correctly.'\n  );\n  assert.equal(\n    points._className,\n    'ui-points',\n    'Can set property className correctly.'\n  );\n\n  points._update();\n  points.flagReset();\n\n  for (var i = 0; i < props.length; i++) {\n    var prop = props[i];\n    assert.equal(\n      points['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      false,\n      'Reset flag ' + prop + ' correctly.'\n    );\n  }\n\n  points.beginning = 0.5;\n  points.ending = 0.5;\n  two.add(points);\n  two.update();\n});\n\nQUnit.test('Two.Points Object Conversion', function (assert) {\n  assert.expect(7);\n\n  var point = new Two.Points(new Two.Anchor(50, 50));\n  point.id = 'my-point';\n\n  var obj = point.toObject();\n\n  assert.equal(typeof obj, 'object', 'Two.Points.toObject creates an object');\n  assert.equal(obj.vertices[0].x, 50, 'Two.Points.toObject preserves x');\n  assert.equal(obj.vertices[0].y, 50, 'Two.Points.toObject preserves y');\n  assert.equal(obj.id, 'my-point', 'Two.Points.toObject preserves id');\n\n  var newPoint = Two.Points.fromObject(obj);\n\n  assert.equal(\n    newPoint.vertices[0].x,\n    point.vertices[0].x,\n    'Two.Points.fromObject preserves x'\n  );\n  assert.equal(\n    newPoint.vertices[0].y,\n    point.vertices[0].y,\n    'Two.Points.fromObject preserves y'\n  );\n\n  var copiedPoint = new Two.Points().copy(point);\n  assert.deepEqual(\n    { ...copiedPoint.toObject(), id: point.id },\n    point.toObject(),\n    'Two.Points.copy creates identical point'\n  );\n});\n\nQUnit.test('Two.ArcSegment', function (assert) {\n  assert.expect(Two.ArcSegment.Properties.length * 4 + 2);\n\n  var innerRadius = 5;\n  var outerRadius = 10;\n  var startAngle = 0;\n  var endAngle = Math.PI / 2;\n\n  var properties = [startAngle, endAngle, innerRadius, outerRadius];\n\n  var path = new Two.ArcSegment(\n    0,\n    0,\n    innerRadius,\n    outerRadius,\n    startAngle,\n    endAngle\n  );\n  assert.equal(\n    path.vertices.length,\n    Two.Resolution * 3,\n    'Amount of vertices set correctly.'\n  );\n\n  for (var i = 0; i < Two.ArcSegment.Properties.length; i++) {\n    var prop = Two.ArcSegment.Properties[i];\n    assert.equal(\n      path[prop],\n      properties[i],\n      'Can get property ' + prop + ' correctly.'\n    );\n    path[prop] = properties[i] * 2;\n    assert.equal(\n      path[prop],\n      properties[i] * 2,\n      'Can set property ' + prop + ' correctly.'\n    );\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      true,\n      'Set ' + prop + \"'s property flag correctly.\"\n    );\n  }\n\n  path._update();\n  path.flagReset();\n\n  for (var i = 0; i < Two.ArcSegment.Properties.length; i++) {\n    var prop = Two.ArcSegment.Properties[i];\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      false,\n      'Reset flag ' + prop + ' correctly.'\n    );\n  }\n\n  path.closed = false;\n  path._update();\n\n  assert.equal(\n    path.vertices[0].equals(path.vertices[path.vertices.length - 1]),\n    true,\n    'Circle has standardized vertex generation when shape is not closed'\n  );\n});\n\nQUnit.test('Two.Path.smooth assigns handles', function (assert) {\n  assert.expect(5);\n\n  var a0 = new Two.Anchor(0, 0);\n  var a1 = new Two.Anchor(50, 100);\n  var a2 = new Two.Anchor(100, 0);\n  var path = new Two.Path([a0, a1, a2]);\n\n  assert.strictEqual(\n    path.automatic,\n    true,\n    'automatic defaults to true before smoothing'\n  );\n\n  path.smooth({ type: 'continuous' });\n\n  assert.strictEqual(\n    path.automatic,\n    false,\n    'smooth disables automatic plotting for manual control'\n  );\n\n  var middle = path.vertices[1];\n  assert.ok(\n    middle.controls.left.length() > 0,\n    'left handle assigned on interior vertex'\n  );\n  assert.ok(\n    middle.controls.right.length() > 0,\n    'right handle assigned on interior vertex'\n  );\n\n  assert.strictEqual(\n    middle.command,\n    Two.Commands.curve,\n    'middle vertex command converted to curve'\n  );\n});\n\nQUnit.test('Two.Path.subdivide recomputes handles', function (assert) {\n  assert.expect(5);\n\n  var start = new Two.Anchor(0, 0);\n  start.controls.right.set(50, 0);\n  var end = new Two.Anchor(100, 0);\n  end.controls.left.set(-50, 0);\n  var path = new Two.Path([start, end]);\n\n  path.subdivide(1);\n\n  assert.strictEqual(path.vertices.length, 3, 'midpoint inserted');\n  assert.ok(\n    path.vertices[0].controls.right.length() > 0,\n    'original start handle preserved after subdivision'\n  );\n  assert.ok(\n    path.vertices[2].controls.left.length() > 0,\n    'original end handle preserved after subdivision'\n  );\n\n  var mid = path.vertices[1];\n  assert.strictEqual(\n    mid.command,\n    Two.Commands.curve,\n    'midpoint inherits curve command'\n  );\n  assert.ok(\n    mid.controls.left.length() > 0 && mid.controls.right.length() > 0,\n    'midpoint handles computed from original curve'\n  );\n});\n\nQUnit.test('Two.ArcSegment Object Conversion', function (assert) {\n  assert.expect(11);\n\n  // Create original arc segment\n  var innerRadius = 5;\n  var outerRadius = 10;\n  var startAngle = 0;\n  var endAngle = Math.PI / 2;\n  var arcSegment = new Two.ArcSegment(\n    0,\n    0,\n    innerRadius,\n    outerRadius,\n    startAngle,\n    endAngle\n  );\n  arcSegment.fill = '#0000ff';\n  arcSegment.stroke = '#ff00ff';\n  arcSegment.linewidth = 3;\n  arcSegment.id = 'my-arcSegment';\n\n  // Convert to object\n  var obj = arcSegment.toObject();\n\n  // Test object has expected properties\n  assert.equal(\n    typeof obj,\n    'object',\n    'Two.ArcSegment.toObject creates an object'\n  );\n  assert.equal(\n    obj.innerRadius,\n    innerRadius,\n    'Two.ArcSegment.toObject preserves innerRadius'\n  );\n  assert.equal(\n    obj.outerRadius,\n    outerRadius,\n    'Two.ArcSegment.toObject preserves outerRadius'\n  );\n  assert.equal(\n    obj.startAngle,\n    startAngle,\n    'Two.ArcSegment.toObject preserves startAngle'\n  );\n  assert.equal(\n    obj.endAngle,\n    endAngle,\n    'Two.ArcSegment.toObject preserves endAngle'\n  );\n  assert.equal(obj.fill, '#0000ff', 'Two.ArcSegment.toObject preserves fill');\n  assert.equal(obj.id, 'my-arcSegment', 'Two.ArcSegment.toObject preserves id');\n\n  // Create new arc segment from object\n  var newArcSegment = Two.ArcSegment.fromObject(obj);\n\n  // Test new circle matches original\n  assert.equal(\n    newArcSegment.radius,\n    arcSegment.radius,\n    'Two.ArcSegment.fromObject preserves radius'\n  );\n  assert.equal(\n    newArcSegment.fill,\n    arcSegment.fill,\n    'Two.ArcSegment.fromObject preserves fill'\n  );\n  assert.equal(\n    newArcSegment.id,\n    arcSegment.id,\n    'Two.ArcSegment.fromObject preserves id'\n  );\n\n  // Test copy method\n  var copiedArcSegment = new Two.ArcSegment().copy(arcSegment);\n  assert.deepEqual(\n    { ...copiedArcSegment.toObject(), id: arcSegment.id },\n    arcSegment.toObject(),\n    'Two.ArcSegment.copy creates identical arcSegment'\n  );\n});\n\nQUnit.test('Two.Circle', function (assert) {\n  assert.expect(Two.Circle.Properties.length * 4 + 2);\n\n  var radius = 50;\n  var properties = [radius];\n\n  var path = new Two.Circle(0, 0, radius);\n  assert.equal(path.vertices.length, 4, 'Amount of vertices set correctly.');\n\n  for (var i = 0; i < Two.Circle.Properties.length; i++) {\n    var prop = Two.Circle.Properties[i];\n    assert.equal(\n      path[prop],\n      properties[i],\n      'Can get property ' + prop + ' correctly.'\n    );\n    path[prop] = properties[i] * 2;\n    assert.equal(\n      path[prop],\n      properties[i] * 2,\n      'Can set property ' + prop + ' correctly.'\n    );\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      true,\n      'Set ' + prop + \"'s property flag correctly.\"\n    );\n  }\n\n  path._update();\n  path.flagReset();\n\n  for (var i = 0; i < Two.Circle.Properties.length; i++) {\n    var prop = Two.Circle.Properties[i];\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      false,\n      'Reset flag ' + prop + ' correctly.'\n    );\n  }\n\n  path.closed = false;\n  path._update();\n\n  assert.equal(\n    path.vertices[0].equals(path.vertices[path.vertices.length - 1]),\n    true,\n    'Circle has standardized vertex generation when shape is not closed'\n  );\n});\n\nQUnit.test('Two.Circle Object Conversion', function (assert) {\n  assert.expect(8);\n\n  // Create original circle\n  var circle = new Two.Circle(100, 100, 50);\n  circle.fill = '#0000ff';\n  circle.stroke = '#ff00ff';\n  circle.linewidth = 3;\n  circle.id = 'my-circle';\n\n  // Convert to object\n  var obj = circle.toObject();\n\n  // Test object has expected properties\n  assert.equal(typeof obj, 'object', 'Two.Circle.toObject creates an object');\n  assert.equal(obj.radius, 50, 'Two.Circle.toObject preserves radius');\n  assert.equal(obj.fill, '#0000ff', 'Two.Circle.toObject preserves fill');\n  assert.equal(obj.id, 'my-circle', 'Two.Circle.toObject preserves id');\n\n  // Create new circle from object\n  var newCircle = Two.Circle.fromObject(obj);\n\n  // Test new circle matches original\n  assert.equal(\n    newCircle.radius,\n    circle.radius,\n    'Two.Circle.fromObject preserves radius'\n  );\n  assert.equal(\n    newCircle.fill,\n    circle.fill,\n    'Two.Circle.fromObject preserves fill'\n  );\n  assert.equal(newCircle.id, circle.id, 'Two.Circle.fromObject preserves id');\n\n  // Test copy method\n  var copiedCircle = new Two.Circle().copy(circle);\n  assert.deepEqual(\n    { ...copiedCircle.toObject(), id: circle.id },\n    circle.toObject(),\n    'Two.Circle.copy creates identical circle'\n  );\n});\n\nQUnit.test('Two.Ellipse', function (assert) {\n  assert.expect(Two.Ellipse.Properties.length * 4 + 2);\n\n  var rx = 50;\n  var ry = 75;\n  var properties = [rx * 2, ry * 2];\n\n  var path = new Two.Ellipse(0, 0, rx, ry);\n  assert.equal(path.vertices.length, 4, 'Amount of vertices set correctly.');\n\n  for (var i = 0; i < Two.Ellipse.Properties.length; i++) {\n    var prop = Two.Ellipse.Properties[i];\n    assert.equal(\n      path[prop],\n      properties[i],\n      'Can get property ' + prop + ' correctly.'\n    );\n    path[prop] = properties[i] * 2;\n    assert.equal(\n      path[prop],\n      properties[i] * 2,\n      'Can set property ' + prop + ' correctly.'\n    );\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      true,\n      'Set ' + prop + \"'s property flag correctly.\"\n    );\n  }\n\n  path._update();\n  path.flagReset();\n\n  for (var i = 0; i < Two.Ellipse.Properties.length; i++) {\n    var prop = Two.Ellipse.Properties[i];\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      false,\n      'Reset flag ' + prop + ' correctly.'\n    );\n  }\n\n  path.closed = false;\n  path._update();\n\n  assert.equal(\n    path.vertices[0].equals(path.vertices[path.vertices.length - 1]),\n    true,\n    'Ellipse has standardized vertex generation when shape is not closed'\n  );\n});\n\nQUnit.test('Two.Ellipse Object Conversion', function (assert) {\n  assert.expect(9);\n\n  // Create original ellipse\n  var ellipse = new Two.Ellipse(100, 100, 50, 75);\n  ellipse.fill = '#0000ff';\n  ellipse.stroke = '#ff00ff';\n  ellipse.linewidth = 3;\n  ellipse.id = 'my-ellipse';\n\n  // Convert to object\n  var obj = ellipse.toObject();\n\n  // Test object has expected properties\n  assert.equal(typeof obj, 'object', 'Two.Ellipse.toObject creates an object');\n  assert.equal(obj.width, 100, 'Two.Ellipse.toObject preserves width');\n  assert.equal(obj.height, 150, 'Two.Ellipse.toObject preserves height');\n  assert.equal(obj.fill, '#0000ff', 'Two.Ellipse.toObject preserves fill');\n  assert.equal(obj.id, 'my-ellipse', 'Two.Ellipse.toObject preserves id');\n\n  // Create new ellipse from object\n  var newEllipse = Two.Ellipse.fromObject(obj);\n\n  // Test new ellipse matches original\n  assert.equal(\n    newEllipse.width,\n    ellipse.width,\n    'Two.Ellipse.fromObject preserves width'\n  );\n  assert.equal(\n    newEllipse.height,\n    ellipse.height,\n    'Two.Ellipse.fromObject preserves height'\n  );\n  assert.equal(\n    newEllipse.fill,\n    ellipse.fill,\n    'Two.Ellipse.fromObject preserves fill'\n  );\n\n  // Test copy method\n  var copiedEllipse = new Two.Ellipse().copy(ellipse);\n  assert.deepEqual(\n    { ...copiedEllipse.toObject(), id: ellipse.id },\n    ellipse.toObject(),\n    'Two.Ellipse.copy creates identical ellipse'\n  );\n});\n\nQUnit.test('Two.Polygon', function (assert) {\n  assert.expect(Two.Polygon.Properties.length * 4 + 1);\n\n  var radius = 50;\n  var sides = 5;\n  var properties = [radius * 2, radius * 2, sides];\n\n  var path = new Two.Polygon(0, 0, radius, sides);\n\n  for (var i = 0; i < Two.Polygon.Properties.length; i++) {\n    var prop = Two.Polygon.Properties[i];\n    assert.equal(\n      path[prop],\n      properties[i],\n      'Can get property ' + prop + ' correctly.'\n    );\n    path[prop] = properties[i] * 2;\n    assert.equal(\n      path[prop],\n      properties[i] * 2,\n      'Can set property ' + prop + ' correctly.'\n    );\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      true,\n      'Set ' + prop + \"'s property flag correctly.\"\n    );\n  }\n\n  path._update();\n  path.flagReset();\n\n  for (var i = 0; i < Two.Polygon.Properties.length; i++) {\n    var prop = Two.Polygon.Properties[i];\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      false,\n      'Reset flag ' + prop + ' correctly.'\n    );\n  }\n\n  path.closed = false;\n  path._update();\n\n  assert.equal(\n    path.vertices[0].equals(path.vertices[path.vertices.length - 1]),\n    true,\n    'Polygon has standardized vertex generation when shape is not closed'\n  );\n});\n\nQUnit.test('Two.Polygon Object Conversion', function (assert) {\n  assert.expect(9);\n\n  // Create original polygon\n  var polygon = new Two.Polygon(100, 100, 50, 5);\n  polygon.fill = '#0000ff';\n  polygon.stroke = '#ff00ff';\n  polygon.linewidth = 3;\n  polygon.id = 'my-polygon';\n\n  // Convert to object\n  var obj = polygon.toObject();\n\n  // Test object has expected properties\n  assert.equal(typeof obj, 'object', 'Two.Polygon.toObject creates an object');\n  assert.equal(obj.width, 100, 'Two.Polygon.toObject preserves width');\n  assert.equal(obj.height, 100, 'Two.Polygon.toObject preserves height');\n  assert.equal(obj.sides, 5, 'Two.Polygon.toObject preserves sides');\n  assert.equal(obj.fill, '#0000ff', 'Two.Polygon.toObject preserves fill');\n  assert.equal(obj.id, 'my-polygon', 'Two.Polygon.toObject preserves id');\n\n  // Create new polygon from object\n  var newPolygon = Two.Polygon.fromObject(obj);\n\n  // Test new polygon matches original\n  assert.equal(\n    newPolygon.width,\n    polygon.width,\n    'Two.Polygon.fromObject preserves width'\n  );\n  assert.equal(\n    newPolygon.sides,\n    polygon.sides,\n    'Two.Polygon.fromObject preserves sides'\n  );\n\n  // Test copy method\n  var copiedPolygon = new Two.Polygon().copy(polygon);\n  assert.deepEqual(\n    { ...copiedPolygon.toObject(), id: polygon.id },\n    polygon.toObject(),\n    'Two.Polygon.copy creates identical polygon'\n  );\n});\n\nQUnit.test('Two.Rectangle', function (assert) {\n  assert.expect(Two.Rectangle.Properties.length * 4 + 1);\n\n  var width = 50;\n  var height = 75;\n  var origin = '{\"x\":0,\"y\":0}';\n  var properties = [width, height, origin];\n\n  var path = new Two.Rectangle(0, 0, width, height);\n\n  for (var i = 0; i < Two.Rectangle.Properties.length; i++) {\n    var prop = Two.Rectangle.Properties[i];\n    var isOrigin = i === 2;\n    assert.equal(\n      isOrigin ? JSON.stringify(path[prop].toObject()) : path[prop],\n      properties[i],\n      'Can get property ' + prop + ' correctly.'\n    );\n    if (isOrigin) {\n      path[prop].set(50, 50);\n      assert.equal(\n        JSON.stringify(path[prop].toObject()),\n        '{\"x\":50,\"y\":50}',\n        'Can set property ' + prop + ' correctly.'\n      );\n    } else {\n      path[prop] = properties[i] * 2;\n      assert.equal(\n        path[prop],\n        properties[i] * 2,\n        'Can set property ' + prop + ' correctly.'\n      );\n    }\n    if (isOrigin) {\n      console.log(path._flagVertices);\n      assert.equal(\n        path['_flagVertices'],\n        true,\n        'Set ' + prop + \"'s property flag correctly.\"\n      );\n    } else {\n      assert.equal(\n        path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n        true,\n        'Set ' + prop + \"'s property flag correctly.\"\n      );\n    }\n  }\n\n  path._update();\n  path.flagReset();\n\n  for (var i = 0; i < Two.Rectangle.Properties.length; i++) {\n    var prop = Two.Rectangle.Properties[i];\n    var isOrigin = i === 2;\n    if (isOrigin) {\n      assert.equal(\n        path['_flagVertices'],\n        false,\n        'Reset flag ' + prop + ' correctly.'\n      );\n    } else {\n      assert.equal(\n        path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n        false,\n        'Reset flag ' + prop + ' correctly.'\n      );\n    }\n  }\n\n  path.closed = false;\n  path._update();\n\n  assert.equal(\n    path.vertices[0].equals(path.vertices[path.vertices.length - 1]),\n    true,\n    'Rectangle has standardized vertex generation when shape is not closed'\n  );\n});\n\nQUnit.test('Two.Rectangle Object Conversion', function (assert) {\n  assert.expect(9);\n\n  // Create original rectangle\n  var rectangle = new Two.Rectangle(100, 100, 50, 75);\n  rectangle.fill = '#0000ff';\n  rectangle.stroke = '#ff00ff';\n  rectangle.linewidth = 3;\n  rectangle.id = 'my-rectangle';\n\n  // Convert to object\n  var obj = rectangle.toObject();\n\n  // Test object has expected properties\n  assert.equal(\n    typeof obj,\n    'object',\n    'Two.Rectangle.toObject creates an object'\n  );\n  assert.equal(obj.width, 50, 'Two.Rectangle.toObject preserves width');\n  assert.equal(obj.height, 75, 'Two.Rectangle.toObject preserves height');\n  assert.equal(obj.fill, '#0000ff', 'Two.Rectangle.toObject preserves fill');\n  assert.equal(obj.id, 'my-rectangle', 'Two.Rectangle.toObject preserves id');\n\n  // Create new rectangle from object\n  var newRectangle = Two.Rectangle.fromObject(obj);\n\n  // Test new rectangle matches original\n  assert.equal(\n    newRectangle.width,\n    rectangle.width,\n    'Two.Rectangle.fromObject preserves width'\n  );\n  assert.equal(\n    newRectangle.height,\n    rectangle.height,\n    'Two.Rectangle.fromObject preserves height'\n  );\n  assert.equal(\n    newRectangle.fill,\n    rectangle.fill,\n    'Two.Rectangle.fromObject preserves fill'\n  );\n\n  // Test copy method\n  var copiedRectangle = new Two.Rectangle().copy(rectangle);\n  copiedRectangle._update();\n  assert.deepEqual(\n    { ...copiedRectangle.toObject(), id: rectangle.id },\n    rectangle.toObject(),\n    'Two.Rectangle.copy creates identical rectangle'\n  );\n});\n\nQUnit.test('Two.RoundedRectangle', function (assert) {\n  assert.expect(Two.RoundedRectangle.Properties.length * 4 + 3);\n\n  var width = 50;\n  var height = 75;\n  var radius = 8;\n  var properties = [width, height, radius];\n\n  var path = new Two.RoundedRectangle(0, 0, width, height, radius);\n\n  for (var i = 0; i < Two.RoundedRectangle.Properties.length; i++) {\n    var prop = Two.RoundedRectangle.Properties[i];\n    assert.equal(\n      path[prop],\n      properties[i],\n      'Can get property ' + prop + ' correctly.'\n    );\n    path[prop] = properties[i] * 2;\n    assert.equal(\n      path[prop],\n      properties[i] * 2,\n      'Can set property ' + prop + ' correctly.'\n    );\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      true,\n      'Set ' + prop + \"'s property flag correctly.\"\n    );\n  }\n\n  radius = new Two.Anchor(4, 6);\n  path.radius = radius.clone();\n  assert.equal(\n    radius.equals(path.radius),\n    true,\n    'Can get and set radius as a vector correctly.'\n  );\n\n  path._update();\n  path.flagReset();\n\n  for (var i = 0; i < Two.RoundedRectangle.Properties.length; i++) {\n    var prop = Two.RoundedRectangle.Properties[i];\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      false,\n      'Reset flag ' + prop + ' correctly.'\n    );\n  }\n\n  assert.equal(path._flagRadius, false, 'Reset flag radius correctly.');\n\n  path.closed = false;\n  path._update();\n\n  assert.equal(\n    path.vertices[0].equals(path.vertices[path.vertices.length - 1]),\n    true,\n    'RoundedRectangle has standardized vertex generation when shape is not closed'\n  );\n});\n\nQUnit.test('Two.RoundedRectangle Object Conversion', function (assert) {\n  assert.expect(10);\n\n  // Create original rounded rectangle\n  var roundedRect = new Two.RoundedRectangle(100, 100, 50, 75, 8);\n  roundedRect.fill = '#0000ff';\n  roundedRect.stroke = '#ff00ff';\n  roundedRect.linewidth = 3;\n  roundedRect.id = 'my-rounded-rect';\n\n  // Convert to object\n  var obj = roundedRect.toObject();\n\n  // Test object has expected properties\n  assert.equal(\n    typeof obj,\n    'object',\n    'Two.RoundedRectangle.toObject creates an object'\n  );\n  assert.equal(obj.width, 50, 'Two.RoundedRectangle.toObject preserves width');\n  assert.equal(\n    obj.height,\n    75,\n    'Two.RoundedRectangle.toObject preserves height'\n  );\n  assert.equal(obj.radius, 8, 'Two.RoundedRectangle.toObject preserves radius');\n  assert.equal(\n    obj.fill,\n    '#0000ff',\n    'Two.RoundedRectangle.toObject preserves fill'\n  );\n  assert.equal(\n    obj.id,\n    'my-rounded-rect',\n    'Two.RoundedRectangle.toObject preserves id'\n  );\n\n  // Create new rounded rectangle from object\n  var newRoundedRect = Two.RoundedRectangle.fromObject(obj);\n\n  // Test new rounded rectangle matches original\n  assert.equal(\n    newRoundedRect.width,\n    roundedRect.width,\n    'Two.RoundedRectangle.fromObject preserves width'\n  );\n  assert.equal(\n    newRoundedRect.height,\n    roundedRect.height,\n    'Two.RoundedRectangle.fromObject preserves height'\n  );\n  assert.equal(\n    newRoundedRect.radius,\n    roundedRect.radius,\n    'Two.RoundedRectangle.fromObject preserves radius'\n  );\n\n  // Test copy method\n  var copiedRoundedRect = new Two.RoundedRectangle().copy(roundedRect);\n  assert.deepEqual(\n    { ...copiedRoundedRect.toObject(), id: roundedRect.id },\n    roundedRect.toObject(),\n    'Two.RoundedRectangle.copy creates identical rounded rectangle'\n  );\n});\n\nQUnit.test(\n  'Two.RoundedRectangle Vector Radius Construction',\n  function (assert) {\n    assert.expect(6);\n\n    var width = 100;\n    var height = 80;\n    var rx = 10;\n    var ry = 15;\n    var vectorRadius = new Two.Vector(rx, ry);\n\n    var rect = new Two.RoundedRectangle(0, 0, width, height, vectorRadius);\n\n    assert.equal(rect.width, width, 'Width set correctly with Vector radius');\n    assert.equal(\n      rect.height,\n      height,\n      'Height set correctly with Vector radius'\n    );\n    assert.equal(\n      rect.radius instanceof Two.Vector,\n      true,\n      'Radius is Vector instance'\n    );\n    assert.equal(rect.radius.x, rx, 'Vector radius x value set correctly');\n    assert.equal(rect.radius.y, ry, 'Vector radius y value set correctly');\n\n    rect._update();\n\n    var expectedX1 = width / 2 - rx;\n    var expectedY1 = -height / 2;\n    assert.equal(\n      rect.vertices[1].x === expectedX1 && rect.vertices[1].y === expectedY1,\n      true,\n      'Vertices calculated correctly with Vector radius'\n    );\n  }\n);\n\nQUnit.test('Two.Star', function (assert) {\n  assert.expect(Two.Star.Properties.length * 4 + 1);\n\n  var innerRadius = 50;\n  var outerRadius = 75;\n  var sides = 5;\n  var properties = [innerRadius, outerRadius, sides];\n\n  var path = new Two.Star(0, 0, innerRadius, outerRadius, sides);\n\n  for (var i = 0; i < Two.Star.Properties.length; i++) {\n    var prop = Two.Star.Properties[i];\n    assert.equal(\n      path[prop],\n      properties[i],\n      'Can get property ' + prop + ' correctly.'\n    );\n    path[prop] = properties[i] * 2;\n    assert.equal(\n      path[prop],\n      properties[i] * 2,\n      'Can set property ' + prop + ' correctly.'\n    );\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      true,\n      'Set ' + prop + \"'s property flag correctly.\"\n    );\n  }\n\n  path._update();\n  path.flagReset();\n\n  for (var i = 0; i < Two.Star.Properties.length; i++) {\n    var prop = Two.Star.Properties[i];\n    assert.equal(\n      path['_flag' + prop.charAt(0).toUpperCase() + prop.slice(1)],\n      false,\n      'Reset flag ' + prop + ' correctly.'\n    );\n  }\n\n  path.closed = false;\n  path._update();\n\n  assert.equal(\n    path.vertices[0].equals(path.vertices[path.vertices.length - 1]),\n    true,\n    'Star has standardized vertex generation when shape is not closed'\n  );\n});\n\nQUnit.test('Two.Star Object Conversion', function (assert) {\n  assert.expect(10);\n\n  // Create original star\n  var star = new Two.Star(100, 100, 50, 75, 5);\n  star.fill = '#0000ff';\n  star.stroke = '#ff00ff';\n  star.linewidth = 3;\n  star.id = 'my-star';\n\n  // Convert to object\n  var obj = star.toObject();\n\n  // Test object has expected properties\n  assert.equal(typeof obj, 'object', 'Two.Star.toObject creates an object');\n  assert.equal(obj.innerRadius, 50, 'Two.Star.toObject preserves innerRadius');\n  assert.equal(obj.outerRadius, 75, 'Two.Star.toObject preserves outerRadius');\n  assert.equal(obj.sides, 5, 'Two.Star.toObject preserves sides');\n  assert.equal(obj.fill, '#0000ff', 'Two.Star.toObject preserves fill');\n  assert.equal(obj.id, 'my-star', 'Two.Star.toObject preserves id');\n\n  // Create new star from object\n  var newStar = Two.Star.fromObject(obj);\n\n  // Test new star matches original\n  assert.equal(\n    newStar.innerRadius,\n    star.innerRadius,\n    'Two.Star.fromObject preserves innerRadius'\n  );\n  assert.equal(\n    newStar.outerRadius,\n    star.outerRadius,\n    'Two.Star.fromObject preserves outerRadius'\n  );\n  assert.equal(\n    newStar.sides,\n    star.sides,\n    'Two.Star.fromObject preserves sides'\n  );\n\n  // Test copy method\n  var copiedStar = new Two.Star().copy(star);\n  assert.deepEqual(\n    { ...copiedStar.toObject(), id: star.id },\n    star.toObject(),\n    'Two.Star.copy creates identical star'\n  );\n});\n"
  },
  {
    "path": "tests/suite/svg-interpreter.js",
    "content": "/**\n * Tests Two.js Utilities related to Svg Interpretation:\n * + two.load()\n * + two.interpret()\n * + polygon.subdivide()\n */\n\n(function () {\n  QUnit.module('SvgInterpreter');\n\n  QUnit.test('Two.load', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    two.load('./images/interpretation/D.svg', function (scene, svg) {\n      const shape = scene.children[0];\n      shape.center();\n\n      var answer = {\n        children: [\n          {\n            vertices: [\n              {\n                x: -77.9515,\n                y: 150,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                rx: 0,\n                ry: 0,\n                xAxisRotation: 0,\n                largeArcFlag: 0,\n                sweepFlag: 1,\n              },\n              {\n                x: -77.8935,\n                y: -150,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 77.899, y: 0 } },\n                rx: 0,\n                ry: 0,\n                xAxisRotation: 0,\n                largeArcFlag: 0,\n                sweepFlag: 1,\n              },\n              {\n                x: 77.9515,\n                y: -81.117,\n                command: 'C',\n                relative: true,\n                controls: { left: { x: 0, y: -67.354 }, right: { x: 0, y: 0 } },\n                rx: 0,\n                ry: 0,\n                xAxisRotation: 0,\n                largeArcFlag: 0,\n                sweepFlag: 1,\n              },\n              {\n                x: 77.9515,\n                y: 73.965,\n                command: 'C',\n                relative: true,\n                controls: {\n                  left: { x: 0, y: -75.976 },\n                  right: { x: -0.001, y: 75.976 },\n                },\n                rx: 0,\n                ry: 0,\n                xAxisRotation: 0,\n                largeArcFlag: 0,\n                sweepFlag: 1,\n              },\n              {\n                x: -77.9515,\n                y: 150,\n                command: 'C',\n                relative: true,\n                controls: { left: { x: 77.957, y: 0 }, right: { x: 0, y: 0 } },\n                rx: 0,\n                ry: 0,\n                xAxisRotation: 0,\n                largeArcFlag: 0,\n                sweepFlag: 1,\n              },\n            ],\n            fill: 'none',\n            stroke: '#333333',\n            linewidth: 10,\n            opacity: 1,\n            visible: true,\n            cap: 'round',\n            join: 'round',\n            miter: '10',\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: -3.54003907432e-6, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n          },\n        ],\n        translation: { x: 200, y: 200 },\n        rotation: 0,\n        scale: { x: 1, y: 1 },\n        opacity: 1,\n        className: '',\n        mask: {\n          vertices: [\n            {\n              x: 0,\n              y: 0,\n              command: 'M',\n              relative: true,\n              controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              rx: 0,\n              ry: 0,\n              xAxisRotation: 0,\n              largeArcFlag: 0,\n              sweepFlag: 1,\n            },\n            {\n              x: 400,\n              y: 0,\n              command: 'L',\n              relative: true,\n              controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              rx: 0,\n              ry: 0,\n              xAxisRotation: 0,\n              largeArcFlag: 0,\n              sweepFlag: 1,\n            },\n            {\n              x: 400,\n              y: 400,\n              command: 'L',\n              relative: true,\n              controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              rx: 0,\n              ry: 0,\n              xAxisRotation: 0,\n              largeArcFlag: 0,\n              sweepFlag: 1,\n            },\n            {\n              x: 0,\n              y: 400,\n              command: 'L',\n              relative: true,\n              controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              rx: 0,\n              ry: 0,\n              xAxisRotation: 0,\n              largeArcFlag: 0,\n              sweepFlag: 1,\n            },\n          ],\n          fill: '#fff',\n          stroke: '#000',\n          linewidth: 1,\n          opacity: 1,\n          visible: true,\n          cap: 'butt',\n          join: 'miter',\n          miter: 4,\n          closed: true,\n          curved: false,\n          automatic: false,\n          beginning: 0,\n          ending: 1,\n          className: '',\n          translation: { x: -200.0005035400391, y: -200 },\n          rotation: 0,\n          scale: 1,\n          skewX: 0,\n          skewY: 0,\n          width: 400,\n          height: 400,\n          origin: { x: -200, y: -200 },\n        },\n      };\n      shape.translation.set(two.width / 2, two.height / 2);\n      two.add(shape).update();\n\n      assert.ok(\n        QUnit.Utils.shapeEquals(answer, shape),\n        'Two.load loads SVG files properly.'\n      );\n\n      assert.done();\n\n      QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n    });\n  });\n\n  QUnit.test('Two.interpret', function (assert) {\n    assert.expect(10);\n    assert.done = assert.async(10);\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/D.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              vertices: [\n                {\n                  x: -77.9515,\n                  y: 150,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: -77.8935,\n                  y: -150,\n                  command: 'L',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: 0 },\n                    right: { x: 77.899, y: 0 },\n                  },\n                },\n                {\n                  x: 77.9515,\n                  y: -81.117,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -67.354 },\n                    right: { x: 0, y: 0 },\n                  },\n                },\n                {\n                  x: 77.9515,\n                  y: 73.965,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -75.976 },\n                    right: { x: -0.001, y: 75.976 },\n                  },\n                },\n                {\n                  x: -77.9515,\n                  y: 150,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 77.957, y: 0 },\n                    right: { x: 0, y: 0 },\n                  },\n                },\n              ],\n              fill: 'none',\n              stroke: '#333333',\n              linewidth: 10,\n              opacity: 1,\n              visible: true,\n              cap: 'round',\n              join: 'round',\n              miter: '10',\n              closed: true,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 200.0005, y: 200 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n            },\n          ],\n          translation: { x: 0, y: 0 },\n          rotation: 0,\n          scale: { x: 1, y: 1 },\n          opacity: 1,\n          className: '',\n          mask: {\n            vertices: [\n              {\n                x: 0,\n                y: 0,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 0,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 0,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n            ],\n            fill: '#fff',\n            stroke: '#000',\n            linewidth: 1,\n            opacity: 1,\n            visible: true,\n            cap: 'butt',\n            join: 'miter',\n            miter: 4,\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n            width: 400,\n            height: 400,\n            origin: { x: -200, y: -200 },\n          },\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg);\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.interpret imports <path> properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/K.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              vertices: [\n                {\n                  x: 416.146,\n                  y: 350,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 415.862,\n                  y: 650,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n              ],\n              fill: 'none',\n              stroke: '#333333',\n              linewidth: 10,\n              opacity: 1,\n              visible: true,\n              cap: 'round',\n              join: 'round',\n              miter: '10',\n              closed: false,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: -300, y: -300 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n            },\n            {\n              vertices: [\n                {\n                  x: 570.858,\n                  y: 350,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 415.961,\n                  y: 544.669,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n              ],\n              fill: 'none',\n              stroke: '#333333',\n              linewidth: 10,\n              opacity: 1,\n              visible: true,\n              cap: 'round',\n              join: 'round',\n              miter: '10',\n              closed: false,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: -300, y: -300 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n            },\n            {\n              vertices: [\n                {\n                  x: 469.29,\n                  y: 477.828,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 584.138,\n                  y: 650,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n              ],\n              fill: 'none',\n              stroke: '#333333',\n              linewidth: 10,\n              opacity: 1,\n              visible: true,\n              cap: 'round',\n              join: 'round',\n              miter: '10',\n              closed: false,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: -300, y: -300 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n            },\n          ],\n          translation: { x: 0, y: 0 },\n          rotation: 0,\n          scale: { x: 1, y: 1 },\n          opacity: 1,\n          className: '',\n          mask: {\n            vertices: [\n              {\n                x: 0,\n                y: 0,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 0,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 0,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n            ],\n            fill: '#fff',\n            stroke: '#000',\n            linewidth: 1,\n            opacity: 1,\n            visible: true,\n            cap: 'butt',\n            join: 'miter',\n            miter: 4,\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n            width: 400,\n            height: 400,\n            origin: { x: -200, y: -200 },\n          },\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg);\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.interpret imports <line> properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/circle.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              vertices: [\n                {\n                  x: 100,\n                  y: 0,\n                  command: 'M',\n                  relative: true,\n                  controls: {\n                    left: { x: 3.381768755491e-15, y: -55.2284749831 },\n                    right: { x: 3.381768755491e-15, y: 55.2284749831 },\n                  },\n                },\n                {\n                  x: 6.123233995737e-15,\n                  y: 100,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 55.2284749831, y: 0 },\n                    right: { x: -55.2284749831, y: 6.763537510982e-15 },\n                  },\n                },\n                {\n                  x: -100,\n                  y: 1.224646799147e-14,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 3.381768755491e-15, y: 55.2284749831 },\n                    right: { x: -1.014530626647e-14, y: -55.2284749831 },\n                  },\n                },\n                {\n                  x: -1.836970198721e-14,\n                  y: -100,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -55.2284749831, y: 6.763537510982e-15 },\n                    right: { x: 55.2284749831, y: -1.352707502196e-14 },\n                  },\n                },\n              ],\n              fill: '#EF4142',\n              stroke: '#00AEEF',\n              linewidth: 25,\n              opacity: 1,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: '10',\n              closed: true,\n              curved: true,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 200, y: 200 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n              radius: 100,\n            },\n          ],\n          translation: { x: 0, y: 0 },\n          rotation: 0,\n          scale: { x: 1, y: 1 },\n          opacity: 1,\n          className: '',\n          mask: {\n            vertices: [\n              {\n                x: 0,\n                y: 0,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 0,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 0,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n            ],\n            fill: '#fff',\n            stroke: '#000',\n            linewidth: 1,\n            opacity: 1,\n            visible: true,\n            cap: 'butt',\n            join: 'miter',\n            miter: 4,\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n            width: 400,\n            height: 400,\n            origin: { x: -200, y: -200 },\n          },\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg);\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.interpret imports <circle> properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/rect.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              vertices: [\n                {\n                  x: -100,\n                  y: -100,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 100,\n                  y: -100,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 100,\n                  y: 100,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: -100,\n                  y: 100,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n              ],\n              fill: '#F7941E',\n              linewidth: 1,\n              opacity: 1,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: 4,\n              closed: true,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 200, y: 200 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n              width: 200,\n              height: 200,\n              origin: { x: 0, y: 0 },\n            },\n          ],\n          translation: { x: 0, y: 0 },\n          rotation: 0,\n          scale: { x: 1, y: 1 },\n          opacity: 1,\n          className: '',\n          mask: {\n            vertices: [\n              {\n                x: 0,\n                y: 0,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 0,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 0,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n            ],\n            fill: '#fff',\n            stroke: '#000',\n            linewidth: 1,\n            opacity: 1,\n            visible: true,\n            cap: 'butt',\n            join: 'miter',\n            miter: 4,\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n            width: 400,\n            height: 400,\n            origin: { x: -200, y: -200 },\n          },\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg);\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.interpret imports <rect> properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/ellipse.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              vertices: [\n                {\n                  x: 150,\n                  y: 0,\n                  command: 'M',\n                  relative: true,\n                  controls: {\n                    left: { x: 5.072653133236e-15, y: -42.469040408 },\n                    right: { x: 5.072653133236e-15, y: 42.469040408 },\n                  },\n                },\n                {\n                  x: 9.184850993605e-15,\n                  y: 76.897,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 82.842712475, y: 0 },\n                    right: { x: -82.842712475, y: 5.20095743982e-15 },\n                  },\n                },\n                {\n                  x: -150,\n                  y: 9.417166491403e-15,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 5.072653133236e-15, y: 42.469040408 },\n                    right: { x: -1.521795939971e-14, y: -42.469040408 },\n                  },\n                },\n                {\n                  x: -2.755455298082e-14,\n                  y: -76.897,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -82.842712475, y: 5.20095743982e-15 },\n                    right: { x: 82.842712475, y: -1.040191487964e-14 },\n                  },\n                },\n              ],\n              fill: '#92278F',\n              linewidth: 1,\n              opacity: 0.5,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: 4,\n              closed: true,\n              curved: true,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 200, y: 200 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n              width: 300,\n              height: 153.794,\n            },\n          ],\n          translation: { x: 0, y: 0 },\n          rotation: 0,\n          scale: { x: 1, y: 1 },\n          opacity: 1,\n          className: '',\n          mask: {\n            vertices: [\n              {\n                x: 0,\n                y: 0,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 0,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 0,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n            ],\n            fill: '#fff',\n            stroke: '#000',\n            linewidth: 1,\n            opacity: 1,\n            visible: true,\n            cap: 'butt',\n            join: 'miter',\n            miter: 4,\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n            width: 400,\n            height: 400,\n            origin: { x: -200, y: -200 },\n          },\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg);\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.interpret imports <ellipse> properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/polyline.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              vertices: [\n                { x: 20.079, y: 104.42, command: 'M', relative: true },\n                { x: 47.352, y: 104.42, command: 'L', relative: true },\n                { x: 47.352, y: 90.785, command: 'L', relative: true },\n                { x: 74.625, y: 90.785, command: 'L', relative: true },\n                { x: 74.625, y: 104.42, command: 'L', relative: true },\n                { x: 101.897, y: 104.42, command: 'L', relative: true },\n                { x: 101.897, y: 70.33, command: 'L', relative: true },\n                { x: 129.17, y: 70.33, command: 'L', relative: true },\n                { x: 129.17, y: 104.42, command: 'L', relative: true },\n                { x: 156.442, y: 104.42, command: 'L', relative: true },\n                { x: 156.442, y: 49.875, command: 'L', relative: true },\n                { x: 183.715, y: 49.875, command: 'L', relative: true },\n                { x: 183.715, y: 104.42, command: 'L', relative: true },\n                { x: 210.988, y: 104.42, command: 'L', relative: true },\n                { x: 210.988, y: 29.42, command: 'L', relative: true },\n                { x: 238.26, y: 29.42, command: 'L', relative: true },\n                { x: 238.26, y: 104.42, command: 'L', relative: true },\n                { x: 265.534, y: 104.42, command: 'L', relative: true },\n                { x: 265.534, y: 8.965, command: 'L', relative: true },\n                { x: 292.805, y: 8.965, command: 'L', relative: true },\n                { x: 292.805, y: 104.42, command: 'L', relative: true },\n                { x: 320.079, y: 104.42, command: 'L', relative: true },\n              ],\n              fill: 'none',\n              stroke: '#0000FF',\n              linewidth: 10,\n              opacity: 1,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: 4,\n              closed: false,\n              curved: false,\n              automatic: true,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 29.921, y: 143.307 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n            },\n          ],\n          translation: { x: 0, y: 0 },\n          rotation: 0,\n          scale: { x: 1, y: 1 },\n          opacity: 1,\n          className: '',\n          mask: {\n            vertices: [\n              {\n                x: 0,\n                y: 0,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 0,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 0,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n            ],\n            fill: '#fff',\n            stroke: '#000',\n            linewidth: 1,\n            opacity: 1,\n            visible: true,\n            cap: 'butt',\n            join: 'miter',\n            miter: 4,\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n            width: 400,\n            height: 400,\n            origin: { x: -200, y: -200 },\n          },\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg);\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.interpret imports <polyline> properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/polygon.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              vertices: [\n                { x: 99.212, y: 21.26, command: 'M', relative: true },\n                { x: 107.433, y: 45.638, command: 'L', relative: true },\n                { x: 132.945, y: 45.638, command: 'L', relative: true },\n                { x: 112.536, y: 60.945, command: 'L', relative: true },\n                { x: 119.905, y: 85.323, command: 'L', relative: true },\n                { x: 99.212, y: 70.867, command: 'L', relative: true },\n                { x: 78.52, y: 85.323, command: 'L', relative: true },\n                { x: 85.89, y: 60.945, command: 'L', relative: true },\n                { x: 65.48, y: 45.638, command: 'L', relative: true },\n                { x: 90.992, y: 45.638, command: 'L', relative: true },\n              ],\n              fill: '#FF0000',\n              linewidth: 1,\n              opacity: 1,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: 4,\n              closed: true,\n              curved: false,\n              automatic: true,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 29.921, y: 143.307 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n            },\n            {\n              vertices: [\n                { x: 240.945, y: 21.26, command: 'M', relative: true },\n                { x: 271.559, y: 38.977, command: 'L', relative: true },\n                { x: 271.559, y: 74.41, command: 'L', relative: true },\n                { x: 240.945, y: 92.126, command: 'L', relative: true },\n                { x: 210.331, y: 74.438, command: 'L', relative: true },\n                { x: 210.331, y: 38.977, command: 'L', relative: true },\n              ],\n              fill: '#00FF00',\n              linewidth: 1,\n              opacity: 1,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: 4,\n              closed: true,\n              curved: false,\n              automatic: true,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 29.921, y: 143.307 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n            },\n          ],\n          translation: { x: 0, y: 0 },\n          rotation: 0,\n          scale: { x: 1, y: 1 },\n          opacity: 1,\n          className: '',\n          mask: {\n            vertices: [\n              {\n                x: 0,\n                y: 0,\n                command: 'M',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 0,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 400,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n              {\n                x: 0,\n                y: 400,\n                command: 'L',\n                relative: true,\n                controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n              },\n            ],\n            fill: '#fff',\n            stroke: '#000',\n            linewidth: 1,\n            opacity: 1,\n            visible: true,\n            cap: 'butt',\n            join: 'miter',\n            miter: 4,\n            closed: true,\n            curved: false,\n            automatic: false,\n            beginning: 0,\n            ending: 1,\n            className: '',\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            skewX: 0,\n            skewY: 0,\n            width: 400,\n            height: 400,\n            origin: { x: -200, y: -200 },\n          },\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg);\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.interpret imports <polygon> properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get(\n        './images/interpretation/linear-gradient.svg',\n        function (resp) {\n          var answer = {\n            children: [\n              {\n                vertices: [\n                  {\n                    x: -100,\n                    y: -100,\n                    command: 'M',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                  {\n                    x: 100,\n                    y: -100,\n                    command: 'L',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                  {\n                    x: 100,\n                    y: 100,\n                    command: 'L',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                  {\n                    x: -100,\n                    y: 100,\n                    command: 'L',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                ],\n                fill: {\n                  stops: [\n                    { offset: 0, opacity: 1, color: '#000000' },\n                    { offset: 0.33, opacity: 1, color: '#FFF200' },\n                    { offset: 0.66, opacity: 1, color: '#EC008C' },\n                    { offset: 1, opacity: 1, color: '#00AEEF' },\n                  ],\n                  spread: 'pad',\n                  left: { x: -100, y: 0 },\n                  right: { x: 100, y: 0 },\n                },\n                linewidth: 1,\n                opacity: 1,\n                visible: true,\n                cap: 'butt',\n                join: 'miter',\n                miter: 4,\n                closed: true,\n                curved: false,\n                automatic: false,\n                beginning: 0,\n                ending: 1,\n                className: '',\n                translation: { x: 200, y: 200 },\n                rotation: 0,\n                scale: 1,\n                skewX: 0,\n                skewY: 0,\n                width: 200,\n                height: 200,\n                origin: { x: 0, y: 0 },\n              },\n            ],\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            opacity: 1,\n            className: '',\n            mask: {\n              vertices: [\n                {\n                  x: 0,\n                  y: 0,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 400,\n                  y: 0,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 400,\n                  y: 400,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 0,\n                  y: 400,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n              ],\n              fill: '#fff',\n              stroke: '#000',\n              linewidth: 1,\n              opacity: 1,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: 4,\n              closed: true,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 0, y: 0 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n              width: 400,\n              height: 400,\n              origin: { x: -200, y: -200 },\n            },\n          };\n          var svg = QUnit.Utils.textToDOM(resp)[0];\n          var shape = two.interpret(svg);\n\n          two.update();\n\n          assert.ok(\n            QUnit.Utils.shapeEquals(answer, shape),\n            'Two.interpret imports <linear-gradient> properly.'\n          );\n          assert.done();\n\n          QUnit.Utils.addElemToTest(assert.test, [\n            two.renderer.domElement,\n            svg,\n          ]);\n        }\n      );\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get(\n        './images/interpretation/radial-gradient.svg',\n        function (resp) {\n          var answer = {\n            children: [\n              {\n                stops: [\n                  { offset: 0, opacity: 1, color: '#000000' },\n                  { offset: 0.33, opacity: 1, color: '#FFF200' },\n                  { offset: 0.66, opacity: 1, color: '#EC008C' },\n                  { offset: 1, opacity: 1, color: '#00AEEF' },\n                ],\n                spread: 'pad',\n                radius: 100,\n                center: { x: 0, y: 0 },\n                focal: { x: 0, y: 0 },\n              },\n              {\n                vertices: [\n                  {\n                    x: -100,\n                    y: -100,\n                    command: 'M',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                  {\n                    x: 100,\n                    y: -100,\n                    command: 'L',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                  {\n                    x: 100,\n                    y: 100,\n                    command: 'L',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                  {\n                    x: -100,\n                    y: 100,\n                    command: 'L',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  },\n                ],\n                fill: {\n                  stops: [\n                    { offset: 0, opacity: 1, color: '#000000' },\n                    { offset: 0.33, opacity: 1, color: '#FFF200' },\n                    { offset: 0.66, opacity: 1, color: '#EC008C' },\n                    { offset: 1, opacity: 1, color: '#00AEEF' },\n                  ],\n                  spread: 'pad',\n                  radius: 100,\n                  center: { x: 0, y: 0 },\n                  focal: { x: 0, y: 0 },\n                },\n                linewidth: 1,\n                opacity: 1,\n                visible: true,\n                cap: 'butt',\n                join: 'miter',\n                miter: 4,\n                closed: true,\n                curved: false,\n                automatic: false,\n                beginning: 0,\n                ending: 1,\n                className: '',\n                translation: { x: 200, y: 200 },\n                rotation: 0,\n                scale: 1,\n                skewX: 0,\n                skewY: 0,\n                width: 200,\n                height: 200,\n                origin: { x: 0, y: 0 },\n              },\n            ],\n            translation: { x: 0, y: 0 },\n            rotation: 0,\n            scale: 1,\n            opacity: 1,\n            className: '',\n            mask: {\n              vertices: [\n                {\n                  x: 0,\n                  y: 0,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 400,\n                  y: 0,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 400,\n                  y: 400,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n                {\n                  x: 0,\n                  y: 400,\n                  command: 'L',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                },\n              ],\n              fill: '#fff',\n              stroke: '#000',\n              linewidth: 1,\n              opacity: 1,\n              visible: true,\n              cap: 'butt',\n              join: 'miter',\n              miter: 4,\n              closed: true,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              className: '',\n              translation: { x: 0, y: 0 },\n              rotation: 0,\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n              width: 400,\n              height: 400,\n              origin: { x: -200, y: -200 },\n            },\n          };\n          var svg = QUnit.Utils.textToDOM(resp)[0];\n          var shape = two.interpret(svg);\n\n          two.update();\n\n          assert.ok(\n            QUnit.Utils.shapeEquals(answer, shape),\n            'Two.interpret imports <radial-gradient> properly.'\n          );\n          assert.done();\n\n          QUnit.Utils.addElemToTest(assert.test, [\n            two.renderer.domElement,\n            svg,\n          ]);\n        }\n      );\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n        type: Two.Types.canvas, //The font-size problem only occurs in canvas renderer\n      });\n\n      QUnit.Utils.get('./images/interpretation/text.svg', function (resp) {\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        const shape = two.interpret(svg);\n        two.update();\n\n        assert.equal(\n          shape.children[1].size,\n          144,\n          'Font size is extracted correctly'\n        );\n\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n  });\n\n  QUnit.test('Two.subdivide', function (assert) {\n    assert.expect(3);\n    assert.done = assert.async(3);\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/D.svg', function (resp) {\n        var answer = {\n          children: [\n            {\n              isShape: true,\n              translation: { x: 200, y: 200 },\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n              vertices: [\n                {\n                  x: -77.9515,\n                  y: 150,\n                  command: 'M',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.950922,\n                  y: 147.007938,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.000378, y: 1.953999 },\n                    right: { x: 0.000377, y: -1.954 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.949281,\n                  y: 138.520252,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.000709, y: 3.663749 },\n                    right: { x: 0.000708, y: -3.66375 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.946719,\n                  y: 125.269692,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.000992, y: 5.129248 },\n                    right: { x: 0.000991, y: -5.129249 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.943378,\n                  y: 107.989008,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001228, y: 6.350498 },\n                    right: { x: 0.001227, y: -6.350499 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.9394,\n                  y: 87.41095,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001417, y: 7.327498 },\n                    right: { x: 0.001416, y: -7.327499 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.934926,\n                  y: 64.268267,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001559, y: 8.060248 },\n                    right: { x: 0.001558, y: -8.060249 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.930097,\n                  y: 39.29371,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001653, y: 8.548748 },\n                    right: { x: 0.001652, y: -8.548749 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.925056,\n                  y: 13.220028,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.0017, y: 8.792998 },\n                    right: { x: 0.001699, y: -8.792999 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.919945,\n                  y: -13.220029,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.0017, y: 8.792998 },\n                    right: { x: 0.001699, y: -8.792999 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.914904,\n                  y: -39.293711,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001653, y: 8.548748 },\n                    right: { x: 0.001652, y: -8.548749 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.910075,\n                  y: -64.268268,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001559, y: 8.060248 },\n                    right: { x: 0.001558, y: -8.060249 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.905601,\n                  y: -87.410951,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001417, y: 7.327498 },\n                    right: { x: 0.001416, y: -7.327499 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.901623,\n                  y: -107.989009,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.001228, y: 6.350498 },\n                    right: { x: 0.001227, y: -6.350499 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.898282,\n                  y: -125.269693,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.000992, y: 5.129248 },\n                    right: { x: 0.000991, y: -5.129249 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.89572,\n                  y: -138.520253,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.000709, y: 3.663749 },\n                    right: { x: 0.000708, y: -3.66375 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.894079,\n                  y: -147.007939,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.000378, y: 1.953999 },\n                    right: { x: 0.000377, y: -1.954 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.8935,\n                  y: -150,\n                  command: 'L',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: 0 },\n                    right: { x: 4.582294, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -64.162005,\n                  y: -149.971042,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -4.566745, y: -0.023669 },\n                    right: { x: 4.566744, y: 0.023668 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -50.524783,\n                  y: -149.831817,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -4.519446, y: -0.073511 },\n                    right: { x: 4.519445, y: 0.07351 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -37.077083,\n                  y: -149.503806,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -4.440397, y: -0.149527 },\n                    right: { x: 4.440396, y: 0.149526 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -23.914153,\n                  y: -148.908487,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -4.329599, y: -0.251716 },\n                    right: { x: 4.329598, y: 0.251715 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -11.131244,\n                  y: -147.967337,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -4.18705, y: -0.38008 },\n                    right: { x: 4.187049, y: 0.380079 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 1.176396,\n                  y: -146.601836,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -4.012753, y: -0.534617 },\n                    right: { x: 4.012752, y: 0.534616 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 12.913519,\n                  y: -144.733463,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -3.806705, y: -0.715328 },\n                    right: { x: 3.806704, y: 0.715327 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 23.984874,\n                  y: -142.283695,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -3.568908, y: -0.922213 },\n                    right: { x: 3.568907, y: 0.922212 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 34.295215,\n                  y: -139.174012,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -3.299361, y: -1.155272 },\n                    right: { x: 3.29936, y: 1.155271 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 43.74929,\n                  y: -135.325891,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -2.998065, y: -1.414505 },\n                    right: { x: 2.998064, y: 1.414504 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 52.251852,\n                  y: -130.660812,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -2.665019, y: -1.699911 },\n                    right: { x: 2.665018, y: 1.69991 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 59.707652,\n                  y: -125.100252,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -2.300223, y: -2.011492 },\n                    right: { x: 2.300222, y: 2.011491 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 66.02144,\n                  y: -118.565691,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -1.903678, y: -2.349246 },\n                    right: { x: 1.903677, y: 2.349245 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 71.097967,\n                  y: -110.978607,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -1.475383, y: -2.713174 },\n                    right: { x: 1.475382, y: 2.713173 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 74.841986,\n                  y: -102.260478,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -1.015339, y: -3.103275 },\n                    right: { x: 1.015338, y: 3.103274 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.158246,\n                  y: -92.332783,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -0.523544, y: -3.519551 },\n                    right: { x: 0.523543, y: 3.51955 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -81.117,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -3.962001 },\n                    right: { x: 0, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -80.312569,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -0.530708 },\n                    right: { x: 0, y: 0.530707 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -77.96623,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -1.027939 },\n                    right: { x: 0, y: 1.027938 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -74.178413,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -1.491694 },\n                    right: { x: 0, y: 1.491693 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -69.049548,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -1.921971 },\n                    right: { x: 0, y: 1.92197 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -62.680068,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -2.318771 },\n                    right: { x: 0, y: 2.31877 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -55.170402,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -2.682095 },\n                    right: { x: 0, y: 2.682094 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -46.620981,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -3.011941 },\n                    right: { x: 0, y: 3.01194 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -37.132235,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -3.308311 },\n                    right: { x: 0, y: 3.30831 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -26.804596,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -3.571203 },\n                    right: { x: 0, y: 3.571202 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -15.738494,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -3.800619 },\n                    right: { x: 0, y: 3.800618 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: -4.034361,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -3.996558 },\n                    right: { x: 0, y: 3.996557 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: 8.207375,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -4.15902 },\n                    right: { x: 0, y: 4.159019 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: 20.886281,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -4.288005 },\n                    right: { x: 0, y: 4.288004 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: 33.901927,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -4.383514 },\n                    right: { x: 0, y: 4.383513 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: 47.153883,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -4.445545 },\n                    right: { x: 0, y: 4.445544 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: 60.541717,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -4.474099 },\n                    right: { x: 0, y: 4.474098 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.9515,\n                  y: 73.964999,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -4.469177 },\n                    right: { x: -0.000059, y: 4.469176 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 77.158078,\n                  y: 86.599903,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0.523601, y: -3.95924 },\n                    right: { x: -0.523602, y: 3.959239 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 74.841617,\n                  y: 97.751317,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 1.015418, y: -3.480184 },\n                    right: { x: -1.015419, y: 3.480183 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 71.09729,\n                  y: 107.511883,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 1.475511, y: -3.032008 },\n                    right: { x: -1.475512, y: 3.032007 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 66.020271,\n                  y: 115.974242,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 1.903879, y: -2.614712 },\n                    right: { x: -1.90388, y: 2.614711 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 59.705736,\n                  y: 123.231036,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 2.300522, y: -2.228298 },\n                    right: { x: -2.300523, y: 2.228297 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 52.248859,\n                  y: 129.374905,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 2.665441, y: -1.872763 },\n                    right: { x: -2.665442, y: 1.872762 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 43.744813,\n                  y: 134.498493,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 2.998634, y: -1.54811 },\n                    right: { x: -2.998635, y: 1.548109 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 34.288774,\n                  y: 138.694441,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 3.300103, y: -1.254336 },\n                    right: { x: -3.300104, y: 1.254335 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 23.975917,\n                  y: 142.055389,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 3.569847, y: -0.991444 },\n                    right: { x: -3.569848, y: 0.991443 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 12.901414,\n                  y: 144.67398,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 3.807866, y: -0.759431 },\n                    right: { x: -3.807867, y: 0.75943 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 1.160441,\n                  y: 146.642855,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 4.014161, y: -0.5583 },\n                    right: { x: -4.014162, y: 0.558299 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -11.151827,\n                  y: 148.054656,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 4.18873, y: -0.388049 },\n                    right: { x: -4.188731, y: 0.388048 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -23.940217,\n                  y: 149.002024,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 4.331575, y: -0.248678 },\n                    right: { x: -4.331576, y: 0.248677 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -37.109554,\n                  y: 149.5776,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 4.442695, y: -0.140188 },\n                    right: { x: -4.442696, y: 0.140187 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -50.564663,\n                  y: 149.874028,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 4.52209, y: -0.062578 },\n                    right: { x: -4.522091, y: 0.062577 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -64.21037,\n                  y: 149.983947,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 4.56976, y: -0.015849 },\n                    right: { x: -4.569761, y: 0.015848 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -77.9515,\n                  y: 150,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 4.585705, y: 0 },\n                    right: { x: 0, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n              ],\n              fill: 'none',\n              stroke: '#333333',\n              linewidth: 10,\n              opacity: 1,\n              visible: true,\n              cap: 'round',\n              join: 'round',\n              miter: '10',\n              closed: true,\n              curved: false,\n              automatic: false,\n              beginning: 0,\n              ending: 1,\n              dashes: [],\n              strokeAttenuation: true,\n            },\n          ],\n        };\n\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg).subdivide();\n        var group = two.makeGroup();\n\n        group.translation.copy(shape.children[0].translation);\n\n        _.each(shape.children[0].vertices, function (v) {\n          var circle = new Two.Circle(v.x, v.y, 3);\n          circle.noStroke().fill = 'red';\n          group.add(circle);\n        });\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.subdivide subdivides curveTo and lineTo properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get(\n        './images/interpretation/compound-path.svg',\n        function (resp) {\n          var answer = {\n            isShape: true,\n            translation: { x: 0, y: 0 },\n            scale: { x: 1, y: 1 },\n            skewX: 0,\n            skewY: 0,\n            children: [\n              {\n                isShape: true,\n                translation: { x: 200, y: 200 },\n                scale: 1,\n                skewX: 0,\n                skewY: 0,\n                vertices: [\n                  {\n                    x: -120,\n                    y: 0,\n                    command: 'M',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 0 },\n                      right: { x: 0, y: 3.248764 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -119.542234,\n                    y: 9.630749,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -0.30285, y: -3.169407 },\n                      right: { x: 0.302849, y: 3.169406 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -118.19687,\n                    y: 19.002473,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -0.591732, y: -3.076081 },\n                      right: { x: 0.591731, y: 3.07608 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -116.005814,\n                    y: 28.073266,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -0.866645, y: -2.968787 },\n                      right: { x: 0.866644, y: 2.968786 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -113.010971,\n                    y: 36.801223,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.12759, y: -2.847524 },\n                      right: { x: 1.127589, y: 2.847523 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -109.254244,\n                    y: 45.144441,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.374567, y: -2.712294 },\n                      right: { x: 1.374566, y: 2.712293 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -104.77754,\n                    y: 53.061014,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.607576, y: -2.563095 },\n                      right: { x: 1.607575, y: 2.563094 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -99.622763,\n                    y: 60.509037,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.826616, y: -2.399927 },\n                      right: { x: 1.826615, y: 2.399926 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -93.831816,\n                    y: 67.446606,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.031688, y: -2.222792 },\n                      right: { x: 2.031687, y: 2.222791 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -87.446607,\n                    y: 73.831815,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.222792, y: -2.031688 },\n                      right: { x: 2.222791, y: 2.031687 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -80.509038,\n                    y: 79.622762,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.399927, y: -1.826616 },\n                      right: { x: 2.399926, y: 1.826615 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -73.061015,\n                    y: 84.777539,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.563095, y: -1.607576 },\n                      right: { x: 2.563094, y: 1.607575 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -65.144442,\n                    y: 89.254243,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.712294, y: -1.374567 },\n                      right: { x: 2.712293, y: 1.374566 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -56.801224,\n                    y: 93.01097,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.847524, y: -1.12759 },\n                      right: { x: 2.847523, y: 1.127589 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -48.073267,\n                    y: 96.005813,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.968787, y: -0.866645 },\n                      right: { x: 2.968786, y: 0.866644 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -39.002474,\n                    y: 98.196869,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -3.076081, y: -0.591732 },\n                      right: { x: 3.07608, y: 0.591731 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -29.63075,\n                    y: 99.542233,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -3.169407, y: -0.30285 },\n                      right: { x: 3.169406, y: 0.302849 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 100,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -3.248765, y: 0 },\n                      right: { x: 0, y: 0 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 98.005292,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 1.302666 },\n                      right: { x: 0, y: -1.302667 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 92.346834,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 2.442499 },\n                      right: { x: 0, y: -2.4425 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 83.513128,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 3.419499 },\n                      right: { x: 0, y: -3.4195 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 71.992672,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 4.233665 },\n                      right: { x: 0, y: -4.233666 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 58.273967,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 4.884998 },\n                      right: { x: 0, y: -4.884999 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 42.845511,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 5.373498 },\n                      right: { x: 0, y: -5.373499 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 26.195807,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 5.699165 },\n                      right: { x: 0, y: -5.699166 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: 8.813352,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 5.861998 },\n                      right: { x: 0, y: -5.861999 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -8.813353,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 5.861998 },\n                      right: { x: 0, y: -5.861999 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -26.195808,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 5.699165 },\n                      right: { x: 0, y: -5.699166 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -42.845512,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 5.373498 },\n                      right: { x: 0, y: -5.373499 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -58.273968,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 4.884998 },\n                      right: { x: 0, y: -4.884999 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -71.992673,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 4.233665 },\n                      right: { x: 0, y: -4.233666 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -83.513129,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 3.419499 },\n                      right: { x: 0, y: -3.4195 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -92.346835,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 2.442499 },\n                      right: { x: 0, y: -2.4425 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -98.005293,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 1.302666 },\n                      right: { x: 0, y: -1.302667 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -20,\n                    y: -100,\n                    command: 'L',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 0 },\n                      right: { x: -3.248765, y: 0 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -29.63075,\n                    y: -99.542234,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 3.169406, y: -0.30285 },\n                      right: { x: -3.169407, y: 0.302849 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -39.002474,\n                    y: -98.19687,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 3.07608, y: -0.591732 },\n                      right: { x: -3.076081, y: 0.591731 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -48.073267,\n                    y: -96.005814,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.968786, y: -0.866645 },\n                      right: { x: -2.968787, y: 0.866644 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -56.801224,\n                    y: -93.010971,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.847523, y: -1.12759 },\n                      right: { x: -2.847524, y: 1.127589 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -65.144442,\n                    y: -89.254244,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.712293, y: -1.374567 },\n                      right: { x: -2.712294, y: 1.374566 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -73.061015,\n                    y: -84.77754,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.563094, y: -1.607576 },\n                      right: { x: -2.563095, y: 1.607575 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -80.509038,\n                    y: -79.622763,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.399926, y: -1.826616 },\n                      right: { x: -2.399927, y: 1.826615 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -87.446607,\n                    y: -73.831816,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.222791, y: -2.031688 },\n                      right: { x: -2.222792, y: 2.031687 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -93.831816,\n                    y: -67.446607,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.031687, y: -2.222792 },\n                      right: { x: -2.031688, y: 2.222791 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -99.622763,\n                    y: -60.509038,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.826615, y: -2.399927 },\n                      right: { x: -1.826616, y: 2.399926 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -104.77754,\n                    y: -53.061015,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.607575, y: -2.563095 },\n                      right: { x: -1.607576, y: 2.563094 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -109.254244,\n                    y: -45.144442,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.374566, y: -2.712294 },\n                      right: { x: -1.374567, y: 2.712293 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -113.010971,\n                    y: -36.801224,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.127589, y: -2.847524 },\n                      right: { x: -1.12759, y: 2.847523 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -116.005814,\n                    y: -28.073267,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0.866644, y: -2.968787 },\n                      right: { x: -0.866645, y: 2.968786 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -118.19687,\n                    y: -19.002474,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0.591731, y: -3.076081 },\n                      right: { x: -0.591732, y: 3.07608 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -119.542234,\n                    y: -9.63075,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0.302849, y: -3.169407 },\n                      right: { x: -0.30285, y: 3.169406 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -120,\n                    y: 0,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -3.248765 },\n                      right: { x: 0, y: 0 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: -120,\n                    y: 0,\n                    command: 'Z',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -100,\n                    command: 'M',\n                    relative: true,\n                    controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -98.005293,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -1.302667 },\n                      right: { x: 0, y: 1.302666 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -92.346835,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -2.4425 },\n                      right: { x: 0, y: 2.442499 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -83.513129,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -3.4195 },\n                      right: { x: 0, y: 3.419499 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -71.992673,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -4.233666 },\n                      right: { x: 0, y: 4.233665 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -58.273968,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -4.884999 },\n                      right: { x: 0, y: 4.884998 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -42.845512,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -5.373499 },\n                      right: { x: 0, y: 5.373498 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -26.195808,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -5.699166 },\n                      right: { x: 0, y: 5.699165 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -8.813353,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -5.861999 },\n                      right: { x: 0, y: 5.861998 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 8.813352,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -5.861999 },\n                      right: { x: 0, y: 5.861998 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 26.195807,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -5.699166 },\n                      right: { x: 0, y: 5.699165 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 42.845511,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -5.373499 },\n                      right: { x: 0, y: 5.373498 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 58.273967,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -4.884999 },\n                      right: { x: 0, y: 4.884998 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 71.992672,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -4.233666 },\n                      right: { x: 0, y: 4.233665 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 83.513128,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -3.4195 },\n                      right: { x: 0, y: 3.419499 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 92.346834,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -2.4425 },\n                      right: { x: 0, y: 2.442499 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 98.005292,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: -1.302667 },\n                      right: { x: 0, y: 1.302666 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: 100,\n                    command: 'L',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 0 },\n                      right: { x: 3.248764, y: 0 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 29.630749,\n                    y: 99.542233,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -3.169407, y: 0.302849 },\n                      right: { x: 3.169406, y: -0.30285 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 39.002473,\n                    y: 98.196869,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -3.076081, y: 0.591731 },\n                      right: { x: 3.07608, y: -0.591732 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 48.073266,\n                    y: 96.005813,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.968787, y: 0.866644 },\n                      right: { x: 2.968786, y: -0.866645 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 56.801223,\n                    y: 93.01097,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.847524, y: 1.127589 },\n                      right: { x: 2.847523, y: -1.12759 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 65.144441,\n                    y: 89.254243,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.712294, y: 1.374566 },\n                      right: { x: 2.712293, y: -1.374567 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 73.061014,\n                    y: 84.777539,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.563095, y: 1.607575 },\n                      right: { x: 2.563094, y: -1.607576 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 80.509037,\n                    y: 79.622762,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.399927, y: 1.826615 },\n                      right: { x: 2.399926, y: -1.826616 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 87.446606,\n                    y: 73.831815,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.222792, y: 2.031687 },\n                      right: { x: 2.222791, y: -2.031688 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 93.831815,\n                    y: 67.446606,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -2.031688, y: 2.222791 },\n                      right: { x: 2.031687, y: -2.222792 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 99.622762,\n                    y: 60.509037,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.826616, y: 2.399926 },\n                      right: { x: 1.826615, y: -2.399927 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 104.777539,\n                    y: 53.061014,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.607576, y: 2.563094 },\n                      right: { x: 1.607575, y: -2.563095 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 109.254243,\n                    y: 45.144441,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.374567, y: 2.712293 },\n                      right: { x: 1.374566, y: -2.712294 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 113.01097,\n                    y: 36.801223,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -1.12759, y: 2.847523 },\n                      right: { x: 1.127589, y: -2.847524 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 116.005813,\n                    y: 28.073266,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -0.866645, y: 2.968786 },\n                      right: { x: 0.866644, y: -2.968787 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 118.196869,\n                    y: 19.002473,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -0.591732, y: 3.07608 },\n                      right: { x: 0.591731, y: -3.076081 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 119.542233,\n                    y: 9.630749,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: -0.30285, y: 3.169406 },\n                      right: { x: 0.302849, y: -3.169407 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 120,\n                    y: 0,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0, y: 3.248764 },\n                      right: { x: 0, y: -3.248765 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 119.542233,\n                    y: -9.63075,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0.302849, y: 3.169406 },\n                      right: { x: -0.30285, y: -3.169407 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 118.196869,\n                    y: -19.002474,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0.591731, y: 3.07608 },\n                      right: { x: -0.591732, y: -3.076081 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 116.005813,\n                    y: -28.073267,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 0.866644, y: 2.968786 },\n                      right: { x: -0.866645, y: -2.968787 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 113.01097,\n                    y: -36.801224,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.127589, y: 2.847523 },\n                      right: { x: -1.12759, y: -2.847524 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 109.254243,\n                    y: -45.144442,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.374566, y: 2.712293 },\n                      right: { x: -1.374567, y: -2.712294 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 104.777539,\n                    y: -53.061015,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.607575, y: 2.563094 },\n                      right: { x: -1.607576, y: -2.563095 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 99.622762,\n                    y: -60.509038,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 1.826615, y: 2.399926 },\n                      right: { x: -1.826616, y: -2.399927 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 93.831815,\n                    y: -67.446607,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.031687, y: 2.222791 },\n                      right: { x: -2.031688, y: -2.222792 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 87.446606,\n                    y: -73.831816,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.222791, y: 2.031687 },\n                      right: { x: -2.222792, y: -2.031688 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 80.509037,\n                    y: -79.622763,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.399926, y: 1.826615 },\n                      right: { x: -2.399927, y: -1.826616 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 73.061014,\n                    y: -84.77754,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.563094, y: 1.607575 },\n                      right: { x: -2.563095, y: -1.607576 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 65.144441,\n                    y: -89.254244,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.712293, y: 1.374566 },\n                      right: { x: -2.712294, y: -1.374567 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 56.801223,\n                    y: -93.010971,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.847523, y: 1.127589 },\n                      right: { x: -2.847524, y: -1.12759 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 48.073266,\n                    y: -96.005814,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 2.968786, y: 0.866644 },\n                      right: { x: -2.968787, y: -0.866645 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 39.002473,\n                    y: -98.19687,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 3.07608, y: 0.591731 },\n                      right: { x: -3.076081, y: -0.591732 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 29.630749,\n                    y: -99.542234,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 3.169406, y: 0.302849 },\n                      right: { x: -3.169407, y: -0.30285 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                  {\n                    x: 20,\n                    y: -100,\n                    command: 'C',\n                    relative: true,\n                    controls: {\n                      left: { x: 3.248764, y: 0 },\n                      right: { x: 0, y: 0 },\n                    },\n                    rx: 0,\n                    ry: 0,\n                    xAxisRotation: 0,\n                    largeArcFlag: 0,\n                    sweepFlag: 1,\n                  },\n                ],\n              },\n            ],\n          };\n          var svg = QUnit.Utils.textToDOM(resp)[0];\n          var shape = two.interpret(svg).subdivide();\n          var group = two.makeGroup();\n\n          group.translation.copy(shape.children[0].translation);\n\n          _.each(shape.children[0].vertices, function (v) {\n            var circle = new Two.Circle(v.x, v.y, 3);\n            circle.noStroke().fill = 'red';\n            group.add(circle);\n          });\n\n          two.update();\n\n          assert.ok(\n            QUnit.Utils.shapeEquals(answer, shape),\n            'Two.subdivide subdivides moveTo properly.'\n          );\n          assert.done();\n\n          QUnit.Utils.addElemToTest(assert.test, [\n            two.renderer.domElement,\n            svg,\n          ]);\n        }\n      );\n    })();\n\n    (function () {\n      var two = new Two({\n        width: 400,\n        height: 400,\n      });\n\n      QUnit.Utils.get('./images/interpretation/donut.svg', function (resp) {\n        var answer = {\n          isShape: true,\n          translation: { x: 0, y: 0 },\n          scale: 1,\n          skewX: 0,\n          skewY: 0,\n          children: [\n            {\n              isShape: true,\n              translation: { x: 200, y: 200.16 },\n              scale: 1,\n              skewX: 0,\n              skewY: 0,\n              vertices: [\n                {\n                  x: 0,\n                  y: -121.338,\n                  command: 'M',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: 0 },\n                    right: { x: -16.753251, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -47.230172,\n                  y: -111.802641,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 14.516671, y: -6.140047 },\n                    right: { x: -14.516672, y: 6.140046 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -85.798875,\n                  y: -85.798875,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 10.978937, y: -10.978938 },\n                    right: { x: -10.978938, y: 10.978937 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -111.802641,\n                  y: -47.230172,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 6.140046, y: -14.516672 },\n                    right: { x: -6.140047, y: 14.516671 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -121.338,\n                  y: 0,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -16.753251 },\n                    right: { x: 0, y: 16.75325 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -111.802641,\n                  y: 47.230171,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -6.140047, y: -14.516672 },\n                    right: { x: 6.140046, y: 14.516671 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -85.798875,\n                  y: 85.798875,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -10.978938, y: -10.978938 },\n                    right: { x: 10.978937, y: 10.978937 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -47.230172,\n                  y: 111.80264,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -14.516672, y: -6.140047 },\n                    right: { x: 14.516671, y: 6.140046 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 0,\n                  y: 121.338,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -16.753251, y: 0 },\n                    right: { x: 16.75325, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 47.230171,\n                  y: 111.80264,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -14.516672, y: 6.140046 },\n                    right: { x: 14.516671, y: -6.140047 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 85.798875,\n                  y: 85.798875,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -10.978938, y: 10.978937 },\n                    right: { x: 10.978937, y: -10.978938 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 111.80264,\n                  y: 47.230171,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -6.140047, y: 14.516671 },\n                    right: { x: 6.140046, y: -14.516672 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 121.337999,\n                  y: 0,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: 16.75325 },\n                    right: { x: 0, y: -16.753251 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 111.80264,\n                  y: -47.230172,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 6.140046, y: 14.516671 },\n                    right: { x: -6.140047, y: -14.516672 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 85.798874,\n                  y: -85.798875,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 10.978937, y: 10.978937 },\n                    right: { x: -10.978938, y: -10.978938 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 47.230171,\n                  y: -111.802641,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 14.516671, y: 6.140046 },\n                    right: { x: -14.516672, y: -6.140047 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 0,\n                  y: -121.338,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 16.753249, y: 0 },\n                    right: { x: 0, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 0,\n                  y: -121.338,\n                  command: 'Z',\n                  relative: true,\n                  controls: { left: { x: 0, y: 0 }, right: { x: 0, y: 0 } },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 0,\n                  y: 62.155,\n                  command: 'M',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: 0 },\n                    right: { x: -8.58175, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -24.193422,\n                  y: 57.270515,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 7.436109, y: 3.145234 },\n                    right: { x: -7.43611, y: -3.145235 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -43.950125,\n                  y: 43.950125,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 5.623937, y: 5.623937 },\n                    right: { x: -5.623938, y: -5.623938 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -57.270516,\n                  y: 24.193421,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 3.145234, y: 7.436109 },\n                    right: { x: -3.145235, y: -7.43611 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -62.155,\n                  y: 0,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: 8.58175 },\n                    right: { x: 0, y: -8.58175 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -57.270516,\n                  y: -24.193422,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -3.145235, y: 7.436109 },\n                    right: { x: 3.145234, y: -7.43611 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -43.950125,\n                  y: -43.950125,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -5.623938, y: 5.623937 },\n                    right: { x: 5.623937, y: -5.623938 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: -24.193422,\n                  y: -57.270516,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -7.43611, y: 3.145234 },\n                    right: { x: 7.436109, y: -3.145235 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 0,\n                  y: -62.155,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -8.58175, y: 0 },\n                    right: { x: 8.58175, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 24.193421,\n                  y: -57.270516,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -7.43611, y: -3.145235 },\n                    right: { x: 7.436109, y: 3.145234 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 43.950124,\n                  y: -43.950125,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -5.623938, y: -5.623938 },\n                    right: { x: 5.623937, y: 5.623937 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 57.270515,\n                  y: -24.193422,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: -3.145235, y: -7.43611 },\n                    right: { x: 3.145234, y: 7.436109 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 62.154999,\n                  y: 0,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 0, y: -8.58175 },\n                    right: { x: 0, y: 8.58175 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 57.270515,\n                  y: 24.193421,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 3.145234, y: -7.43611 },\n                    right: { x: -3.145235, y: 7.436109 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 43.950124,\n                  y: 43.950125,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 5.623937, y: -5.623938 },\n                    right: { x: -5.623938, y: 5.623937 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 24.193421,\n                  y: 57.270515,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 7.436109, y: -3.145235 },\n                    right: { x: -7.43611, y: 3.145234 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n                {\n                  x: 0,\n                  y: 62.155,\n                  command: 'C',\n                  relative: true,\n                  controls: {\n                    left: { x: 8.58175, y: 0 },\n                    right: { x: 0, y: 0 },\n                  },\n                  rx: 0,\n                  ry: 0,\n                  xAxisRotation: 0,\n                  largeArcFlag: 0,\n                  sweepFlag: 1,\n                },\n              ],\n            },\n          ],\n        };\n        var svg = QUnit.Utils.textToDOM(resp)[0];\n        var shape = two.interpret(svg).subdivide(3);\n        var group = two.makeGroup();\n\n        group.translation.copy(shape.children[0].translation);\n\n        _.each(shape.children[0].vertices, function (v) {\n          var circle = new Two.Circle(v.x, v.y, 3);\n          circle.noStroke().fill = 'red';\n          group.add(circle);\n        });\n\n        two.update();\n\n        assert.ok(\n          QUnit.Utils.shapeEquals(answer, shape),\n          'Two.subdivide subdivides holes properly.'\n        );\n        assert.done();\n\n        QUnit.Utils.addElemToTest(assert.test, [two.renderer.domElement, svg]);\n      });\n    })();\n  });\n})();\n"
  },
  {
    "path": "tests/suite/svg.js",
    "content": "/**\n * Tests Two.js Svg Rendering Functionality:\n */\n\n(function () {\n  QUnit.module('SVGRenderer');\n\n  QUnit.test('Two.makeLine', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var line = two.makeLine(0, 0, two.width, two.height);\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + line.id);\n\n    assert.equal(\n      elem.getAttribute('d'),\n      'M 0 0 L 400 400 ',\n      'Two.makeLine applies d attribute properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeRectangle', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var rect = two.makeRectangle(two.width / 2, two.height / 2, 100, 100);\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + rect.id);\n\n    assert.equal(\n      elem.getAttribute('d'),\n      'M -50 -50 L 50 -50 L 50 50 L -50 50 Z ',\n      'Two.makeRectangle applies d attribute properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeEllipse', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var ellipse = two.makeEllipse(two.width / 2, two.height / 2, 100, 100);\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + ellipse.id);\n\n    assert.equal(\n      elem.getAttribute('d'),\n      'M 100 0 C 100 55.228474 55.228474 100 0 100 C -55.228475 100 -100 55.228474 -100 0 C -100.000001 -55.228475 -55.228475 -100 -0.000001 -100 C 55.228474 -100.000001 100 -55.228475 100 0 Z ',\n      'Two.makeEllipse applies d attribute properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeCircle', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var circle = two.makeCircle(two.width / 2, two.height / 2, 50);\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + circle.id);\n\n    assert.equal(\n      elem.getAttribute('d'),\n      'M 50 0 C 50 27.614237 27.614237 50 0 50 C -27.614238 50 -50 27.614237 -50 0 C -50.000001 -27.614238 -27.614238 -50 -0.000001 -50 C 27.614237 -50.000001 50 -27.614238 50 0 Z ',\n      'Two.makeCircle applies d attribute properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makePoints', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var points = two.makePoints(200, 200, 205, 200, 195, 200);\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + points.id);\n    assert.equal(\n      elem.getAttribute('d'),\n      'M 200 199.5 a 0.5 0.5 0 1 0 0.001 0 ZM 205 199.5 a 0.5 0.5 0 1 0 0.001 0 ZM 195 199.5 a 0.5 0.5 0 1 0 0.001 0 Z',\n      'Two.makePoints applies d attribute properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makePath', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var amount = 20;\n    var points = [];\n    for (var i = 0; i < amount; i++) {\n      var pct = i / amount;\n      var x = pct * 300 + 50;\n      var y = i % 2 ? 25 : 75;\n      points.push(new Two.Anchor(x, y));\n    }\n    var poly = two.makePath(points, true);\n    var path = two.makePath();\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + poly.id);\n    assert.equal(\n      elem.getAttribute('d'),\n      'M -142.5 25 L -127.5 -25 L -112.5 25 L -97.5 -25 L -82.5 25 L -67.5 -25 L -52.5 25 L -37.5 -25 L -22.5 25 L -7.5 -25 L 7.5 25 L 22.5 -25 L 37.5 25 L 52.5 -25 L 67.5 25 L 82.5 -25 L 97.5 25 L 112.5 -25 L 127.5 25 L 142.5 -25 ',\n      'Two.makePath applies d attribute properly.'\n    );\n\n    elem = two.renderer.domElement.querySelector('#' + path.id);\n    assert.equal(\n      elem.getAttribute('transform'),\n      'matrix(1 0 0 1 0 0)',\n      'Two.makePath applies transform attribute properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeCurve', function (assert) {\n    assert.expect(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var amount = 20;\n    var points = [];\n    for (var i = 0; i < amount; i++) {\n      var pct = i / amount;\n      var x = pct * 300 + 50;\n      var y = i % 2 ? 25 : 75;\n      points.push(new Two.Anchor(x, y));\n    }\n    var curve = two.makeCurve(points, true);\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + curve.id);\n\n    assert.equal(\n      elem.getAttribute('d'),\n      'M -142.5 25 C -142.5 25 -144.726506 -25 -127.5 -25 C -110.273495 -25 -129.726506 24.999999 -112.5 25 C -95.273495 25 -114.726506 -25 -97.5 -25 C -80.273495 -25 -99.726506 24.999999 -82.5 25 C -65.273495 25 -84.726506 -25 -67.5 -25 C -50.273495 -25 -69.726506 24.999999 -52.5 25 C -35.273495 25 -54.726506 -25 -37.5 -25 C -20.273495 -25 -39.726506 24.999999 -22.5 25 C -5.273495 25 -24.726506 -25 -7.5 -25 C 9.726505 -25 -9.726506 24.999999 7.5 25 C 24.726505 25 5.273494 -25 22.5 -25 C 39.726505 -25 20.273494 24.999999 37.5 25 C 54.726505 25 35.273494 -25 52.5 -25 C 69.726505 -25 50.273494 24.999999 67.5 25 C 84.726505 25 65.273494 -25 82.5 -25 C 99.726505 -25 80.273494 24.999999 97.5 25 C 114.726505 25 95.273494 -25 112.5 -25 C 129.726505 -25 110.273494 24.999999 127.5 25 C 144.726505 25 142.5 -25 142.5 -25 ',\n      'The d attribute set properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeLinearGradient', function (assert) {\n    assert.expect(8);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var gradient = two.makeLinearGradient(\n      0,\n      -two.height / 2,\n      0,\n      two.height / 2,\n      new Two.Gradient.Stop(0, 'rgb(255, 100, 100)'),\n      new Two.Gradient.Stop(1, 'rgb(100, 100, 255)')\n    );\n\n    var rect = two.makeRectangle(\n      two.width / 2,\n      two.height / 2,\n      two.width / 4,\n      two.height / 4\n    );\n    rect.fill = gradient;\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + gradient.id);\n\n    assert.equal(\n      elem.tagName,\n      'linearGradient',\n      'Two.LinearGradient renders as a <linear-gradient /> tag.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('x1')),\n      0,\n      'The x1 attribute applied properly.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('y1')),\n      -200,\n      'The y1 attribute applied properly.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('x2')),\n      0,\n      'The x2 attribute applied properly.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('y2')),\n      200,\n      'The y2 attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('spreadMethod'),\n      'pad',\n      'The spreadMethod attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('gradientUnits'),\n      'objectBoundingBox',\n      'The gradientUnits attribute applied properly.'\n    );\n    assert.equal(\n      elem.innerHTML,\n      '<stop offset=\"0%\" stop-color=\"rgb(255, 100, 100)\" stop-opacity=\"1\"></stop><stop offset=\"100%\" stop-color=\"rgb(100, 100, 255)\" stop-opacity=\"1\"></stop>',\n      'The innerHTML applied properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeRadialGradient', function (assert) {\n    assert.expect(9);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var gradient = two.makeRadialGradient(\n      0,\n      0,\n      two.width / 2,\n      new Two.Gradient.Stop(0, 'rgb(255, 100, 100)'),\n      new Two.Gradient.Stop(1, 'rgb(100, 100, 255)')\n    );\n\n    var rect = two.makeRectangle(\n      two.width / 2,\n      two.height / 2,\n      two.width / 4,\n      two.height / 4\n    );\n    rect.fill = gradient;\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + gradient.id);\n\n    assert.equal(\n      elem.tagName,\n      'radialGradient',\n      'Two.RadialGradient renders as a <radial-gradient /> tag.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('cx')),\n      0,\n      'The cx attribute applied properly.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('cy')),\n      0,\n      'The cy attribute applied properly.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('fx')),\n      0,\n      'The fx attribute applied properly.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('fy')),\n      0,\n      'The fy attribute applied properly.'\n    );\n    assert.equal(\n      parseFloat(elem.getAttribute('r')),\n      200,\n      'The r attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('spreadMethod'),\n      'pad',\n      'The spreadMethod attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('gradientUnits'),\n      'objectBoundingBox',\n      'The gradeintUnits attribute applied properly.'\n    );\n    assert.equal(\n      elem.innerHTML,\n      '<stop offset=\"0%\" stop-color=\"rgb(255, 100, 100)\" stop-opacity=\"1\"></stop><stop offset=\"100%\" stop-color=\"rgb(100, 100, 255)\" stop-opacity=\"1\"></stop>',\n      'The innerHTML applied properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Simple)', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(path, two.width / 2, two.height / 2, 150, 100);\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + image.id);\n      var id = texture.id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.Image applied the correct texture properly.'\n      );\n      assert.equal(image.mode, 'fill', 'Two.Image uses default fill mode.');\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: fit)', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      150,\n      100,\n      'fit'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + image.id);\n      var id = texture.id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.Image applied the correct texture properly in fit mode.'\n      );\n      assert.equal(\n        image.mode,\n        'fit',\n        'Two.Image mode property is set correctly to fit.'\n      );\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: fill)', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      150,\n      100,\n      'fill'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + image.id);\n      var id = texture.id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.Image applied the correct texture properly in fill mode.'\n      );\n      assert.equal(\n        image.mode,\n        'fill',\n        'Two.Image mode property is set correctly to fill.'\n      );\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: crop)', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      150,\n      100,\n      'crop'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + image.id);\n      var id = texture.id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.Image applied the correct texture properly in crop mode.'\n      );\n      assert.equal(\n        image.mode,\n        'crop',\n        'Two.Image mode property is set correctly to crop.'\n      );\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: tile)', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      150,\n      100,\n      'tile'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + image.id);\n      var id = texture.id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.Image applied the correct texture properly in tile mode.'\n      );\n      assert.equal(\n        image.mode,\n        'tile',\n        'Two.Image mode property is set correctly to tile.'\n      );\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode switching)', function (assert) {\n    assert.expect(3);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      150,\n      100,\n      'fit'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + image.id);\n      var id = texture.id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.Image applied the correct texture properly in initial fit mode.'\n      );\n      assert.equal(\n        image.mode,\n        'fit',\n        'Two.Image initial mode property is set correctly to fit.'\n      );\n\n      // Change mode and verify\n      image.mode = 'fill';\n      two.update();\n\n      assert.equal(\n        image.mode,\n        'fill',\n        'Two.Image mode property changes correctly from fit to fill.'\n      );\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeSprite (Simple)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/sequence/00000.png';\n    var sprite = two.makeSprite(path, two.width / 2, two.height / 2);\n    var texture = sprite.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + sprite.id);\n      var id = texture.id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.Sprite applied the correct texture properly.'\n      );\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImageSequence', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var paths = [];\n    for (var i = 0; i < 30; i++) {\n      paths.push('/tests/images/sequence/' + QUnit.Utils.digits(i, 5) + '.png');\n    }\n    var sequence = two.makeImageSequence(\n      paths,\n      two.width / 2,\n      two.height / 2,\n      2\n    );\n    sequence.index = 3;\n    var texture = sequence.textures[sequence.index];\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n\n      two.update();\n\n      var elem = two.renderer.domElement.querySelector('#' + sequence.id);\n      var id = sequence.textures[sequence.index].id;\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.ImageSequence applied the correct texture properly.'\n      );\n\n      sequence.index = 7;\n      id = sequence.textures[sequence.index].id;\n\n      two.update();\n\n      assert.equal(\n        'url(#' + id + ')',\n        elem.getAttribute('fill'),\n        'Two.ImageSequence can change index properly.'\n      );\n\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    two.renderer.domElement.style.cursor = 'pointer';\n    two.renderer.domElement.addEventListener(\n      'click',\n      function () {\n        if (two.playing) {\n          two.pause();\n        } else {\n          sequence.loop = true;\n          sequence.play();\n          two.play();\n        }\n      },\n      false\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeSprite', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var sprite = two.makeSprite(\n      path,\n      two.width / 2,\n      two.height / 2,\n      4,\n      4,\n      2,\n      false\n    );\n    var texture = sprite.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      assert.ok(true, 'Two.Sprite created properly.');\n\n      sprite.index = 10;\n      two.update();\n\n      var elem = texture._renderer.elem;\n      var statement = [\n        elem.getAttribute('x'),\n        elem.getAttribute('y'),\n        elem.getAttribute('width'),\n        elem.getAttribute('height'),\n      ].join(',');\n\n      assert.equal(\n        statement,\n        '-640,-640,1025,1025',\n        'Two.Sprite changed index properly.'\n      );\n      assert.done();\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    two.renderer.domElement.style.cursor = 'pointer';\n    two.renderer.domElement.addEventListener(\n      'click',\n      function () {\n        if (two.playing) {\n          two.pause();\n        } else {\n          sprite.loop = true;\n          sprite.play();\n          two.play();\n        }\n      },\n      false\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeGroup', function (assert) {\n    assert.expect(2);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var group = two.makeGroup();\n    group.className = 'hello world';\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + group.id);\n\n    assert.equal(elem.tagName, 'g', 'Two.Group renders as a <g/> tag.');\n    assert.equal(\n      elem.getAttribute('class'),\n      'hello world',\n      'The class attribute applied properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Two.makeText', function (assert) {\n    assert.expect(17);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var text = two.makeText('Hello World', two.width / 2, two.height / 2);\n    text.fill = '#00aeff';\n    text.noStroke();\n\n    text.className = 'hello world';\n\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + text.id);\n\n    assert.equal(elem.tagName, 'text', 'Two.Text renders as a <text /> tag.');\n    assert.equal(\n      elem.getAttribute('transform'),\n      'matrix(1 0 0 1 200 200)',\n      'The transform attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('font-family'),\n      'sans-serif',\n      'The font-family attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('font-size'),\n      '13',\n      'The font-size proeprty applied properly'\n    );\n    assert.equal(\n      elem.getAttribute('line-height'),\n      '17',\n      'The line-height attribute applied properly'\n    );\n    assert.equal(\n      elem.getAttribute('text-anchor'),\n      'middle',\n      'The text-anchor attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('dominant-baseline'),\n      'middle',\n      'The dominant-baseline attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('font-style'),\n      'normal',\n      'The font-style attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('font-weight'),\n      '500',\n      'The font-weight attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('text-decoration'),\n      'none',\n      'The text-decoration attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('fill'),\n      '#00aeff',\n      'The fill attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('stroke-width'),\n      '0',\n      'The stroke-width attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('opacity'),\n      '1',\n      'The opacity attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('visibility'),\n      'visible',\n      'The visibility attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('class'),\n      'hello world',\n      'The class attribute applied properly.'\n    );\n    assert.equal(\n      elem.getAttribute('direction', 'ltr'),\n      'ltr',\n      'The direction attribute applied properly.'\n    );\n    assert.equal(\n      elem.innerHTML,\n      text.value,\n      'The value attribute applied properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('Styles', function (assert) {\n    assert.expect(9);\n\n    var two = new Two({\n      width: 400,\n      height: 400,\n    });\n\n    var shape = two.makeRectangle(two.width / 2, two.height / 2, 50, 50);\n\n    shape.rotation = Math.PI / 2;\n    shape.scale = 0.5;\n\n    shape.fill = 'lightcoral';\n    shape.stroke = '#333';\n    shape.linewidth = 10;\n    shape.opacity = 0.5;\n    shape.join = 'miter';\n    shape.cap = 'butt';\n    shape.miter = 10;\n    shape.className = 'pretty';\n\n    shape.closed = false;\n    shape.curved = true;\n\n    shape.visible = false;\n    shape.visible = true;\n\n    // Update Rendering\n    two.update();\n\n    var elem = two.renderer.domElement.querySelector('#' + shape.id);\n    var matrix = elem.getAttribute('transform');\n    assert.equal(\n      matrix.match(/matrix\\((.*)\\)/)[1],\n      shape._matrix.toString(),\n      'Two.Shape._matrix gets and sets properly.'\n    );\n\n    assert.equal(\n      elem.getAttribute('fill'),\n      shape.fill,\n      'Two.Shape.fill gets and sets properly.'\n    );\n    assert.equal(\n      elem.getAttribute('stroke'),\n      shape.stroke,\n      'Two.Shape.stroke gets and sets properly.'\n    );\n    assert.equal(\n      elem.getAttribute('stroke-linejoin'),\n      shape.join,\n      'Two.Shape.join gets and sets properly.'\n    );\n    assert.equal(\n      elem.getAttribute('stroke-linecap'),\n      shape.cap,\n      'Two.Shape.cap gets and sets properly.'\n    );\n    assert.equal(\n      elem.getAttribute('visibility'),\n      'visible',\n      'Two.Shape.visible gets and sets properly.'\n    );\n    assert.equal(\n      elem.getAttribute('stroke-miterlimit'),\n      shape.miter,\n      'Two.Shape.miter gets and sets properly.'\n    );\n    assert.equal(\n      elem.getAttribute('class'),\n      shape.className,\n      'Two.Shape.className gets and sets properly.'\n    );\n    assert.ok(\n      elem.getAttribute('stroke-opacity') == shape.opacity &&\n        elem.getAttribute('fill-opacity') == shape.opacity,\n      'Two.Shape.opacity gets and sets properly.'\n    );\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n})();\n"
  },
  {
    "path": "tests/suite/webgl.js",
    "content": "/**\n * Tests Two.js WebGl Rendering Functionality:\n */\n\n(function () {\n  QUnit.module('WebGLRenderer');\n\n  var getRatio = function (v) {\n    return Math.round(window.devicePixelRatio);\n  };\n  var deviceRatio = getRatio(document.createElement('canvas').getContext('2d'));\n  var suffix = '@' + deviceRatio + 'x.png';\n\n  QUnit.test('Two.makeLine', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    }).appendTo(document.body);\n\n    two.makeLine(0, 0, two.width, two.height);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/line' + suffix,\n      two.renderer,\n      'Two.makeLine renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeRectangle', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    two.makeRectangle(two.width / 2, two.height / 2, 100, 100);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/rectangle' + suffix,\n      two.renderer,\n      'Two.makeRectangle renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeEllipse', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    two.makeEllipse(two.width / 2, two.height / 2, 100, 100);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/ellipse' + suffix,\n      two.renderer,\n      'Two.makeEllipse renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeCircle', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    two.makeCircle(two.width / 2, two.height / 2, 50);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/circle' + suffix,\n      two.renderer,\n      'Two.makeCircle renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makePoints', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: 1,\n    });\n\n    var points = two.makePoints(200, 200, 220, 200, 180, 200);\n    points.size = 10;\n    points.noStroke();\n    points.fill = '#00AEFF';\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/points' + suffix,\n      two.renderer,\n      'Two.makePoints renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makePath', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var amount = 20;\n    var points = [];\n    for (var i = 0; i < amount; i++) {\n      var pct = i / amount;\n      var x = pct * 300 + 50;\n      var y = i % 2 ? 25 : 75;\n      points.push(new Two.Anchor(x, y));\n    }\n    two.makePath(points, true);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/polygon' + suffix,\n      two.renderer,\n      'Two.makePath renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeCurve', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var amount = 20;\n    var points = [];\n    for (var i = 0; i < amount; i++) {\n      var pct = i / amount;\n      var x = pct * 300 + 50;\n      var y = i % 2 ? 25 : 75;\n      points.push(new Two.Anchor(x, y));\n    }\n    two.makeCurve(points, true);\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/curve' + suffix,\n      two.renderer,\n      'Two.makeCurve renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeLinearGradient', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var gradient = two.makeLinearGradient(\n      0,\n      -two.height / 2,\n      0,\n      two.height / 2,\n      new Two.Gradient.Stop(0, 'rgb(255, 100, 100)'),\n      new Two.Gradient.Stop(1, 'rgb(100, 100, 255)')\n    );\n\n    var rect = two.makeRectangle(\n      two.width / 2,\n      two.height / 2,\n      two.width / 4,\n      two.height / 4\n    );\n    rect.fill = gradient;\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/linear-gradient' + suffix,\n      two.renderer,\n      'Two.makeLinearGradient renders properly.'\n    );\n  });\n\n  QUnit.test('Two.makeRadialGradient', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var gradient = two.makeRadialGradient(\n      0.5,\n      0.5,\n      2,\n      new Two.Gradient.Stop(0, 'rgb(255, 100, 100)'),\n      new Two.Gradient.Stop(1, 'rgb(100, 100, 255)')\n    );\n\n    var rect = two.makeRectangle(\n      two.width / 2,\n      two.height / 2,\n      two.width / 4,\n      two.height / 4\n    );\n    rect.fill = gradient;\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/radial-gradient' + suffix,\n      two.renderer,\n      'Two.makeLinearGradient renders properly.'\n    );\n  });\n\n  QUnit.test('two.makeImage (Simple)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(path, two.width / 2, two.height / 2, 200, 100);\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fill' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly with default fill mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: fit)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'fit'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fit' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in fit mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: fill)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'fill'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fill' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in fill mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: crop)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'crop'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-crop' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in crop mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: tile)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'tile'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-tile' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in tile mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode: stretch)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'stretch'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-stretch' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in stretch mode.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeImage (Mode switching)', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(2);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 200,\n      height: 100,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var image = two.makeImage(\n      path,\n      two.width / 2,\n      two.height / 2,\n      200,\n      100,\n      'fit'\n    );\n    var texture = image.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-fit' + suffix,\n        two.renderer,\n        'Two.makeImage renders properly in initial fit mode.',\n        function () {\n          image.mode = 'fill';\n          two.update();\n\n          QUnit.Utils.compare.call(\n            assert,\n            './images/canvas/image-fill' + suffix,\n            two.renderer,\n            'Two.Image changes mode properly from fit to fill.'\n          );\n        }\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    QUnit.Utils.addInstanceToTest(assert.test, two);\n  });\n\n  QUnit.test('two.makeSprite (Simple)', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/sequence/00000.png';\n    var sprite = two.makeSprite(path, two.width / 2, two.height / 2);\n    var texture = sprite.texture;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/sprite-simple' + suffix,\n        two.renderer,\n        'Two.makeSprite renders properly.'\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n  });\n\n  QUnit.test('two.makeImageSequence', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(2);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var paths = [];\n    for (var i = 0; i < 30; i++) {\n      paths.push('/tests/images/sequence/' + QUnit.Utils.digits(i, 5) + '.png');\n    }\n    var sequence = two.makeImageSequence(\n      paths,\n      two.width / 2,\n      two.height / 2,\n      2\n    );\n    sequence.index = 3;\n    var texture = sequence.textures[sequence.index];\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-sequence-1' + suffix,\n        two.renderer,\n        'Two.ImageSequence applied the correct texture properly.',\n        function () {\n          sequence.index = 7;\n          texture = sequence.textures[sequence.index];\n          texture._flagImage = true;\n\n          texture.bind(Two.Events.Types.load, function () {\n            texture.unbind(Two.Events.Types.load);\n\n            two.update();\n\n            QUnit.Utils.compare.call(\n              assert,\n              './images/canvas/image-sequence-2' + suffix,\n              two.renderer,\n              'Two.ImageSequence can change index properly.'\n            );\n          });\n\n          texture._update();\n        }\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    two.renderer.domElement.style.cursor = 'pointer';\n    two.renderer.domElement.addEventListener(\n      'click',\n      function () {\n        if (two.playing) {\n          two.pause();\n        } else {\n          sequence.loop = true;\n          sequence.play();\n          two.play();\n        }\n      },\n      false\n    );\n  });\n\n  QUnit.test('two.makeSprite', function (assert) {\n    assert.expect(2);\n    assert.done = assert.async(2);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var path = '/tests/images/spritesheet.jpg';\n    var sprite = two.makeSprite(\n      path,\n      two.width / 2,\n      two.height / 2,\n      4,\n      4,\n      2,\n      false\n    );\n    var texture = sprite.texture;\n    sprite.index = 3;\n\n    var loaded = function () {\n      texture.unbind(Two.Events.Types.load, loaded);\n      two.update();\n\n      QUnit.Utils.compare.call(\n        assert,\n        './images/canvas/image-sequence-1' + suffix,\n        two.renderer,\n        'Two.makeSprite renders properly.',\n        function () {\n          sprite.index = 7;\n          two.update();\n\n          QUnit.Utils.compare.call(\n            assert,\n            './images/canvas/image-sequence-2' + suffix,\n            two.renderer,\n            'Two.Sprite changed index properly.'\n          );\n        }\n      );\n    };\n\n    texture.bind(Two.Events.Types.load, loaded);\n    texture._update();\n\n    two.renderer.domElement.style.cursor = 'pointer';\n    two.renderer.domElement.addEventListener(\n      'click',\n      function () {\n        if (two.playing) {\n          two.pause();\n        } else {\n          sprite.loop = true;\n          sprite.play();\n          two.play();\n        }\n      },\n      false\n    );\n  });\n\n  QUnit.test('Two.makeText', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var text = two.makeText('Hello World', two.width / 2, two.height / 2);\n    text.fill = '#00aeff';\n    text.noStroke();\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/text' + suffix,\n      two.renderer,\n      'Two.makeText renders properly.'\n    );\n  });\n\n  QUnit.test('Styles', function (assert) {\n    assert.expect(1);\n    assert.done = assert.async(1);\n\n    var two = new Two({\n      type: Two.Types.webgl,\n      width: 400,\n      height: 400,\n      ratio: deviceRatio,\n    });\n\n    var shape = two.makeRectangle(two.width / 2, two.height / 2, 50, 50);\n\n    shape.rotation = Math.PI / 2;\n    shape.scale = 0.5;\n\n    shape.fill = 'lightcoral';\n    shape.stroke = '#333';\n    shape.linewidth = 10;\n    shape.opacity = 0.5;\n    shape.join = 'miter';\n    shape.cap = 'butt';\n    shape.miter = 10;\n\n    shape.closed = false;\n    shape.curved = true;\n\n    shape.visible = false;\n    shape.visible = true;\n\n    two.update();\n\n    QUnit.Utils.compare.call(\n      assert,\n      './images/canvas/styles' + suffix,\n      two.renderer,\n      'Styles render properly.'\n    );\n  });\n})();\n"
  },
  {
    "path": "tests/typescript/index.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar two_js_1 = require(\"two.js\");\nvar two = new two_js_1.default({\n    fullscreen: true,\n    autostart: false,\n});\nvar path;\npath = two_js_1.default.Circle.fromObject({\n    radius: 5,\n    stroke: 'blue',\n    fill: 'yellow',\n    // position: new Two.Vector(),\n    rotation: 5,\n    translation: { x: 0, y: 0 },\n});\npath = new two_js_1.default.Line(5, 5, 10, 10);\ntwo.add(path);\nvar group = new two_js_1.default.Group(path);\ntwo.add(group);\npath = new two_js_1.default.Circle(3, 3, 10);\ngroup.add(path);\npath = new two_js_1.default.Rectangle(25, 25, 5, 5);\ngroup.add(path);\npath = new two_js_1.default.Polygon(30, 30, 5, 7);\ngroup.add(path);\npath = new two_js_1.default.ArcSegment(5, 25, 5, 10, 0, Math.PI);\ngroup.add(path);\npath = new two_js_1.default.Ellipse(5, 30, 10, 5);\ngroup.add(path);\npath = new two_js_1.default.Star(30, 5, 10, 5);\ngroup.add(path);\npath = new two_js_1.default.Points([new two_js_1.default.Anchor(-2, 0), new two_js_1.default.Anchor(2, 0)]);\ngroup.add(path);\npath = new two_js_1.default.Path([new two_js_1.default.Anchor(-1, 0), new two_js_1.default.Anchor(1, 0)]);\npath.closed = false;\npath.automatic = true;\npath.position.set(two.width / 2, two.height / 2);\npath.scale = new two_js_1.default.Vector(1, 1);\ngroup.add(path);\ntwo.appendTo(document.body);\n"
  },
  {
    "path": "tests/typescript/index.ts",
    "content": "import Two from 'two.js';\n\nconst two = new Two({\n  fullscreen: true,\n  autostart: false,\n});\n\nlet path;\n\npath = Two.Circle.fromObject({\n  radius: 5,\n  stroke: 'blue',\n  fill: 'yellow',\n  // position: new Two.Vector(),\n  rotation: 5,\n  translation: { x: 0, y: 0 },\n});\n\npath = new Two.Line(5, 5, 10, 10);\ntwo.add(path);\n\nconst group = new Two.Group(path);\ntwo.add(group);\n\npath = new Two.Circle(3, 3, 10);\ngroup.add(path);\n\npath = new Two.Rectangle(25, 25, 5, 5);\ngroup.add(path);\n\npath = new Two.Polygon(30, 30, 5, 7);\ngroup.add(path);\n\npath = new Two.ArcSegment(5, 25, 5, 10, 0, Math.PI);\ngroup.add(path);\n\npath = new Two.Ellipse(5, 30, 10, 5);\ngroup.add(path);\n\npath = new Two.Star(30, 5, 10, 5);\ngroup.add(path);\n\npath = new Two.Points([new Two.Anchor(-2, 0), new Two.Anchor(2, 0)]);\ngroup.add(path);\n\npath = new Two.Path([new Two.Anchor(-1, 0), new Two.Anchor(1, 0)]);\npath.closed = false;\npath.automatic = true;\n\npath.position.set(two.width / 2, two.height / 2);\npath.scale = new Two.Vector(1, 1);\ngroup.add(path);\n\ntwo.appendTo(document.body);\n"
  },
  {
    "path": "tests/typescript/package.json",
    "content": "{\n  \"name\": \"typescript\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"two.js\": \"file:../../\",\n    \"typescript\": \"^5.2.2\"\n  }\n}"
  },
  {
    "path": "utils/INSTRUCTIONS.md",
    "content": "# TypeScript Types Declaration Build Instructions\n\n1. Remove all @extends commands from `/src` and `/extras`\n2. Run `npm run types`\n\n---\n\nIn output code `types.d.ts` do:\n\n3. Remove all `Two.*` references — e.g: `Two.Vector` -> `Vector`\n4. Remove all `typeof` references _except_ for static properties of Two — e.g: `Two.Group`\n5. Remove all `'.js'` import references\n6. Change `\"src/two\"` to `two.js`\n7. Change all `\"src/` references to `\"two.js/src/`\n8. Change all `\"extras/\"` references to `\"two.js/extras/`\n9. Fix all `constructor` methods to have proper argument ingestion\n  - Includes: `Two.Rectangle`, `Two.Circle`, `Two.Ellipse`, `Two.Line`, and `Two.Polygon`\n10. Fix private property assignment on `Two.Group`. e.g: `_stroke` -> `stroke`\n  - Includes `Two.Text`\n11. Add optional overloaded functions to `Two.makeCurve` and `Two.makePath` for `closed` attribute\n  - And other functions with `...` argument syntax\n\nThings to do to improve TypeScript Types:\n\n1. Add default values to all methods (including constructors) to clarify what is mandatory and what is optional.\n2. Figure out how to handle both ingestion of single argument Array's and full arguments as arrays.\n"
  },
  {
    "path": "utils/build.js",
    "content": "const esbuild = require('esbuild');\nconst fs = require('fs');\nconst path = require('path');\nconst _ = require('underscore');\nconst gzip = require('gzip-size');\n\nconst publishDateString = new Date().toISOString();\nconst config = getJSON('package');\nconst paths = {\n  entry: path.resolve(__dirname, '../src/two.js'),\n  umd: path.resolve(__dirname, '../build/two.js'),\n  esm: path.resolve(__dirname, '../build/two.module.js'),\n  min: path.resolve(__dirname, '../build/two.min.js'),\n  license: path.resolve(__dirname, '../LICENSE'),\n};\n\nasync function buildModules() {\n  esbuild.buildSync({\n    entryPoints: [paths.entry],\n    outfile: paths.umd,\n    bundle: true,\n    minify: false,\n    format: 'iife',\n    globalName: 'Two',\n  });\n\n  esbuild.buildSync({\n    entryPoints: [paths.entry],\n    outfile: paths.esm,\n    bundle: true,\n    target: 'es6',\n    format: 'esm',\n  });\n\n  esbuild.buildSync({\n    entryPoints: [paths.entry],\n    outfile: paths.min,\n    bundle: true,\n    minify: true,\n    format: 'iife',\n    globalName: 'Two',\n  });\n\n  const license = await fs.promises.readFile(paths.license, {\n    encoding: 'utf-8',\n  });\n  const licenseComment = ['/*', license.trim(), '*/'].join('\\n');\n\n  const umdOutput = await fs.promises.readFile(paths.umd);\n  const esmOutput = await fs.promises.readFile(paths.esm);\n  const minOutput = await fs.promises.readFile(paths.min);\n\n  const moduleExports = `(function(){if(typeof exports==='object'&&typeof module!=='undefined'){module.exports=Two}})()`;\n\n  return Promise.all([\n    fs.promises.writeFile(\n      paths.umd,\n      [licenseComment, template(umdOutput, true), moduleExports].join('\\n')\n    ),\n    fs.promises.writeFile(\n      paths.esm,\n      [licenseComment, template(esmOutput, false)].join('\\n')\n    ),\n    fs.promises.writeFile(\n      paths.min,\n      [licenseComment, template(minOutput, true), moduleExports].join('\\n')\n    ),\n  ]);\n}\n\nfunction template(buffer, isExposed) {\n  const code = buffer.toString();\n  const generate = _.template(code);\n  let result = generate({\n    version: config.version,\n    publishDate: publishDateString,\n  });\n  if (isExposed) {\n    result = result.replace(/\\}\\)\\(\\);\\s*$/, '})().default;');\n  }\n  return result;\n}\n\nfunction publishModule() {\n  let size;\n  const result = {};\n\n  size = getFileSize('two.js');\n  result.development = formatFileSize(size);\n\n  size = getFileSize('two.min.js');\n  result.production = formatFileSize(size);\n\n  const contents = JSON.stringify(result);\n  const outputPath = path.resolve(__dirname, './file-sizes.json');\n\n  return fs.promises.writeFile(outputPath, contents);\n}\n\nfunction getFileSize(filename) {\n  const file = fs.readFileSync(path.resolve(__dirname, '../build', filename));\n  return gzip.sync(file);\n}\n\nfunction formatFileSize(v) {\n  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n  let iterations = 0;\n  while (v > 1000) {\n    v *= 0.001;\n    iterations++;\n  }\n  return [Math.round(v), sizes[iterations]].join('');\n}\n\nasync function build() {\n  let startTime, elapsed;\n\n  startTime = Date.now();\n  try {\n    await buildModules();\n  } catch (error) {\n    console.log(error);\n  }\n  elapsed = Date.now() - startTime;\n  console.log('Built and minified Two.js:', elapsed / 1000, 'seconds');\n\n  elapsed = Date.now() - startTime;\n  try {\n    await publishModule();\n  } catch (error) {\n    console.log(error);\n  }\n  console.log(\n    'Published additional statistics to wiki:',\n    elapsed / 1000,\n    'seconds'\n  );\n}\n\nfunction getJSON(filename) {\n  var file = fs.readFileSync(path.resolve(__dirname, '..', `${filename}.json`));\n  return JSON.parse(file);\n}\n\nbuild();\n"
  },
  {
    "path": "utils/docs.template",
    "content": "---\ntitle: <%= root.longname %>\npageClass: docs\nlang: en-US\n---\n\n# <%= root.longname %>\n\n<% if (root.augments) { %>\n<div class=\"extends\">\n\nExtends: [<%= root.augments %>](<%= root.augmentsHref %>)\n\n</div>\n<% } %>\n\n<%= root.description %>\n\n<% if (root.meta) { %>\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"<%= 'https://github.com/jonobr1/two.js/blob/main/' + root.meta.path.replace(/^[\\w\\d\\/]*two\\.js\\//i, '') + '/' + root.meta.filename %>\" />\n</div>\n<% } %>\n\n<carbon-ads />\n\n<% if (root.params && root.params.length > 0) { %>\n### Constructor\n<% } %>\n<% _.each(root.params, function(param, i) { %><% if (i <= 0) { %>\n| Argument | Description |\n| ---- | ----------- |\n| <% if (param.name) { %> <%= param.name %> <% } %> | <%= param.description %> |<% } else { %>\n| <% if (param.name) { %> <%= param.name %> <% } %> | <%= param.description %> |<% } %><% }); %>\n\n<% _.each(citations, function(item) { %>\n\n<div class=\"<%= item.scope %> <%= item.kind %> <%= item.overloaded ? 'overloaded' : '' %>\">\n\n## <%= item.shortname %>\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#<%= item.shortname %>\"><span class=\"prefix\"><%= item.prefixname %></span><span class=\"shortname\"><%= item.shortname %></span></a></h2>\n\n<% if (item.overloaded) { %>\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n<% } %>\n\n<% if (item.returns && item.returns.length == 1) { %>\n<div class=\"returns\">\n\n__Returns__: <% _.each(item.returns, function(result) { %><% if (result.type) { %><% _.each(result.type.names, function(name) { %><%= name.replace(/([<>])/ig, '\\\\$1') %><% }); %>\n<% } %>\n<% if (result.description) { %>\n<%= result.description %>\n<% } %>\n<% }); %>\n</div>\n<% } %>\n\n<% if (item.returns && item.returns.length > 1) { %>\n<div class=\"returns\">\n\n__Returns__:\n<% _.each(item.returns, function(result) { %>\n<% if (result.type) { %>\n<% _.each(result.type.names, function(name) { %>\n+ `<%= name.replace(/([<>])/ig, '\\\\$1') %>`\n<% }); %>\n<% } %>\n<% if (result.description) { %>\n<%= result.description %>\n<% } %>\n<% }); %>\n</div>\n<% } %>\n\n<% if (item.fires && item.fires.length > 0) { %>\n<div class=\"fires\">\n\n__Triggers__:\n<% _.each(item.fires, function(name) { %>\n+ `<%= name %>`\n<% }); %>\n</div>\n<% } %>\n\n<% if (item.properties && item.properties.length > 0) { %>\n<div class=\"properties\">\n<% _.each(item.properties, function(property, i) { %>\n<% if (i > 0) { %>\n__<%= property.name %>__: <%= package.template(property.description) %>\n<% } else { %>\n<%= package.template(property.description) %>\n<% } %>\n<% }); %>\n</div>\n<% } %>\n\n<% if (item.params && item.params.length > 0 ) { %>\n<div class=\"params\">\n<% _.each(item.params, function(param, i) { %><% if (i <= 0) { %>\n| Argument | Description |\n| ---- | ----------- |\n| <% if (param.name) { %> <%= param.name %> <% } %> | <%= param.description %> |<% } else { %>\n| <% if (param.name) { %> <%= param.name %> <% } %> | <%= param.description %> |<% } %><% }); %>\n</div>\n<% } %>\n\n\n<% if (item.description) { %>\n<div class=\"description\">\n\n<%= item.description %>\n\n</div>\n<% } %>\n\n<% if (item.see && item.see.length > 0) { %>\n<div class=\"see\">\n\nSee: <%= _.map(item.see, function(see) { return see; }).join(', ') %>\n\n</div>\n<% } %>\n\n<% if (item.meta) { %>\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"<%= 'https://github.com/jonobr1/two.js/blob/main/' + item.meta.path.replace(/^[\\w\\d\\/]*two\\.js\\//i, '') + '/' + item.meta.filename + '#L' + item.meta.lineno %>\">\n    <%= item.meta.filename + ':' + item.meta.lineno %>\n  </a>\n\n</div>\n<% } %>\n\n<% if (item.tags && item.tags.length > 0) { %>\n<div class=\"tags\">\n<% _.each(item.tags, function(tag) { %>\n<% if (tag.title && tag.text) { %>\n::: tip <%= tag.title %>\n<%= tag.text || '&nbsp;' %>\n:::\n<% } %>\n<% }); %>\n</div>\n<% } %>\n\n</div>\n\n<% }); %>\n"
  },
  {
    "path": "utils/document.js",
    "content": "var _ = require('underscore');\nvar fs = require('fs');\nvar path = require('path');\nvar compiler = require('jsdoc-api');\nvar sourceFiles = require('./source-files');\n\nvar pkg = JSON.parse(\n  fs.readFileSync(path.resolve(__dirname, '../package.json'))\n);\nvar directory = [];\n\nvar template = _.template(\n  fs.readFileSync(path.resolve(__dirname, './docs.template'), {\n    encoding: 'utf8',\n  })\n);\n\npkg.template = function (str) {\n  if (typeof str === 'string') {\n    var regex = /\\$\\w*/g;\n    var match = str.match(regex);\n    if (match && match.length > 0) {\n      var prop = match[0].replace('$', '');\n      str = str.replace(regex, pkg[prop]);\n    }\n  }\n  return str;\n};\n\npreprocess();\n\nfunction preprocess() {\n  _.each(sourceFiles, function (file) {\n    var sourceFile = path.resolve(__dirname, '../', file);\n    var pivotDir = [\n      '/docs/',\n      file.replace('jsm/', '').replace('src/', '').replace('.js', '/'),\n    ].join('');\n\n    var citations = compiler.explainSync({\n      files: sourceFile,\n      cache: false,\n    });\n\n    var root = getRoot(citations);\n\n    directory.push({ name: root.longname, dir: pivotDir });\n  });\n\n  process();\n}\n\nfunction process() {\n  _.each(sourceFiles, function (file) {\n    var sourceFile = path.resolve(__dirname, '../', file);\n    var pivotDir = [\n      '/docs/',\n      file.replace('jsm/', '').replace('src/', '').replace('.js', '/'),\n    ].join('');\n\n    var outputDir = path.resolve(__dirname, '../wiki' + pivotDir);\n    var outputFile = path.join(outputDir, '/README.md');\n\n    var citations = compiler.explainSync({\n      files: sourceFile,\n      cache: false,\n    });\n\n    citations.slice(0).forEach(function (object) {\n      var a = object.undocumented;\n      var b = /package:undefined/i.test(object.longname);\n      var c = /Two\\.Utils\\.Events\\.(bind|unbind)/i.test(object.memberof);\n      var d = /(private|protected)/i.test(object.access);\n\n      if (a || b || c || d) {\n        // Remove private / hidden / incomplete documented citations\n        citations.splice(citations.indexOf(object), 1);\n      } else {\n        expandLink(object, 'description');\n        _.each(object.params, expandParam, object);\n        _.each(object.returns, expandParam, object);\n        _.each(object.properties, expandParam, object);\n        _.each(object.tags, expandTag, object);\n        object.see = _.map(object.see, expandSee, object);\n\n        var sn;\n        sn = object.longname.replace(/#/gi, '.');\n        var snList = sn.split('.');\n        var snIndex = snList.length > 2 ? 2 : 1;\n        object.shortname = snList.slice(snIndex).join('.');\n        object.prefixname = sn.replace(object.shortname, '');\n\n        // name and href for augments property\n        var an;\n\n        if (Array.isArray(object.augments) && object.augments.length > 0) {\n          an = object.augments[object.augments.length - 1].split('.');\n        } else if (typeof object.augments === 'string') {\n          an = object.augments.split('.');\n        }\n\n        if (an) {\n          object.augmentsHref = getHref(an.slice(1).join('.'));\n        }\n      }\n    });\n\n    var citationsByScope = {\n      instance: [],\n      static: [],\n    };\n\n    _.each(citations, function (citation) {\n      if (/#/i.test(citation.longname)) {\n        citationsByScope.instance.push(citation);\n      } else {\n        citationsByScope.static.push(citation);\n      }\n    });\n\n    // citationsByScope.instance.sort(sortByFunctionThenAlphabetical);\n    // citationsByScope.static.sort(sortByFunctionThenAlphabetical);\n\n    citations = citationsByScope.static.concat(citationsByScope.instance);\n\n    // console.log(\n    //   citations.map(function(a) {\n    //     return a.scope + '-' + a.longname;\n    //   })\n    // );\n\n    // Ensure outputDir is inside the project root\n    var projectRoot = path.resolve(__dirname, '../');\n    var relative = path.relative(projectRoot, outputDir);\n\n    if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {\n      fs.mkdirSync(outputDir, { recursive: true });\n    } else {\n      throw new Error(\n        'Refusing to create output directory outside project root: ' + outputDir\n      );\n    }\n    fs.writeFileSync(\n      outputFile.replace('README.md', 'docs.json'),\n      JSON.stringify(citations)\n    );\n\n    fs.writeFileSync(\n      outputFile,\n      template({\n        root: getRoot(citations, true),\n        citations: citations,\n        package: pkg,\n      })\n    );\n\n    console.log('Generated', outputFile);\n  });\n}\n\nfunction getHref(name) {\n  name = name.toLowerCase();\n\n  for (var i = 0; i < sourceFiles.length; i++) {\n    var sf = sourceFiles[i];\n    if (sf.includes(name)) {\n      return transform(sf);\n    }\n  }\n\n  return null;\n\n  function transform(str) {\n    var path = str.replace('src/', '').replace('jsm/', '').replace('.js', '');\n    return `/docs/${path}/`;\n  }\n}\n\nfunction getRoot(citations, shouldSplice) {\n  var list = citations.slice(0);\n  for (var i = 0; i < list.length; i++) {\n    var citation = list[i];\n    if (/class/i.test(citation.kind)) {\n      var index = _.indexOf(citations, citation);\n      if (shouldSplice) {\n        citations.splice(index, 1);\n      }\n      return citation;\n    }\n  }\n  return null;\n}\n\nfunction expandSee(see) {\n  return expandLink({ text: see }, 'text').text;\n}\n\nfunction expandTag(tag) {\n  switch (tag.title) {\n    case 'overloaded':\n      this.overloaded = true;\n      delete tag.title;\n      delete tag.originalTitle;\n      delete tag.text;\n      break;\n    default:\n      expandLink(tag, 'text');\n  }\n}\n\nfunction expandParam(param) {\n  expandLink(param, 'description');\n}\n\nfunction expandLink(object, property) {\n  var value = object[property];\n  var shouldRecurse = false;\n\n  if (value) {\n    var regex = /\\{@link ([\\w\\d:/?\\-.#]*)\\}/i;\n    var link = value.match(regex);\n\n    if (link && link.length > 1) {\n      var name = link[1];\n\n      if (/http/i.test(name)) {\n        object[property] = value.replace(regex, '[$1]($1)');\n        shouldRecurse = true;\n      } else {\n        var fragments = name.split(/[.#]/i);\n        var longname = name.replace(/#/i, '.');\n\n        var dir = getDirectoryMatch(longname);\n        var hash = fragments.length > 2 ? fragments.slice(2).join('-') : '';\n        if (dir === null && fragments.length === 2) {\n          dir = '/docs/two/';\n          hash = fragments[1];\n        }\n        var href = [\n          '[',\n          fragments.join('.'),\n          ']',\n          '(',\n          dir,\n          hash ? '#' + hash.toLowerCase() : '',\n          ')',\n        ].join('');\n\n        object[property] = value.replace(regex, href);\n        shouldRecurse = true;\n      }\n    }\n  }\n\n  if (shouldRecurse) {\n    expandLink(object, property);\n  }\n\n  return object;\n}\n\nfunction getDirectoryMatch(str) {\n  var index = -1;\n  for (var i = 0; i < directory.length; i++) {\n    var { name } = directory[i];\n    if (str.includes(name)) {\n      index = i;\n    }\n  }\n  if (index < 0) {\n    return null;\n  }\n  return directory[index].dir;\n}\n"
  },
  {
    "path": "utils/file-sizes.json",
    "content": "{\"development\":\"94KB\",\"production\":\"50KB\"}"
  },
  {
    "path": "utils/source-files.js",
    "content": "var sourceFiles = [\n  'src/two.js',\n  'src/registry.js',\n  'src/collection.js',\n  'src/children.js',\n  'src/events.js',\n  'src/vector.js',\n  'src/anchor.js',\n  'src/matrix.js',\n  'src/renderers/svg.js',\n  'src/renderers/canvas.js',\n  'src/renderers/webgl.js',\n  'src/element.js',\n  'src/shape.js',\n  'src/path.js',\n  'src/shapes/line.js',\n  'src/shapes/rectangle.js',\n  'src/shapes/ellipse.js',\n  'src/shapes/circle.js',\n  'src/shapes/points.js',\n  'src/shapes/polygon.js',\n  'src/shapes/star.js',\n  'src/shapes/rounded-rectangle.js',\n  'src/text.js',\n  'src/effects/stop.js',\n  'src/effects/gradient.js',\n  'src/effects/linear-gradient.js',\n  'src/effects/radial-gradient.js',\n  'src/effects/texture.js',\n  'src/effects/sprite.js',\n  'src/effects/image.js',\n  'src/group.js',\n  'extras/jsm/arc.js',\n  'extras/jsm/zui.js',\n  'src/shapes/arc-segment.js',\n  'src/effects/image-sequence.js',\n];\n\nif (typeof module != 'undefined' && module.exports) {\n  module.exports = sourceFiles;\n} else if (typeof define === 'function' && define.amd) {\n  define('source-files', [], function () {\n    return sourceFiles;\n  });\n}\n"
  },
  {
    "path": "wiki/.vuepress/components/carbon-ads.vue",
    "content": "<script>\n  export default {\n    name: 'carbon-ads',\n    watch: {\n      '$route' (to, from) {\n        if (\n          to.path !== from.path\n          // Only reload if the ad has been loaded\n          // otherwise it's possible that the script is appended but\n          // the ads are not loaded yet. This would result in duplicated ads.\n          && this.$el.querySelector('#carbonads')\n        ) {\n          this.$el.innerHTML = ''\n          this.load()\n        }\n      }\n    },\n    mounted () {\n      this.load()\n    },\n    methods: {\n      load () {\n        const s = document.createElement('script')\n        s.id = '_carbonads_js'\n        s.src = `//cdn.carbonads.com/carbon.js?serve=CESI52JY&placement=twojsorg`\n        this.$el.appendChild(s)\n      }\n    },\n    render (h) {\n      return h('div', { class: 'carbon-ads' })\n    }\n  }\n</script>\n\n<style lang=\"stylus\">\n\n  #carbonads * {\n    margin: initial;\n    padding: initial;\n  }\n\n  #carbonads {\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n      Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial,\n      sans-serif;\n  }\n\n  #carbonads {\n    display: flex;\n    max-width: 330px;\n    background-color: hsl(0, 0%, 98%);\n    border: 1px solid #ddd;\n    border-radius: 4px;\n    z-index: 100;\n    overflow: hidden;\n  }\n\n  #carbonads a {\n    color: inherit;\n    text-decoration: none;\n    border: none;\n    font-weight: normal;\n  }\n\n  #carbonads a:hover {\n    color: inherit;\n  }\n\n  #carbonads span {\n    position: relative;\n    display: block;\n    overflow: hidden;\n  }\n\n  #carbonads .carbon-wrap {\n    display: flex;\n  }\n\n  #carbonads .carbon-img {\n    display: block;\n    margin: 0;\n    line-height: 1;\n  }\n\n  #carbonads .carbon-img img {\n    display: block;\n\n  }\n\n  #carbonads .carbon-text {\n    font-size: 10px;\n    padding: 10px;\n    margin-bottom: 16px;\n    line-height: 1.5;\n    text-align: left;\n  }\n\n  #carbonads .carbon-poweredby {\n    display: block;\n    padding: 6px 8px;\n    background: #f1f1f2;\n    text-align: center;\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n    font-weight: 600;\n    font-size: 8px;\n    line-height: 1;\n    border-top-left-radius: 3px;\n    position: absolute;\n    bottom: 0;\n    right: 0;\n  }\n\n</style>\n"
  },
  {
    "path": "wiki/.vuepress/components/custom-button.vue",
    "content": "<template>\n  <a ref=\"link\" class=\"button\" :class=\"type\" :href=\"href\">\n    <span ref=\"icon\" class=\"icon\" :class=\"type\"></span>\n    <span ref=\"label\" class=\"label\">\n      {{ text }}\n    </span>\n    <span ref=\"size\" class=\"size\">\n      {{ size }}\n    </span>\n  </a>\n</template>\n\n<script>\n  module.exports = {\n    name: 'custom-button',\n    props: {\n      type: {\n        type: String,\n        default: 'download'\n      },\n      text: String,\n      size: String,\n      href: String\n    },\n  };\n</script>\n\n<style lang=\"stylus\" scoped>\n  a.button {\n    display: inline-block\n    background: tint($green, 95);\n    color: $green;\n    border: 2px solid $green;\n    border-radius: 9999px;\n    padding: .5rem 1rem .5rem 2.75rem;\n    margin: 0 1rem .5rem 0;\n    position: relative;\n\n    span {\n      display: inline-block;\n    }\n\n    &.source {\n      position: fixed;\n      top: 6rem;\n      right: 1rem;\n      left: auto;\n      bottom: auto;\n      z-index: 10;\n    }\n\n    &:hover {\n      background: tint($orange, 92);\n      color: $orange;\n      border: 2px solid $orange;\n      text-decoration: none;\n\n      .icon {\n        background-color: $orange;\n      }\n    }\n\n    .icon {\n      width: 1.5em;\n      height: 1.5em;\n      background-color: $green;\n      background-position: center;\n      background-size: 100%;\n      background-repeat: no-repeat;\n      position: absolute;\n      top: .475rem;\n      left: .8rem;\n\n      &.download {\n        mask-image: url(/images/download.svg);\n        -webkit-mask-image: url(/images/download.svg);\n      }\n      &.sponsor {\n        mask-image: url(/images/sponsor.svg);\n        -webkit-mask-image: url(/images/sponsor.svg);\n      }\n      &.github {\n        mask-image: url(/images/github.svg);\n        -webkit-mask-image: url(/images/github.svg);\n      }\n      &.source {\n        mask-image: url(/images/source.svg);\n        -webkit-mask-image: url(/images/source.svg);\n      }\n      &.npm {\n        mask-image: url(/images/npm.svg);\n        -webkit-mask-image: url(/images/npm.svg);\n      }\n      &.gpt {\n        mask-image: url(/images/gpt.svg);\n        -webkit-mask-image: url(/images/gpt.svg);\n      }\n    }\n\n    .label {\n      font-weight: 600;\n    }\n\n    .size {\n      font-weight: 200;\n    }\n  }\n\n  @media (max-width: $MQMobile) {\n\n    a.button.source {\n      bottom: 1rem;\n      right: 0.5rem;\n      top: auto;\n      left: auto;\n      width: 0;\n      padding: 0.5rem 1.25rem;\n      span.icon.source {\n        top: 50%;\n        left: 50%;\n        transform: translate(-50%, -50%);\n      }\n      span.label {\n        display: none;\n      }\n    }\n\n  }\n\n</style>\n"
  },
  {
    "path": "wiki/.vuepress/components/example-card.vue",
    "content": "<template>\n  <div class=\"example\">\n\n    <h2 ref=\"h2\">{{ title }}</h2>\n\n    <a class=\"cover\" :href=\"href\" target=\"_blank\" rel=\"noopener noreferrer\">\n      <span class=\"thumb\" role=\"img\" :style=\"{ backgroundImage: `url(${src || '/images/thumbnail.svg'})` }\"></span>\n      <div class=\"title\">\n        <span class=\"type\" :class=\"platform\"></span> {{ title }}\n      </div>\n    </a>\n\n    <ul class=\"tags\">\n      <li v-for=\"tag in tagList\" :key=\"tag.id\" class=\"tag\" :class=\"tag.color\">\n        {{ tag.name }}\n      </li>\n    </ul>\n\n  </div>\n</template>\n\n<script>\n\n  var LISTED_TAGS = [];\n\n  module.exports = {\n    name: 'example-card',\n    data() {\n      return {\n        platforms: [\n          'internet','codepen', 'codesandbox', 'glitch', 'jsfiddle',\n          'observable'\n        ],\n        colors: [\n          'red', 'blue', 'yellow', 'green', 'orange', 'purple'\n        ]\n      }\n    },\n    props: {\n      title: String,\n      href: String,\n      src: String,\n      tags: String\n    },\n    computed: {\n      platform() {\n\n        for (var i = 0; i < this.platforms.length; i++) {\n\n          var platform = this.platforms[i];\n\n          if (this._props.href.includes(platform)) {\n            return platform;\n          }\n\n        }\n\n        return this.platforms[0];\n\n      },\n      tagList() {\n\n        var tags = [];\n        var names = this._props.tags.replace(/\\s/ig, '').split(',');\n\n        for (var i = 0; i < names.length; i++) {\n\n          var name = names[i];\n          var color;\n          var tagIndex = LISTED_TAGS.indexOf(name);\n\n          if (tagIndex < 0) {\n            tagIndex = LISTED_TAGS.length;\n            LISTED_TAGS.push(name);\n          }\n\n          color = this.colors[tagIndex % this.colors.length];\n\n          tags.push({\n            id: tagIndex,\n            name,\n            color\n          });\n\n        }\n\n        return tags;\n\n      }\n    },\n    mounted() {\n\n      var { h2 } = this.$refs;\n\n      if (!this.$page.headers) {\n        this.$page.headers = [];\n      }\n\n      var mainHeader = convertHeader(h2)\n\n      this.$page.headers.push(mainHeader);\n\n      var $tags = this.$el.querySelector('ul.tags');\n\n      for (var i = 0; i < this.tagList.length; i++) {\n\n        var { name } = this.tagList[i];\n        var elem = $tags.children[i];\n        var header = generateHeaderAtLevel(elem, 3);\n\n        header.title = `${this.title} > ${header.title}`;\n        header.slug = `${mainHeader.slug}`;\n        elem.id = header.slug;\n\n        this.$page.headers.push(header);\n\n      }\n\n    }\n  };\n\n  function convertHeader(elem) {\n\n    var level = parseInt(elem.tagName.replace('h', ''))\n    var title = elem.textContent;\n    var slug = title.trim().toLowerCase().replace(/\\s/gi, '-');\n\n    elem.id = slug;\n\n    return {\n      level, title, slug\n    };\n\n  }\n\n  function generateHeaderAtLevel(elem, level) {\n\n    var title = elem.textContent.toLowerCase();\n    var slug = title.trim().replace(/\\s/gi, '-');\n\n    return {\n      level, title, slug\n    };\n\n  }\n\n</script>\n\n<style lang=\"stylus\" scoped>\n\n.thumb\n  display block\n  width 100%\n  height 15vh\n  min-height 120px\n  background-size cover\n  background-position center\n.title\n  font-size 1rem\n  font-weight 600\n  padding .5rem .7rem .5rem 2.7rem\n  border 1px solid #ccc\n  border-bottom 0\n  color $textColor\n  position relative\n  &:hover\n    color $textColor\n.type\n  background-repeat no-repeat\n  background-position center\n  height 1rem\n  display inline-block\n  position absolute\n  width 2rem\n  left .5rem\n  top .7rem\n  &.codepen\n    background-image url(/images/codepen.svg)\n  &.codesandbox\n    background-image url(/images/codesandbox.svg)\n  &.glitch\n    background-image url(/images/glitch.svg)\n  &.jsfiddle\n    background-image url(/images/jsfiddle.svg)\n  &.observable\n    background-image url(/images/observable.svg)\n  &.internet\n    background-image: url(/images/internet.svg)\n@media (max-width: $MQMobile)\n  .thumb\n    height 240px\n\nul.tags\n  list-style: none;\n  .tag\n    border-radius 0.2rem\n    padding 0rem 0.75rem\n    font-size .625rem\n    line-height 1.25rem\n    font-weight 400\n    color $textColor\n    margin 0 .15rem .25rem 0\n    display inline-block\n    cursor default\n    &.grey\n      background tint($grey, 70)\n    &.red\n      background tint($red, 70)\n    &.orange\n      background tint($orange, 70)\n    &.yellow\n      background tint($yellow, 70)\n    &.green\n      background tint($green, 70)\n    &.blue\n      background tint($blue, 70)\n    &.purple\n      background tint($purple, 70)\n\n</style>\n"
  },
  {
    "path": "wiki/.vuepress/components/inline-editor.vue",
    "content": "<template>\n  <div\n    :data-prefill=\"prefill\"\n    data-default-tab=\"js,result\"\n    data-editable=\"true\"\n    data-theme-id=\"40346\"\n    data-height=\"500\"\n    class=\"codepen\">\n\n      <pre data-lang=\"html\"></pre>\n      <pre data-lang=\"css\" data-options-autoprefixer=\"true\"></pre>\n      <pre data-lang=\"js\" ref=\"pre\"></pre>\n\n  </div>\n\n</template>\n\n<script>\n\n  var beautifyOptions = {\n    indent_size: 2\n  };\n\n  module.exports = {\n    name: 'inline-editor',\n    computed: {\n      prefill() {\n        var result = {};\n        var props = this._props;\n        if (props.title) {\n          result.title = props.title;\n        }\n        if (props.description) {\n          result.description = props.description;\n        }\n        if (props.tags) {\n          result.tags = props.tags.split(',');\n        }\n        if (props.scripts) {\n          result.scripts = props.scripts.split(',');\n        }\n        return JSON.stringify(result);\n      }\n    },\n    props: {\n      scripts: String\n    },\n    mounted: function() {\n\n      var textContent = getTextContent(this.$slots.default);\n      this.$refs.pre.innerHTML = textContent;\n\n      var script = document.createElement('script');\n      script.type = 'text/javascript';\n      script.className = 'codepen';\n      script.async = '';\n      script.src = 'https://static.codepen.io/assets/embed/ei.js';\n      document.body.appendChild(script);\n\n    },\n    beforeDestroyed: function() {\n      // TODO: Maybe remove all script tags that are codepen references\n    }\n  };\n\n  function getTextContent(nodes) {\n    var result = '';\n    for (var i = 0; i < nodes.length; i++) {\n      var node = nodes[i];\n      if (node.children && node.children.length > 0) {\n        if (result !== '') {\n          result += '\\n';\n        }\n        result += getTextContent(node.children);\n        result += '\\n';\n      } else if (typeof node.text === 'string') {\n        result += node.text;\n      }\n    }\n    if (window.js_beautify) {\n      return window.js_beautify(result, beautifyOptions);\n    } else {\n      return result;\n    }\n  }\n\n</script>\n"
  },
  {
    "path": "wiki/.vuepress/components/redirect-page.vue",
    "content": "<template>\n  <div></div>\n</template>\n\n<script>\n  module.exports = {\n    name: 'redirect-page',\n    props: {\n      src: String\n    },\n    mounted: function() {\n      if (/^https:\\/\\//i.test(this._props.src)) {\n        window.location.href = this._props.src;\n      } else {\n        this.$router.replace(this._props.src).catch(() => {});\n      }\n    }\n  };\n</script>\n"
  },
  {
    "path": "wiki/.vuepress/components/version-link.vue",
    "content": "<template>\n  <a class=\"version\" target=\"_blank\" rel=\"noopener noreferrer\" :href=\"'https://github.com/jonobr1/two.js/releases/tag/' + v \">{{ v }}<OutboundLink /></a>\n</template>\n\n<script>\n  module.exports = {\n    name: 'version-link',\n    props: {\n      v: String\n    },\n  };\n</script>\n\n<style lang=\"stylus\" scoped>\n    .version {\n        font-size: 1.25rem;\n    }\n</style>\n"
  },
  {
    "path": "wiki/.vuepress/config.js",
    "content": "var fs = require('fs');\nvar path = require('path');\n\nvar sourceFiles = require('../../utils/source-files');\nvar fileSizes = getJSON('file-sizes');\n\nvar root = { title: 'Base', children: [] };\nvar effects = { title: 'Effects', children: [] };\nvar renderers = { title: 'Renderers', children: [] };\nvar shapes = { title: 'Shapes', children: [] };\nvar extras = { title: 'Extras', children: [] };\n\nvar sidebarForDocs = [root, effects, renderers, shapes, extras];\n\nfor (var i = 0; i < sourceFiles.length; i++) {\n  var name = sourceFiles[i]\n    .replace('jsm/', '')\n    .replace('extras', '/extras')\n    .replace('src/', '/')\n    .replace('.js', '/');\n\n  name = `/docs${name}`;\n\n  if (name.match('effects')) {\n    effects.children.push(name);\n  } else if (name.match('renderers')) {\n    renderers.children.push(name);\n  } else if (name.match('shapes')) {\n    shapes.children.push(name);\n  } else if (name.match('extras')) {\n    extras.children.push(name);\n  } else {\n    root.children.push(name);\n  }\n}\n\nfunction getJSON(filename) {\n  var file = fs.readFileSync(\n    path.resolve(__dirname, '../../utils/', `${filename}.json`)\n  );\n  return JSON.parse(file);\n}\n\nmodule.exports = {\n  head: [\n    ['link', { rel: 'icon', href: '/images/favicon.gif' }],\n    [\n      'link',\n      { rel: 'stylesheet', href: 'https://use.typekit.net/edp1hux.css' },\n    ],\n    [\n      'meta',\n      {\n        name: 'google-site-verification',\n        content: 'eNzLpThZ5XFyxVRedxqW7JxwibqK83DLO-Pqx9rTIDo',\n      },\n    ],\n    ['meta', { property: 'og:title', content: 'Two.js' }],\n    [\n      'meta',\n      {\n        property: 'og:description',\n        content: 'A renderer agnostic two-dimensional drawing api for the web.',\n      },\n    ],\n    ['meta', { property: 'og:url', content: 'https://two.js.org/' }],\n    ['meta', { property: 'og:type', content: 'website' }],\n    [\n      'meta',\n      {\n        property: 'og:image',\n        content: 'https://two.js.org/images/thumbnail.jpg',\n      },\n    ],\n    ['meta', { name: 'twitter:card', content: 'summary' }],\n    ['meta', { name: 'twitter:site', content: '@jonofyi' }],\n    ['meta', { name: 'twitter:title', content: 'Two.js' }],\n    [\n      'meta',\n      {\n        name: 'twitter:description',\n        content: 'A renderer agnostic two-dimensional drawing api for the web.',\n      },\n    ],\n    [\n      'meta',\n      {\n        name: 'twitter:image',\n        content: 'https://two.js.org/images/thumbnail.jpg',\n      },\n    ],\n    [\n      'script',\n      {\n        type: 'text/javascript',\n        src: 'https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.0/beautify.min.js',\n        async: '',\n      },\n    ],\n  ],\n  description: 'A renderer agnostic two-dimensional drawing api for the web.',\n  themeConfig: {\n    repo: 'jonobr1/two.js',\n    repoLabel: 'GitHub',\n    logo: '/images/logo.svg',\n    docsDir: 'wiki',\n    docsBranch: 'dev',\n    editLinks: true,\n    editLinkText: 'See a typo? Help us improve it.',\n    smoothScroll: true,\n    lastUpdated: 'Last Updated',\n    activeHeaderLinks: false,\n    searchPlaceholder: 'Search',\n    searchMaxSuggestions: 10,\n    developmentSize: fileSizes.development,\n    productionSize: fileSizes.production,\n    nav: [\n      {\n        text: 'Docs',\n        link: '/docs/two/',\n      },\n      {\n        text: 'Examples',\n        link: '/examples/',\n      },\n      {\n        text: 'Changelog',\n        link: '/changelog/',\n      },\n    ],\n    sidebar: {\n      '/changelog/': ['/changelog/'],\n      '/docs/': sidebarForDocs,\n    },\n    markdown: {\n      lineNumbers: true,\n    },\n  },\n};\n"
  },
  {
    "path": "wiki/.vuepress/enhanceApp.js",
    "content": "export default ({ router }) => {\n  router.addRoutes([\n    { path: '/projects/', redirect: '/examples/' },\n    { path: '/docs/', redirect: '/docs/two/' }\n  ])\n}\n"
  },
  {
    "path": "wiki/.vuepress/plugins/search/SearchBox.vue",
    "content": "<template>\n  <div class=\"search-box\">\n    <input\n      ref=\"input\"\n      aria-label=\"Search\"\n      :value=\"query\"\n      :class=\"{ 'focused': focused }\"\n      :placeholder=\"placeholder\"\n      autocomplete=\"off\"\n      spellcheck=\"false\"\n      @input=\"query = $event.target.value\"\n      @focus=\"onFocus\"\n      @blur=\"onBlur\"\n      @keyup.enter=\"go(focusIndex)\"\n      @keyup.up=\"onUp\"\n      @keyup.down=\"onDown\"\n    >\n    <ul\n      v-if=\"showSuggestions\"\n      class=\"suggestions\"\n      :class=\"{ 'align-right': alignRight }\"\n      @mouseleave=\"unfocus\"\n    >\n      <li\n        v-for=\"(s, i) in suggestions\"\n        :key=\"i\"\n        class=\"suggestion\"\n        :class=\"{ focused: i === focusIndex }\"\n        @mousedown=\"go(i)\"\n        @mouseenter=\"focus(i)\"\n      >\n        <a\n          :href=\"s.path\"\n          @click.prevent\n        >\n          <span class=\"page-title\">{{ s.title || s.path }}</span>\n          <span\n            v-if=\"s.header\"\n            class=\"header\"\n            :class=\"{ 'is-tag' : s.isTag }\"\n          ><span class=\"chevron\">&gt;</span> {{ s.header.title }}</span>\n        </a>\n      </li>\n    </ul>\n  </div>\n</template>\n\n<script>\nimport matchQuery from './match-query'\n\nexport default {\n  name: 'SearchBox',\n\n  data () {\n    return {\n      query: '',\n      focused: false,\n      focusIndex: 0,\n      placeholder: undefined,\n      searchOpenClass: 'search-open'\n    }\n  },\n\n  computed: {\n    showSuggestions () {\n      return (\n        this.focused\n        && this.suggestions\n        && this.suggestions.length\n      )\n    },\n\n    suggestions () {\n      const query = this.query.trim().toLowerCase()\n      if (!query) {\n        return\n      }\n\n      const { pages } = this.$site\n      const max = this.$site.themeConfig.searchMaxSuggestions || 10\n      const localePath = this.$localePath\n      const res = []\n      for (let i = 0; i < pages.length; i++) {\n        if (res.length >= max) break\n        const p = pages[i]\n        // filter out results that do not match current locale\n        if (this.getPageLocalePath(p) !== localePath) {\n          continue\n        }\n\n        // filter out results that do not match searchable paths\n        if (!this.isSearchable(p)) {\n          continue\n        }\n\n        if (matchQuery(query, p)) {\n          res.push(p)\n        } else if (p.headers) {\n          for (let j = 0; j < p.headers.length; j++) {\n            if (res.length >= max) break\n            const h = p.headers[j]\n            if (h.title && matchQuery(query, p, h.title)) {\n              var obj = Object.assign({}, p, {\n                path: p.path + '#' + h.slug,\n                header: h\n              });\n\n              if (h.title.startsWith(\"#\")) {\n                obj = this.formatTagSuggestion(obj);\n              }\n\n              res.push(obj);\n            }\n          }\n        }\n      }\n      return res\n    },\n    // make suggestions align right when there are not enough items\n    alignRight () {\n      const navCount = (this.$site.themeConfig.nav || []).length\n      const repo = this.$site.repo ? 1 : 0\n      return navCount + repo <= 2\n    }\n  },\n\n  mounted () {\n    this.placeholder = this.$site.themeConfig.searchPlaceholder || ''\n    document.addEventListener('keydown', this.onHotkey)\n  },\n\n  beforeDestroy () {\n    document.removeEventListener('keydown', this.onHotkey)\n  },\n\n  methods: {\n    getPageLocalePath (page) {\n      for (const localePath in this.$site.locales || {}) {\n        if (localePath !== '/' && page.path.indexOf(localePath) === 0) {\n          return localePath\n        }\n      }\n      return '/'\n    },\n\n    isSearchable (page) {\n      let searchPaths = null\n\n      // all paths searchables\n      if (searchPaths === null) { return true }\n\n      searchPaths = Array.isArray(searchPaths) ? searchPaths : new Array(searchPaths)\n\n      return searchPaths.filter(path => {\n        return page.path.match(path)\n      }).length > 0\n    },\n\n    formatTagSuggestion(s) {\n      var lastParent = null;\n      for (var i = 0; i < s.headers.length; i++) {\n        var header = s.headers[i];\n        if (header.level == s.header.level - 1) {\n          //set last eligible parent heading\n          lastParent = header\n        } else if (header.level == s.header.level && header.slug == s.header.slug) {\n          //stop here\n          break;\n        }\n      }\n\n      if (lastParent) {\n        s.path = s.regularPath + \"#\" + lastParent.slug;\n        s.title = lastParent.title;\n        s.isTag = true;\n      }\n\n      return s;\n    },\n\n    onHotkey (event) {\n      if (event.srcElement === document.body && [].includes(event.key)) {\n        this.$refs.input.focus()\n        event.preventDefault()\n      }\n    },\n\n    onFocus () {\n      this.focused = true\n      document.getElementById(\"app\").classList.add(this.searchOpenClass)\n\n    },\n\n    onBlur () {\n      this.focused = false\n      document.getElementById(\"app\").classList.remove(this.searchOpenClass);\n    },\n\n    onUp () {\n      if (this.showSuggestions) {\n        if (this.focusIndex > 0) {\n          this.focusIndex--\n        } else {\n          this.focusIndex = this.suggestions.length - 1\n        }\n      }\n    },\n\n    onDown () {\n      if (this.showSuggestions) {\n        if (this.focusIndex < this.suggestions.length - 1) {\n          this.focusIndex++\n        } else {\n          this.focusIndex = 0\n        }\n      }\n    },\n\n    go (i) {\n      if (!this.showSuggestions) {\n        return\n      }\n      this.$router.push(this.suggestions[i].path).catch(() => {});\n      this.query = ''\n      this.focusIndex = 0\n      this.$refs.input.blur()\n    },\n\n    focus (i) {\n      this.focusIndex = i\n    },\n\n    unfocus () {\n      this.focusIndex = -1\n    }\n  }\n}\n</script>\n\n<style lang=\"stylus\">\n.search-box\n  display inline-block\n  position relative\n  margin-right 1rem\n  input\n    cursor text\n    max-width 22.5rem\n    width 33.3vw\n    height 2rem\n    color lighten($textColor, 25%)\n    display inline-block\n    border 1px solid darken($borderColor, 10%)\n    border-radius 2rem\n    font-family $fontFamily\n    font-size 1rem\n    font-weight 600\n    line-height 2rem\n    padding 0 0.5rem 0 2rem\n    outline none\n    transition all .2s ease\n    background-size 1rem\n    background #fff url(/images/search.svg) 0.55rem 0.25rem no-repeat\n    padding-left 2.5rem\n    &:focus\n      cursor auto\n      border-color tint($accentColor, 50)\n      color $textColor\n    &::placeholder\n      color #999\n  .suggestions\n    background #fff\n    width 100%\n    position absolute\n    top 1.85rem\n    border 1px solid tint($accentColor, 50)\n    border-radius 6px\n    padding 0rem\n    list-style-type none\n    &.align-right\n      right 0\n  .suggestion\n    padding .75rem 1.25rem\n    border-radius 4px\n    cursor pointer\n    font-size 1rem\n    line-height auto\n    font-weight normal\n    a\n      white-space normal\n      color $sidebarText\n      .page-title\n        font-weight 600\n      .header\n        font-size 0.9em\n        margin-left 0.25em\n      .is-tag\n        .chevron\n          display none\n    &.focused\n      background-color tint($orange, 92)\n      a\n        color $accentColor\n\n// Match IE11\n@media all and (-ms-high-contrast: none)\n  .search-box input\n    height 2rem\n\n@media (max-width: $MQMobile)\n  .search-box\n    margin-right 0\n    width 100%\n    input\n      height 3rem\n      width 100vw\n      max-width 100vw\n      background-position 0.5rem 0.75rem\n      left 0\n      border-radius 0\n      border 1px solid #e6e6e6\n      border-left none\n      border-right none\n    ul.suggestions\n      left 0\n      right 0\n      border-radius 0\n      border-right 0\n      border-left 0\n</style>\n"
  },
  {
    "path": "wiki/.vuepress/plugins/search/match-query.js",
    "content": "import get from 'lodash/get';\n\nexport default (query, page, additionalStr = null) => {\n  let domain = get(page, 'title', '');\n\n  if (get(page, 'frontmatter.tags')) {\n    domain += ` ${page.frontmatter.tags.join(' ')}`;\n  }\n\n  if (additionalStr) {\n    domain += ` ${additionalStr}`;\n  }\n\n  return matchTest(query, domain);\n};\n\nconst matchTest = (query, domain) => {\n  const escapeRegExp = (str) => str.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n\n  // eslint-disable-next-line no-control-regex\n  const nonASCIIRegExp = new RegExp('[^\\x00-\\x7F]');\n\n  const words = query\n    .split(/\\s+/g)\n    .map((str) => str.trim())\n    .filter((str) => !!str);\n\n  if (!nonASCIIRegExp.test(query)) {\n    // if the query only has ASCII chars, treat as English\n    const hasTrailingSpace = query.endsWith(' ');\n    const searchRegex = new RegExp(\n      words\n        .map((word, index) => {\n          if (words.length === index + 1 && !hasTrailingSpace) {\n            // The last word - ok with the word being \"startswith\"-like\n            return `(?=.*\\\\b${escapeRegExp(word)})`;\n          } else {\n            // Not the last word - expect the whole word exactly\n            return `(?=.*\\\\b${escapeRegExp(word)}\\\\b)`;\n          }\n        })\n        .join('') + '.+',\n      'gi'\n    );\n    return searchRegex.test(domain);\n  } else {\n    // if the query has non-ASCII chars, treat as other languages\n    return words.some((word) => domain.toLowerCase().indexOf(word) > -1);\n  }\n};\n"
  },
  {
    "path": "wiki/.vuepress/styles/index.styl",
    "content": "body {\n  font-family: $fontFamily;\n  font-size: 1rem;\n  line-height: 1.5rem;\n  // word-spacing: 0.1875rem;\n  color: $textColor;\n\n  .sidebar-open {\n    overflow: hidden;\n    width: 100vw;\n    height: 100vh;\n  }\n\n  p {\n    line-height: 1.5rem;\n    margin-top: 0;\n    margin-bottom: 1.25rem;\n  }\n\n  .edit-link span {\n    display: none;\n  }\n\n  #carbonads {\n    margin-top: 2rem;\n  }\n\n  .page-edit .edit-link a,\n  .page a {\n    color: $green;\n    text-decoration: none;\n    font-weight: 600;\n    transition: color .2s, border-color .2s, background-color .2s;\n    word-break: break-word;\n    border-bottom: 1px solid $green;\n\n    span { //the .icon.outbound is contained in a span that adds a space to the end of links.\n      display: none;\n    }\n\n    &:visited {\n      text-decoration: none;\n    }\n    &:hover {\n      color:  tint($green, 30%);\n      text-decoration: none;\n      border-bottom-color: tint($green, 60%);\n    }\n    &.header-anchor {\n      border-bottom-width: 0;\n      display: none;\n    }\n  }\n\n  .theme-default-content {\n\n    code {\n      font-family: $codeFamily;\n      font-size: .85rem;\n      line-height: 100%;\n      color: shade($codeColor, 50%);\n      padding: .2rem .4rem;\n      border-radius: 4px;\n      background: #f6f6f6;\n      word-break: break-word; //possibly only for mobile.\n    }\n\n    pre[class*=\"language-\"] code {\n      color: $textColor;\n      font-size: 1rem;\n    }\n\n    div[class*=\"language-\"]::before {\n      color: transparent;\n    }\n\n    code[class*=\"language-\"],\n    pre[class*=\"language-\"] {\n      color: #ccc;\n      background: none;\n      font-family: $codeFamily;\n      // font-weight: 700;\n      font-size: 1rem;\n      text-align: left;\n      white-space: pre;\n      word-spacing: normal;\n      word-break: normal;\n      word-wrap: normal;\n      line-height: 1.5rem;\n\n      -moz-tab-size: 4;\n      -o-tab-size: 4;\n      tab-size: 4;\n\n      -webkit-hyphens: none;\n      -moz-hyphens: none;\n      -ms-hyphens: none;\n      hyphens: none;\n\n    }\n\n    /* Code blocks */\n    pre[class*=\"language-\"] {\n      padding: 1rem;\n      margin: .5em 0;\n      overflow: auto;\n      font-weight: 600;\n      font-size: 1rem;\n    }\n\n    :not(pre) > code[class*=\"language-\"],\n    pre[class*=\"language-\"] {\n      // background: #2d2d2d;\n    }\n\n    /* Inline code */\n    :not(pre) > code[class*=\"language-\"] {\n      padding: .1rem;\n      border-radius: .3rem;\n      white-space: normal;\n    }\n\n    .token.comment,\n    .token.block-comment,\n    .token.prolog,\n    .token.doctype,\n    .token.cdata {\n      // color: #999;\n      color: $codeColor;\n      font-style: italic;\n    }\n\n    .token.punctuation {\n      // color: #ccc;\n      color: $codeColor;\n    }\n\n    .token.tag,\n    .token.attr-name,\n    .token.namespace,\n    .token.deleted {\n      // color: #e2777a;\n      color: $codeColor;\n    }\n\n    .token.function-name {\n      // color: #6196cc;\n      color: $codeColor;\n    }\n\n    .token.boolean {\n      color: #2aa89a;\n    }\n\n    .token.number {\n      color: rgb(255, 64, 64);\n      // color: #f08d49;\n    }\n\n    .token.function {\n      color: $codeColor;\n    }\n\n    .token.property,\n    .token.class-name,\n    .token.constant,\n    .token.symbol {\n      // color: #f8c555;\n      color: rgb(255, 64, 64);\n    }\n\n    .token.selector,\n    .token.important,\n    .token.atrule,\n    .token.keyword,\n    .token.builtin {\n      // color: #cc99cd;\n      color: #7a52cc;\n    }\n\n    .token.string,\n    .token.char,\n    .token.attr-value,\n    .token.regex,\n    .token.variable {\n      // color: #7ec699;\n      color: rgb(255, 128, 0);\n    }\n\n    .token.operator,\n    .token.entity,\n    .token.url {\n      // color: #67cdcc;\n      color: $codeColor;\n    }\n\n    .token.important,\n    .token.bold {\n      font-weight: bold;\n    }\n    .token.italic {\n      font-style: italic;\n    }\n\n    .token.entity {\n      cursor: help;\n    }\n\n    .token.inserted {\n      // color: green;\n      color: $codeColor;\n    }\n\n  }\n\n  div[class*=\"language-\"] {\n    background: rgba(255,244,95,.2);\n    border: none;\n    font-weight: bold;\n    color: $codeColor;\n  }\n\n  .theme-default-content div[class*=\"language-\"] {\n    margin: 0.85rem 0rem;\n    border-radius: 0;\n  }\n\n  div.inspiration {\n    margin-bottom: 1.5rem;\n    margin-left: -0.5rem;\n    margin-right: -0.5rem;\n    ul {\n      list-style: none;\n      padding: 0;\n      li {\n        display: inline-block;\n        // padding: 0 !important;\n        padding: 0 0.4rem 0.5rem 0.4rem !important;\n        margin: 0 0.25rem;\n        position: relative;\n        a {\n          text-align: center;\n          display: block;\n          width: 100%;\n          height: 100%;\n          padding: 0.5rem !important;\n          border: 1px solid $green;\n          border-radius: 0.33rem;\n          &:hover {\n            border-color: tint($green, 30%);\n          }\n        }\n      }\n    }\n  }\n\n  div.project-footnote {\n    text-align: center;\n    font-size: 85%;\n    margin-bottom: 2rem;\n    a {\n      color: tint($black, 15%);\n      font-weight: normal;\n      border-bottom: none;\n      text-decoration: underline;\n      &:hover {\n        color: tint($black, 30%);\n        border-bottom: none;\n        text-decoration: underline !important;\n      }\n    }\n    p {\n      margin: 0;\n    }\n  }\n\n}\n\n.theme-default-content:not(.custom) {\n  a:hover {\n    text-decoration: none;\n  }\n\n  .icon.outbound {\n    padding-left: 5px;\n  }\n\n  h1 {\n    font-size: 2.25rem;\n  }\n  h1:first-child + p {\n    margin-top: 1rem;\n  }\n  h2 {\n    border: none;\n    padding: 4rem 0 0 0;\n    margin: -1rem 0 0 0;\n    height: 0;\n    overflow: hidden;\n    pointer-events: none;\n  }\n  h2.longname {\n    visibility: visible;\n    font-size: 1.25rem;\n    padding: 0;\n    margin: 0 0 1.5rem 0;\n    height: auto;\n\n    a{\n      color: $orange;\n      text-decoration: none;\n      border-left: 2px solid $orange;\n      padding: .25rem 0 .4rem 1.25rem;\n\n      &:hover {\n        color: $red;\n        border-color: $red;\n      }\n    }\n\n    span {\n      display: inline-block;\n    }\n\n    .prefix {\n      //font-weight: normal;\n    }\n\n    .shortname {\n      //font-weight: 600;\n    }\n  }\n  h3 {\n    display: inline-block;\n    font-size: 1.25rem;\n    border: none;\n    border-top: 2px solid $black;\n    padding-top: .625rem;\n    margin: 2.5rem 0 1.25rem 0;\n  }\n  h3.visible {\n    margin-top: 0;\n\n    a {\n      color: inherit;\n      font-weight: inherit;\n      border-bottom: none;\n\n      &:hover {\n        color: inherit;\n        border-bottom: none;\n      }\n    }\n  }\n  h4 {\n    margin: 0 0 .25rem 0;\n    padding: 0rem;\n  }\n  ul {\n    margin: 0;\n  }\n  li {\n    padding-left: .5rem;\n  }\n}\n\n.custom-block.tip {\n  background-color: tint($purple, 90);\n  color: $purple;\n  border: none;\n  margin: 1.5rem 0;\n  padding: 1rem 1rem 1rem 3.5rem;\n  position: relative;\n\n  p:last-child {\n    margin-bottom: 0;\n  }\n\n  .custom-block-title {\n    position: absolute;\n    top: 1rem;\n    left: 1rem;\n    width: 1.5rem;\n    height: 1.5rem;\n    background: transparent;\n    color: transparent;\n    overflow: hidden;\n    text-indent: -999px;\n    background-image: url(/images/info.svg);\n    background-position: center;\n    background-size: 100%;\n    background-repeat: no-repeat;\n  }\n\n  a {\n    color: $purple;\n    border-bottom: 1px solid lighten($purple, 40%);\n\n    &:hover {\n      color: lighten($purple, 40%);\n      border-bottom-color: lighten($purple, 60%);\n      text-decoration: none;\n    }\n  }\n\n  code {\n    color: $purple;\n    background: transparent;\n  }\n}\n\n\n.docs {\n  .sidebar {\n    .sidebar-sub-headers {\n      margin-left: 2.4rem;\n      padding-left: 0px;\n      font-size: 1rem;\n\n      a.sidebar-link {\n        padding-left: 0.75rem;\n        color: $green;\n        border-left: 2px solid tint($green, 20%);\n        padding-top: 0.125rem;\n        padding-bottom: 0.125rem;\n\n        &:hover {\n          color: $orange;\n          border-left: 2px solid tint($orange, 20%);\n        }\n\n        &:.active {\n          color: $green;\n        }\n      }\n    }\n  }\n    div.fires {\n    padding-bottom: 1rem;\n    & > p {\n      margin-bottom: 0.5rem;\n    }\n  }\n  .page {\n    table {\n      tr {\n        th:first-child,\n        td:first-child {\n          text-align: center;\n        }\n        td:first-child {\n          font-family: $codeFamily;\n        }\n      }\n    }\n    div.member {\n      h2.longname {\n        a {\n          color: $blue;\n          border-left: 2px solid $blue;\n        }\n      }\n    }\n    div.function {\n      h2.longname {\n        a {\n          color: $orange;\n          border-left: 2px solid $orange;\n        }\n      }\n    }\n  }\n}\n\n\n.examples {\n\n  .examples-wrapper {\n    display: flex;\n    flex-wrap: wrap;\n    margin: 0 -1rem;\n  }\n\n  .example {\n\n    padding: 0 0.5rem 1rem;\n    width: calc(25% - 1rem);\n\n    h2 {\n      margin-top: -4rem;\n    }\n\n    h3 {\n      font-size: 0;\n      border: none;\n      margin: 0;\n      padding: 0;\n    }\n\n    a.cover {\n      border-bottom none !important;\n      &:hover {\n        border-bottom none !important;\n      }\n    }\n\n    .thumb {\n      border-radius: .5rem .5rem 0 0;\n      border-top: 1px solid $arrowBgColor;\n      border-left: 1px solid $arrowBgColor;\n      border-right: 1px solid $arrowBgColor;\n      box-sizing: border-box;\n    }\n\n    .tags {\n      display: flex;\n      flex-wrap: wrap;\n      padding: 0.75rem 0.75rem 0.5rem;\n      border-radius: 0 0 .5rem .5rem;\n      border: 1px solid $arrowBgColor;\n    }\n  }\n\n}\n\n.change-log .sidebar {\n  .sidebar-link {\n    display:none;\n  }\n  .sidebar-sub-headers {\n    padding: 0;\n    font-size: 1rem;\n\n      a.sidebar-link {\n        display:inline-block;\n        font-size: 1rem;\n        line-height: 1.7rem;\n        font-weight: 600;\n        padding: 0.35rem 1rem 0.35rem 1.25rem;\n        border-left: 0.25rem solid transparent;\n      }\n  }\n}\n\nth {\n  font-weight: 600;\n}\n\ntr {\n  background-color: #f9f9f9;\n}\n\ntr:nth-child(2n), thead tr {\n  background-color: transparent;\n}\n\ntable {\n  border-collapse: separate;\n  border-spacing: 0;\n}\ntable tr th,\ntable tr td {\n  border: none;\n  border-right: 1px solid #eee;\n  border-bottom: 1px solid #eee;\n}\ntable tr th:first-child,\ntable tr td:first-child {\n  border-left: 1px solid #eee;\n}\ntable tr th {\n  background: transparent;\n  border-top: 1px solid #eee;\n  text-align: left;\n}\n\n/* top-left border-radius */\ntable tr:first-child th:first-child {\n  border-top-left-radius: 6px;\n}\n\n/* top-right border-radius */\ntable tr:first-child th:last-child {\n  border-top-right-radius: 6px;\n}\n\n/* bottom-left border-radius */\ntable tr:last-child td:first-child {\n  border-bottom-left-radius: 6px;\n}\n\n/* bottom-right border-radius */\ntable tr:last-child td:last-child {\n  border-bottom-right-radius: 6px;\n}\n\n.page a.lineno {\n  color: lighten($codeColor, 40%);\n  font-family: $codeFamily;\n  // font-weight: 700;\n  font-size: .85rem;\n  line-height: 100%;\n  word-break: break-word; //possibly only for mobile.\n  border-bottom 1px solid lighten($codeColor, 40%);\n  padding-bottom: 0.2rem;\n\n  &:hover {\n    border-bottom-color: $codeColor;\n    color: $codeColor;\n  }\n}\n\n.change-log .version {\n  font-size: 1.25rem;\n  margin-left: .5rem;\n  display: inline-block;\n\n  p { display: inline; }\n}\n\n.returns {\n  margin: 0 0 1.25rem 0;\n}\n\n.sidebar, .page {\n  transition: opacity .35s ease-in-out;\n}\n.search-open {\n  .sidebar, .page {\n    opacity: .5;\n  }\n}\n\n.theme-container .sidebar-mask {\n  display: block !important;\n}\n\n@media (max-width: $MQMobile) {\n\n  .page {\n    padding-top: 4rem;\n    padding-bottom: 4rem !important;\n  }\n\n  .theme-default-content:not(.custom) {\n    h2 {\n      padding-top: 8rem;\n      margin-top: -5rem;\n    }\n  }\n\n  .examples .example {\n    width: 100%;\n\n    h2 {\n      margin-top: -8rem;\n    }\n  }\n}\n"
  },
  {
    "path": "wiki/.vuepress/styles/palette.styl",
    "content": "$black = #333;\n$grey = #eee;\n$gray = $grey;\n$white = #fff;\n\n$red = rgb(255, 64, 64);\n$orange = rgb(255, 128, 0);\n$blue = rgb(0, 200, 255);\n$green = rgb(0, 191, 168);\n$purple = rgb(171, 130, 255);\n$yellow = rgb(255, 244, 95);\n\n//fonts\n$fontFamily = \"proxima-nova\", arial, sans-serif;\n$codeFamily = \"anonymous-pro\", monospace;\n\n// colors\n$accentColor = $orange;\n$textColor = #333;\n$sidebarText = #555;\n$codeColor = rgb(124,124,124);\n$borderColor = #eaecef;\n$codeBgColor = #282c34;\n$arrowBgColor = #ccc;\n$badgeTipColor = $purplebg;\n$badgeWarningColor = darken(#ffe564, 35%);\n$badgeErrorColor = #DA5961;\n\n// layout\n$navbarHeight = 3.6rem;\n$sidebarWidth = 20rem;\n$contentWidth = 860px;\n$homePageWidth = 960px;\n\n// responsive breakpoints\n$MQNarrow = 959px;\n$MQMobile = 719px;\n$MQMobileNarrow = 419px;\n$nprogressColor = $green\n"
  },
  {
    "path": "wiki/.vuepress/theme/components/Navbar.vue",
    "content": "<template>\n  <header class=\"navbar\" ref=\"header\" :class=\"{ 'has-sidebar': hasSidebar }\">\n    <SidebarButton @toggle-sidebar=\"$emit('toggle-sidebar')\" />\n\n    <RouterLink\n      :to=\"$localePath\"\n      class=\"home-link\"\n    >\n      <img\n        v-if=\"$site.themeConfig.logo\"\n        class=\"logo\"\n        ref=\"navLogo\"\n        :src=\"$withBase($site.themeConfig.logo)\"\n        :alt=\"$siteTitle\"\n      >\n      <span\n        v-if=\"$siteTitle\"\n        ref=\"siteName\"\n        class=\"site-name\"\n        :class=\"{ 'can-hide': $site.themeConfig.logo }\"\n      >{{ $siteTitle }}</span>\n    </RouterLink>\n\n    <div class=\"search\" ref=\"search\">\n      <SearchBox v-if=\"$site.themeConfig.search !== false && $page.frontmatter.search !== false\" />\n    </div>\n\n    <div\n      class=\"links\"\n      ref=\"links\"\n      :style=\"linksWrapMaxWidth ? {\n        'max-width': linksWrapMaxWidth + 'px'\n      } : {}\"\n    >\n      <NavLinks />\n    </div>\n  </header>\n</template>\n\n<script>\nimport SearchBox from '../../plugins/search/SearchBox.vue'\nimport SidebarButton from '@theme/components/SidebarButton.vue'\nimport NavLinks from '@theme/components/NavLinks.vue'\nexport default {\n  name: 'Navbar',\n  components: {\n    SidebarButton,\n    NavLinks,\n    SearchBox\n  },\n  data () {\n    return {\n      mobileDesktopBreakpoint: 719,\n      linksWrapMaxWidth: null\n    }\n  },\n  computed: {\n    algolia () {\n      return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}\n    },\n    isAlgoliaSearch () {\n      return this.algolia && this.algolia.apiKey && this.algolia.indexName\n    },\n    hasSidebar () {\n      var pathStart = this.$page.path.match(/\\/.+?\\//);\n      pathStart = (pathStart && pathStart.length) ? pathStart[0] : pathStart;\n      return (pathStart in this.$site.themeConfig.sidebar && this.$site.themeConfig.sidebar[pathStart].length > 0);\n    },\n  },\n  watch:{\n    $route: function(to, from){\n      setTimeout(this.handleSearchPosition, 10);\n    }\n  },\n  methods: {\n    handleSearchPosition() {\n      if (!this.$refs.search && !this.$refs.navLogo && !this.refs.links) {\n        return;\n      }\n      if(document.documentElement.clientWidth > this.mobileDesktopBreakpoint) {\n        var buffer = 50;\n        var offset = 148;\n        var content = document.getElementsByClassName(\"theme-default-content\")[0];\n        if (content) //align with content\n            offset = content.offsetLeft + parseFloat(window.getComputedStyle(content, null).getPropertyValue('padding-left').replace(\"px\",\"\"));\n        if (offset + this.$refs.search.offsetWidth > this.$refs.links.offsetLeft + buffer\n          || offset < this.$refs.navLogo.offsetLeft + this.$refs.navLogo.offsetWidth + buffer)\n          offset = 148; //align with logo\n\n        this.$refs.search.style.left = offset.toString() + \"px\";\n\n      } else {\n        this.$refs.search.style.left = \"0px\";\n      }\n    },\n  },\n  mounted () {\n    const NAVBAR_VERTICAL_PADDING = parseInt(css(this.$el, 'paddingLeft')) + parseInt(css(this.$el, 'paddingRight'))\n    const handleLinksWrapWidth = () => {\n      if (document.documentElement.clientWidth < this.mobileDesktopBreakpoint) {\n        this.linksWrapMaxWidth = null\n      } else {\n        this.linksWrapMaxWidth = this.$el.offsetWidth - NAVBAR_VERTICAL_PADDING\n          - (this.$refs.siteName && this.$refs.siteName.offsetWidth || 0);\n      }\n      this.handleSearchPosition();\n\n      if (document.body.clientWidth < (959 + this.$refs.links.clientWidth)) {\n        //question: how to access $MQNarrow\n        //todo: recalculate and override the width and left of the search\n      }\n    }\n    handleLinksWrapWidth();\n    window.addEventListener('resize', handleLinksWrapWidth, false);\n  }\n}\nfunction css (el, property) {\n  // NOTE: Known bug, will return 'auto' if style value is 'auto'\n  const win = el.ownerDocument.defaultView\n  // null means not to return pseudo styles\n  return win.getComputedStyle(el, null)[property]\n}\n</script>\n\n<style lang=\"stylus\">\n$navbar-vertical-padding = 0rem\n$navbar-horizontal-padding = 1.5rem\n.navbar\n  padding $navbar-vertical-padding $navbar-horizontal-padding\n  line-height $navbarHeight - 1.4rem\n  .repo-link\n    display none\n  a, span, img\n    display inline-block\n  .home-link \n    padding: .7rem 0;\n  .logo\n    height $navbarHeight - 1.4rem\n    min-width $navbarHeight - 1.4rem\n    margin-right 0.8rem\n    vertical-align top\n    width 5.343rem\n  .site-name\n    font-size 1.3rem\n    font-weight 600\n    color $textColor\n    position relative\n  .search\n    position: absolute\n    left: 19rem\n    top: 0.7rem\n    .search-box\n      flex 0 0 auto\n      vertical-align top\n  .links\n    padding-left 1.5rem\n    box-sizing border-box\n    background-color transparent\n    white-space nowrap\n    font-size 0.9rem\n    position absolute\n    right $navbar-horizontal-padding\n    top $navbar-vertical-padding\n    height 100%\n    display flex\n    .external span\n      display none\n  .nav-links \n    .nav-item \n      height 100%\n      line-height 3.5rem\n      a \n        font-size 1rem\n        color $textColor\n        height 100%\n        line-height 3.5rem\n        border-top 2px solid $white\n        transition color .2s, border-color .2s\n        &:hover \n          border-bottom none\n          border-top-color $orange\n          color $orange\n        &.router-link-active \n          border-bottom none\n          border-top-color $orange\n          color $orange\n          &:hover \n            border-bottom none\n            border-top-color $red\n            color $red\n@media (max-width: $MQMobile)\n  .sidebar-button\n    display: none;\n  .navbar\n    width 100%\n    padding 0\n    border-bottom none\n    .can-hide\n      display none\n    .logo\n      width 4rem\n      margin-left 1rem\n    .links\n      padding-left 1.5rem\n      .nav-link\n        padding 0 0.5rem\n        font-weight 600\n    .site-name\n      width calc(100vw - 9.4rem)\n      overflow hidden\n      white-space nowrap\n      text-overflow ellipsis\n    .search\n      top 3.5rem\n      left 0\n      background-color #fff\n      width 100%\n  .has-sidebar\n    .navbar\n      padding-left 4rem\n    .home-link\n      display none\n    .sidebar-button\n      display block\n</style>\n"
  },
  {
    "path": "wiki/.vuepress/theme/components/Sidebar.vue",
    "content": "<template>\n  <aside class=\"sidebar\">\n\n    <RouterLink\n      :to=\"$localePath\"\n      class=\"home-link\"\n    >\n      <img\n        v-if=\"$site.themeConfig.logo\"\n        class=\"logo\"\n        ref=\"navLogo\"\n        :src=\"$withBase($site.themeConfig.logo)\"\n        :alt=\"$siteTitle\"\n      >\n      <span\n        v-if=\"$siteTitle\"\n        ref=\"siteName\"\n        class=\"site-name\"\n        :class=\"{ 'can-hide': $site.themeConfig.logo }\"\n      >{{ $siteTitle }}</span>\n    </RouterLink>\n\n    <slot name=\"top\" />\n\n    <SidebarLinks\n      :depth=\"0\"\n      :items=\"items\"\n    />\n    <slot name=\"bottom\" />\n  </aside>\n</template>\n\n<script>\nimport SidebarLinks from '@theme/components/SidebarLinks.vue'\nimport NavLinks from '@theme/components/NavLinks.vue'\nexport default {\n  name: 'Sidebar',\n  components: { SidebarLinks, NavLinks },\n  props: ['items']\n}\n</script>\n\n<style lang=\"stylus\">\n.sidebar\n  transition transform 0.35s ease-in-out\n  .arrow\n    display: none;\n  .home-link\n    display none\n  ul\n    padding 0\n    margin 0\n    list-style-type none\n  .sidebar-heading\n    color $sidebarText\n    font-size 1rem\n    transition color .2s\n    &:hover\n      color $green\n    &.open\n      color $green\n      &:hover\n        color $orange\n  a\n    display inline-block\n  .nav-links\n    display none\n    border-bottom 1px solid $borderColor\n    padding 0.5rem 0 0.75rem 0\n    a\n      font-weight 600\n      transition color .2s\n    .nav-item, .repo-link\n      display block\n      line-height 1.25rem\n      font-size 1.1em\n      padding 0.5rem 0 0.5rem 1.5rem\n  & > .sidebar-links\n    padding 1.5rem 0\n    & > li > a.sidebar-link\n      font-size 1.1em\n      line-height 1.7\n      font-weight 600\n      transition color .2s\n    & > li:not(:first-child)\n      margin-top 0rem\n  a.sidebar-link\n    font-size 1em\n    line-height 1.7\n    font-weight 600\n    color $sidebarText\n    transition color .2s\n    &:hover\n      color $green\n      border-bottom none\n    &.active\n      color $green\n      border-left-color transparent\n      border-bottom none\n      font-weight 600\n      &:hover\n        color: $orange\n.sidebar-mask\n  transition opacity 0.35s ease-in-out\n  background-color rgba(0,0,0,.5)\n  z-index -1\n  opacity 0\n\n@media (max-width: $MQMobile)\n  .sidebar\n    padding-top 0\n    z-index 30\n    .home-link\n      display block\n      padding 1.1rem 1rem\n      border-bottom 1px solid #eee\n      line-height 1rem\n      .logo\n        width 4rem\n    .nav-links\n      display block\n      .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after\n        top calc(1rem - 2px)\n    & > .sidebar-links\n      padding 1rem 0\n  .sidebar-open .sidebar-mask\n    opacity 1\n    z-index 25\n</style>\n"
  },
  {
    "path": "wiki/.vuepress/theme/index.js",
    "content": "module.exports = function() {\n  return {\n    extend: '@vuepress/theme-default',\n    plugins: {\n      '@vuepress/search': false,\n      '@vuepress/google-analytics': {\n        ga: 'UA-40550435-1'\n      },\n      'vuepress-plugin-sitemap': {\n        hostname: 'https://two.js.org',\n        exclude: ['/404.html']\n      }\n    }\n  };\n};\n"
  },
  {
    "path": "wiki/.vuepress/theme/layouts/NotFound.vue",
    "content": "<template>\n  <div class=\"theme-container\">\n    <div class=\"theme-default-content\">\n      <h1>404</h1>\n\n      <blockquote>{{ getMsg() }}</blockquote>\n\n      <RouterLink to=\"/\">\n        Take me home.\n      </RouterLink>\n    </div>\n  </div>\n</template>\n\n<script>\nconst msgs = [\n  `There’s nothing here.`,\n  `How did we get here?`,\n  `That's a Four-Oh-Four.`,\n  `Looks like we've got some broken links.`\n]\nexport default {\n  methods: {\n    getMsg () {\n      return msgs[Math.floor(Math.random() * msgs.length)]\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "wiki/README.md",
    "content": "---\ntitle: Two.js • Homepage\nlang: en-US\n---\n\n# Two.js\n\nA two-dimensional drawing api geared towards modern web browsers. It is renderer agnostic enabling the same api to draw in multiple contexts: svg, canvas, and webgl.\n\n<div>\n<custom-button text=\"Github\" type=\"github\" href=\"https://github.com/jonobr1/two.js\" />\n<custom-button text=\"Package\" type=\"npm\" href=\"https://www.npmjs.com/package/two.js\" />\n<custom-button text=\"Sponsor\" type=\"sponsor\" href=\"https://github.com/sponsors/jonobr1\" />\n<custom-button text=\"ChatGPT\" type=\"gpt\" href=\"https://chatgpt.com/g/g-hkcTX8uPm-two-js-tutor\" />\n</div>\n\n<carbon-ads />\n\n## Download\n\n<h3 class=\"visible\"><a href=\"#download\">Download</a></h3>\n\n<div>\n<custom-button text=\"Development\" type=\"download\" href=\"https://raw.githubusercontent.com/jonobr1/two.js/dev/build/two.js\" :size=\"$themeConfig.developmentSize\" />\n<custom-button text=\"Production\" type=\"download\" href=\"https://raw.githubusercontent.com/jonobr1/two.js/dev/build/two.min.js\" :size=\"$themeConfig.productionSize\" />\n</div>\n\n::: tip\nPrior to v0.7.0-alpha.1 Two.js requires Underscore.js and Backbone.js Events. If you're already loading these files elsewhere then you can build the project yourself and get the file size even smaller. For more information on custom builds check out the source on github.\n:::\n\nNode.js Version with npm:\n```\nnpm install --save two.js@latest\n```\n\n## Overview\n\n<h3 class=\"visible\"><a href=\"#overview\">Overview</a></h3>\n\n* #### Focus on Vector Shapes\n  Two.js is deeply inspired by flat [motion graphics](http://en.wikipedia.org/wiki/Motion_graphics). As a result, two.js aims to make the creation and animation of flat shapes easier and more concise.\n\n* #### Scenegraph\n  At its core two.js relies on a [scenegraph](http://en.wikipedia.org/wiki/Scene_graph). This means that when you draw or create an object (a Two.Path or Two.Group), two actually stores and remembers that. After you make the object you can apply any number of operations to it — e.g: rotation, position, scale, etc..\n\n* #### Animation Loop\n  Two.js has a built in animation loop. It is simple in nature and can be automated or paired with another animation library. For more information check out the [examples](/examples/).\n\n* #### SVG Interpreter\n  Two.js features a [Scalable Vector Graphics](http://en.wikipedia.org/wiki/Scalable_Vector_Graphics) Interpreter. This means developers and designers alike can create SVG elements in commercial applications like [Adobe Illustrator](http://www.adobe.com/products/illustrator) and bring them into your two.js scene. For more information check out the [examples](/examples/).\n\n* #### Friends with Bitmap Imagery\n  Despite its early focus on easing vector shape creation and animation, Two.js offers many easy-to-use features to handle and render bitmap images. Easily load single images, sprite sheets, and image sequences with just a few method calls.\n\n## Basic Usage\n\n<h3 class=\"visible\"><a href=\"#basic-usage\">Basic Usage</a></h3>\n\nIn order to start any of these demos you'll want to [download](#download) two.js and add it to your HTML document. Once downloaded add this tag to the `<head>` of your document: `<script src=\"./path-to-two/two.js\"></script>`. When you visit the page, you should be able to open up the console and type `Two`. If this returns a function (and not an error) then you're ready to begin!\n\n## Drawing Your First Shapes\n\n<h3 class=\"visible\"><a href=\"#drawing-your-first-shapes\">Drawing Your First Shapes</a></h3>\n\nBefore we get into all the fancy animating it's good to get a feel for how to make shapes in two.js. In order to do this we need to have an instance of two. This sets up a dom element that contains either an svg or canvas element to add to the webpage. The two object has a scene which holds all shapes as well as methods for creating shapes.\n\n::: tip\nFor a list of all properties and construction parameters check out the [documentation](./docs/).\n:::\n\n<inline-editor scripts=\"https://cdn.jsdelivr.net/npm/two.js@latest/build/two.js\">\n// Make an instance of two and place it on the page.\nvar params = { fullscreen: true };\nvar elem = document.body;\nvar two = new Two(params).appendTo(elem);\n\n// Two.js has convenient methods to make shapes and insert them into the scene.\nvar radius = 50;\nvar x = two.width * 0.5;\nvar y = two.height * 0.5 - radius * 1.25;\nvar circle = two.makeCircle(x, y, radius);\n\ny = two.height * 0.5 + radius * 1.25;\nvar width = 100;\nvar height = 100;\nvar rect = two.makeRectangle(x, y, width, height);\n\n// The object returned has many stylable properties:\ncircle.fill = '#FF8000';\n// And accepts all valid CSS color:\ncircle.stroke = 'orangered';\ncircle.linewidth = 5;\n\nrect.fill = 'rgb(0, 200, 255)';\nrect.opacity = 0.75;\nrect.noStroke();\n\n// Don’t forget to tell two to draw everything to the screen\ntwo.update();\n\n</inline-editor>\n\n## Shapes and Groups\n\n<h3 class=\"visible\"><a href=\"#shapes-and-groups\">Shapes and Groups</a></h3>\n\nAdding shapes to groups makes managing multiple shapes easier and more sane. Group's provide an easy way to move your content through `position`, `rotation`, and `scale`. These operations emit from the coordinate space `(0, 0)`. In the example below we can see that the initial orientation of the circle and rectangle changed from the first example. These shapes are oriented around `(0, 0)`, which allows us to transform the group around the centeroid of the shapes. In addition Group's styling operations trickle down and apply to each shape.\n\n<inline-editor scripts=\"https://cdn.jsdelivr.net/npm/two.js@latest/build/two.js\">\nvar params = { fullscreen: true }\nvar elem = document.body;\nvar two = new Two(params).appendTo(elem);\n\nvar circle = two.makeCircle(-70, 0, 50);\nvar rect = two.makeRectangle(70, 0, 100, 100);\ncircle.fill = '#FF8000';\ncircle.stroke = 'orangered';\nrect.fill = 'rgba(0, 200, 255, 0.75)';\nrect.stroke = '#1C75BC';\n\n// Groups can take an array of shapes and/or groups.\nvar group = two.makeGroup(circle, rect);\n\n// And have position, rotation, scale like all shapes.\ngroup.position.set(two.width / 2, two.height / 2);\ngroup.rotation = Math.PI;\ngroup.scale = 0.75;\n\n// You can also set the same properties a shape have.\ngroup.linewidth = 7;\n\ntwo.update();\n\n</inline-editor>\n\n## Adding Motion\n\n<h3 class=\"visible\"><a href=\"#adding-motion\">Adding Motion</a></h3>\n\nFinally, let's add some motion to our shapes. So far the examples use `two.update();` to draw content to the screen. The instance of two.js has two particular methods for animation. The first is `two.play();` which calls `two.update();` at 60 frames-per-second. This rate, however, will slowdown if there's too much content to render per frame.\n\nThe second method is `two.bind();` This method takes a string as its first parameter indicating what event to listen to and a function as its second argument delineating what to do when the event described in the first parameter happens. To sync a function with the animation loop simply invoke `two.bind('update', referenceToFunction);` as outlined below:\n\n<inline-editor scripts=\"https://cdn.jsdelivr.net/npm/two.js@latest/build/two.js\">\nvar params = { fullscreen: true };\nvar elem = document.body;\nvar two = new Two(params).appendTo(elem);\n\nvar circle = two.makeCircle(-70, 0, 50);\nvar rect = two.makeRectangle(70, 0, 100, 100);\ncircle.fill = '#FF8000';\nrect.fill = 'rgba(0, 200, 255, 0.75)';\n\nvar cx = two.width * 0.5;\nvar cy = two.height * 0.5;\nvar group = two.makeGroup(circle, rect);\ngroup.position.set(cx, cy);\ngroup.scale = 0;\ngroup.noStroke();\n\n// Bind a function to scale and rotate the group to the animation loop.\ntwo.bind('update', update);\n// Finally, start the animation loop\ntwo.play();\n\nfunction update(frameCount) {\n  // This code is called every time two.update() is called.\n  if (group.scale > 0.9999) {\n    group.scale = group.rotation = 0;\n  }\n  var t = (1 - group.scale) * 0.04;\n  group.scale += t;\n  group.rotation += t * 4 * Math.PI;\n}\n\n</inline-editor>\n\n### Next Steps\n\nNow that you got a quick glimpse into some of the functionality two.js offers, check out the [official](/examples/#official-examples) and [community](/examples/#community-examples) examples to see what else you can do. These examples range from showing off specific features of the library to using the library in other environments, like [React](/examples/#react) and [Angular](/examples/#angular).\n\nLooking for more information on a specific property? Then head over to the [documentation](/docs/two/) which outlines all of the library's public features.\n\nHaven't found what you're looking for? Then ask a question on our [GitHub](https://github.com/jonobr1/two.js/issues/new?assignees=&labels=question&template=question.md&title=%5BQuestion%5D) page.\n\n---\n\n<br />\n\n#### Project Credits\n\nTwo.js is dependency free, but its creation would not have been possible without these great contributions to the JavaScript ecosystem:\n\n<div class=\"inspiration\">\n\n+ [Three.js](http://threejs.org/)\n+ [Vuepress](https://vuepress.vuejs.org/)\n+ [Underscore.js](https://underscorejs.org/)\n+ [Backbone.js](https://backbonejs.org/)\n+ [QUnit](https://qunitjs.com/)\n+ [Resemble.js](https://github.com/rsmbl/Resemble.js)\n+ [Canvas2Blob](https://github.com/blueimp/JavaScript-Canvas-to-Blob)\n+ [jsdoc](https://jsdoc.app/)\n+ [ESLint](https://eslint.org/)\n+ [rollup.js](https://rollupjs.org/)\n\n</div>\n\n<div class=\"project-footnote\">\n\nTwo.js is a project by [Jono](http://jono.fyi/) and numerous [contributors](https://github.com/jonobr1/two.js/graphs/contributors) • Site design by [Yuin](https://yuinchien.com/) • Site development in collaboration with [Tonia](https://toniab.com/)\n\n<br />\n\nPublished under the [MIT License](https://github.com/jonobr1/two.js/blob/dev/LICENSE) © 2012 – {{ 1900 + new Date().getYear() }}\n\n[Safety & Security](/security) • [Privacy Policy](/privacy)\n\n</div>\n"
  },
  {
    "path": "wiki/change-log/README.md",
    "content": "<redirect-page src=\"/changelog/\"></redirect-page>\n"
  },
  {
    "path": "wiki/changelog/README.md",
    "content": "---\npageClass: change-log\nsidebarDepth: 3\ntitle: Two.js Changelog\nlang: en-US\n---\n\n# Changelog\n\nAll notable changes to this project will be documented in this file. The format is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<carbon-ads />\n\n## Nightly\n\n- Separated type signatures into their own respective files\n\n## December 22, 2025 v0.8.23\n\n<h3 class=\"visible\">Dec 22, 2025</h3><version-link v=\"v0.8.23\" />\n\n- Made `Two.Event` private getter more relaxed\n- Added `Two.Shape.dispose`\n- Added test suite for `dispose` methods\n\n## December 3, 2025 v0.8.22\n\n<h3 class=\"visible\">Dec 3, 2025</h3><version-link v=\"v0.8.22\" />\n\n- Improved `Two.Path.subdivide` to preserve curves (by filling out `Two.Anchor.controls` property)\n- Added `Two.Path.smooth` command\n- Added `Two.Path.contains`, `Two.Group.contains`, and `Two.Shape.contains` method for hit testing\n- Added `Two.getShapesAtPoint` and `Two.Group.getShapesAtPoint` with visibility/filter options\n\n## October 1, 2025 v0.8.21\n\n<h3 class=\"visible\">Oct 1, 2025</h3><version-link v=\"v0.8.21\" />\n\n- Added `Two.Path.strokeAttenuation`, `Two.Group.strokeAttenuation`, `Two.Points.strokeAttenuation`, `Two.Text.strokeAttenuation`\n- Improved type signatures on all `fromObject` methods for better hinting\n- Improved precision for curve details\n- Added Privacy Policy\n- Added SECURITY.md\n- Added Incident Response Plan\n- Enabled Code Scanning\n- Added more tests for security vulnerabilities\n\n## September 8, 2025 v0.8.20\n\n<h3 class=\"visible\">Sep 8, 2025</h3><version-link v=\"v0.8.20\" />\n\n- Fixed `Two.Element.copy` to fail gracefully\n- Added `Two.Image` as a new shape that inherits `Two.Rectangle`\n- Expand `Two.RoundedRectangle` constructor to accept `Two.Vector` as the radius\n- Added `Two.Element.dispose`, `Two.Path.dispose`, `Two.Group.dispose`, `Two.Text.dispose`, `Two.Points.dispose`, `Two.ImageSequence.dispose`, `Two.Sprite.dispose`\n- Improved `Two.release` to account for renderer specific deallocations\n- Improved `Two.ImageSequence` to take all optional arguments on instantiation\n- Fixed command setting on `Two.Line.left` and `Two.Line.right`\n\n## June 27, 2025 v0.8.19\n\n<h3 class=\"visible\">Jun 27, 2025</h3><version-link v=\"v0.8.19\" />\n\n- Improved TypeScript types for `Two.Path.dashes`, `Two.Points.dashes`, `Two.Text.dashes`\n- Improved TypeScript types for the `Two.Element.renderer` property\n- Added `Two.Element.renderer.onBeforeRender` and `Two.Element.renderer.onAfterRender` for higher context manipulation\n\n## April 8, 2025 v0.8.18\n\n<h3 class=\"visible\">Apr 8, 2025</h3><version-link v=\"v0.8.18\" />\n\n- Improve TypeScript types with correct inheritance and no errors\n- Fix `Two.release` in order to check for object existence on `fill` and `stroke` properties\n\n## Mar 17, 2025 v0.8.17\n\n<h3 class=\"visible\">Mar 17, 2025</h3><version-link v=\"v0.8.17\" />\n\n- Improved and updated TypeScript types based on public / private properties and new latest Two.js features\n- Added `Two.Gradient.dispose` and `Two.Texture.dispose` to properly remove effects from scenegraphs\n- Changed name `Two.nextFrameID` to `Two.NextFrameId`\n- Improved `Two.release` method to include detaching an object's effects (e.g: `Two.Texture`, `Two.Gradient`)\n- Fixed order of operations on `Two.Shape` transformations\n\n## Feb 13, 2025 v0.8.16\n\n<h3 class=\"visible\">Feb 13, 2025</h3><version-link v=\"v0.8.16\" />\n\n- Improved TypeScript types (albeit raising errors because of how expressive Two.js / JavaScript is)\n- Move `Two.Utils.shim` to `Two.Utils.polyfill`. Shim exists but is internal specific to remove circular dependencies\n- Add `Two.CanvasRenderer` compatiblity with [skia-canvas](https://skia-canvas.org/)\n- `Two.Path.subdivide` accurately amends the final vertices in the updated list\n- `Two.ZUI.reset` updates the surfaces to be reflect reset orientation\n- `Two.Group.getBoundingClientRect(shallow)` correctly infers min and max values to calculate dimensions correctly\n\n## Dec 31, 2024 v0.8.15\n\n<h3 class=\"visible\">Dec 31, 2024</h3><version-link v=\"v0.8.15\" />\n\n- Made `CanvasPolyfill` object and merged it into `Two.Utils` to remove circular dependencies\n- Made `Two.Collection._events` and `Two.Children.ids` not enumerated for more seamless invocation of `Array` methods like `Array.filter` and `Object` methods like `Object.keys`\n- Added static method `fromObject` to all `Two.Element` and descendant classes\n- Added method `copy` to all `Two.Element` and descendant classes\n- Added tests for `toObject` methods\n- Improved TypeScript types for argument relation\n- `Two.WebGLRenderer` correctly handles negative scale values\n- `Two.Path.noStroke`, `Two.Text.noStroke`, and derivative methods now include setting both the stroke color to `\"none\"` and the line width to 0\n- Fixed `Two.Arc` flag behavior for `Two.Arc.width` and `Two.Arc.height` properties\n\n## Jun 10, 2024 v0.8.14\n\n<h3 class=\"visible\">Jun 10, 2024</h3><version-link v=\"v0.8.14\" />\n\n- Made `objectBoundingBox` application consistent accross renderers\n- Patched texture scaling in `Two.SVGRenderer`\n- Updated Typescript definitions [@kevin-nano](https://github.com/kevin-nano)\n\n## Feb 22, 2024 v0.8.13\n\n<h3 class=\"visible\">Feb 22, 2024</h3><version-link v=\"v0.8.13\" />\n\n- Added `Two.Text.direction` for rendering text either left to right or right to left\n- Improved `Two.WebGLRenderer` texture crispness / aliasing\n- Improved consistency of `Two.Text.baseline` rendering across all renderers\n\n## Oct 16, 2023 v0.8.12\n\n<h3 class=\"visible\">Oct 16, 2023</h3><version-link v=\"v0.8.12\" />\n\n- Added `\"no-referrer\"` policy to image requests\n- Updated extras to be compatible with `Two.Matrix` API changes\n- `Two.Path.noFill()` yields `\"none\"` instead of `\"transparent\"`\n- `Two.Path.noStroke()` yields `\"none\"` instead of `\"transparent\"`\n- `Two.Text.noFill()` yields `\"none\"` instead of `\"transparent\"`\n- `Two.Text.noStroke()` yields `\"none\"` instead of `\"transparent\"`\n- `Two.Points.noFill()` yields `\"none\"` instead of `\"transparent\"`\n- `Two.Points.noStroke()` yields `\"none\"` instead of `\"transparent\"`\n\n## Aug 7, 2023 v0.8.11\n\n<h3 class=\"visible\">Aug 7, 2023</h3><version-link v=\"v0.8.11\" />\n\n- Fixed getters on `Two.Anchor.left` and `Two.Anchor.right` [@eatgrass](https://github.com/eatgrass)\n- Improved `Two.Path.getBoundingClientRect`, `Two.Group.getBoundingClientRect`, and `Two.Text.getBoundingClientRect` to correctly handle projected points that are rotated by matrices\n- Improved `types.d.ts` for `Two.Group`\n- `Two.Text` rendered to SVG directly instead of `<defs />`\n- `Two.SVGRenderer` disposes of unused effects from `<defs />`\n- Override `Two.Collection.map` method to be more explicit\n- Improved `types.d.ts` for renderers and base `Two` class\n\n## Jun 9, 2022 v0.8.10\n\n<h3 class=\"visible\">Jun 9, 2022</h3><version-link v=\"v0.8.10\" />\n\n- Added `Two.Arc` to `/extras` directory\n- Made `/extras/js/zui.js` ES6 compliant\n- Removed `/extras` modules from generated documentation\n- Added method documentation to `Two.ZUI`\n- Improved `types.d.ts`\n- Fixed Commonjs imports from `package.json`\n\n## May 9, 2022 v0.8.7\n\n<h3 class=\"visible\">May 9, 2022</h3><version-link v=\"v0.8.7\" />\n\n- Fixed documentation links\n- Made `Two.Group` and `Two.Text` properties public in TypeScript types\n- Added overloaded methods to TypeScript types\n- Fixed `Two.Path.ending` discrepancies when `Two.Path.curved = true`\n\n## Mar 29, 2022 v0.8.5\n\n<h3 class=\"visible\">Mar 29, 2022</h3><version-link v=\"v0.8.5\" />\n\n- Added `Two.Gradient` parent parameter to `Two.Stop.clone`\n- **_Breaking_**: Added export maps so extras are imported like so `import { ZUI } from 'two.js/extras/zui.js'`\n\n## Jan 29, 2022 v0.8.3\n\n<h3 class=\"visible\">Jan 29, 2022</h3><version-link v=\"v0.8.3\" />\n\n- Improved `Two.Element.className` flagging and `classList` construction\n- Manually amended `types.d.ts` for better TypeScript developing\n\n## Jan 19, 2022 v0.8.2\n\n<h3 class=\"visible\">Jan 21, 2022</h3><version-link v=\"v0.8.2\" />\n\n- Removed `#` private declared variables for better EcmaScript compatibility\n- Fixed `Two.Path.begining` and `Two.Path.ending` interpolation when `Two.Anchor.relative = false`\n- Added `Two.Utils.read.path(string)` which allows for string interpretation of an SVG path's `d` attribute\n- Added `Two.Shape.worldMatrix`\n\n## Jan 10, 2022 v0.8.0\n\n<h3 class=\"visible\">Jan 10, 2022</h3><version-link v=\"v0.8.0\" />\n\n- Added `Two.Element` as an EcmaScript 6 class\n- Converted `Two.ImageSequence` to EcmaScript 6 class\n- Converted `Two.Sprite` to EcmaScript 6 class\n- Converted `Two.Texture` to EcmaScript 6 class\n- Converted `Two.Stop` to EcmaScript 6 class\n- Converted `Two.RadialGradient` to EcmaScript 6 class\n- Converted `Two.LinearGradient` to EcmaScript 6 class\n- Converted `Two.Gradient` to EcmaScript 6 class\n- Converted `Two.Star` to EcmaScript 6 class\n- Converted `Two.RoundedRectangle` to EcmaScript 6 class\n- Converted `Two.Rectangle` to EcmaScript 6 class\n- Converted `Two.Polygon` to EcmaScript 6 class\n- Converted `Two.Points` to EcmaScript 6 class\n- Converted `Two.Line` to EcmaScript 6 class\n- Converted `Two.Ellipse` to EcmaScript 6 class\n- Converted `Two.Circle` to EcmaScript 6 class\n- Converted `Two.ArcSegment` to EcmaScript 6 class\n- Converted `Two.Path` to EcmaScript 6 class\n- Converted `Two.Group` to EcmaScript 6 class\n- Converted `Two.Text` to EcmaScript 6 class\n- Converted `Two.Shape` to EcmaScript 6 class\n- Converted `Two.Matrix` to EcmaScript 6 class\n- Converted `Two.Registry` to EcmaScript 6 class\n- Converted `Two.Children` to EcmaScript 6 class\n- Converted `Two.Collection` to EcmaScript 6 class\n- Converted `Two.Events` to EcmaScript 6 class\n- Converted `Two.Anchor` to EcmaScript 6 class\n- Converted `Two.Vector` to EcmaScript 6 class\n- `Two.interpret` appropriately inherits classes to `<g />` tags [@elShiaLabeouf](https://github.com/elShiaLabeouf)\n- `Two.interpret` inherits classes and `data-*` attributes [@elShiaLabeouf](https://github.com/elShiaLabeouf)\n\n## Dec 15, 2021 v0.7.13\n\n<h3 class=\"visible\">Dec 15, 2021</h3><version-link v=\"v0.7.13\" />\n\n- Improved SVG gradient interpretation\n- `Two.interpret` can properly unwrap CSS `url()` commands\n- Added `Two.Gradient.units` and respected in all renderers\n- Default units space for `Two.Gradient` is `objectBoundingBox`\n- Removed destructive attribute assignments in `Two.interpret`\n- Interpreted gradients are reused as `<defs />`\n\n## Nov 24, 2021 v0.7.12\n\n<h3 class=\"visible\">Nov 24, 2021</h3><version-link v=\"v0.7.12\" />\n\n- Made mask removal possible on `Two.Group`s, `Two.Path`s, and `Two.Points`'\n- Simplified `Two.Points.vertices` management for renderers improving adaptability\n\n## Nov 20, 2021 v0.7.10\n\n<h3 class=\"visible\">Nov 20, 2021</h3><version-link v=\"v0.7.10\" />\n\n- Fixed empty vertices issue on `Two.Points`\n\n## Nov 11, 2021 v0.7.9\n\n<h3 class=\"visible\">Nov 11, 2021</h3><version-link v=\"v0.7.9\" />\n\n- Made `Two.load` and `Two.interpret` consistently apply SVG `viewBox`\n- Made `Two.Rectangle.origin` act as inverse anchor positioning\n- Made `Two.Path.toObject` objectify `fill` and `stroke` attributes that are `Two.Texture` and `Two.Gradient`\n- Improved reassigned id selection on interpreted SVG elements\n- Improved `Two.load` group duplication\n- Added `Two.Points` and `Two.makePoints` to all renderers\n- Made `Two.release` mimic behavior of `Two.remove`\n\n## Jul 13, 2021 v0.7.8\n\n<h3 class=\"visible\">Jul 13, 2021</h3><version-link v=\"v0.7.8\" />\n\n- Made `types.d.ts` a shim of a module so that it can at least be loaded in TypeScript projects while the ES6 class declaration is still work-in-progress\n\n## Jul 10, 2021 v0.7.6\n\n<h3 class=\"visible\">Jul 10, 2021</h3><version-link v=\"v0.7.6\" />\n\n- Fixed `id` setting on all objects of Two.js\n- Fixed `Two.ArcSegment.clone` outer radius issue\n- Standardized vertex generation in higher order primitives\n- Type declaration improvements\n- Improved `Two.Group.center`, `Two.Group.corner`, `Two.Path.center`, `Two.Path.corner`\n- Improved SVG gradient interpretation\n- Added `<defs />` interpretation\n- Added `Two.Path.mask` and `Two.Text.mask` properties\n- Standardized `Texture.id` to be compliant with other `Two.Shape.id`\n- Added ability to interpret `<clipPath />` and `<image />` SVG elements\n\n## Apr 23, 2021 v0.7.5\n\n<h3 class=\"visible\">Apr 23, 2021</h3><version-link v=\"v0.7.5\" />\n\n- Improved `Two.Circle.beginning` & `Two.Circle.ending` behavior\n- Improved `fitted` logic on instances of `Two`\n- Improved `Two.Children` and `Two.Collection` instantiation versatility\n- Improved `Two.Group.getBoundingClientRect`, `Two.Path.getBoundingClientRect`, and `Two.Text.getBoundingClientRect`\n- Improved higher level shapes', like `Two.Circle`, ability to handle zeroed out arguments\n\n## Apr 2, 2021 v0.7.4\n\n<h3 class=\"visible\">Apr 2, 2021</h3><version-link v=\"v0.7.4\" />\n\n- Improved Typescript type declarations\n- Successfully export `extras/` directory in both JavaScript and JavaScript Module format\n- Added `Two.Shape.skewX` and `Two.Shape.skewY` [@gburlet](https://github.com/gburlet)\n\n## Mar 26, 2021 v0.7.3\n\n<h3 class=\"visible\">Mar 26, 2021</h3><version-link v=\"v0.7.3\" />\n\n- Exposed `Two.Shape.renderer` for easier access to underlying SVG elements\n- Added Typescript declaration types to Two.js\n- Made `Two.Group` listen to `Two.Shape.id` changes through new observable property and flags\n- Improved `Two.interpret` performance and functionality through improved regular expressions [@adroitwhiz](https://github.com/adroitwhiz)\n- `Two.Group.visible` is a property held on the group and does not trickle down to children\n- Improved scientific notation interpretation in SVG elements\n- `Two.load` appends a hidden DOM element to the document's `<head />` so that `getComputedStyles` works more consistently with `Two.interpret`\n\n## Jan 12, 2021 v0.7.1\n\n<h3 class=\"visible\">Jan 12, 2021</h3><version-link v=\"v0.7.1\" />\n\n- Added `Two.AutoCalculateImportedMatrices` to control different ways of importing SVG transformations\n- Fixed `Two.Text.className` application\n- Added basic text support in `Two.interpret`\n- Exposed `Two.fit` for external use\n- Added a `fitted` argument to the `Two` constructor to make instance adaptively match the size of its parent element\n- Simplified `Two.release` method\n- Added support for scientific notation in `Two.interpret`\n- Added `Two.Text.decoration` styling to `Two.CanvasRenderer` and `Two.WebGLRenderer`\n- Increased precision on `Two.Utils.toFixed` for Firefox transformation engine performance increase\n- Added support to import `<use />` tags from SVG interpretation [@eulertour](https://github.com/eulertour)\n- Removed extraneous underscore calls from `Two.Utils` [@adroitwhiz](https://github.com/adroitwhiz)\n- Ensured `Two.interpret` respects the `add` parameter to be added (or not) to the current instance\n- Modularized Two.js classes / files [@adroitwhiz](https://github.com/adroitwhiz)\n- Made Two.js compatible with `import` modules [@adroitwhiz](https://github.com/adroitwhiz)\n- Added wiki as [VuePress](https://vuepress.vuejs.org/) project\n\n## Jan 22, 2020 v0.7.0\n\n<h3 class=\"visible\">Jan 22, 2020</h3><version-link v=\"v0.7.0\" />\n\n- Exposed `resolution` parameter in `Two.makeCircle` and `Two.makeEllipse` as the final parameter\n- Made `Two.Circle` and `Two.Ellipse` recalculate controls points on `_update` and made vertex amounts cyclical [@adroitwhiz](https://github.com/adroitwhiz)\n- Added [ESLint](https://eslint.org/) scripts to development environment [@adroitwhiz](https://github.com/adroitwhiz)\n- Improve performance of WebGLRenderer by leveraging uniform to construct plane and removing duplicate render calls [@adroitwhiz](https://github.com/adroitwhiz)\n- Unpacked `scale` objects in WebGLRenderer [@adroitwhiz](https://github.com/adroitwhiz)\n- Removed unnecessary `gl.colorMask` calls [@adroitwhiz](https://github.com/adroitwhiz)\n- Removed `Two.Utils.toFixed` calls on Canvas and WebGLRenderers [@adroitwhiz](https://github.com/adroitwhiz)\n- `Two.Shape.clone` clones `Two.Shape.matrix` when `Two.Shape.matrix.manual` is set to `true`\n- Improved `Two.Group.mask` rendering in `Two.WebGLRenderer`\n- Fixed `Two.WebGLRenderer.setSize` recursive loop error\n- Connected `Two.Shape.className` to `Two.Shape.classList` for searching and class assignment in SVG elements\n- Performance improvements on canvas HTML5 styles [@brandonheyer](https://github.com/brandonheyer)\n- Added trickle down styling to `Two.Group.closed`, `Two.Group.curved`, and `Two.Group.automatic`\n- Check for Duplicity on `Two.Group.add`\n- Accounted for offset positions in `Two.Path.center` and `Two.Group.center` methods\n- Exposed `Two.Shape.matrix` as a publicly accessible property\n- Removed `Two.Utils.deltaTransformPoint` and patched `Two.Utils.decomposeMatrix` to more accurately parse matrices\n- Added support for various position inclusion of gradients and other effects in `Two.interpret`\n- Improved `Two.Utils.applySvgAttributes` rotation from SVG interpretation\n- Added `Two.makeArrow` for Simple Triangular Tipped Arrows [@mike168m](https://github.com/mike168m)\n- Improved `Two.Matrix` efficiency of calculations\n- Added `Two.Path.dashes.offset` and `Two.Text.dashes.offset` properties for animating dashed strokes in all renderers\n- Fixed `Two.Path.corner` method to not be additive on successive invocations\n- Split `Two.Matrix.toArray` into two different functions. One for 2D transforms and one for a plain object (JSON) representation\n- Added `Two.Matrix.toTransformArray` intended for 2D transformation use internally\n- `WebGLRenderer` more robustly supports displaying bitmap content\n- Added `<g />` attributes to be inherited by children in SVG interpretation\n- Added `offscreenElement` as an option when constructing WebGL Renderers for WebWorker compatibility\n- Added `Two.Shape.position` accessor to `Two.Shape.translation` for ease of use with [matter.js](http://brm.io/matter-js/)\n- Added `Two.Path.dashes` and `Two.Text.dashes` support to WebGL and Canvas Renderers\n\n## Dec 8, 2018 v0.7.0-beta.3\n\n<h3 class=\"visible\">Dec 8, 2018</h3><version-link v=\"v0.7.0-beta.3\" />\n\n- Canvas Renderer supports dashed and non dashed paths\n- Enforce `Two.Rectangle` has four `vertices`\n- Fixed `Two.Path.closed` on latest `ending` calculations\n\n## Nov 18, 2018 v0.7.0-beta.2\n\n<h3 class=\"visible\">Nov 18, 2018</h3><version-link v=\"v0.7.0-beta.2\" />\n\n- Updated Two.js compatibility with webpack and node-canvas 2.0.0+\n\n## Nov 3, 2018 v0.7.0-beta.1\n\n<h3 class=\"visible\">Nov 3, 2018</h3><version-link v=\"v0.7.0-beta-1\" />\n\n- Altered `Two.Path.clone` and `Two.Text.clone` to use references where possible and to `_update()` on return\n- Improved multi-decimal and arc SVG interpretation\n- Added `Two.Commands.arc` for better arc rendering across all renderers\n- `Two.Path` and `Two.Text` now have `dashes` property to define stroke dashing behavior [@danvanorden](https://github.com/danvanorden)\n- `Two.Vector` arithmetic methods made more consistent — still need to improve performance\n- `Two.Path.vertices` will not clone vectors, improving developer clarity\n- Two.js clone methods do not force adding to a parent\n- `Two.ImageSequence`, `Two.Sprite`, and `Two.Rectangle` have `origin` properties for offset rendering\n- `Two.Group.getBoundingClientRect` will pass-through on effects instead of break\n- `Two.interpret` apply SVG node `style` attributes to paths. Inherits from groups ~~and infers SVG `viewBox` attribute against Two.js instance~~\n- `Two.interpret` improves multi-decimal formatted `d` attributes\n- `Two.ZUI` added through the new `/extras` folder\n- `Two.Text.getBoundingClientRect` now returns an estimated bounding box object\n- `Two.interpret` properly assigns back calculated `Z` coordinates\n- `Two.load` now immediately returns a `Two.Group` for use without callbacks if desired\n- Added `Two.Group.length` to return the calculated length of all child paths\n- `Two.Group.beginning` and `Two.Group.ending` calculate based on child `Two.Path`s for intuitive grouped animating\n- Added `Two.Utils.shim` to properly handle `canvas` and `image` element spoofing in headless environments\n- Improved conformance between primitive shapes\n- `Two.Path.getBoundingClientRect` considers control points from bezier curves\n- `Two.Path.beginning` and `Two.Path.ending` calculate based on distance increasing accuracy for animation, but also performance load\n- Moved `Two.Path._vertices` underlying to list of rendered points to `Two.Path._renderer.vertices`\n- Improved accuracy of `Two.Path.ending` and `Two.Path.beginning` on open paths\n- Added specific `clone` method to `Two.ArcSegment`, `Two.Circle`, `Two.Ellipse`, `Two.Polygon`, `Two.Rectangle`, `Two.RoundedRectangle`, and `Two.Star` primitives\n- Added ability to read `viewBox` property from root SVG node in `Two.interpret`\n- Added more reliable transform getter in `Two.interpret`\n- Added `rx` and `ry` property reading on `Two.Utils.read.rect`\n- Added `Two.Utils.read['rounded-rect']` to interpret Rounded Rectangles\n- Added ability for `Two.RoundedRectangle.radius` to be a `Two.Vector` for x, y component styling\n- Added ES6 compatible `./build/two.module.js` for importing library\n- Improved `Q` SVG interpretation\n- `Two.Texture`, `Two.Sprite`, and `Two.ImageSequence` implemented in `WebGLRenderer`\n- Added `className` property to `Two.Shape`s for easier CSS styling in `SVGRenderer` [@fr0](https://github.com/fr0)\n- `Two.Events.resize` is now bound to a renderer's `setSize` function giving a more generic solution to change scenegraph items based on dimensions changing\n\n## Dec 1, 2017 v0.7.0-alpha.1\n\n<h3 class=\"visible\">Dec 1, 2017</h3><version-link v=\"v0.7.0-alpha-1\" />\n\n- Fixed closed `Two.Path.getPointAt` method to clamp properly\n- Added `Two.Texture.repeat` for describing pattern invocations\n- Added `Two.Texture`, `Two.Sprite`, and `Two.ImageSequence`\n- Removed `Two.Shape` inheritance for `Two.Gradient`s\n- Added `Two.Vector.rotate` method [@ferm10n](https://github.com/ferm10n)\n- Objects clone to parent only if parent exists [@ferm10n](https://github.com/ferm10n)\n- Vendor agnostic `requestAnimationFrame` [@ferm10n](https://github.com/ferm10n)\n- `Two.Utils.Events.listenTo` and `Two.Utils.Events.stopListening` [@ferm10n](https://github.com/ferm10n)\n- `Two.Utils.Events` added to `Two.Path.prototype` for event inheritance [@ferm10n](https://github.com/ferm10n)\n- Enhanced `Two.Shape.scale` to allow both numbers and `Two.Vector` as property value\n- Made `Two.interpret` use latest primitives\n- Added `Two.Circle` primitive\n- `Two.Shape.translation` is now a getter/setter and can be replaced\n- Fixed translation interpretation to strip out `'px'` strings\n- Removed `Two.SineRing` — make `Two.Star.curved = true` and it's the same effect\n- Enhanced `Two.ArcSegment`, `Two.Ellipse`, `Two.Polygon`, `Two.Rectangle`, `Two.RoundedRectangle`, `Two.Star`\n- Fixed `Two.Anchor.relative` interpretation in `svg`, `canvas`, and `webgl` renderers\n- Made Getters / Setters enumerable for iteration compatibility\n- Fixed Two.Utils.Collection.splice method and added additional test\n- Added compatibility with [node.js](http://nodejs.org/), [browserify](http://browserify.org), and [node-canvas](https://github.com/Automattic/node-canvas)\n- Removed third party dependencies\n- Added `remove` method to `Two.Text`\n- Fixed ordering on same parent additions for `Two.Group`\n\n## Feb 9, 2016 v0.6.0\n\n<h3 class=\"visible\">Feb 9, 2016</h3><version-link v=\"v0.6.0\" />\n\n- Updated `Two.CanvasRenderer.ctx.imageSmoothingEnabled` to not use deprecated invocation, [issue 178](https://github.com/jonobr1/two.js/issues/178)\n- Fixed `Two.Group.mask` in `SVGRenderer` to append to DOM correctly\n- Updated `require` imports to be compatible with [require.js](http://requirejs.org/)\n- Added `Two.Text` for programmatically writing text in Two.js\n\n## Oct 1, 2015 v0.5.0\n\n<h3 class=\"visible\">Oct 1, 2015</h3><version-link v=\"v0.5.0\" />\n\n- Added support for `two.interpret` to import `svg`'s gradients\n- Added `Two.Utils.xhr` and `two.load` methods to asynchronously load SVG files\n- Added `Two.Gradient`, `Two.LinearGradient`, and `Two.RadialGradient`\n- Added dependency check to ensure ASM loading in environments like NPM as well as in the browser\n- Properly deleted `webgl` textures on removal of `Two.Path`\n- Added support for `two.interpret` to import `svg`'s [Elliptical Arcs](http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands)\n- Added `Two.ArcSegment` and `Two.SineRing` as new shapes invoked like `Two.Path` [@chrisdelbuck](http://github.com/chrisdelbuck)\n- Added `Two.Line`, `Two.Rectangle`, `Two.RoundedRectangle`, `Two.Ellipse`, `Two.Polygon`, and `Two.Star` as new shapes invoked like `Two.Path`\n- **_Breaking_**: renamed `Two.Polygon` to `Two.Path`\n- Performance enhancements to `webgl` renderer\n- Performance enhancements to `canvas` renderer [Leo Koppelkamm](https://github.com/ponychicken)\n- Enabled render ordering in `Two.Group.children` based on previous augmentation\n- Augmented `Two.Group.children` to inherit from `Two.Collection` effectively making it an array instead of a map [Leo Koppelkamm](https://github.com/ponychicken)\n  - The map can still be accessed at `Two.Group.children.ids`\n\n## Jul 22, 2014 v0.4.0\n\n<h3 class=\"visible\">Jul 22, 2014</h3> <version-link v=\"v0.4.0\" />\n\n- Updated `Two.interpret` to handle polybezier path data\n- Added `Two.Group.mask` and `Two.Polygon.clip` in order to create clipping masks\n- `Two.Group` has own `opacity` property [Leo Koppelkamm](https://github.com/ponychicken)\n- Rendering optimizations [Leo Koppelkamm](https://github.com/ponychicken)\n- `Two.noConflict` non-destructive command internally to the library\n- `Two.interpret` decomposes `transform` attribute of source tag\n- `Two.interpret` handles item irregularities from [Inkscape](http://www.inkscape.org/)\n- Changed `Two.Identifier` to use underscores instead of hyphens for dot-notation access [Leo Koppelkamm](https://github.com/ponychicken)\n- Added `Two.Group.getById` and `Two.Group.getByClassName` methods for convenient selection [Leo Koppelkamm](https://github.com/ponychicken)\n- Added `classList` to all `Two.Shape`s [Leo Koppelkamm](https://github.com/ponychicken)\n- Enabled inference of applied styles on imported svgs [Leo Koppelkamm](https://github.com/ponychicken)\n- Added `Two.Polygon.getPointAt` method to get coordinates on a curve/line\n- Added `Two.Polygon.length` property and `Two.Polygon._updateLength` method to calculate length of curve/line\n- Updated `Two.Group.prototype` observable properties on `Two.Polygon.Properties` to ensure each property is considered unique\n- ~~`Two.Polygon.vertices` first and last vertex create automated control points when `Two.Polygon.curved = true`~~\n- Updated `Two.Polygon.subdivide` method to accommodate `Two.makeEllipse`\n- Enabled `id` to be properly interpreted from SVG elements [@chrisdelbuck](http://github.com/chrisdelbuck)\n- Updated `webgl` renderer `getBoundingClientRect` to accommodate `relative` anchors\n- Updated `beginning` and `ending` to clamp to each other\n- Reorganized `Two.Polygon._update` and `Two.Polygon.plot` in order to handle `beginning` and `ending` properties\n- Updated `Two.getComputedMatrix` and `Two.Polygon.getBoundingClientRect` to adhere to nested transformations\n- Updated `Two.Anchor` to change `control` points relatively by default through `anchor.relative` property\n- Updated `Two.Polygon.subdivide` method to accommodate `curved = false` circumstances\n- Updated `svg`, `canvas`, and `webgl` renderers to properly reflect holes in curved `Two.Polygon`s\n- Updated `Two.Group` `clone` method\n- Added `toObject` method to `Two.Group`, `Two.Polygon`, `Two.Anchor`\n- `Two.Polygon` initializes `polygon.cap = 'butt'` and `polygon.join = 'miter'` based on Adobe Illustrator defaults\n- `Two.Polygon.subdivide` method now works with `Two.Commands.move` for noncontiguous polygons\n- Internally update matrices on `getBoundingClientRect` in order to remove the need to defer or wait for internal variables to be up-to-date\n- Refactor of renderers and scenegraph for performance optimization and lower memory footprint\n  - Relinquished internal _events_ for _flags_\n  - Prototypical declaration of `Object.defineProperty`\n  - Added `_update` and `flagReset` methods to `Two.Shape`, `Two.Group`, and `Two.Polygon`\n  - Decoupled `canvas` and `webgl` renderers and are now independent\n  - Added `_matrix.manual` to override the default behavior of a `Two.Polygon` transformation\n  - Localized variables per file as much as possible to reduce Garbage Collection on runtime\n\n## Oct 25, 2013 v0.3.0\n\n<h3 class=\"visible\">Oct 25, 2013</h3><version-link v=\"v0.3.0\" />\n\n- Can properly pass `domElement` on construction of new instance of two\n- Added `overdraw` boolean to `webgl` renderer [@arodic](https://github.com/arodic)\n- Added support for ie9 svg interpretation [@tomconroy](https://github.com/tomconroy)\n- Added `subdivide` method for `Two.Polygon` and `Two.Group`\n- Ensure sure that `manual` properly is set on construction of `Two.Polygon` that it binds `Two.Anchor.controls` change events\n- Added automatic High DPI support for `webgl` renderer\n- Updated `two.interpret(svg)` to handle compound paths\n- Added [`Two.Anchor`](http://jonobr1.github.io/two.js/#two-anchor) which represents all anchor points drawn in two.js\n- Modified source to not have any instances of `window` for node use\n- Updated to underscore.js 1.5.1\n- Added `Two.Utils.getReflection` method to properly get reflection's in svg interpretation\n- Made `Two.Vector` inherently not broadcast events and now needs to be explicitly bound to in order to broadcast events, which two.js does internally for you\n- Created `Two.Utils.Collection` an observable array-like class that `polygon.vertices` inherit [@fchasen](http://github.com/fchasen)\n- Added `Two.Events.insert` and `Two.Events.remove` for use with `Two.Utils.Collection`\n- Properly recurses `getBoundingClientRect` for both `Two.Group` and `Two.Polygon`\n- Added `Two.Version` to clarify forthcoming builds\n- Updated hierarchy ordering of `group.children` in `canvas` and `webgl` renderers\n- Updated shallow and bidirectional `remove` method for `Two.Group` and `Two.Polygon`\n- Added `corner` method to `Two.Group` and `Two.Polygon` allowing anchoring along the upper lefthand corner of the form\n- Modified `center` method of `Two.Group` and `Two.Polygon` to not affect the `translation` property to stay inline with `corner` method and any future orientation and anchoring logic\n- Added automatic High DPI support for `canvas` renderer\n- Added `overdraw` boolean to `canvas` renderer\n- Added AMD loader compatibility [@thomasrudin](http://github.com/thomasrudin)\n- Deferred `two.update();` to account for canvas and webgl\n- Added `remove` and `clear` methods to `two` instance\n- Updated svg interpretation for `webgl` context\n- ~~Added matrix property to all `Two.Shape`'s for advanced transformations~~\n- Added `inverse` method to `Two.Matrix`\n- Remove execution path dependency on utils/build.js [@masonblier](https://github.com/masonblier)\n- Added `timeDelta` property to every `two` instance\n- Added gruntfile, package.json for more integration with `npm`, and dependency free build (`build/two.clean.js`) [@iros](https://github.com/iros)\n- Crossbrowser compatibility with `noStroke` and `noFill` commands\n\n## May 3, 2013 v0.2.0\n\n<h3 class=\"visible\">May 3, 2013</h3><version-link v=\"v0.2.0\" />\n\n- First alpha release\n\n## Jan 29, 2013 v0.1.0-alpha\n\n<h3 class=\"visible\">Jan 29, 2013</h3><version-link v=\"v0.1.0-alpha\" />\n\n- Proof of Concept built from Three.js\n"
  },
  {
    "path": "wiki/docs/README.md",
    "content": "<redirect-page src=\"/docs/two/\"></redirect-page>\n"
  },
  {
    "path": "wiki/docs/anchor/README.md",
    "content": "---\ntitle: Two.Anchor\npageClass: docs\nlang: en-US\n---\n\n# Two.Anchor\n\n\n<div class=\"extends\">\n\nExtends: [Two.Vector](/docs/vector/)\n\n</div>\n\n\nAn object that holds 3 [Two.Vector](/docs/vector/)s, the anchor point and its corresponding handles: `left` and `right`. In order to properly describe the bezier curve about the point there is also a command property to describe what type of drawing should occur when Two.js renders the anchors.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/anchor.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the root anchor point. |\n|  y  | The y position of the root anchor point. |\n|  ax  | The x position of the left handle point. |\n|  ay  | The y position of the left handle point. |\n|  bx  | The x position of the right handle point. |\n|  by  | The y position of the right handle point. |\n|  command  | The command to describe how to render. Applicable commands are [Two.Commands](/docs/two/#commands) |\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Anchor.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Anchor\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Anchor](/docs/anchor/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Anchor](/docs/anchor/) from an object notation of a [Two.Anchor](/docs/anchor/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/anchor.js#L70\">\n    anchor.js:70\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Anchor.toObject](/docs/anchor/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Anchor.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The anchor to apply values to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Anchor](/docs/anchor/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/anchor.js#L82\">\n    anchor.js:82\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Anchor.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Anchor\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Anchor](/docs/anchor/), set all its values to the current instance and return it for use.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/anchor.js#L128\">\n    anchor.js:128\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Anchor.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- An object with properties filled out to mirror [Two.Anchor](/docs/anchor/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCreate a JSON compatible plain object of the current instance. Intended for use with storing values in a database.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/anchor.js#L138\">\n    anchor.js:138\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Anchor.fromObject](/docs/anchor/#fromobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toString\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toString\"><span class=\"prefix\">Two.Anchor.</span><span class=\"shortname\">toString</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: String\n\n\n- A String with comma-separated values reflecting the various values on the current instance.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCreate a string form of the current instance. Intended for use with storing values in a database. This is lighter to store than the JSON compatible [Two.Anchor.toObject](/docs/anchor/#toobject).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/anchor.js#L163\">\n    anchor.js:163\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/children/README.md",
    "content": "---\ntitle: Two.Group.Children\npageClass: docs\nlang: en-US\n---\n\n# Two.Group.Children\n\n\n<div class=\"extends\">\n\nExtends: [Two.Collection](/docs/collection/)\n\n</div>\n\n\nA children collection which is accesible both by index and by object `id`.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/children.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n\n\n\n\n<div class=\"instance member \">\n\n## Children.ids\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Children.ids\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">Children.ids</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nMap of all elements in the list keyed by `id`s.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/children.js#L11\">\n    children.js:11\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## Children.attach\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Children.attach\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">Children.attach</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  children  | The objects which extend [Two.Shape](/docs/shape/) to be added. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdds elements to the `ids` map.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/children.js#L34\">\n    children.js:34\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## Children.detach\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Children.detach\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">Children.detach</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  children  | The objects which extend [Two.Shape](/docs/shape/) to be removed. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nRemoves elements to the `ids` map.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/children.js#L50\">\n    children.js:50\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/collection/README.md",
    "content": "---\ntitle: Two.Collection\npageClass: docs\nlang: en-US\n---\n\n# Two.Collection\n\n\n<div class=\"extends\">\n\nExtends: [Two.Events](/docs/events/)\n\n</div>\n\n\nAn `Array` like object with additional event propagation on actions. `pop`, `shift`, and `splice` trigger `removed` events. `push`, `unshift`, and `splice` with more than 2 arguments trigger 'inserted'. Finally, `sort` and `reverse` trigger `order` events.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/collection.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n\n\n\n"
  },
  {
    "path": "wiki/docs/effects/gradient/README.md",
    "content": "---\ntitle: Two.Gradient\npageClass: docs\nlang: en-US\n---\n\n# Two.Gradient\n\n\n<div class=\"extends\">\n\nExtends: [Two.Element](/docs/element/)\n\n</div>\n\n\nThis is the base class for constructing different types of gradients with Two.js. The two common gradients are [Two.LinearGradient](/docs/effects/linear-gradient/) and [Two.RadialGradient](/docs/effects/radial-gradient/).\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  stops  | A list of [Two.Stop](/docs/effects/stop/)s that contain the gradient fill pattern for the gradient. |\n\n\n\n<div class=\"static member \">\n\n## Stop\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Stop\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">Stop</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [Two.Stop](/docs/effects/stop/)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L57\">\n    gradient.js:57\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Gradient](/docs/effects/gradient/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L63\">\n    gradient.js:63\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Gradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Gradient](/docs/effects/gradient/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Gradient](/docs/effects/gradient/) from an object notation of a [Two.Gradient](/docs/effects/gradient/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L69\">\n    gradient.js:69\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Gradient.toObject](/docs/effects/gradient/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## spread\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#spread\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">spread</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nIndicates what happens if the gradient starts or ends inside the bounds of the target rectangle. Possible values are `'pad'`, `'reflect'`, and `'repeat'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute](https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute) for more information\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L34\">\n    gradient.js:34\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## units\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#units\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">units</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nIndicates how coordinate values are interpreted by the renderer. Possible values are `'userSpaceOnUse'` and `'objectBoundingBox'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/pservers.html#RadialGradientElementGradientUnitsAttribute](https://www.w3.org/TR/SVG11/pservers.html#RadialGradientElementGradientUnitsAttribute) for more information\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L41\">\n    gradient.js:41\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## stops\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#stops\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">stops</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAn ordered list of [Two.Stop](/docs/effects/stop/)s for rendering the gradient.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L48\">\n    gradient.js:48\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Gradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Gradient](/docs/effects/gradient/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L91\">\n    gradient.js:91\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  gradient  | The reference [Two.Gradient](/docs/effects/gradient/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Gradient](/docs/effects/gradient/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L120\">\n    gradient.js:120\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L139\">\n    gradient.js:139\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Gradient.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nDetach instance from renderer including any `<defs />` or textures stored in memory.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/gradient.js#L193\">\n    gradient.js:193\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/effects/image/README.md",
    "content": "---\ntitle: Two.Image\npageClass: docs\nlang: en-US\n---\n\n# Two.Image\n\n\n<div class=\"extends\">\n\nExtends: [Two.Rectangle](/docs/shapes/rectangle/)\n\n</div>\n\n\nA convenient package to display images scaled to fit specific dimensions. Unlike [Two.Sprite](/docs/effects/sprite/), this class scales the image to the provided width and height rather than using the image's native dimensions. By default, images are scaled to 'fill' within the bounds while preserving aspect ratio.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | The URL path or [Two.Texture](/docs/effects/texture/) to be used as the bitmap data displayed on the image. |\n|  ox  | The initial `x` position of the Two.Image. |\n|  oy  | The initial `y` position of the Two.Image. |\n|  width  | The width to display the image at. |\n|  height  | The height to display the image at. |\n|  mode  | The fill mode |\n\n\n\n<div class=\"static member \">\n\n## Modes\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Modes\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">Modes</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDifferent mode types to render an image inspired by Figma.\n\n\n\n__Modes.fill__: Scale image to fill the bounds while preserving aspect ratio.\n\n\n\n__Modes.fit__: Scale image to fit within bounds while preserving aspect ratio.\n\n\n\n__Modes.crop__: Scale image to fill bounds while preserving aspect ratio, cropping excess.\n\n\n\n__Modes.tile__: Repeat image at original size to fill the bounds.\n\n\n\n__Modes.stretch__: Stretch image to fill dimensions, ignoring aspect ratio.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L76\">\n    image.js:76\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Image](/docs/effects/image/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L93\">\n    image.js:93\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Image\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Image](/docs/effects/image/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Image](/docs/effects/image/) from an object notation of a [Two.Image](/docs/effects/image/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L99\">\n    image.js:99\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Image.toObject](/docs/effects/image/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## texture\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#texture\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">texture</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe texture to be used as bitmap data to display image in the scene.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L59\">\n    image.js:59\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  image  | The reference [Two.Image](/docs/effects/image/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Image](/docs/effects/image/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L117\">\n    image.js:117\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Image\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Image](/docs/effects/image/) with the same properties of the current image.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L136\">\n    image.js:136\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the image.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L159\">\n    image.js:159\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Image.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Image\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the image's renderer resources and detach all events.\nThis method disposes the texture (calling dispose() for thorough cleanup) and inherits comprehensive\ncleanup from the Rectangle/Path hierarchy while preserving the renderer type\nfor potential re-attachment.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image.js#L173\">\n    image.js:173\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/effects/image-sequence/README.md",
    "content": "---\ntitle: Two.ImageSequence\npageClass: docs\nlang: en-US\n---\n\n# Two.ImageSequence\n\n\n<div class=\"extends\">\n\nExtends: [Two.Rectangle](/docs/shapes/rectangle/)\n\n</div>\n\n\nA convenient package to display still or animated images organized as a series of still images.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | A list of URLs or [Two.Texture](/docs/effects/texture/)s. |\n|  ox  | The initial `x` position of the Two.ImageSequence. |\n|  oy  | The initial `y` position of the Two.ImageSequence. |\n|  frameRate  | The frame rate at which the images should playback at. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.ImageSequence](/docs/effects/image-sequence/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L171\">\n    image-sequence.js:171\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## DefaultFrameRate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#DefaultFrameRate\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">DefaultFrameRate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\ndefault frame rate that [Two.ImageSequence.frameRate](/docs/effects/image-sequence/#framerate) is set to when instantiated.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L184\">\n    image-sequence.js:184\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ImageSequence\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.ImageSequence](/docs/effects/image-sequence/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.ImageSequence](/docs/effects/image-sequence/) from an object notation of a [Two.ImageSequence](/docs/effects/image-sequence/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L190\">\n    image-sequence.js:190\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.ImageSequence.toObject](/docs/effects/image-sequence/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## textures\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#textures\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">textures</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of textures to be used as frames for animating the [Two.ImageSequence](/docs/effects/image-sequence/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L139\">\n    image-sequence.js:139\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## frameRate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#frameRate\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">frameRate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe number of frames to animate against per second.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L154\">\n    image-sequence.js:154\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## index\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#index\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">index</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe index of the current tile of the sprite to display. Defaults to `0`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L164\">\n    image-sequence.js:164\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  imageSequence  | The reference [Two.ImageSequence](/docs/effects/image-sequence/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.ImageSequence](/docs/effects/image-sequence/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L208\">\n    image-sequence.js:208\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## play\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#play\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">play</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  firstFrame  | The index of the frame to start the animation with. |\n|  lastFrame  | The index of the frame to end the animation with. Defaults to the last item in the [Two.ImageSequence.textures](/docs/effects/image-sequence/#textures). |\n|  onLastFrame  | Optional callback function to be triggered after playing the last frame. This fires multiple times when the image sequence is looped. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nInitiate animation playback of a [Two.ImageSequence](/docs/effects/image-sequence/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L227\">\n    image-sequence.js:227\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## pause\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#pause\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">pause</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nHalt animation playback of a [Two.ImageSequence](/docs/effects/image-sequence/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L261\">\n    image-sequence.js:261\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## stop\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#stop\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">stop</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nHalt animation playback of a [Two.ImageSequence](/docs/effects/image-sequence/) and set the current frame back to the first frame.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L271\">\n    image-sequence.js:271\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ImageSequence\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.ImageSequence](/docs/effects/image-sequence/) with the same properties of the current image sequence.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L283\">\n    image-sequence.js:283\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L311\">\n    image-sequence.js:311\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.ImageSequence.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ImageSequence\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the image sequence's renderer resources and detach all events.\nThis method stops any running animation, clears animation callbacks, unbinds\ntextures collection events, and disposes individual textures (calling dispose()\nfor thorough cleanup) while preserving the renderer type for potential\nre-attachment to a new renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/image-sequence.js#L331\">\n    image-sequence.js:331\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/effects/linear-gradient/README.md",
    "content": "---\ntitle: Two.LinearGradient\npageClass: docs\nlang: en-US\n---\n\n# Two.LinearGradient\n\n\n<div class=\"extends\">\n\nExtends: [Two.Gradient](/docs/effects/gradient/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x1  | The x position of the first end point of the linear gradient. |\n|  y1  | The y position of the first end point of the linear gradient. |\n|  x2  | The x position of the second end point of the linear gradient. |\n|  y2  | The y position of the second end point of the linear gradient. |\n|  stops  | A list of [Two.Stop](/docs/effects/stop/)s that contain the gradient fill pattern for the gradient. |\n\n\n\n<div class=\"static member \">\n\n## Stop\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Stop\"><span class=\"prefix\">Two.LinearGradient.</span><span class=\"shortname\">Stop</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [Two.Stop](/docs/effects/stop/)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js#L64\">\n    linear-gradient.js:64\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.LinearGradient.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.LinearGradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.LinearGradient](/docs/effects/linear-gradient/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.LinearGradient](/docs/effects/linear-gradient/) from an object notation of a [Two.LinearGradient](/docs/effects/linear-gradient/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js#L72\">\n    linear-gradient.js:72\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.LinearGradient.toObject](/docs/effects/linear-gradient/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## left\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#left\"><span class=\"prefix\">Two.LinearGradient.</span><span class=\"shortname\">left</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe x and y value for where the first end point is placed on the canvas.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js#L39\">\n    linear-gradient.js:39\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## right\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#right\"><span class=\"prefix\">Two.LinearGradient.</span><span class=\"shortname\">right</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe x and y value for where the second end point is placed on the canvas.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js#L44\">\n    linear-gradient.js:44\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.LinearGradient.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  gradient  | The reference [Two.LinearGradient](/docs/effects/linear-gradient/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.LinearGradient](/docs/effects/linear-gradient/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js#L90\">\n    linear-gradient.js:90\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.LinearGradient.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Gradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.LinearGradient](/docs/effects/linear-gradient/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js#L112\">\n    linear-gradient.js:112\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.LinearGradient.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/linear-gradient.js#L147\">\n    linear-gradient.js:147\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/effects/radial-gradient/README.md",
    "content": "---\ntitle: Two.RadialGradient\npageClass: docs\nlang: en-US\n---\n\n# Two.RadialGradient\n\n\n<div class=\"extends\">\n\nExtends: [Two.Gradient](/docs/effects/gradient/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the origin of the radial gradient. |\n|  y  | The y position of the origin of the radial gradient. |\n|  radius  | The radius of the radial gradient. |\n|  stops  | A list of [Two.Stop](/docs/effects/stop/)s that contain the gradient fill pattern for the gradient. |\n|  focalX  | The x position of the focal point on the radial gradient. |\n|  focalY  | The y position of the focal point on the radial gradient. |\n\n\n\n<div class=\"static member \">\n\n## Stop\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Stop\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">Stop</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [Two.Stop](/docs/effects/stop/)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L87\">\n    radial-gradient.js:87\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.RadialGradient](/docs/effects/radial-gradient/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L93\">\n    radial-gradient.js:93\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.RadialGradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.RadialGradient](/docs/effects/radial-gradient/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.RadialGradient](/docs/effects/radial-gradient/) from an object notation of a [Two.RadialGradient](/docs/effects/radial-gradient/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L99\">\n    radial-gradient.js:99\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.RadialGradient.toObject](/docs/effects/radial-gradient/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## center\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#center\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">center</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe x and y value for where the origin of the radial gradient is.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L55\">\n    radial-gradient.js:55\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## focal\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#focal\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">focal</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe x and y value for where the focal point of the radial gradient is.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L63\">\n    radial-gradient.js:63\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis effects the spray or spread of the radial gradient.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  gradient  | The reference [Two.RadialGradient](/docs/effects/radial-gradient/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.RadialGradient](/docs/effects/radial-gradient/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L117\">\n    radial-gradient.js:117\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.RadialGradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.RadialGradient](/docs/effects/radial-gradient/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L143\">\n    radial-gradient.js:143\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.RadialGradient.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/radial-gradient.js#L179\">\n    radial-gradient.js:179\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/effects/sprite/README.md",
    "content": "---\ntitle: Two.Sprite\npageClass: docs\nlang: en-US\n---\n\n# Two.Sprite\n\n\n<div class=\"extends\">\n\nExtends: [Two.Rectangle](/docs/shapes/rectangle/)\n\n</div>\n\n\nA convenient package to display still or animated images through a tiled image source. For more information on the principals of animated imagery through tiling see [Texture Atlas](https://en.wikipedia.org/wiki/Texture_atlas) on Wikipedia.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | The URL path or [Two.Texture](/docs/effects/texture/) to be used as the bitmap data displayed on the sprite. |\n|  ox  | The initial `x` position of the Two.Sprite. |\n|  oy  | The initial `y` position of the Two.Sprite. |\n|  cols  | The number of columns the sprite contains. |\n|  rows  | The number of rows the sprite contains. |\n|  frameRate  | The frame rate at which the partitions of the image should playback at. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Sprite](/docs/effects/sprite/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L208\">\n    sprite.js:208\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Sprite\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Sprite](/docs/effects/sprite/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Sprite](/docs/effects/sprite/) from an object notation of a [Two.Sprite](/docs/effects/sprite/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L223\">\n    sprite.js:223\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Sprite.toObject](/docs/effects/sprite/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## texture\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#texture\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">texture</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe texture to be used as bitmap data to display image in the scene.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L163\">\n    sprite.js:163\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## columns\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#columns\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">columns</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe number of columns to split the texture into. Defaults to `1`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L177\">\n    sprite.js:177\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## rows\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#rows\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">rows</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe number of rows to split the texture into. Defaults to `1`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L185\">\n    sprite.js:185\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## frameRate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#frameRate\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">frameRate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe number of frames to animate against per second. Defaults to `0` for non-animated sprites.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L193\">\n    sprite.js:193\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## index\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#index\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">index</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe index of the current tile of the sprite to display. Defaults to `0`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L201\">\n    sprite.js:201\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  sprite  | The reference [Two.Sprite](/docs/effects/sprite/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Sprite](/docs/effects/sprite/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L241\">\n    sprite.js:241\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## play\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#play\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">play</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  firstFrame  | The index of the frame to start the animation with. |\n|  lastFrame  | The index of the frame to end the animation with. Defaults to the last item in the [Two.Sprite.textures](/docs/effects/sprite/#textures). |\n|  onLastFrame  | Optional callback function to be triggered after playing the last frame. This fires multiple times when the sprite is looped. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nInitiate animation playback of a [Two.Sprite](/docs/effects/sprite/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L260\">\n    sprite.js:260\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## pause\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#pause\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">pause</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nHalt animation playback of a [Two.Sprite](/docs/effects/sprite/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L294\">\n    sprite.js:294\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## stop\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#stop\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">stop</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nHalt animation playback of a [Two.Sprite](/docs/effects/sprite/) and set the current frame back to the first frame.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L304\">\n    sprite.js:304\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Sprite\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Sprite](/docs/effects/sprite/) with the same properties of the current sprite.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L316\">\n    sprite.js:316\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L348\">\n    sprite.js:348\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Sprite.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Sprite\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the sprite's renderer resources and detach all events.\nThis method stops any running animation, clears animation callbacks, disposes\nthe texture (calling dispose() for thorough cleanup), and inherits comprehensive\ncleanup from the Rectangle/Path hierarchy while preserving the renderer type\nfor potential re-attachment.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/sprite.js#L368\">\n    sprite.js:368\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/effects/stop/README.md",
    "content": "---\ntitle: Two.Stop\npageClass: docs\nlang: en-US\n---\n\n# Two.Stop\n\n\n<div class=\"extends\">\n\nExtends: [Two.Element](/docs/element/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  offset  | The offset percentage of the stop represented as a zero-to-one value. Default value flip flops from zero-to-one as new stops are created. |\n|  color  | The color of the stop. Default value flip flops from white to black as new stops are created. |\n|  opacity  | The opacity value. Default value is 1, cannot be lower than 0. |\n\n\n\n<div class=\"static member \">\n\n## Index\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Index\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">Index</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe current index being referenced for calculating a stop's default offset value.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L88\">\n    stop.js:88\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Stop](/docs/effects/stop/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L94\">\n    stop.js:94\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Stop\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Stop](/docs/effects/stop/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Stop](/docs/effects/stop/) from an object notation of a [Two.Stop](/docs/effects/stop/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L100\">\n    stop.js:100\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Stop.toObject](/docs/effects/stop/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## offset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#offset\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">offset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe offset percentage of the stop represented as a zero-to-one value.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L65\">\n    stop.js:65\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## opacity\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#opacity\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">opacity</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe alpha percentage of the stop represented as a zero-to-one value.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L71\">\n    stop.js:71\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis is only supported on the [Two.SVGRenderer](/docs/renderers/svg/). You can get the same effect by encoding opacity into `rgba` strings in the color.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## color\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#color\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">color</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe color of the stop.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L78\">\n    stop.js:78\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  stop  | The reference [Two.Stop](/docs/effects/stop/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Stop](/docs/effects/stop/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L118\">\n    stop.js:118\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Stop\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent gradient to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Stop](/docs/effects/stop/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L137\">\n    stop.js:137\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Stop.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/stop.js#L162\">\n    stop.js:162\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/effects/texture/README.md",
    "content": "---\ntitle: Two.Texture\npageClass: docs\nlang: en-US\n---\n\n# Two.Texture\n\n\n<div class=\"extends\">\n\nExtends: [Two.Element](/docs/element/)\n\n</div>\n\n\nFundamental to work with bitmap data, a.k.a. pregenerated imagery, in Two.js. Supported formats include jpg, png, gif, and tiff. See [Two.Texture.RegularExpressions](/docs/effects/texture/#regularexpressions) for a full list of supported formats.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | The URL path to an image file or an `<img />` element. |\n|  callback  | An optional callback function once the image has been loaded. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Texture](/docs/effects/texture/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L188\">\n    texture.js:188\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Texture\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Texture](/docs/effects/texture/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Texture](/docs/effects/texture/) from an object notation of a [Two.Texture](/docs/effects/texture/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L194\">\n    texture.js:194\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Texture.toObject](/docs/effects/texture/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## RegularExpressions\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#RegularExpressions\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">RegularExpressions</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA map of compatible DOM Elements categorized by media format.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L212\">\n    texture.js:212\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## ImageRegistry\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#ImageRegistry\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">ImageRegistry</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA canonical listing of image data used in a single session of Two.js.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L218\">\n    texture.js:218\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis object is used to cache image data between different textures.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## getAbsoluteURL\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getAbsoluteURL\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">getAbsoluteURL</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: String\n\n\n- The serialized absolute path.\n\n\n</div>\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nSerializes a URL as an absolute path for canonical attribution in [Two.Texture.ImageRegistry](/docs/effects/texture/#imageregistry).\n\n\n</div>\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  path  |  |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L225\">\n    texture.js:225\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## loadHeadlessBuffer\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#loadHeadlessBuffer\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">loadHeadlessBuffer</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nLoads an image as a buffer in headless environments.\n\n\n</div>\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  texture  | The [Two.Texture](/docs/effects/texture/) to be loaded. |\n|  onLoad  | The callback function to be triggered once the image is loaded. |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L240\">\n    texture.js:240\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\n- This function uses node's `fs.readFileSync` to spoof the `<img />` loading process in the browser.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## getTag\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getTag\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">getTag</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: String\n\n\n- Returns the tag name of an image, video, or canvas node.\n\n\n</div>\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nRetrieves the tag name of an image, video, or canvas node.\n\n\n</div>\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  image  | The image to infer the tag name from. |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L252\">\n    texture.js:252\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## getImage\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getImage\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">getImage</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: HTMLImageElement\n\n\n- Returns either a cached version of the image or a new one that is registered in [Two.Texture.ImageRegistry](/docs/effects/texture/#imageregistry).\n\n\n</div>\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nConvenience function to set [Two.Texture.image](/docs/effects/texture/#image) properties with canonical versions set in [Two.Texture.ImageRegistry](/docs/effects/texture/#imageregistry).\n\n\n</div>\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | The URL path of the image. |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L266\">\n    texture.js:266\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static interface \">\n\n## Register\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Register\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">Register</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nA collection of functions to register different types of textures. Used internally by a [Two.Texture](/docs/effects/texture/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L301\">\n    texture.js:301\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## load\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#load\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">load</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  texture  | The texture to load. |\n|  callback  | The function to be called once the texture is loaded. |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L418\">\n    texture.js:418\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## FlagOffset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#FlagOffset\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">FlagOffset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCached method to let renderers know `offset` has been updated on a [Two.Texture](/docs/effects/texture/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L670\">\n    texture.js:670\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## FlagScale\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#FlagScale\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">FlagScale</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCached method to let renderers know `scale` has been updated on a [Two.Texture](/docs/effects/texture/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L679\">\n    texture.js:679\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## loaded\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#loaded\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">loaded</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nShorthand value to determine if image has been loaded into the texture.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L132\">\n    texture.js:132\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## repeat\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#repeat\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">repeat</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nCSS style declaration to tile [Two.Path](/docs/path/). Valid values include: `'no-repeat'`, `'repeat'`, `'repeat-x'`, `'repeat-y'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/2dcontext/#dom-context-2d-createpattern](https://www.w3.org/TR/2dcontext/#dom-context-2d-createpattern)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L138\">\n    texture.js:138\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## offset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#offset\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">offset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA two-component vector describing any pixel offset of the texture when applied to a [Two.Path](/docs/path/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L145\">\n    texture.js:145\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## src\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#src\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">src</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe URL path to the image data.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L161\">\n    texture.js:161\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis property is ultimately serialized in a [Two.Registry](/docs/registry/) to cache retrieval.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## image\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#image\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">image</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe corresponding DOM Element of the texture. Can be a `<img />`, `<canvas />`, or `<video />` element. See [Two.Texture.RegularExpressions](/docs/effects/texture/#regularexpressions) for a full list of supported elements.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L176\">\n    texture.js:176\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nIn headless environments this is a `Canvas.Image` object. See [https://github.com/Automattic/node-canvas](https://github.com/Automattic/node-canvas) for more information on headless image objects.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Texture\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Texture](/docs/effects/texture/) with the same properties of the current texture.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L449\">\n    texture.js:449\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  texture  | The reference [Two.Texture](/docs/effects/texture/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Texture](/docs/effects/texture/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L463\">\n    texture.js:463\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the texture.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L483\">\n    texture.js:483\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Texture.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nDetach instance from renderer including any `<defs />` or textures stored in memory.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/effects/texture.js#L554\">\n    texture.js:554\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/element/README.md",
    "content": "---\ntitle: Two.Element\npageClass: docs\nlang: en-US\n---\n\n# Two.Element\n\n\n<div class=\"extends\">\n\nExtends: [Two.Events](/docs/events/)\n\n</div>\n\n\nThe foundational object for the Two.js scenegraph.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Element.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Element\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Element](/docs/element/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Element](/docs/element/) from an object notation of a [Two.Element](/docs/element/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js#L63\">\n    element.js:63\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Element.toObject](/docs/element/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## renderer\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#renderer\"><span class=\"prefix\">Two.Element.</span><span class=\"shortname\">renderer</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nObject access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js#L25\">\n    element.js:25\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWith the [Two.SVGRenderer](/docs/renderers/svg/) you can access the underlying SVG element created via `shape.renderer.elem`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## id\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#id\"><span class=\"prefix\">Two.Element.</span><span class=\"shortname\">id</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nSession specific unique identifier.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js#L32\">\n    element.js:32\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nIn the [Two.SVGRenderer](/docs/renderers/svg/) change this to change the underlying SVG element's id too.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## className\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#className\"><span class=\"prefix\">Two.Element.</span><span class=\"shortname\">className</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA class to be applied to the element to be compatible with CSS styling.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js#L39\">\n    element.js:39\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nOnly rendered to DOM in the SVG renderer.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## classList\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#classList\"><span class=\"prefix\">Two.Element.</span><span class=\"shortname\">classList</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nA list of class strings stored if imported / interpreted  from an SVG element.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js#L46\">\n    element.js:46\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## flagReset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#flagReset\"><span class=\"prefix\">Two.Element.</span><span class=\"shortname\">flagReset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCalled internally by Two.js's renderer to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js#L79\">\n    element.js:79\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Element.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the element's renderer object and detach any events.\nThis cleans up renderer-specific resources and unbinds all event listeners.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/element.js#L109\">\n    element.js:109\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/events/README.md",
    "content": "---\ntitle: Two.Events\npageClass: docs\nlang: en-US\n---\n\n# Two.Events\n\n\n\nObject inherited by many Two.js objects in order to facilitate custom events.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n\n\n\n\n<div class=\"static member \">\n\n## Types\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Types\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">Types</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nObject of different types of Two.js specific events.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L167\">\n    events.js:167\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## addEventListener\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#addEventListener\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">addEventListener</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  name  | The name of the event to bind a function to. |\n|  handler  | The function to be invoked when the event is dispatched. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCall to add a listener to a specific event name.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L13\">\n    events.js:13\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## on\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#on\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">on</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Events.addEventListener](/docs/events/#addeventlistener).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L30\">\n    events.js:30\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## bind\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#bind\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">bind</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Events.addEventListener](/docs/events/#addeventlistener).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L38\">\n    events.js:38\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## removeEventListener\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#removeEventListener\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">removeEventListener</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  name  | The name of the event intended to be removed. |\n|  handler  | The handler intended to be removed. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCall to remove listeners from a specific event. If only `name` is passed then all the handlers attached to that `name` will be removed. If no arguments are passed then all handlers for every event on the obejct are removed.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L47\">\n    events.js:47\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## off\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#off\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">off</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Events.removeEventListener](/docs/events/#removeeventlistener).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L90\">\n    events.js:90\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## unbind\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#unbind\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">unbind</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Events.removeEventListener](/docs/events/#removeeventlistener).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L98\">\n    events.js:98\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispatchEvent\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispatchEvent\"><span class=\"prefix\">Two.Events.</span><span class=\"shortname\">dispatchEvent</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  name  | The name of the event to dispatch. |\n|  args  | Anything can be passed after the name and those will be passed on to handlers attached to the event in the order they are passed. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCall to trigger a custom event. Any additional arguments passed after the name will be passed along to the attached handlers.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/events.js#L107\">\n    events.js:107\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/extras/arc/README.md",
    "content": "---\ntitle: Two.Arc\npageClass: docs\nlang: en-US\n---\n\n# Two.Arc\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/arc.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the arc. |\n|  y  | The y position of the arc. |\n|  width  | The width, horizontal diameter, of the arc. |\n|  height  | The height, vertical diameter, of the arc. |\n|  startAngle  | The starting angle of the arc in radians. |\n|  endAngle  | The ending angle of the arc in radians. |\n|  resolution  | The number of vertices used to construct the circle. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Arc.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Arc](/docs/extras/arc/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/arc.js#L94\">\n    arc.js:94\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## width\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#width\"><span class=\"prefix\">Two.Arc.</span><span class=\"shortname\">width</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe horizontal size of the arc.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/arc.js#L52\">\n    arc.js:52\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## height\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#height\"><span class=\"prefix\">Two.Arc.</span><span class=\"shortname\">height</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe vertical size of the arc.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/arc.js#L60\">\n    arc.js:60\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## startAngle\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#startAngle\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">startAngle</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe angle of one side for the arc segment.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/arc.js#L68\">\n    arc.js:68\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## endAngle\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#endAngle\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">endAngle</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe angle of the other side for the arc segment.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/arc.js#L76\">\n    arc.js:76\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Arc.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ArcSegment\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.ArcSegment](/docs/shapes/arc-segment/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/arc.js#L154\">\n    arc.js:154\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/extras/zui/README.md",
    "content": "---\ntitle: Two.ZUI\npageClass: docs\nlang: en-US\n---\n\n# Two.ZUI\n\n\n\n[Two.ZUI](/docs/two/#zui) is an extra class to turn your Two.js scene into a Google Maps or Adobe Illustrator style interface. See [https://codepen.io/jonobr1/pen/PobMKwb](https://codepen.io/jonobr1/pen/PobMKwb) for example usage.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  group  | The scene or group to |\n|  domElement  | The HTML Element to attach event listeners to. |\n\n\n\n<div class=\"instance function \">\n\n## addLimits\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#addLimits\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">addLimits</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  min  | The minimum scale the ZUI can zoom out to. |\n|  max  | The maximum scale the ZUI can zoom in to. |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L101\">\n    zui.js:101\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## clientToSurface\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clientToSurface\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">clientToSurface</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- An object with x, y, and z components\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nConvert an x, y coordinate in the user’s space to the object's projected space. Optionally pass a z property on the object to apply depth.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L129\">\n    zui.js:129\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member overloaded\">\n\n## clientToSurface\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clientToSurface\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">clientToSurface</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- An object with x, y, and z components\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  | The x component of position to be transformed. |\n|  b  | The y component of position to be transformed. |\n|  c  | The optional z component of position to be transformed. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nConvert an x, y coordinate in the user’s space to the object's projected space. Optionally pass a z property on the object to apply depth.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L138\">\n    zui.js:138\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## surfaceToClient\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#surfaceToClient\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">surfaceToClient</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- An object with x, y, and z components\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nConvert an x, y coordinate in projected space to the user’s space. Optionally pass a z property on the object to apply depth.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L166\">\n    zui.js:166\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member overloaded\">\n\n## surfaceToClient\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#surfaceToClient\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">surfaceToClient</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- An object with x, y, and z components\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  | The x component of position to be transformed. |\n|  b  | The y component of position to be transformed. |\n|  c  | The optional z component of position to be transformed. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nConvert an x, y coordinate in projected space to the user’s space. Optionally pass a z property on the object to apply depth.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L175\">\n    zui.js:175\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## zoomBy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#zoomBy\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">zoomBy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  byF  | The factor to scale by. |\n|  clientX  | The x position of the user's input. |\n|  clientY  | The y position of the user's input. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nA function to zoom by an incremental amount and a position. Typically used for pinch-and-zoom or mousewheel effects.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L203\">\n    zui.js:203\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## zoomSet\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#zoomSet\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">zoomSet</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  zoom  | The level of the zoom. |\n|  clientX  | The x position of the user's input. |\n|  clientY  | The y position of the user's input. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nA function to set the zoom amount and the origin position. This is used internally by {@Two.ZUI#zoomBy}.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L217\">\n    zui.js:217\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## translateSurface\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#translateSurface\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">translateSurface</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x amount to pan. |\n|  y  | The y amount to pan. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSet the position of the ZUI by an incremental translation amount.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L247\">\n    zui.js:247\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## reset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#reset\"><span class=\"prefix\">Two.ZUI.</span><span class=\"shortname\">reset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReset the zoom and scale factors to their original instantiated state.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/extras/jsm/zui.js#L282\">\n    zui.js:282\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/group/README.md",
    "content": "---\ntitle: Two.Group\npageClass: docs\nlang: en-US\n---\n\n# Two.Group\n\n\n<div class=\"extends\">\n\nExtends: [Two.Shape](/docs/shape/)\n\n</div>\n\n\nThis is the primary class for grouping objects that are then drawn in Two.js. In Illustrator this is a group, in After Effects it would be a Null Object. Whichever the case, the `Two.Group` contains a transformation matrix and commands to style its children, but it by itself doesn't render to the screen.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  children  | A list of objects that inherit [Two.Shape](/docs/shape/). For instance, the array could be a [Two.Path](/docs/path/), [Two.Text](/docs/text/), and [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/). |\n\n\n\n<div class=\"static function \">\n\n## InsertChildren\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#InsertChildren\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">InsertChildren</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  children  | The objects to be inserted. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCached method to let renderers know children have been added to a [Two.Group](/docs/group/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L262\">\n    group.js:262\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## RemoveChildren\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#RemoveChildren\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">RemoveChildren</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  children  | The objects to be removed. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCached method to let renderers know children have been removed from a [Two.Group](/docs/group/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L274\">\n    group.js:274\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## OrderChildren\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#OrderChildren\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">OrderChildren</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCached method to let renderers know order has been updated on a [Two.Group](/docs/group/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L286\">\n    group.js:286\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Group](/docs/group/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L295\">\n    group.js:295\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## fill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fill\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">fill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what all child shapes should be filled in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L116\">\n    group.js:116\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## stroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#stroke\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">stroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what all child shapes should be outlined in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L123\">\n    group.js:123\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## linewidth\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#linewidth\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">linewidth</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe thickness in pixels of the stroke for all child shapes.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L130\">\n    group.js:130\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## opacity\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#opacity\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">opacity</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe opaqueness of all child shapes.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L136\">\n    group.js:136\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nBecomes multiplied by the individual child's opacity property.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## visible\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#visible\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">visible</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDisplay the path or not.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L143\">\n    group.js:143\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nFor [Two.CanvasRenderer](/docs/renderers/canvas/) and [Two.WebGLRenderer](/docs/renderers/webgl/) when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## cap\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#cap\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">cap</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty](https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L150\">\n    group.js:150\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## join\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#join\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">join</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty](https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L157\">\n    group.js:157\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## miter\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#miter\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">miter</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty](https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L164\">\n    group.js:164\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## closed\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#closed\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">closed</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDetermines whether a final line is drawn between the final point in the `vertices` array and the first point of all child shapes.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L171\">\n    group.js:171\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## curved\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#curved\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">curved</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nWhen the child's path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L177\">\n    group.js:177\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## automatic\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#automatic\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">automatic</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDetermines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L183\">\n    group.js:183\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## beginning\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#beginning\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">beginning</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nNumber between zero and one to state the beginning of where the path is rendered.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\n[Two.Group.beginning](/docs/group/#beginning) is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L189\">\n    group.js:189\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis is great for animating in and out stroked paths in conjunction with [Two.Group.ending](/docs/group/#ending).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## ending\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#ending\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">ending</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nNumber between zero and one to state the ending of where the path is rendered.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\n[Two.Group.ending](/docs/group/#ending) is a percentage value that represents at what percentage into all child shapes should the renderer start drawing.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L197\">\n    group.js:197\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis is great for animating in and out stroked paths in conjunction with [Two.Group.beginning](/docs/group/#beginning).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## length\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#length\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">length</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe sum of distances between all child lengths.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L205\">\n    group.js:205\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## mask\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#mask\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">mask</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe Two.js object to clip from a group's rendering.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L211\">\n    group.js:211\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## additions\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#additions\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">additions</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nAn automatically updated list of children that need to be appended to the renderer's scenegraph.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L235\">\n    group.js:235\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## subtractions\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subtractions\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">subtractions</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nAn automatically updated list of children that need to be removed from the renderer's scenegraph.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L242\">\n    group.js:242\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## children\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#children\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">children</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nA list of all the children in the scenegraph.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L249\">\n    group.js:249\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nTher order of this list indicates the order each element is rendered to the screen.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Group\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  group  | The reference [Two.Group](/docs/group/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Group](/docs/group/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L487\">\n    group.js:487\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Group\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Group](/docs/group/) with the same properties of the current group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L508\">\n    group.js:508\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L551\">\n    group.js:551\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Group\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the group's renderer resources and detach all events.\nThis method recursively disposes all child objects, unbinds the children\ncollection events, and preserves the renderer type for potential re-attachment\nto a new renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L577\">\n    group.js:577\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getShapesAtPoint\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getShapesAtPoint\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">getShapesAtPoint</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Array.\\<Shape\\>\n\n\nOrdered list of intersecting shapes, front to back.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | X coordinate in world space. |\n|  y  | Y coordinate in world space. |\n|  options  | Hit test configuration. |\n|  options.visibleOnly  | Limit results to visible shapes. |\n|  options.includeGroups  | Include groups in the hit results. |\n|  options.mode  | Whether to return all intersecting shapes or only the top-most. |\n|  options.deepest  | Alias for `mode: 'deepest'`. |\n|  options.precision  | Segmentation precision for curved geometry. |\n|  options.tolerance  | Pixel tolerance applied to hit testing. |\n|  options.fill  | Override fill testing behaviour. |\n|  options.stroke  | Override stroke testing behaviour. |\n|  options.filter  | Predicate to filter shapes from the result set. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nTraverse the group hierarchy and return shapes that contain the specified point.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L612\">\n    group.js:612\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nExpects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## corner\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#corner\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">corner</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nOrient the children of the group to the upper left-hand corner of that group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L695\">\n    group.js:695\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## center\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#center\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">center</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nOrient the children of the group to the center of that group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L717\">\n    group.js:717\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getById\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getById\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">getById</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Shape\n\n\n- Or `null` if nothing is found.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRecursively search for id. Returns the first element found.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L743\">\n    group.js:743\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getByClassName\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getByClassName\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">getByClassName</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Array.\\<Two.Shape\\>\n\n\n- Or empty array if nothing is found.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRecursively search for classes. Returns an array of matching elements.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L770\">\n    group.js:770\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getByType\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getByType\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">getByType</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Array.\\<Two.Shape\\>\n\n\n- Empty array if nothing is found.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRecursively search for children of a specific type, e.g. [Two.Path](/docs/path/). Pass a reference to this type as the param. Returns an array of matching elements.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L793\">\n    group.js:793\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## add\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#add\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">add</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  objects  | An array of objects to be added. Can also be supplied as individual arguments. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdd objects to the group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L816\">\n    group.js:816\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## remove\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#remove\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">remove</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  objects  | An array of objects to be removed. Can be also removed as individual arguments. If no arguments are passed, then it removes itself from its parent. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nRemove objects from the group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L848\">\n    group.js:848\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getBoundingClientRect\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getBoundingClientRect\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">getBoundingClientRect</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- Returns object with top, left, right, bottom, width, height attributes.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  shallow  | Describes whether to calculate off local matrix or world matrix. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nReturn an object with top, left, right, bottom, width, and height parameters of the group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L889\">\n    group.js:889\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noFill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noFill\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">noFill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nApply `noFill` method to all child shapes.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L970\">\n    group.js:970\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noStroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noStroke\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">noStroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nApply `noStroke` method to all child shapes.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L982\">\n    group.js:982\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## subdivide\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subdivide\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">subdivide</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nApply `subdivide` method to all child shapes.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L994\">\n    group.js:994\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## strokeAttenuation\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#strokeAttenuation\"><span class=\"prefix\">Two.Group.</span><span class=\"shortname\">strokeAttenuation</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nWhen set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space for all child shapes.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nWhen `strokeAttenuation` is `false`, this property is applied to all child shapes, making their stroke widths automatically adjust to compensate for the group's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke widths scale normally with transformations.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/group.js#L1300\">\n    group.js:1300\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/matrix/README.md",
    "content": "---\ntitle: Two.Matrix\npageClass: docs\nlang: en-US\n---\n\n# Two.Matrix\n\n\n\nA class to store 3 x 3 transformation matrix information. In addition to storing data `Two.Matrix` has suped up methods for commonplace mathematical operations.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  | The value for element at the first column and first row. |\n|  b  | The value for element at the second column and first row. |\n|  c  | The value for element at the third column and first row. |\n|  d  | The value for element at the first column and second row. |\n|  e  | The value for element at the second column and second row. |\n|  f  | The value for element at the third column and second row. |\n|  g  | The value for element at the first column and third row. |\n|  h  | The value for element at the second column and third row. |\n|  i  | The value for element at the third column and third row. |\n\n\n\n<div class=\"static member \">\n\n## Identity\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Identity\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">Identity</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA stored reference to the default value of a 3 x 3 matrix.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L58\">\n    matrix.js:58\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## Multiply\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Multiply\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">Multiply</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Array.\\<Number\\>\n\n\n- If an optional `C` matrix isn't passed then a new one is created and returned.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  A  | The first [Two.Matrix](/docs/matrix/) to multiply |\n|  B  | The second [Two.Matrix](/docs/matrix/) to multiply |\n|  C  | An optional [Two.Matrix](/docs/matrix/) to apply the result to |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMultiply two matrices together and return the result.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L64\">\n    matrix.js:64\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Matrix\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | The object notation of a Two.Matrix to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Matrix](/docs/matrix/) from an object notation of a [Two.Matrix](/docs/matrix/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L129\">\n    matrix.js:129\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Matrix.toObject](/docs/matrix/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## elements\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#elements\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">elements</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe underlying data stored as an array.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L27\">\n    matrix.js:27\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## manual\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#manual\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">manual</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDetermines whether Two.js automatically calculates the values for the matrix or if the developer intends to manage the matrix.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L33\">\n    matrix.js:33\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\n- Setting to `true` nullifies [Two.Shape.translation](/docs/shape/#translation), [Two.Shape.rotation](/docs/shape/#rotation), and [Two.Shape.scale](/docs/shape/#scale).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## set\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#set\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">set</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  | The value for element at the first column and first row |\n|  b  | The value for element at the second column and first row |\n|  c  | The value for element at the third column and first row |\n|  d  | The value for element at the first column and second row |\n|  e  | The value for element at the second column and second row |\n|  f  | The value for element at the third column and second row |\n|  g  | The value for element at the first column and third row |\n|  h  | The value for element at the second column and third row |\n|  i  | The value for element at the third column and third row |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSet an array of values onto the matrix. Order described in [Two.Matrix](/docs/matrix/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L141\">\n    matrix.js:141\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## set\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#set\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">set</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  | The array of elements to apply |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSet an array of values onto the matrix. Order described in [Two.Matrix](/docs/matrix/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L156\">\n    matrix.js:156\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  m  | The matrix to copy |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the matrix of one to the current instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L189\">\n    matrix.js:189\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## identity\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#identity\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">identity</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nTurn matrix to the identity, like resetting.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L211\">\n    matrix.js:211\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## multiply\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiply\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">multiply</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  s  | The scalar to be multiplied. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMultiply all components of the matrix against a single scalar value.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L230\">\n    matrix.js:230\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## multiply\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiply\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">multiply</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The `x` component to be multiplied. |\n|  y  | The `y` component to be multiplied. |\n|  z  | The `z` component to be multiplied. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMultiply all components of a matrix against a 3 component vector.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L238\">\n    matrix.js:238\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## multiply\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiply\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">multiply</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  a  | The value at the first column and first row of the matrix to be multiplied. |\n|  b  | The value at the second column and first row of the matrix to be multiplied. |\n|  c  | The value at the third column and first row of the matrix to be multiplied. |\n|  d  | The value at the first column and second row of the matrix to be multiplied. |\n|  e  | The value at the second column and second row of the matrix to be multiplied. |\n|  f  | The value at the third column and second row of the matrix to be multiplied. |\n|  g  | The value at the first column and third row of the matrix to be multiplied. |\n|  h  | The value at the second column and third row of the matrix to be multiplied. |\n|  i  | The value at the third column and third row of the matrix to be multiplied. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMultiply all components of a matrix against another matrix.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L248\">\n    matrix.js:248\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## inverse\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#inverse\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">inverse</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  output  | The optional matrix to apply the inversion to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nReturn an inverted version of the matrix. If no optional one is passed a new matrix is created and returned.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L342\">\n    matrix.js:342\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## scale\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#scale\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">scale</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  s  | The one dimensional scale to apply to the matrix. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nUniformly scale the transformation matrix.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L388\">\n    matrix.js:388\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## scale\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#scale\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">scale</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  sx  | The horizontal scale factor. |\n|  sy  | The vertical scale factor |\n</div>\n\n\n\n\n<div class=\"description\">\n\nScale the transformation matrix in two dimensions.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L395\">\n    matrix.js:395\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## rotate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#rotate\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">rotate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  n  | The amount to rotate in Number. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nRotate the matrix.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L411\">\n    matrix.js:411\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## translate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#translate\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">translate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The horizontal translation value to apply |\n|  y  | The vertical translation value to apply |\n</div>\n\n\n\n\n<div class=\"description\">\n\nTranslate the matrix to specific `x` / `y` values.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L424\">\n    matrix.js:424\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## skewX\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#skewX\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">skewX</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  n  | The amount to skew |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSkew the matrix by an angle in the x axis direction.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L435\">\n    matrix.js:435\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## skewY\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#skewY\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">skewY</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  n  | The amount to skew |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSkew the matrix by an angle in the y axis direction.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L447\">\n    matrix.js:447\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toString\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toString\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">toString</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: String\n\n\n- The transformation matrix as a 6 component string separated by spaces.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  fullMatrix  | Return the full 9 elements of the matrix or just 6 for 2D transformations. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a transform string. Used for the Two.js rendering APIs.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L459\">\n    matrix.js:459\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toTransformArray\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toTransformArray\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">toTransformArray</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  fullMatrix  | Return the full 9 elements of the matrix or just 6 in the format for 2D transformations. |\n|  output  | An array empty or otherwise to apply the values to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a transform array. Used for the Two.js rendering APIs.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L473\">\n    matrix.js:473\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toArray\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toArray\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">toArray</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  fullMatrix  | Return the full 9 elements of the matrix or just 6 for 2D transformations. |\n|  output  | An array empty or otherwise to apply the values to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a transform array. Used for the Two.js rendering APIs.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L532\">\n    matrix.js:532\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCreate a JSON compatible object that represents information of the matrix.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L584\">\n    matrix.js:584\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Matrix.fromObject](/docs/matrix/#fromobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Matrix.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nClone the current matrix.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/matrix.js#L598\">\n    matrix.js:598\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/path/README.md",
    "content": "---\ntitle: Two.Path\npageClass: docs\nlang: en-US\n---\n\n# Two.Path\n\n\n<div class=\"extends\">\n\nExtends: [Two.Shape](/docs/shape/)\n\n</div>\n\n\nThis is the primary primitive class for creating all drawable shapes in Two.js. Unless specified methods return their instance of `Two.Path` for the purpose of chaining.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  vertices  | A list of [Two.Anchor](/docs/anchor/)s that represent the order and coordinates to construct the rendered shape. |\n|  closed  | Describes whether the shape is closed or open. |\n|  curved  | Describes whether the shape automatically calculates bezier handles for each vertex. |\n|  manual  | Describes whether the developer controls how vertices are plotted or if Two.js automatically plots coordinates based on closed and curved booleans. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Path](/docs/path/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L423\">\n    path.js:423\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Path\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Path](/docs/path/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Path](/docs/path/) from an object notation of a [Two.Path](/docs/path/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L449\">\n    path.js:449\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Path.toObject](/docs/path/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## closed\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#closed\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">closed</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDetermines whether a final line is drawn between the final point in the `vertices` array and the first point.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L302\">\n    path.js:302\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## curved\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#curved\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">curved</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nWhen the path is `automatic = true` this boolean determines whether the lines between the points are curved or not.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L308\">\n    path.js:308\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## beginning\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#beginning\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">beginning</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nNumber between zero and one to state the beginning of where the path is rendered.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\n[Two.Path.beginning](/docs/path/#beginning) is a percentage value that represents at what percentage into the path should the renderer start drawing.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L314\">\n    path.js:314\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis is great for animating in and out stroked paths in conjunction with [Two.Path.ending](/docs/path/#ending).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## ending\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#ending\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">ending</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nNumber between zero and one to state the ending of where the path is rendered.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\n[Two.Path.ending](/docs/path/#ending) is a percentage value that represents at what percentage into the path should the renderer start drawing.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L322\">\n    path.js:322\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis is great for animating in and out stroked paths in conjunction with [Two.Path.beginning](/docs/path/#beginning).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## fill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fill\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">fill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what the path should be filled in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L332\">\n    path.js:332\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## stroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#stroke\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">stroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what the path should be outlined in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L339\">\n    path.js:339\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## linewidth\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#linewidth\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">linewidth</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe thickness in pixels of the stroke.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L346\">\n    path.js:346\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## opacity\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#opacity\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">opacity</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe opaqueness of the path.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L352\">\n    path.js:352\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nCan be used in conjunction with CSS Colors that have an alpha value.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## className\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#className\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">className</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA class to be applied to the element to be compatible with CSS styling.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L359\">\n    path.js:359\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nOnly rendered to DOM in the SVG renderer.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## visible\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#visible\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">visible</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDisplay the path or not.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L366\">\n    path.js:366\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nFor [Two.CanvasRenderer](/docs/renderers/canvas/) and [Two.WebGLRenderer](/docs/renderers/webgl/) when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## cap\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#cap\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">cap</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty](https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L373\">\n    path.js:373\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## join\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#join\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">join</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty](https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L380\">\n    path.js:380\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## miter\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#miter\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">miter</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty](https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty)\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L387\">\n    path.js:387\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## vertices\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#vertices\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">vertices</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAn ordered list of anchor points for rendering the path.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nA list of [Two.Anchor](/docs/anchor/) objects that consist of what form the path takes.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L394\">\n    path.js:394\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThe array when manipulating is actually a [Two.Collection](/docs/collection/).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## automatic\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#automatic\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">automatic</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDetermines whether or not Two.js should calculate curves, lines, and commands automatically for you or to let the developer manipulate them for themselves.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L402\">\n    path.js:402\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## dashes\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dashes\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">dashes</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nArray of numbers. Odd indices represent dash length. Even indices represent dash space.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nA list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n\n</div>\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray) for more information on the SVG stroke-dasharray attribute.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L408\">\n    path.js:408\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## dashes.offset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dashes.offset\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">dashes.offset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA number in pixels to offset [Two.Path.dashes](/docs/path/#dashes) display.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L416\">\n    path.js:416\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  path  | The reference [Two.Path](/docs/path/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Path](/docs/path/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L473\">\n    path.js:473\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Path\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Path](/docs/path/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L504\">\n    path.js:504\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L542\">\n    path.js:542\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Path.fromObject](/docs/path/#fromobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Path\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the path's renderer resources and detach all events.\nThis method cleans up vertices collection events, individual vertex events,\ncontrol point events, and disposes fill/stroke effects (calling dispose()\non Gradients and Textures for thorough cleanup) while preserving the\nrenderer type for potential re-attachment to a new renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L572\">\n    path.js:572\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noFill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noFill\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">noFill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nShort hand method to set fill to `none`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L645\">\n    path.js:645\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noStroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noStroke\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">noStroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nShort hand method to set stroke to `none`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L655\">\n    path.js:655\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## corner\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#corner\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">corner</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nOrient the vertices of the shape to the upper left-hand corner of the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L666\">\n    path.js:666\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## center\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#center\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">center</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nOrient the vertices of the shape to the center of the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L696\">\n    path.js:696\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getBoundingClientRect\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getBoundingClientRect\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">getBoundingClientRect</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- Returns object with top, left, right, bottom, width, height attributes.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  shallow  | Describes whether to calculate off local matrix or world matrix. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nReturn an object with top, left, right, bottom, width, and height parameters of the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L721\">\n    path.js:721\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## contains\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#contains\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">contains</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Boolean\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | x coordinate to hit test against |\n|  y  | y coordinate to hit test against |\n|  options  | Optional options object |\n|  options.ignoreVisibility  | If `true`, hit test against `path.visible = false` shapes |\n|  options.tolerance  | Padding to hit test against in pixels |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCheck to see if coordinates are within a [Two.Path](/docs/path/)'s bounding rectangle\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L836\">\n    path.js:836\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nExpects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getPointAt\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getPointAt\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">getPointAt</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  t  | Percentage value describing where on the [Two.Path](/docs/path/) to estimate and assign coordinate values. |\n|  obj  | Object to apply calculated x, y to. If none available returns new `Object`. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nGiven a float `t` from 0 to 1, return a point or assign a passed `obj`'s coordinates to that percentage on this [Two.Path](/docs/path/)'s curve.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L925\">\n    path.js:925\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## plot\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#plot\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">plot</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nBased on closed / curved and sorting of vertices plot where all points should be and where the respective handles should be too.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L1059\">\n    path.js:1059\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWhile this method is public it is internally called by [Two.Path._update](/docs/path/#_update) when `automatic = true`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## smooth\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#smooth\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">smooth</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  options  | Configuration for smoothing. |\n|  options.type  | Type of smoothing algorithm. |\n|  options.from  | Index of vertices to start smoothing |\n|  options.to  | Index of vertices to terminate smoothing |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdjust vertex handles to generate smooth curves without toggling `automatic`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L1078\">\n    path.js:1078\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## subdivide\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subdivide\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">subdivide</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  limit  | How many times to recurse subdivisions. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nInsert a [Two.Anchor](/docs/anchor/) at the midpoint between every item in [Two.Path.vertices](/docs/path/#vertices).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L1175\">\n    path.js:1175\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## length\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#length\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">length</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe sum of distances between all [Two.Path.vertices](/docs/path/#vertices).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L1621\">\n    path.js:1621\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## mask\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#mask\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">mask</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe shape whose alpha property becomes a clipping area for the path.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L1730\">\n    path.js:1730\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## clip\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clip\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">clip</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nTells Two.js renderer if this object represents a mask for another object (or not).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L1751\">\n    path.js:1751\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## strokeAttenuation\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#strokeAttenuation\"><span class=\"prefix\">Two.Path.</span><span class=\"shortname\">strokeAttenuation</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nWhen set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nWhen `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/path.js#L1779\">\n    path.js:1779\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/registry/README.md",
    "content": "---\ntitle: Two.Registry\npageClass: docs\nlang: en-US\n---\n\n# Two.Registry\n\n\n\nAn arbitrary class to manage a directory of things. Mainly used for keeping tabs of textures in Two.js.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/registry.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n\n\n\n\n<div class=\"instance function \">\n\n## add\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#add\"><span class=\"prefix\">Two.Registry.</span><span class=\"shortname\">add</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  id  | A unique identifier. |\n|  obj  | Any type of variable to be registered to the directory. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdds any value to the directory. Assigned by the `id`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/registry.js#L12\">\n    registry.js:12\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## remove\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#remove\"><span class=\"prefix\">Two.Registry.</span><span class=\"shortname\">remove</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  id  | A unique identifier. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nRemove any value from the directory by its `id`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/registry.js#L24\">\n    registry.js:24\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## get\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#get\"><span class=\"prefix\">Two.Registry.</span><span class=\"shortname\">get</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\nThe associated value. If unavailable then `undefined` is returned.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  id  | A unique identifier. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nGet a registered value by its `id`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/registry.js#L35\">\n    registry.js:35\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## contains\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#contains\"><span class=\"prefix\">Two.Registry.</span><span class=\"shortname\">contains</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Boolean\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  id  | A unique identifier. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nConvenience method to see if a value is registered to an `id` already.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/registry.js#L46\">\n    registry.js:46\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/renderers/canvas/README.md",
    "content": "---\ntitle: Two.CanvasRenderer\npageClass: docs\nlang: en-US\n---\n\n# Two.CanvasRenderer\n\n\n<div class=\"extends\">\n\nExtends: [Two.Events](/docs/events/)\n\n</div>\n\n\nThis class is used by [Two]() when constructing with `type` of `Two.Types.canvas`. It takes Two.js' scenegraph and renders it to a `<canvas />`.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  parameters  | This object is inherited when constructing a new instance of [Two](). |\n|  parameters.domElement  | The `<canvas />` to draw to. If none given a new one will be constructed. |\n|  parameters.overdraw  | Determines whether the canvas should clear the background or not. Defaults to `true`. |\n|  parameters.smoothing  | Determines whether the canvas should antialias drawing. Set it to `false` when working with pixel art. `false` can lead to better performance, since it would use a cheaper interpolation algorithm. |\n\n\n\n<div class=\"static member \">\n\n## Utils\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Utils\"><span class=\"prefix\">Two.CanvasRenderer.</span><span class=\"shortname\">Utils</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA massive object filled with utility functions and properties to render Two.js objects to a `<canvas />`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js#L1128\">\n    canvas.js:1128\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## domElement\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#domElement\"><span class=\"prefix\">Two.CanvasRenderer.</span><span class=\"shortname\">domElement</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe `<canvas />` associated with the Two.js scene.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js#L1097\">\n    canvas.js:1097\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## ctx\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#ctx\"><span class=\"prefix\">Two.CanvasRenderer.</span><span class=\"shortname\">ctx</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAssociated two dimensional context to render on the `<canvas />`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js#L1103\">\n    canvas.js:1103\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## overdraw\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#overdraw\"><span class=\"prefix\">Two.CanvasRenderer.</span><span class=\"shortname\">overdraw</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDetermines whether the canvas clears the background each draw call.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js#L1109\">\n    canvas.js:1109\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## scene\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#scene\"><span class=\"prefix\">Two.CanvasRenderer.</span><span class=\"shortname\">scene</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe root group of the scenegraph.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js#L1120\">\n    canvas.js:1120\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## setSize\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#setSize\"><span class=\"prefix\">Two.CanvasRenderer.</span><span class=\"shortname\">setSize</span></a></h2>\n\n\n\n\n\n\n\n\n<div class=\"fires\">\n\n__Triggers__:\n\n+ `event:resize`\n\n</div>\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  width  | The new width of the renderer. |\n|  height  | The new height of the renderer. |\n|  ratio  | The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nChange the size of the renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js#L1134\">\n    canvas.js:1134\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## render\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#render\"><span class=\"prefix\">Two.CanvasRenderer.</span><span class=\"shortname\">render</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRender the current scene to the `<canvas />`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/canvas.js#L1162\">\n    canvas.js:1162\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/renderers/svg/README.md",
    "content": "---\ntitle: Two.SVGRenderer\npageClass: docs\nlang: en-US\n---\n\n# Two.SVGRenderer\n\n\n<div class=\"extends\">\n\nExtends: [Two.Events](/docs/events/)\n\n</div>\n\n\nThis class is used by [Two]() when constructing with `type` of `Two.Types.svg` (the default type). It takes Two.js' scenegraph and renders it to a `<svg />`.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/svg.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  parameters  | This object is inherited when constructing a new instance of [Two](). |\n|  parameters.domElement  | The `<svg />` to draw to. If none given a new one will be constructed. |\n\n\n\n<div class=\"static member \">\n\n## Utils\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Utils\"><span class=\"prefix\">Two.SVGRenderer.</span><span class=\"shortname\">Utils</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA massive object filled with utility functions and properties to render Two.js objects to a `<svg />`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/svg.js#L1214\">\n    svg.js:1214\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## domElement\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#domElement\"><span class=\"prefix\">Two.SVGRenderer.</span><span class=\"shortname\">domElement</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe `<svg />` associated with the Two.js scene.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/svg.js#L1190\">\n    svg.js:1190\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## scene\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#scene\"><span class=\"prefix\">Two.SVGRenderer.</span><span class=\"shortname\">scene</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe root group of the scenegraph.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/svg.js#L1196\">\n    svg.js:1196\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## defs\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#defs\"><span class=\"prefix\">Two.SVGRenderer.</span><span class=\"shortname\">defs</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe `<defs />` to apply gradients, patterns, and bitmap imagery.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/svg.js#L1203\">\n    svg.js:1203\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## setSize\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#setSize\"><span class=\"prefix\">Two.SVGRenderer.</span><span class=\"shortname\">setSize</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  width  | The new width of the renderer. |\n|  height  | The new height of the renderer. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nChange the size of the renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/svg.js#L1220\">\n    svg.js:1220\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nTriggers a `Two.Events.resize`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## render\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#render\"><span class=\"prefix\">Two.SVGRenderer.</span><span class=\"shortname\">render</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRender the current scene to the `<svg />`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/svg.js#L1240\">\n    svg.js:1240\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/renderers/webgl/README.md",
    "content": "---\ntitle: Two.WebGLRenderer\npageClass: docs\nlang: en-US\n---\n\n# Two.WebGLRenderer\n\n\n<div class=\"extends\">\n\nExtends: [Two.Events](/docs/events/)\n\n</div>\n\n\nThis class is used by [Two]() when constructing with `type` of `Two.Types.webgl`. It takes Two.js' scenegraph and renders it to a `<canvas />` through the WebGL api.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  parameters  | This object is inherited when constructing a new instance of [Two](). |\n|  parameters.domElement  | The `<canvas />` to draw to. If none given a new one will be constructed. |\n|  parameters.offscreenElement  | The offscreen two dimensional `<canvas />` to render each element on WebGL texture updates. |\n|  parameters.antialias  | Determines whether the canvas should clear render with antialias on. |\n\n\n\n<div class=\"global function \">\n\n## \n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#\"><span class=\"prefix\">// * @type {(gl: any, programs: any) => any}</span><span class=\"shortname\"></span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  gl  |  |\n|  programs  |  |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L68\">\n    webgl.js:68\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Utils\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Utils\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">Utils</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA massive object filled with utility functions and properties to render Two.js objects to a `<canvas />` through the WebGL API.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1842\">\n    webgl.js:1842\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## domElement\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#domElement\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">domElement</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe `<canvas />` associated with the Two.js scene.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1721\">\n    webgl.js:1721\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## scene\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#scene\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">scene</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe root group of the scenegraph.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1732\">\n    webgl.js:1732\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## overdraw\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#overdraw\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">overdraw</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDetermines whether the canvas clears the background each draw call.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1758\">\n    webgl.js:1758\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## ctx\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#ctx\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">ctx</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAssociated two dimensional context to render on the `<canvas />`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1765\">\n    webgl.js:1765\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## programs\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#programs\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">programs</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAssociated WebGL programs to render all elements from the scenegraph.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1783\">\n    webgl.js:1783\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## setSize\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#setSize\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">setSize</span></a></h2>\n\n\n\n\n\n\n\n\n<div class=\"fires\">\n\n__Triggers__:\n\n+ `event:resize`\n\n</div>\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  width  | The new width of the renderer. |\n|  height  | The new height of the renderer. |\n|  ratio  | The new pixel ratio (pixel density) of the renderer. Defaults to calculate the pixel density of the user's screen. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nChange the size of the renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1848\">\n    webgl.js:1848\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## render\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#render\"><span class=\"prefix\">Two.WebGLRenderer.</span><span class=\"shortname\">render</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRender the current scene to the `<canvas />`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/renderers/webgl.js#L1897\">\n    webgl.js:1897\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shape/README.md",
    "content": "---\ntitle: Two.Shape\npageClass: docs\nlang: en-US\n---\n\n# Two.Shape\n\n\n<div class=\"extends\">\n\nExtends: [Two.Element](/docs/element/)\n\n</div>\n\n\nThe foundational transformation object for the Two.js scenegraph.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Shape\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Shape](/docs/shape/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Shape](/docs/shape/) from an object notation of a [Two.Shape](/docs/shape/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L154\">\n    shape.js:154\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Shape.toObject](/docs/shape/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## renderer\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#renderer\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">renderer</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nObject access to store relevant renderer specific variables. Warning: manipulating this object can create unintended consequences.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L86\">\n    shape.js:86\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWith the [Two.SVGRenderer](/docs/renderers/svg/) you can access the underlying SVG element created via `shape.renderer.elem`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## matrix\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#matrix\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">matrix</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nThe transformation matrix of the shape.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L95\">\n    shape.js:95\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\n[Two.Shape.position](/docs/shape/#position), [Two.Shape.rotation](/docs/shape/#rotation), [Two.Shape.scale](/docs/shape/#scale), [Two.Shape.skewX](/docs/shape/#skewx), and [Two.Shape.skewY](/docs/shape/#skewy) apply their values to the matrix when changed. The matrix is what is sent to the renderer to be drawn.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## worldMatrix\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#worldMatrix\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">worldMatrix</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\n\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nThe transformation matrix of the shape in the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L103\">\n    shape.js:103\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## position\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#position\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">position</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe x and y value for where the shape is placed relative to its parent.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L110\">\n    shape.js:110\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## rotation\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#rotation\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">rotation</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value in Number for how much the shape is rotated relative to its parent.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L116\">\n    shape.js:116\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## scale\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#scale\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">scale</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value for how much the shape is scaled relative to its parent.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L122\">\n    shape.js:122\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis value can be replaced with a [Two.Vector](/docs/vector/) to do non-uniform scaling. e.g: `shape.scale = new Two.Vector(2, 1);`\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## skewX\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#skewX\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">skewX</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value in Number for how much the shape is skewed relative to its parent.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nSkew the shape by an angle in the x axis direction.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L129\">\n    shape.js:129\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## skewY\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#skewY\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">skewY</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value in Number for how much the shape is skewed relative to its parent.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nSkew the shape by an angle in the y axis direction.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L136\">\n    shape.js:136\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## translation\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#translation\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">translation</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Shape.position](/docs/shape/#position).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L179\">\n    shape.js:179\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## addTo\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#addTo\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">addTo</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  group  | The parent the shape adds itself to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nConvenience method to add itself to the scenegraph.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L190\">\n    shape.js:190\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## remove\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#remove\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">remove</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRemove self from the scene / parent.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L201\">\n    shape.js:201\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## contains\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#contains\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">contains</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Boolean\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | x coordinate to hit test against |\n|  y  | y coordinate to hit test against |\n|  options  | Optional options object |\n|  options.ignoreVisibility  | If `true`, hit test against `shape.visible = false` shapes |\n|  options.tolerance  | Padding to hit test against in pixels |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCheck to see if coordinates are within a [Two.Shape](/docs/shape/)'s bounding rectangle\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L216\">\n    shape.js:216\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nExpects *world-space coordinates* – the same pixel-space you get from the renderer (e.g., mouse `clientX`/`clientY` adjusted for the canvas’s offset and pixel ratio).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  shape  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Shape](/docs/shape/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L267\">\n    shape.js:267\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Shape\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | Optional argument to automatically add the shape to a scenegraph. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Shape](/docs/shape/) with the same values as the current shape.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L307\">\n    shape.js:307\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCreate a JSON compatible object that represents information of the shape.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L334\">\n    shape.js:334\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Shape.fromObject](/docs/shape/#fromobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Shape.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the shape's bound objects by unbinding relevant events.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shape.js#L354\">\n    shape.js:354\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/arc-segment/README.md",
    "content": "---\ntitle: Two.ArcSegment\npageClass: docs\nlang: en-US\n---\n\n# Two.ArcSegment\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the arc segment. |\n|  y  | The y position of the arc segment. |\n|  innerRadius  | The inner radius value of the arc segment. |\n|  outerRadius  | The outer radius value of the arc segment. |\n|  startAngle  | The start angle of the arc segment in Number. |\n|  endAngle  | The end angle of the arc segment in Number. |\n|  resolution  | The number of vertices used to construct the arc segment. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.ArcSegment](/docs/shapes/arc-segment/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L128\">\n    arc-segment.js:128\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ArcSegment\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.ArcSegment](/docs/shapes/arc-segment/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.ArcSegment](/docs/shapes/arc-segment/) from an object notation of a [Two.ArcSegment](/docs/shapes/arc-segment/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L134\">\n    arc-segment.js:134\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.ArcSegment.toObject](/docs/shapes/arc-segment/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## innerRadius\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#innerRadius\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">innerRadius</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the inner radius of the arc segment.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L86\">\n    arc-segment.js:86\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## outerRadius\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#outerRadius\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">outerRadius</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the outer radius of the arc segment.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L94\">\n    arc-segment.js:94\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## startAngle\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#startAngle\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">startAngle</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe angle of one side for the arc segment.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L102\">\n    arc-segment.js:102\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## endAngle\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#endAngle\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">endAngle</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe angle of the other side for the arc segment.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L110\">\n    arc-segment.js:110\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  arcSegment  | The reference [Two.ArcSegment](/docs/shapes/arc-segment/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.ArcSegment](/docs/shapes/arc-segment/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L152\">\n    arc-segment.js:152\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ArcSegment\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.ArcSegment](/docs/shapes/arc-segment/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L337\">\n    arc-segment.js:337\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.ArcSegment.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/arc-segment.js#L375\">\n    arc-segment.js:375\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/circle/README.md",
    "content": "---\ntitle: Two.Circle\npageClass: docs\nlang: en-US\n---\n\n# Two.Circle\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/circle.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the circle. |\n|  y  | The y position of the circle. |\n|  radius  | The radius value of the circle. |\n|  resolution  | The number of vertices used to construct the circle. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Circle.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Circle](/docs/shapes/circle/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/circle.js#L68\">\n    circle.js:68\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Circle.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Circle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Circle](/docs/shapes/circle/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Circle](/docs/shapes/circle/) from an object notation of a [Two.Circle](/docs/shapes/circle/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/circle.js#L74\">\n    circle.js:74\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Circle.toObject](/docs/shapes/circle/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## radius\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#radius\"><span class=\"prefix\">Two.Circle.</span><span class=\"shortname\">radius</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the radius of the circle.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/circle.js#L50\">\n    circle.js:50\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Circle.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  circle  | The reference [Two.Circle](/docs/shapes/circle/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Circle](/docs/shapes/circle/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/circle.js#L92\">\n    circle.js:92\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Circle.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Circle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Circle](/docs/shapes/circle/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/circle.js#L171\">\n    circle.js:171\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Circle.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/circle.js#L203\">\n    circle.js:203\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/ellipse/README.md",
    "content": "---\ntitle: Two.Ellipse\npageClass: docs\nlang: en-US\n---\n\n# Two.Ellipse\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the ellipse. |\n|  y  | The y position of the ellipse. |\n|  rx  | The radius value of the ellipse in the x direction. |\n|  ry  | The radius value of the ellipse in the y direction. |\n|  resolution  | The number of vertices used to construct the ellipse. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Ellipse.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Ellipse](/docs/shapes/ellipse/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js#L93\">\n    ellipse.js:93\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Ellipse.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Ellipse\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Ellipse](/docs/shapes/ellipse/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Ellipse](/docs/shapes/ellipse/) from an object notation of a [Two.Ellipse](/docs/shapes/ellipse/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js#L99\">\n    ellipse.js:99\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Ellipse.toObject](/docs/shapes/ellipse/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## width\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#width\"><span class=\"prefix\">Two.Ellipse.</span><span class=\"shortname\">width</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe width of the ellipse.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js#L67\">\n    ellipse.js:67\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## height\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#height\"><span class=\"prefix\">Two.Ellipse.</span><span class=\"shortname\">height</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe height of the ellipse.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js#L75\">\n    ellipse.js:75\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Ellipse.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  ellipse  | The reference [Two.Ellipse](/docs/shapes/ellipse/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Ellipse](/docs/shapes/ellipse/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js#L117\">\n    ellipse.js:117\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Ellipse.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Ellipse\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Ellipse](/docs/shapes/ellipse/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js#L196\">\n    ellipse.js:196\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Ellipse.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/ellipse.js#L231\">\n    ellipse.js:231\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/line/README.md",
    "content": "---\ntitle: Two.Line\npageClass: docs\nlang: en-US\n---\n\n# Two.Line\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/line.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x1  | The x position of the first vertex on the line. |\n|  y1  | The y position of the first vertex on the line. |\n|  x2  | The x position of the second vertex on the line. |\n|  y2  | The y position of the second vertex on the line. |\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/points/README.md",
    "content": "---\ntitle: Two.Points\npageClass: docs\nlang: en-US\n---\n\n# Two.Points\n\n\n<div class=\"extends\">\n\nExtends: [Two.Shape](/docs/shape/)\n\n</div>\n\n\nThis is a primary primitive class for quickly and easily drawing points in Two.js. Unless specified methods return their instance of `Two.Points` for the purpose of chaining.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  vertices  | A list of [Two.Vector](/docs/vector/)s that represent the order and coordinates to construct a rendered set of points. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Points](/docs/shapes/points/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L170\">\n    points.js:170\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Points\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Points](/docs/shapes/points/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Points](/docs/shapes/points/) from an object notation of a [Two.Points](/docs/shapes/points/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L188\">\n    points.js:188\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Points.toObject](/docs/shapes/points/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## size\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#size\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">size</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nNumber describing the diameter each point should have\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nSet the size of each point in the collection of [Two.Points](/docs/shapes/points/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L76\">\n    points.js:76\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## sizeAttenuation\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#sizeAttenuation\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">sizeAttenuation</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nBoolean dictating whether Two.js should scale the size of the points based on its matrix hierarchy.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nSet to `true` if you'd like the size of the points to be relative to the scale of its parents; `false` to disregard. Default is `false`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L83\">\n    points.js:83\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## beginning\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#beginning\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">beginning</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nNumber between zero and one to state the beginning of where the path is rendered.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\n[Two.Points.beginning](/docs/shapes/points/#beginning) is a percentage value that represents at what percentage into the path should the renderer start drawing.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L90\">\n    points.js:90\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## ending\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#ending\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">ending</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nNumber between zero and one to state the ending of where the path is rendered.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\n[Two.Points.ending](/docs/shapes/points/#ending) is a percentage value that represents at what percentage into the path should the renderer start drawing.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L97\">\n    points.js:97\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## fill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fill\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">fill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what the path should be filled in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L106\">\n    points.js:106\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## stroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#stroke\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">stroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what the path should be outlined in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L113\">\n    points.js:113\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## linewidth\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#linewidth\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">linewidth</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe thickness in pixels of the stroke.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L120\">\n    points.js:120\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## opacity\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#opacity\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">opacity</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe opaqueness of the path.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L126\">\n    points.js:126\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nCan be used in conjunction with CSS Colors that have an alpha value.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## className\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#className\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">className</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA class to be applied to the element to be compatible with CSS styling.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L133\">\n    points.js:133\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nOnly rendered to DOM in the SVG renderer.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## visible\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#visible\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">visible</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDisplay the points or not.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L140\">\n    points.js:140\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nFor [Two.CanvasRenderer](/docs/renderers/canvas/) and [Two.WebGLRenderer](/docs/renderers/webgl/) when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## vertices\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#vertices\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">vertices</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAn ordered list of vector points for rendering points.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nA list of [Two.Vector](/docs/vector/) objects that consist of which coordinates to draw points at.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L147\">\n    points.js:147\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThe array when manipulating is actually a [Two.Collection](/docs/collection/).\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## dashes\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dashes\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">dashes</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nArray of numbers. Odd indices represent dash length. Even indices represent dash space.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nA list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n\n</div>\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray) for more information on the SVG stroke-dasharray attribute.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L155\">\n    points.js:155\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## dashes.offset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dashes.offset\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">dashes.offset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA number in pixels to offset [Two.Points.dashes](/docs/shapes/points/#dashes) display.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L163\">\n    points.js:163\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  points  | The reference [Two.Points](/docs/shapes/points/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Points](/docs/shapes/points/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L212\">\n    points.js:212\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Points\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Points](/docs/shapes/points/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L240\">\n    points.js:240\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the points object.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L278\">\n    points.js:278\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Points\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the points' renderer resources and detach all events.\nThis method cleans up vertices collection events, individual vertex events,\nand disposes fill/stroke effects (calling dispose() on Gradients and\nTextures for thorough cleanup) while preserving the renderer type for\npotential re-attachment to a new renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L307\">\n    points.js:307\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noFill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noFill\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">noFill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nShort hand method to set fill to `none`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L368\">\n    points.js:368\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noStroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noStroke\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">noStroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nShort hand method to set stroke to `none`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L375\">\n    points.js:375\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## corner\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#corner\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">corner</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nOrient the vertices of the shape to the upper left-hand corner of the points object.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L382\">\n    points.js:382\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## center\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#center\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">center</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nOrient the vertices of the shape to the center of the points object.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L389\">\n    points.js:389\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getBoundingClientRect\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getBoundingClientRect\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">getBoundingClientRect</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- Returns object with top, left, right, bottom, width, height attributes.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  shallow  | Describes whether to calculate off local matrix or world matrix. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nReturn an object with top, left, right, bottom, width, and height parameters of the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L396\">\n    points.js:396\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## subdivide\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subdivide\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">subdivide</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  limit  | How many times to recurse subdivisions. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nInsert a [Two.Vector](/docs/vector/) at the midpoint between every item in [Two.Points.vertices](/docs/shapes/points/#vertices).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L405\">\n    points.js:405\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## length\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#length\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">length</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe sum of distances between all [Two.Points.vertices](/docs/shapes/points/#vertices).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L625\">\n    points.js:625\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## strokeAttenuation\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#strokeAttenuation\"><span class=\"prefix\">Two.Points.</span><span class=\"shortname\">strokeAttenuation</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nWhen set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nWhen `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/points.js#L708\">\n    points.js:708\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/polygon/README.md",
    "content": "---\ntitle: Two.Polygon\npageClass: docs\nlang: en-US\n---\n\n# Two.Polygon\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the polygon. |\n|  y  | The y position of the polygon. |\n|  radius  | The radius value of the polygon. |\n|  sides  | The number of vertices used to construct the polygon. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Polygon](/docs/shapes/polygon/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L117\">\n    polygon.js:117\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Polygon\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Polygon](/docs/shapes/polygon/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Polygon](/docs/shapes/polygon/) from an object notation of a [Two.Polygon](/docs/shapes/polygon/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L123\">\n    polygon.js:123\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Polygon.toObject](/docs/shapes/polygon/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## radius\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#radius\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">radius</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe radius value of the polygon.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L78\">\n    polygon.js:78\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis property is tied to [Two.Polygon.width](/docs/shapes/polygon/#width) and [Two.Polygon.height](/docs/shapes/polygon/#height). When you set `radius`, it affects `width` and `height`. Likewise, if you set `width` or `height` it will change the `radius`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## width\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#width\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">width</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the width of the polygon.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L87\">\n    polygon.js:87\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis property is tied to [Two.Polygon.radius](/docs/shapes/polygon/#radius). When you set `radius`, it affects the `width`. Likewise, if you set `width` it will change the `radius`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## height\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#height\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">height</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the height of the polygon.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L93\">\n    polygon.js:93\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis property is tied to [Two.Polygon.radius](/docs/shapes/polygon/#radius). When you set `radius`, it affects the `height`. Likewise, if you set `height` it will change the `radius`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## sides\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#sides\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">sides</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe amount of sides the polyogn has.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L99\">\n    polygon.js:99\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  polygon  | The reference [Two.Polygon](/docs/shapes/polygon/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Polygon](/docs/shapes/polygon/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L141\">\n    polygon.js:141\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Polygon\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Polygon](/docs/shapes/polygon/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L217\">\n    polygon.js:217\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Polygon.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/polygon.js#L251\">\n    polygon.js:251\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/rectangle/README.md",
    "content": "---\ntitle: Two.Rectangle\npageClass: docs\nlang: en-US\n---\n\n# Two.Rectangle\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the rectangle. |\n|  y  | The y position of the rectangle. |\n|  width  | The width value of the rectangle. |\n|  height  | The width value of the rectangle. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Rectangle](/docs/shapes/rectangle/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L62\">\n    rectangle.js:62\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Rectangle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Rectangle](/docs/shapes/rectangle/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Rectangle](/docs/shapes/rectangle/) from an object notation of a [Two.Rectangle](/docs/shapes/rectangle/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L68\">\n    rectangle.js:68\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Rectangle.toObject](/docs/shapes/rectangle/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## width\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#width\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">width</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the width of the rectangle.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L35\">\n    rectangle.js:35\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## height\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#height\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">height</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the height of the rectangle.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L40\">\n    rectangle.js:40\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## origin\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#origin\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">origin</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA two-component vector describing the origin offset to draw the rectangle. Default is `0, 0`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L46\">\n    rectangle.js:46\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  rectangle  | The reference [Two.Rectangle](/docs/shapes/rectangle/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Rectangle](/docs/shapes/rectangle/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L86\">\n    rectangle.js:86\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Rectangle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Rectangle](/docs/shapes/rectangle/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L183\">\n    rectangle.js:183\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Rectangle.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rectangle.js#L219\">\n    rectangle.js:219\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/rounded-rectangle/README.md",
    "content": "---\ntitle: Two.RoundedRectangle\npageClass: docs\nlang: en-US\n---\n\n# Two.RoundedRectangle\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the rounded rectangle. |\n|  y  | The y position of the rounded rectangle. |\n|  width  | The width value of the rounded rectangle. |\n|  height  | The width value of the rounded rectangle. |\n|  radius  | The radius value of the rounded rectangle. |\n|  resolution  | The number of vertices used to construct the rounded rectangle. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L123\">\n    rounded-rectangle.js:123\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.RoundedRectangle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/) from an object notation of a [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L129\">\n    rounded-rectangle.js:129\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.RoundedRectangle.toObject](/docs/shapes/rounded-rectangle/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## width\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#width\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">width</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe width of the rounded rectangle.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L89\">\n    rounded-rectangle.js:89\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## height\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#height\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">height</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe height of the rounded rectangle.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L97\">\n    rounded-rectangle.js:97\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## radius\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#radius\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">radius</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the radius of the rounded rectangle.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L105\">\n    rounded-rectangle.js:105\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  roundedRectangle  | The reference [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L147\">\n    rounded-rectangle.js:147\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.RoundedRectangle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.RoundedRectangle](/docs/shapes/rounded-rectangle/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L292\">\n    rounded-rectangle.js:292\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.RoundedRectangle.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/rounded-rectangle.js#L328\">\n    rounded-rectangle.js:328\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/shapes/star/README.md",
    "content": "---\ntitle: Two.Star\npageClass: docs\nlang: en-US\n---\n\n# Two.Star\n\n\n<div class=\"extends\">\n\nExtends: [Two.Path](/docs/path/)\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The x position of the star. |\n|  y  | The y position of the star. |\n|  innerRadius  | The inner radius value of the star. |\n|  outerRadius  | The outer radius value of the star. |\n|  sides  | The number of sides used to construct the star. |\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Star](/docs/shapes/star/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L114\">\n    star.js:114\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Star\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Star](/docs/shapes/star/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Star](/docs/shapes/star/) from an object notation of a [Two.Star](/docs/shapes/star/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L120\">\n    star.js:120\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Star.toObject](/docs/shapes/star/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## innerRadius\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#innerRadius\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">innerRadius</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the inner radius of the star.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L80\">\n    star.js:80\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## outerRadius\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#outerRadius\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">outerRadius</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe size of the outer radius of the star.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L88\">\n    star.js:88\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## sides\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#sides\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">sides</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe amount of sides the star has.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L96\">\n    star.js:96\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  star  | The reference [Two.Star](/docs/shapes/star/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Star](/docs/shapes/star/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L138\">\n    star.js:138\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Star\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Star](/docs/shapes/star/) with the same properties of the current path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L216\">\n    star.js:216\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Star.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the path.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/shapes/star.js#L252\">\n    star.js:252\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/text/README.md",
    "content": "---\ntitle: Two.Text\npageClass: docs\nlang: en-US\n---\n\n# Two.Text\n\n\n<div class=\"extends\">\n\nExtends: [Two.Shape](/docs/shape/)\n\n</div>\n\n\nThis is a primitive class for creating drawable text that can be added to the scenegraph.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  message  | The String to be rendered to the scene. |\n|  x  | The position in the x direction for the object. |\n|  y  | The position in the y direction for the object. |\n|  styles  | An object where styles are applied. Attribute must exist in Two.Text.Properties. |\n\n\n\n<div class=\"static member \">\n\n## Ratio\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Ratio\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">Ratio</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nApproximate aspect ratio of a typeface's character width to height.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L330\">\n    text.js:330\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Properties\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Properties\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">Properties</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA list of properties that are on every [Two.Text](/docs/text/).\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L336\">\n    text.js:336\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## Measure\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Measure\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Measure</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- The width and height of the [Two.Text](/docs/text/) instance.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  text  | The instance of [Two.Text](/docs/text/) to measure. |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L360\">\n    text.js:360\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## fromObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fromObject\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">fromObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Text\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object notation of a [Two.Text](/docs/text/) to create a new instance |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new [Two.Text](/docs/text/) from an object notation of a [Two.Text](/docs/text/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L396\">\n    text.js:396\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Text.toObject](/docs/text/#toobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## value\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#value\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">value</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe characters to be rendered to the the screen. Referred to in the documentation sometimes as the `message`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L161\">\n    text.js:161\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## family\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#family\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">family</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe font family Two.js should attempt to register for rendering. The default value is `'sans-serif'`. Comma separated font names can be supplied as a \"stack\", similar to the CSS implementation of `font-family`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L167\">\n    text.js:167\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## size\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#size\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">size</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe font size in Two.js point space. Defaults to `13`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L173\">\n    text.js:173\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## leading\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#leading\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">leading</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe height between lines measured from base to base in Two.js point space. Defaults to `17`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L179\">\n    text.js:179\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## alignment\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#alignment\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">alignment</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAlignment of text in relation to [Two.Text.translation](/docs/text/#translation)'s coordinates. Possible values include `'left'`, `'center'`, `'right'`. Defaults to `'center'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L185\">\n    text.js:185\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## baseline\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#baseline\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">baseline</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe vertical aligment of the text in relation to [Two.Text.translation](/docs/text/#translation)'s coordinates. Possible values include `'top'`, `'middle'`, `'bottom'`, and `'baseline'`. Defaults to `'baseline'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L191\">\n    text.js:191\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nIn headless environments where the canvas is based on [https://github.com/Automattic/node-canvas](https://github.com/Automattic/node-canvas), `baseline` seems to be the only valid property.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## style\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#style\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">style</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe font's style. Possible values include '`normal`', `'italic'`. Defaults to `'normal'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L198\">\n    text.js:198\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## weight\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#weight\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">weight</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA number at intervals of 100 to describe the font's weight. This compatibility varies with the typeface's variant weights. Larger values are bolder. Smaller values are thinner. Defaults to `'500'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L204\">\n    text.js:204\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## decoration\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#decoration\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">decoration</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nString to delineate whether text should be decorated with for instance an `'underline'`. Defaults to `'none'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L210\">\n    text.js:210\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## direction\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#direction\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">direction</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nString to determine what direction the text should run. Possibly values are `'ltr'` for left-to-right and `'rtl'` for right-to-left. Defaults to `'ltr'`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L216\">\n    text.js:216\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## fill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fill\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">fill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what the text object should be filled in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L222\">\n    text.js:222\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## stroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#stroke\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">stroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe value of what the text object should be filled in with.\n\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/CSS/color_value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) for more information on CSS's colors as `String`.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L229\">\n    text.js:229\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## linewidth\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#linewidth\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">linewidth</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe thickness in pixels of the stroke.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L236\">\n    text.js:236\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## opacity\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#opacity\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">opacity</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe opaqueness of the text object.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L242\">\n    text.js:242\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nCan be used in conjunction with CSS Colors that have an alpha value.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## visible\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#visible\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">visible</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDisplay the text object or not.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L249\">\n    text.js:249\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nFor [Two.CanvasRenderer](/docs/renderers/canvas/) and [Two.WebGLRenderer](/docs/renderers/webgl/) when set to false all updating is disabled improving performance dramatically with many objects in the scene.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## mask\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#mask\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">mask</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe shape whose alpha property becomes a clipping area for the text.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L256\">\n    text.js:256\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## clip\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clip\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">clip</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nObject to define clipping area.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L263\">\n    text.js:263\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis property is currently not working because of SVG spec issues found here {@link https://code.google.com/p/chromium/issues/detail?id=370951}.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## dashes\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dashes\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">dashes</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nArray of numbers. Odd indices represent dash length. Even indices represent dash space.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nA list of numbers that represent the repeated dash length and dash space applied to the stroke of the text.\n\n</div>\n\n\n\n<div class=\"see\">\n\nSee: [https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray) for more information on the SVG stroke-dasharray attribute.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L304\">\n    text.js:304\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## dashes.offset\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dashes.offset\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">dashes.offset</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA number in pixels to offset [Two.Text.dashes](/docs/text/#dashes) display.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L312\">\n    text.js:312\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  text  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the properties of one [Two.Text](/docs/text/) onto another.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L420\">\n    text.js:420\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Text\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  parent  | The parent group or scene to add the clone to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreate a new instance of [Two.Text](/docs/text/) with the same properties of the current text object.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L439\">\n    text.js:439\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the text object.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L468\">\n    text.js:468\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nWorks in conjunction with [Two.Text.fromObject](/docs/text/#fromobject)\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dispose\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dispose\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">dispose</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Text\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRelease the text's renderer resources and detach all events.\nThis method disposes fill and stroke effects (calling dispose() on\nGradients and Textures for thorough cleanup) while preserving the\nrenderer type for potential re-attachment to a new renderer.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L487\">\n    text.js:487\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noFill\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noFill\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">noFill</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nShort hand method to set fill to `none`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L529\">\n    text.js:529\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## noStroke\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#noStroke\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">noStroke</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nShort hand method to set stroke to `none`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L539\">\n    text.js:539\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getBoundingClientRect\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getBoundingClientRect\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">getBoundingClientRect</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n- Returns object with top, left, right, bottom, width, height attributes.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  shallow  | Describes whether to calculate off local matrix or world matrix. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nReturn an object with top, left, right, bottom, width, and height parameters of the text object.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L554\">\n    text.js:554\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## strokeAttenuation\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#strokeAttenuation\"><span class=\"prefix\">Two.Text.</span><span class=\"shortname\">strokeAttenuation</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nWhen set to `true`, stroke width scales with transformations (default behavior). When `false`, stroke width remains constant in screen space.\n\n\n</div>\n\n\n\n\n\n\n<div class=\"description\">\n\nWhen `strokeAttenuation` is `false`, the stroke width is automatically adjusted to compensate for the object's world transform scale, maintaining constant visual thickness regardless of zoom level. When `true` (default), stroke width scales normally with transformations.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/text.js#L871\">\n    text.js:871\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/two/README.md",
    "content": "---\ntitle: Two\npageClass: docs\nlang: en-US\n---\n\n# Two\n\n\n<div class=\"extends\">\n\nExtends: [Two.Events](/docs/events/)\n\n</div>\n\n\nThe entrypoint for Two.js. Instantiate a `new Two` in order to setup a scene to render to. `Two` is also the publicly accessible namespace that all other sub-classes, functions, and utilities attach to.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  options  |  |\n|  options.fullscreen  | Set to `true` to automatically make the stage adapt to the width and height of the parent document. This parameter overrides `width` and `height` parameters if set to `true`. This overrides `options.fitted` as well. |\n|  options.fitted  | Set to `true` to automatically make the stage adapt to the width and height of the parent element. This parameter overrides `width` and `height` parameters if set to `true`. |\n|  options.width  | The width of the stage on construction. This can be set at a later time. |\n|  options.height  | The height of the stage on construction. This can be set at a later time. |\n|  options.type  | The type of renderer to setup drawing with. See [Two.Types](/docs/two/#types) for available options. |\n|  options.autostart  | Set to `true` to add the instance to draw on `requestAnimationFrame`. This is a convenient substitute for [Two.play](/docs/two/#play). |\n|  options.domElement  | The canvas or SVG element to draw into. This overrides the `options.type` argument. |\n\n\n\n<div class=\"static member \">\n\n## Types\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Types\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Types</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe different rendering types available in the library.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L277\">\n    two.js:277\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Version\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Version\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Version</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe current working version of the library, `v0.8.23`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L283\">\n    two.js:283\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## PublishDate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#PublishDate\"><span class=\"prefix\">Two.</span><span class=\"shortname\">PublishDate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe automatically generated publish date in the build process to verify version release candidates.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L289\">\n    two.js:289\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Identifier\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Identifier\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Identifier</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nString prefix for all Two.js object's ids. This trickles down to SVG ids.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L295\">\n    two.js:295\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Resolution\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Resolution\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Resolution</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nDefault amount of vertices to be used for interpreting Arcs and ArcSegments.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L301\">\n    two.js:301\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## AutoCalculateImportedMatrices\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#AutoCalculateImportedMatrices\"><span class=\"prefix\">Two.</span><span class=\"shortname\">AutoCalculateImportedMatrices</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nWhen importing SVGs through the [Two.interpret](/docs/two/#interpret) and [Two.load](/docs/two/#load), this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L307\">\n    two.js:307\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\n`false` copies the exact transformation matrix values, but also sets the path's `matrix.manual = true`.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Instances\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Instances\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Instances</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nRegistered list of all Two.js instances in the current session.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L315\">\n    two.js:315\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## uniqueId\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#uniqueId\"><span class=\"prefix\">Two.</span><span class=\"shortname\">uniqueId</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\nEver increasing Number.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nSimple method to access an incrementing value. Used for `id` allocation on all Two.js objects.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L321\">\n    two.js:321\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Commands\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Commands\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Commands</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nMap of possible path commands. Taken from the SVG specification. Commands include: `move`, `line`, `curve`, `arc`, and `close`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L363\">\n    two.js:363\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## Utils\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#Utils\"><span class=\"prefix\">Two.</span><span class=\"shortname\">Utils</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA massive object filled with utility functions and properties.\n\n\n\n__Two.Utils.read__: A collection of SVG parsing functions indexed by element name.\n\n\n\n__Two.Utils.read.path__: Parse SVG path element or `d` attribute string.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L369\">\n    two.js:369\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## type\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#type\"><span class=\"prefix\">Two.</span><span class=\"shortname\">type</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA string representing which type of renderer the instance has instantiated.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L133\">\n    two.js:133\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## renderer\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#renderer\"><span class=\"prefix\">Two.</span><span class=\"shortname\">renderer</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe instantiated rendering class for the instance. For a list of possible rendering types check out Two.Types.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L139\">\n    two.js:139\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## scene\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#scene\"><span class=\"prefix\">Two.</span><span class=\"shortname\">scene</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe base level [Two.Group](/docs/group/) which houses all objects for the instance. Because it is a [Two.Group](/docs/group/) transformations can be applied to it that will affect all objects in the instance. This is handy as a makeshift inverted camera.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L145\">\n    two.js:145\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## width\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#width\"><span class=\"prefix\">Two.</span><span class=\"shortname\">width</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe width of the instance's dom element.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L151\">\n    two.js:151\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## height\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#height\"><span class=\"prefix\">Two.</span><span class=\"shortname\">height</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe height of the instance's dom element.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L157\">\n    two.js:157\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## frameCount\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#frameCount\"><span class=\"prefix\">Two.</span><span class=\"shortname\">frameCount</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nAn integer representing how many frames have elapsed.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L163\">\n    two.js:163\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## timeDelta\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#timeDelta\"><span class=\"prefix\">Two.</span><span class=\"shortname\">timeDelta</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA number representing how much time has elapsed since the last frame in milliseconds.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L169\">\n    two.js:169\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## playing\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#playing\"><span class=\"prefix\">Two.</span><span class=\"shortname\">playing</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nA boolean representing whether or not the instance is being updated through the automatic `requestAnimationFrame`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L175\">\n    two.js:175\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## fit\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#fit\"><span class=\"prefix\">Two.</span><span class=\"shortname\">fit</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nIf `options.fullscreen` or `options.fitted` in construction create this function. It sets the `width` and `height` of the instance to its respective parent `window` or `element` depending on the `options` passed.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L221\">\n    two.js:221\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## appendTo\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#appendTo\"><span class=\"prefix\">Two.</span><span class=\"shortname\">appendTo</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  elem  | The DOM element to append the Two.js stage to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nShorthand method to append your instance of Two.js to the `document`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L377\">\n    two.js:377\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## play\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#play\"><span class=\"prefix\">Two.</span><span class=\"shortname\">play</span></a></h2>\n\n\n\n\n\n\n\n\n<div class=\"fires\">\n\n__Triggers__:\n\n+ `event:play`\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCall to start an internal animation loop.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L397\">\n    two.js:397\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis function initiates a `requestAnimationFrame` loop.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## pause\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#pause\"><span class=\"prefix\">Two.</span><span class=\"shortname\">pause</span></a></h2>\n\n\n\n\n\n\n\n\n<div class=\"fires\">\n\n__Triggers__:\n\n+ `event:pause`\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCall to stop the internal animation loop for a specific instance of Two.js.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L410\">\n    two.js:410\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## release\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#release\"><span class=\"prefix\">Two.</span><span class=\"shortname\">release</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Element\n\n\nThe object passed for event deallocation.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  obj  | Object to release from event listening. If none provided then the root [Two.Group](/docs/group/) will be used. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nRelease a [Two.Element](/docs/element/)’s events from memory and recurse through its children, effects, and/or vertices.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L425\">\n    two.js:425\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## getShapesAtPoint\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#getShapesAtPoint\"><span class=\"prefix\">Two.</span><span class=\"shortname\">getShapesAtPoint</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Array.\\<Two.Shape\\>\n\n\nOrdered list of shapes under the specified point, front to back.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | X coordinate in world space. |\n|  y  | Y coordinate in world space. |\n|  options  | Hit test configuration. |\n|  options.visibleOnly  | Limit results to visible shapes. |\n|  options.includeGroups  | Include groups in the hit results. |\n|  options.mode  | Whether to return all intersecting shapes or only the top-most. |\n|  options.deepest  | Alias for `mode: 'deepest'`. |\n|  options.precision  | Segmentation precision for curved geometry. |\n|  options.tolerance  | Pixel tolerance applied to hit testing. |\n|  options.fill  | Override fill testing behaviour. |\n|  options.stroke  | Override stroke testing behaviour. |\n|  options.filter  | Predicate to filter shapes from the result set. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nReturns shapes underneath the provided coordinates. Coordinates are expected in world space (matching the renderer output).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L537\">\n    two.js:537\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nDelegates to [Two.Group.getShapesAtPoint](/docs/group/#getshapesatpoint) on the root scene.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## update\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#update\"><span class=\"prefix\">Two.</span><span class=\"shortname\">update</span></a></h2>\n\n\n\n\n\n\n\n\n<div class=\"fires\">\n\n__Triggers__:\n\n+ `event:update`\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nUpdate positions and calculations in one pass before rendering. Then render to the canvas.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L563\">\n    two.js:563\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nThis function is called automatically if using [Two.play](/docs/two/#play) or the `autostart` parameter in construction.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## render\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#render\"><span class=\"prefix\">Two.</span><span class=\"shortname\">render</span></a></h2>\n\n\n\n\n\n\n\n\n<div class=\"fires\">\n\n__Triggers__:\n\n+ `event:render`\n\n</div>\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRender all drawable and visible objects of the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L599\">\n    two.js:599\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## add\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#add\"><span class=\"prefix\">Two.</span><span class=\"shortname\">add</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  objects  | An array of Two.js objects. Alternatively can add objects as individual arguments. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nA shorthand method to add specific Two.js objects to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L612\">\n    two.js:612\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## remove\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#remove\"><span class=\"prefix\">Two.</span><span class=\"shortname\">remove</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  objects  | An array of Two.js objects. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nA shorthand method to remove specific Two.js objects from the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L627\">\n    two.js:627\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clear\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clear\"><span class=\"prefix\">Two.</span><span class=\"shortname\">clear</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nRemoves all objects from the instance's scene. If you intend to have the browser garbage collect this, don't forget to delete the references in your application as well.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L643\">\n    two.js:643\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeLine\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeLine\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeLine</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Line\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x1  |  |\n|  y1  |  |\n|  x2  |  |\n|  y2  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js line and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L653\">\n    two.js:653\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeArrow\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeArrow\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeArrow</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Path\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x1  |  |\n|  y1  |  |\n|  x2  |  |\n|  y2  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js arrow and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L670\">\n    two.js:670\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeRectangle\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeRectangle\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeRectangle</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Rectangle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  |  |\n|  y  |  |\n|  width  |  |\n|  height  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js rectangle and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L744\">\n    two.js:744\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeRoundedRectangle\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeRoundedRectangle\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeRoundedRectangle</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.RoundedRectangle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  |  |\n|  y  |  |\n|  width  |  |\n|  height  |  |\n|  sides  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js rounded rectangle and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L761\">\n    two.js:761\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeCircle\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeCircle\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeCircle</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Circle\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  |  |\n|  y  |  |\n|  radius  |  |\n|  resolution  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js circle and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L779\">\n    two.js:779\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeEllipse\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeEllipse\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeEllipse</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Ellipse\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  |  |\n|  y  |  |\n|  rx  |  |\n|  ry  |  |\n|  resolution  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js ellipse and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L796\">\n    two.js:796\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeStar\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeStar\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeStar</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Star\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  |  |\n|  y  |  |\n|  outerRadius  |  |\n|  innerRadius  |  |\n|  sides  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js star and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L814\">\n    two.js:814\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeCurve\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeCurve\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeCurve</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Path\n\n\n- Where `path.curved` is set to `true`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  points  | An array of [Two.Anchor](/docs/anchor/) points. |\n|  | Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into [Two.Anchor](/docs/anchor/)s for use in the path. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js path that is curved and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L832\">\n    two.js:832\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nIn either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makePolygon\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makePolygon\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makePolygon</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Polygon\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  |  |\n|  y  |  |\n|  radius  |  |\n|  sides  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js polygon and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L872\">\n    two.js:872\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeArcSegment\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeArcSegment\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeArcSegment</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ArcSegment\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  |  |\n|  y  |  |\n|  innerRadius  |  |\n|  outerRadius  |  |\n|  startAngle  |  |\n|  endAngle  |  |\n|  resolution  | The number of vertices that should comprise the arc segment. |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L889\">\n    two.js:889\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makePoints\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makePoints\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makePoints</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Points\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  points  | An array of [Two.Vector](/docs/vector/) points |\n|  | Alternatively you can pass alternating `x` / `y` coordinate values as individual agrguments. These will be combined into [Two.Vector](/docs/vector/)s for use in the points object. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js points object and adds it to the current scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L923\">\n    two.js:923\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makePath\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makePath\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makePath</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Path\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  points  | An array of [Two.Anchor](/docs/anchor/) points |\n|  | Alternatively you can pass alternating `x` / `y` coordinate values as individual arguments. These will be combined into [Two.Anchor](/docs/anchor/)s for use in the path. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js path and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L954\">\n    two.js:954\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n::: tip nota-bene\nIn either case of passing an array or passing numbered arguments the last argument is an optional `Boolean` that defines whether the path should be open or closed.\n:::\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeText\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeText\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeText</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Text\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  message  |  |\n|  x  |  |\n|  y  |  |\n|  styles  | An object to describe any of the [Two.Text.Properties](/docs/text/#properties) including `fill`, `stroke`, `linewidth`, `family`, `alignment`, `leading`, `opacity`, etc.. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js text object and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1004\">\n    two.js:1004\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeLinearGradient\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeLinearGradient\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeLinearGradient</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.LinearGradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x1  |  |\n|  y1  |  |\n|  x2  |  |\n|  y2  |  |\n|  args  | Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js linear gradient and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1020\">\n    two.js:1020\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeRadialGradient\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeRadialGradient\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeRadialGradient</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.RadialGradient\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x1  |  |\n|  y1  |  |\n|  radius  |  |\n|  args  | Any number of color stops sometimes referred to as ramp stops. If none are supplied then the default black-to-white two stop gradient is applied. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js linear-gradient object and adds it to the scene. In the case of an effect it's added to an invisible \"definitions\" group.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1040\">\n    two.js:1040\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeSprite\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeSprite\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeSprite</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Sprite\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | The URL path to an image or an already created [Two.Texture](/docs/effects/texture/). |\n|  x  |  |\n|  y  |  |\n|  columns  |  |\n|  rows  |  |\n|  frameRate  |  |\n|  autostart  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js sprite object and adds it to the scene. Sprites can be used for still images as well as animations.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1059\">\n    two.js:1059\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeImage\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeImage\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeImage</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Image\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | The URL path to an image or an already created [Two.Texture](/docs/effects/texture/). |\n|  x  |  |\n|  y  |  |\n|  width  |  |\n|  height  |  |\n|  mode  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js image object and adds it to the scene. Images are scaled to fit the provided width and height.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1082\">\n    two.js:1082\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeImageSequence\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeImageSequence\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeImageSequence</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.ImageSequence\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | An array of paths or of [Two.Textures](/docs/effects/texture/). |\n|  x  |  |\n|  y  |  |\n|  frameRate  |  |\n|  autostart  |  |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js image sequence object and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1101\">\n    two.js:1101\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeTexture\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeTexture\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeTexture</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Texture\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  src  | The URL path to an image or a DOM image-like element. |\n|  callback  | Function to be invoked when the image is loaded. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js texture object.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1122\">\n    two.js:1122\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## makeGroup\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#makeGroup\"><span class=\"prefix\">Two.</span><span class=\"shortname\">makeGroup</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Group\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  objects  | Two.js objects to be added to the group in the form of an array or as individual arguments. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCreates a Two.js group object and adds it to the scene.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1135\">\n    two.js:1135\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## interpret\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#interpret\"><span class=\"prefix\">Two.</span><span class=\"shortname\">interpret</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Group\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  svg  | The SVG node to be parsed. |\n|  shallow  | Don't create a top-most group but append all content directly. |\n|  add  | – Automatically add the reconstructed SVG node to scene. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nInterpret an SVG Node and add it to this instance's scene. The distinction should be made that this doesn't `import` svg's, it solely interprets them into something compatible for Two.js - this is slightly different than a direct transcription.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1154\">\n    two.js:1154\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## load\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#load\"><span class=\"prefix\">Two.</span><span class=\"shortname\">load</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Group\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  pathOrSVGContent  | The URL path of an SVG file or an SVG document as text. |\n|  callback  | Function to call once loading has completed. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nLoad an SVG file or SVG text and interpret it into Two.js legible objects.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/two.js#L1185\">\n    two.js:1185\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/docs/vector/README.md",
    "content": "---\ntitle: Two.Vector\npageClass: docs\nlang: en-US\n---\n\n# Two.Vector\n\n\n<div class=\"extends\">\n\nExtends: [Two.Events](/docs/events/)\n\n</div>\n\n\nA class to store `x` / `y` component vector data. In addition to storing data `Two.Vector` has suped up methods for commonplace mathematical operations.\n\n\n<div class=\"meta\">\n  <custom-button text=\"Source\" type=\"source\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js\" />\n</div>\n\n\n<carbon-ads />\n\n\n### Constructor\n\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | Any number to represent the horizontal `x` component of the vector. |\n|  y  | Any number to represent the vertical `y` component of the vector. |\n\n\n\n<div class=\"static member \">\n\n## zero\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#zero\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">zero</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nHandy reference to a vector with component values 0, 0 at all times.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L77\">\n    vector.js:77\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## left\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#left\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">left</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nHandy reference to a vector with component values -1, 0 at all times.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L84\">\n    vector.js:84\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## right\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#right\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">right</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nHandy reference to a vector with component values 1, 0 at all times.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L91\">\n    vector.js:91\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## up\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#up\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">up</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nHandy reference to a vector with component values 0, -1 at all times.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L98\">\n    vector.js:98\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static member \">\n\n## down\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#down\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">down</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nHandy reference to a vector with component values 0, 1 at all times.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L105\">\n    vector.js:105\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## add\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#add\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">add</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Vector\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v1  | First [Two.Vector](/docs/vector/) |\n|  v2  | Second [Two.Vector](/docs/vector/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdd two vectors together.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L112\">\n    vector.js:112\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## sub\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#sub\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">sub</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Vector\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v1  | First [Two.Vector](/docs/vector/) |\n|  v2  | Second [Two.Vector](/docs/vector/) |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSubtract two vectors: `v2` from `v1`.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L124\">\n    vector.js:124\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## subtract\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subtract\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">subtract</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Vector.sub](/docs/vector/#sub).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L136\">\n    vector.js:136\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## ratioBetween\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#ratioBetween\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">ratioBetween</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\nThe ratio betwen two points `v1` and `v2`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v1  | First [Two.Vector](/docs/vector/) |\n|  v2  | Second [Two.Vector](/docs/vector/) |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L145\">\n    vector.js:145\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## angleBetween\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#angleBetween\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">angleBetween</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\nThe angle between points `v1` and `v2`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v1  | First [Two.Vector](/docs/vector/) |\n|  v2  | Second [Two.Vector](/docs/vector/) |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L156\">\n    vector.js:156\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## distanceBetween\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#distanceBetween\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">distanceBetween</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\nThe distance between points `v1` and `v2`. Distance is always positive.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v1  | First [Two.Vector](/docs/vector/) |\n|  v2  | Second [Two.Vector](/docs/vector/) |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L177\">\n    vector.js:177\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"static function \">\n\n## distanceBetweenSquared\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#distanceBetweenSquared\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">distanceBetweenSquared</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\nThe squared distance between points `v1` and `v2`.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v1  | First [Two.Vector](/docs/vector/) |\n|  v2  | Second [Two.Vector](/docs/vector/) |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L188\">\n    vector.js:188\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## x\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#x\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">x</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe horizontal x-component of the vector.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L62\">\n    vector.js:62\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance member \">\n\n## y\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#y\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">y</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n<div class=\"properties\">\n\n\nThe vertical y-component of the vector.\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L69\">\n    vector.js:69\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## set\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#set\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">set</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | Value of `x` component |\n|  y  | Value of `y` component |\n</div>\n\n\n\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L204\">\n    vector.js:204\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## copy\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#copy\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">copy</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The [Two.Vector](/docs/vector/) to copy |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCopy the `x` / `y` components of another object [Two.Vector](/docs/vector/).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L216\">\n    vector.js:216\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clear\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clear\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">clear</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nSet the `x` / `y` component values of the vector to zero.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L228\">\n    vector.js:228\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## clone\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#clone\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">clone</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Two.Vector\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nCreate a new vector and copy the existing values onto the newly created instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L239\">\n    vector.js:239\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## add\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#add\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">add</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The [Two.Vector](/docs/vector/) to add |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdd an object with `x` / `y` component values to the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L249\">\n    vector.js:249\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## add\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#add\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">add</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  n  | Number to add |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdd the **same** number to both `x` / `y` component values of the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L257\">\n    vector.js:257\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## add\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#add\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">add</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | Number to add to `x` component |\n|  y  | Number to add to `y` component |\n</div>\n\n\n\n\n<div class=\"description\">\n\nAdd `x` / `y` values to their respective component value on the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L265\">\n    vector.js:265\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## addSelf\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#addSelf\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">addSelf</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Vector.add](/docs/vector/#add).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L291\">\n    vector.js:291\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## sub\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#sub\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">sub</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The amount as a [Two.Vector](/docs/vector/) to subtract |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSubtract an object with `x` / `y` component values to the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L300\">\n    vector.js:300\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## sub\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#sub\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">sub</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  n  | Number to subtract |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSubtract the **same** number to both `x` / `y` component values of the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L308\">\n    vector.js:308\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## sub\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#sub\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">sub</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | Number to subtract from `x` component |\n|  y  | Number to subtract from `y` component |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSubtract `x` / `y` values to their respective component value on the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L316\">\n    vector.js:316\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## subtract\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subtract\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">subtract</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Vector.sub](/docs/vector/#sub).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L342\">\n    vector.js:342\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## subSelf\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subSelf\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">subSelf</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Vector.sub](/docs/vector/#sub).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L351\">\n    vector.js:351\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## subtractSelf\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#subtractSelf\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">subtractSelf</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Vector.sub](/docs/vector/#sub).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L360\">\n    vector.js:360\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## multiply\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiply\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">multiply</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The [Two.Vector](/docs/vector/) to multiply |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMultiply an object with `x` / `y` component values to the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L369\">\n    vector.js:369\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## multiply\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiply\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">multiply</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  n  | The number to multiply |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMultiply the **same** number to both x / y component values of the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L377\">\n    vector.js:377\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## multiply\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiply\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">multiply</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The number to multiply to `x` component |\n|  y  | The number to multiply to `y` component |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMultiply `x` / `y` values to their respective component value on the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L385\">\n    vector.js:385\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## multiplySelf\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiplySelf\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">multiplySelf</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Vector.multiply](/docs/vector/#multiply).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L411\">\n    vector.js:411\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## multiplyScalar\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#multiplyScalar\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">multiplyScalar</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  s  | The scalar to multiply by. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nMulitiply the vector by a single number. Shorthand to call [Two.Vector.multiply](/docs/vector/#multiply) directly.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L420\">\n    vector.js:420\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## divide\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#divide\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">divide</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The [Two.Vector](/docs/vector/) to divide |\n</div>\n\n\n\n\n<div class=\"description\">\n\nDivide an object with `x` / `y` component values to the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L430\">\n    vector.js:430\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## divide\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#divide\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">divide</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  n  | The number to divide |\n</div>\n\n\n\n\n<div class=\"description\">\n\nDivide the **same** number to both x / y component values of the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L438\">\n    vector.js:438\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function overloaded\">\n\n## divide\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#divide\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">divide</span></a></h2>\n\n\n<div class=\"overloaded-label\">\n\n_Overloaded_\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  x  | The number to divide on the `x` component |\n|  y  | The number to divide on the `y` component |\n</div>\n\n\n\n\n<div class=\"description\">\n\nDivide `x` / `y` values to their respective component value on the instance.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L446\">\n    vector.js:446\n  </a>\n\n</div>\n\n\n\n<div class=\"tags\">\n\n\n\n</div>\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## divideSelf\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#divideSelf\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">divideSelf</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nAlias for [Two.Vector.divide](/docs/vector/#divide).\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L478\">\n    vector.js:478\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## divideScalar\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#divideScalar\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">divideScalar</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  s  | The scalar to divide by. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nDivide the vector by a single number. Shorthand to call [Two.Vector.divide](/docs/vector/#divide) directly.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L487\">\n    vector.js:487\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## negate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#negate\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">negate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nInvert each component's sign value.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L497\">\n    vector.js:497\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## dot\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#dot\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">dot</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nGet the [dot product](https://en.wikipedia.org/wiki/Dot_product) of the vector.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L506\">\n    vector.js:506\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## length\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#length\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">length</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nGet the length of a vector.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L516\">\n    vector.js:516\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## lengthSquared\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#lengthSquared\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">lengthSquared</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nGet the length of the vector to the power of two. Widely used as less expensive than [Two.Vector.length](/docs/vector/#length) because it isn't square-rooting any numbers.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L526\">\n    vector.js:526\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## normalize\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#normalize\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">normalize</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nNormalize the vector from negative one to one.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L536\">\n    vector.js:536\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## distanceTo\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#distanceTo\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">distanceTo</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nGet the distance between two vectors.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L545\">\n    vector.js:545\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## distanceToSquared\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#distanceToSquared\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">distanceToSquared</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Number\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nGet the distance between two vectors to the power of two. Widely used as less expensive than [Two.Vector.distanceTo](/docs/vector/#distanceto) because it isn't square-rooting any numbers.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L555\">\n    vector.js:555\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## setLength\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#setLength\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">setLength</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  l  | length to set vector to. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nSet the length of a vector.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L567\">\n    vector.js:567\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## equals\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#equals\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">equals</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Boolean\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The vector to compare against. |\n|  eps  | An options epsilon for precision. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nQualify if one vector roughly equal another. With a margin of error defined by epsilon.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L577\">\n    vector.js:577\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## lerp\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#lerp\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">lerp</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  v  | The destination vector to step towards. |\n|  t  | The zero to one value of how close the current vector gets to the destination vector. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nLinear interpolate one vector to another by an amount `t` defined as a zero to one number.\n\n</div>\n\n\n\n<div class=\"see\">\n\nSee: [Matt DesLauriers](https://twitter.com/mattdesl/status/1031305279227478016) has a good thread about this.\n\n</div>\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L590\">\n    vector.js:590\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## isZero\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#isZero\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">isZero</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Boolean\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  eps  | Optional precision amount to check against. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nCheck to see if vector is roughly zero, based on the `epsilon` precision value.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L604\">\n    vector.js:604\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toString\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toString\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">toString</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: String\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a comma-separated string of x, y value. Great for storing in a database.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L616\">\n    vector.js:616\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## toObject\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#toObject\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">toObject</span></a></h2>\n\n\n\n\n<div class=\"returns\">\n\n__Returns__: Object\n\n\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"description\">\n\nReturn a JSON compatible plain object that represents the vector.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L626\">\n    vector.js:626\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n\n<div class=\"instance function \">\n\n## rotate\n\n<h2 class=\"longname\" aria-hidden=\"true\"><a href=\"#rotate\"><span class=\"prefix\">Two.Vector.</span><span class=\"shortname\">rotate</span></a></h2>\n\n\n\n\n\n\n\n\n\n\n\n\n<div class=\"params\">\n\n| Argument | Description |\n| ---- | ----------- |\n|  radians  | The amount to rotate the vector by in radians. |\n</div>\n\n\n\n\n<div class=\"description\">\n\nRotate a vector.\n\n</div>\n\n\n\n\n\n<div class=\"meta\">\n\n  <a class=\"lineno\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/jonobr1/two.js/blob/main/src/vector.js#L636\">\n    vector.js:636\n  </a>\n\n</div>\n\n\n\n\n</div>\n\n\n"
  },
  {
    "path": "wiki/donate/README.md",
    "content": "<redirect-page src=\"https://github.com/sponsors/jonobr1\"></redirect-page>\n"
  },
  {
    "path": "wiki/examples/README.md",
    "content": "---\npageClass: examples\ntitle: Two.js Examples\nlang: en-US\n---\n\n# Examples\n\nVarious examples of how to use different features of Two.js with associated tags for convenient filtering through the global search bar up at the top. If you've made something with Two.js you'd like to share, then please [submit](https://github.com/jonobr1/two.js/issues/new?assignees=&labels=output&template=output.md&title=%5BOutput%5D) it.\n\n<carbon-ads />\n\n### Official Examples\n\n<div class=\"examples-wrapper\">\n\n<example-card\n  title=\"Advanced Anchors\"\n  href=\"https://codepen.io/jonobr1/pen/jOLQoov\"\n  src=\"https://shots.codepen.io/jonobr1/pen/jOLQoov-512.jpg\"\n  tags=\"import, interpret, mouse, points, vertices, ui\" />\n\n<example-card\n  title=\"Animate Strokes\"\n  href=\"https://codepen.io/jonobr1/pen/qBXgQQN\"\n  src=\"https://shots.codepen.io/jonobr1/pen/qBXgQQN-512.jpg\"\n  tags=\"import, interpret, mouse, beginning, ending\" />\n\n<example-card\n  title=\"Clipping Mask\"\n  href=\"https://codepen.io/jonobr1/pen/abyRyoY\"\n  src=\"https://shots.codepen.io/jonobr1/pen/abyRyoY-512.jpg\"\n  tags=\"import, mouse, shapes, grid, sine\" />\n\n<example-card\n  title=\"Depth via Groups\"\n  href=\"https://codepen.io/jonobr1/pen/qBXLzMq\"\n  src=\"https://shots.codepen.io/jonobr1/pen/qBXLzMq-800.jpg\"\n  tags=\"import, shapes, animated, non-interactive\" />\n\n<example-card\n  title=\"Dynamic Text\"\n  href=\"https://codepen.io/jonobr1/pen/MWvVBdx\"\n  src=\"https://shots.codepen.io/username/pen/MWvVBdx-512.jpg\"\n  tags=\"es6, import, text, vector, keypress\" />\n\n<example-card\n  title=\"Dynamic Vertices\"\n  href=\"https://codepen.io/jonobr1/pen/gOxQRXd\"\n  src=\"https://shots.codepen.io/jonobr1/pen/gOxQRXd-512.jpg\"\n  tags=\"es6, import, vector, mouse, path\" />\n\n<example-card\n  title=\"Get Point on Path\"\n  href=\"https://codepen.io/jonobr1/pen/OJjBwwG\"\n  src=\"https://shots.codepen.io/jonobr1/pen/OJjBwwG-512.jpg\"\n  tags=\"import, shapes, mouse, sine\" />\n\n<example-card\n  title=\"Gradients\"\n  href=\"https://codepen.io/jonobr1/pen/yLoEEQJ\"\n  src=\"https://shots.codepen.io/username/pen/yLoEEQJ-512.jpg\"\n  tags=\"es6, import, vector, mouse, radial-gradient, linear-gradent\" />\n\n<example-card\n  title=\"Interpret SVG's\"\n  href=\"https://codepen.io/jonobr1/pen/KKvYXzp\"\n  src=\"https://shots.codepen.io/jonobr1/pen/KKvYXzp-512.jpg\"\n  tags=\"es6, import, animated, non-interactive, vertices, scale\" />\n\n<example-card\n  title=\"Morphing Vertices\"\n  href=\"https://codepen.io/jonobr1/pen/wvqRLbR\"\n  src=\"https://shots.codepen.io/jonobr1/pen/wvqRLbR-512.jpg\"\n  tags=\"import, animated, non-interactive, vertices, circle\" />\n\n<example-card\n  title=\"Particle Sandbox\"\n  href=\"https://codesandbox.io/s/twojs-particle-sandbox-wsxcx\"\n  src=\"https://codesandbox.io/api/v1/sandboxes/wsxcx/screenshot.png\"\n  tags=\"import, react, sandbox, vertices, position, rotation, scale, canvas, webgl, svg, test, renderers, memory\" />\n\n<example-card\n  title=\"Rendering Types\"\n  href=\"https://codepen.io/jonobr1/pen/NWvJZPp\"\n  src=\"https://shots.codepen.io/jonobr1/pen/NWvJZPp-512.jpg\"\n  tags=\"import, animated, mouse, easing\" />\n\n<example-card\n  title=\"Rubber Ball\"\n  href=\"https://codepen.io/jonobr1/pen/mdMzzQB\"\n  src=\"https://shots.codepen.io/jonobr1/pen/mdMzzQB-512.jpg\"\n  tags=\"import, vector, mouse, circle, vertices\" />\n\n<example-card\n  title=\"Simple Pen Tool\"\n  href=\"https://codepen.io/jonobr1/pen/gOxNejb\"\n  src=\"https://shots.codepen.io/jonobr1/pen/gOxNejb-512.jpg\"\n  tags=\"import, vector, mouse, ui, vertices, points, layers, group\" />\n\n<example-card\n  title=\"Transform Shape\"\n  href=\"https://codepen.io/jonobr1/pen/QWqMmma\"\n  src=\"https://shots.codepen.io/username/pen/QWqMmma-1280.webp\"\n  tags=\"helper, drag, ui, mouse, translate, rotate, scale\" />\n\n<example-card\n  title=\"Watch Face\"\n  href=\"https://codepen.io/jonobr1/pen/MWEzMGv\"\n  src=\"https://shots.codepen.io/jonobr1/pen/MWEzMGv-512.jpg\"\n  tags=\"line, dashes, group, cap, rotation, text, non-interactive\" />\n\n<example-card\n  title=\"With Tween.js\"\n  href=\"https://codepen.io/jonobr1/pen/dyzrZjM\"\n  src=\"https://shots.codepen.io/jonobr1/pen/dyzrZjM-512.jpg\"\n  tags=\"import, third-party, animated, non-interactive\" />\n\n<example-card\n  title=\"Zoom & Pan UI\"\n  href=\"https://codepen.io/jonobr1/pen/PobMKwb\"\n  src=\"https://shots.codepen.io/jonobr1/pen/PobMKwb-512.jpg\"\n  tags=\"mouse, drag, ui, zui\" />\n\n</div>\n\n---\n\n### Community Examples\n\n<div class=\"examples-wrapper\">\n\n<example-card\n  title=\"Folded Corner\"\n  href=\"https://codepen.io/jonobr1/pen/yLwMOmj\"\n  src=\"https://assets.codepen.io/171564/internal/screenshots/pens/yLwMOmj.custom.png?version=1706554035\"\n  tags=\"animated, interactive, geometry, mouse\" />\n\n<example-card\n  title=\"Video Texture\"\n  href=\"https://codepen.io/jonobr1/pen/PoazvEv\"\n  src=\"https://assets.codepen.io/171564/internal/screenshots/pens/PoazvEv.custom.png\"\n  tags=\"animated, video, texture\" />\n\n<example-card\n  title=\"Animated Spritesheet\"\n  href=\"https://codepen.io/jonobr1/pen/ZEowwQR\"\n  src=\"https://archive.jono.fyi/forums/codepen/ken-stance.png\"\n  tags=\"sprite, animated, image, bitmap\" />\n\n<example-card\n  title=\"Luniland\"\n  href=\"https://luniland.halabe.com/\"\n  src=\"https://user-images.githubusercontent.com/44894/151491608-bdfd3cf4-2a44-4ea7-b983-b2d9d9a033cd.png\"\n  tags=\"game, interactive, animated, gsap, physics, space\" />\n\n<example-card\n  title=\"Two.js as Three.js Texture\"\n  href=\"https://codepen.io/jonobr1/pen/RwjPVZg\"\n  src=\"https://assets.codepen.io/171564/internal/screenshots/pens/RwjPVZg.custom.png\"\n  tags=\"animated, 3d, non-interactive, text, canvas\" />\n\n<example-card\n  title=\"Download Scene as SVG\"\n  href=\"https://codepen.io/jonobr1/pen/ZEXeojy\"\n  src=\"https://shots.codepen.io/jonobr1/pen/ZEXeojy-512.jpg\"\n  tags=\"interactive, button, export\" />\n\n<example-card\n  title=\"Animated Flag\"\n  href=\"https://observablehq.com/@jonobr1/turn-any-svg-logo-into-an-animated-flag-using-two-js\"\n  src=\"https://static.observableusercontent.com/thumbnail/027c323c66df1be2917aa63b46326981c548508d5227b970ba85200a8cdc0391.jpg\"\n  tags=\"animated, logo, non-interactive, sine, svg\" />\n\n<example-card\n  title=\"Arrow\"\n  href=\"https://jsfiddle.net/jonobr1/0reh49of/\"\n  tags=\"static, simple, primitive, non-interactive\" />\n\n<example-card\n  title=\"Basic Angular Template\"\n  href=\"https://codesandbox.io/s/nostalgic-volhard-6zzfd\"\n  src=\"https://screenshots.codesandbox.io/6zzfd/8.png\"\n  tags=\"angular\" />\n\n<example-card\n  title=\"Basic React Template\"\n  href=\"https://codesandbox.io/s/sharp-proskuriakova-h5weu\"\n  src=\"https://screenshots.codesandbox.io/ygxbc/0.png\"\n  tags=\"react\" />\n\n<example-card\n  title=\"Basic GSAP Animation Setup\"\n  href=\"https://codepen.io/jonobr1/pen/YzYZgvq\"\n  src=\"https://assets.codepen.io/171564/internal/screenshots/pens/YzYZgvq.custom.png\"\n  tags=\"gsap, interactive, animated, rotation, interpret\" />\n\n<example-card\n  title=\"Dashes\"\n  href=\"https://jsfiddle.net/jonobr1/x1gc2d0L/\"\n  tags=\"circle, animated, non-interactive, simple\" />\n\n<example-card\n  title=\"Fake Camera\"\n  href=\"https://codepen.io/jonobr1/pen/wvmYXXr\"\n  src=\"https://assets.codepen.io/171564/internal/screenshots/pens/wvmYXXr.custom.png?version=1664319669\"\n  tags=\"animated, group, position, non-interactive\" />\n\n<example-card\n  title=\"GM: Animated Sticker\"\n  href=\"https://codepen.io/jonobr1/pen/QWqdVLb\"\n  src=\"https://shots.codepen.io/jonobr1/pen/QWqdVLb-512.jpg\"\n  tags=\"svg, interpret, getComputedMatrix, gradient, non-interactive, animated\" />\n\n<example-card\n  title=\"Haiku Generator\"\n  href=\"https://glitch.com/~haiku-generator\"\n  src=\"https://cdn.glitch.me/project-avatar/fb84449e-ddab-4960-a4b0-6e08a0d280a6.png?2019-03-09T12:31:45.056Z\"\n  tags=\"text, mouse, animated\" />\n\n<example-card\n  title=\"Handshake\"\n  href=\"https://codepen.io/jonobr1/pen/MWaeWRr\"\n  src=\"https://shots.codepen.io/jonobr1/pen/MWaeWRr-512.jpg\"\n  tags=\"animated, non-interactive, load, image\" />\n\n<example-card\n  title=\"Insta Logo\"\n  href=\"https://codepen.io/lrsordi/pen/rvWZPm\"\n  src=\"https://shots.codepen.io/username/pen/rvWZPm-512.jpg\"\n  tags=\"animated, mouse, logo, TweenMax\" />\n\n<example-card\n  title=\"Over Google Maps\"\n  href=\"https://jsfiddle.net/jonobr1/sj1q47vp/\"\n  tags=\"text, interactive, ui\" />\n\n<example-card\n  title=\"Points\"\n  href=\"https://jsfiddle.net/jonobr1/r2zumg0w/9/\"\n  tags=\"static, simple, primitive, non-interactive, styles\" />\n\n<example-card\n  title=\"Rain\"\n  href=\"https://codepen.io/jonobr1/pen/eedpwP\"\n  src=\"https://shots.codepen.io/jonobr1/pen/eedpwP-512.jpg\"\n  tags=\"animated, mouse, line, stroke\" />\n\n<example-card\n  title=\"Rainbow\"\n  href=\"https://codepen.io/jonobr1/pen/ieFnh\"\n  src=\"https://shots.codepen.io/jonobr1/pen/ieFnh-512.jpg\"\n  tags=\"animated, non-interactive, beginning, ending\" />\n\n<example-card\n  title=\"South Tirol\"\n  href=\"https://observablehq.com/@ivanbacher/map-drawing-with-two-js-south-tirol-1\"\n  src=\"https://static.observableusercontent.com/thumbnail/75d334ea0e8fe1497f061bf43b6a1e2e624dedb7535e9fecb66dff451b0bd6ea.jpg\"\n  tags=\"map, third-party, outline, static, bw\" />\n\n<example-card\n  title=\"Sparks\"\n  href=\"https://codepen.io/jonobr1/pen/yRpoPQ\"\n  src=\"https://shots.codepen.io/jonobr1/pen/yRpoPQ-512.jpg\"\n  tags=\"animated, mouse, line, stroke\" />\n\n<example-card\n  title=\"Spring Tree\"\n  href=\"https://codepen.io/jonobr1/pen/BmKRGj\"\n  src=\"https://shots.codepen.io/jonobr1/pen/BmKRGj-512.jpg\"\n  tags=\"mouse, drag, physics, geometry\" />\n\n<example-card\n  title=\"Sprite Sheet: Circle\"\n  href=\"https://observablehq.com/@jonobr1/two-js-circle-sprite-sheet\"\n  src=\"https://static.observableusercontent.com/thumbnail/d803971cbf312f3042824d68a6b1612fad4ac031bba1a50238503881f3729999.jpg\"\n  tags=\"static, non-interactive, canvas, grid, sprite\" />\n\n<example-card\n  title=\"Starfield\"\n  href=\"https://observablehq.com/@phocks/two-js-starfield-experiment\"\n  src=\"https://static.observableusercontent.com/thumbnail/b4fbd836d41dee4ec08452748179567361af623aaca05c8048bdf8b7de31effc.jpg\"\n  tags=\"animated, non-interactive, webgl\" />\n\n<example-card\n  title=\"Swash Sword\"\n  href=\"https://codepen.io/jonobr1/pen/Nwrdpp\"\n  src=\"https://shots.codepen.io/jonobr1/pen/Nwrdpp-512.jpg\"\n  tags=\"mouse, drag, interpret, physics\" />\n\n<example-card\n  title=\"TV Jitter\"\n  href=\"https://codepen.io/jonobr1/pen/WXPEgJ\"\n  src=\"https://shots.codepen.io/jonobr1/pen/WXPEgJ-800.jpg?version=1636856\"\n  tags=\"animated, non-interactive, logo\" />\n\n<example-card\n  title=\"Twisting Squares\"\n  href=\"https://codepen.io/daniel-hult/pen/bGbojYm\"\n  tags=\"animated, non-interactive, rectangle, rotation\" />\n\n<example-card\n  title=\"Animated Logo\"\n  href=\"https://codepen.io/jonobr1/pen/poyBggB\"\n  src=\"https://shots.codepen.io/jonobr1/pen/poyBggB-512.jpg\"\n  tags=\"animated, non-interactive, logo, shapes\" />\n\n<example-card\n  title=\"USA\"\n  href=\"https://codepen.io/jonobr1/pen/ORrrvY\"\n  src=\"https://shots.codepen.io/jonobr1/pen/ORrrvY-512.jpg\"\n  tags=\"animated, stop-motion, svg, interpret\" />\n\n<example-card\n  title=\"Wavy Road\"\n  href=\"https://codepen.io/jonobr1/pen/ajkdp\"\n  src=\"https://shots.codepen.io/jonobr1/pen/ajkdp-512.jpg\"\n  tags=\"mouse, curve, line, dashes, group\" />\n\n<example-card\n  title=\"Word Stacks\"\n  href=\"https://codepen.io/jonobr1/pen/qBONdKX\"\n  src=\"https://shots.codepen.io/jonobr1/pen/qBONdKX-800.jpg?version=1636856\"\n  tags=\"physics, mouse, drag, text\" />\n\n</div>\n\n\n<br />\n\n#### Even More References\n\nThere are many more examples that exist out on the internet. Some starting points are:\n\n- [Two.js Collection on Codepen](https://codepen.io/collection/DRdLJk)\n- [A Beginner's Guide to Drawing 2D Graphics With Two.js](https://code.tutsplus.com/tutorials/a-beginners-guide-to-drawing-2d-graphics-using-twojs--cms-31681)\n- [Drawing and Animating with Two.js and Illustrator](https://modernweb.com/drawing-and-animating-with-two-js-and-illustrator/)\n- [Two.js tutor as a ChatGPT bot](https://chatgpt.com/g/g-hkcTX8uPm-two-js-tutor)\n"
  },
  {
    "path": "wiki/incident-response-plan/README.md",
    "content": "---\npageClass: fine-print\ntitle: Two.js Incident Response Plan\nlang: en-US\n---\n\n# Incident Response Plan\n\n### Principles:\n\n- **Transparency:** All incidents and fixes are documented here for the community.\n- **Stewardship:** Take responsibility for protecting users and the project.\n- **Protection:** Act to minimize harm and provide guidance.\n\n# How Two.js Handles Incidents\n\n### 1. Detection & Triage\n\n- We monitor security reports sent via [security](/security) outreach, GitHub advisories, issues, and npm notifications.\n- If we spot a bug or report that looks like a security risk, we treat it as an incident.\n\n### 2. Assessment\n- Check the severity:\n  - **Critical:** npm package or repo compromised, malicious code, supply chain attack.\n  - **High:** Vulnerabilities that allow code execution, XSS, or leak secrets.\n  - **Medium:** Denial of service, memory leaks, or integrity issues.\n  - **Low:** Docs defacement, minor regressions.\n\n### 3. Response\n- Acknowledge the report (privately if sensitive, publicly if not).\n- For critical/high issues:\n  - Rotate any exposed secrets/tokens.\n  - Patch the bug or vulnerability.\n  - Deprecate or yank affected npm versions if needed.\n  - Rebuild and redeploy docs/site from a clean commit.\n- For medium/low issues:\n  - Patch and document the fix.\n\n### 4. Communication\n- Update this wiki page with a summary of the incident and the fix.\n- For major issues, we post a GitHub Release note and a pinned Issue.\n- Provide upgrade or mitigation steps for users.\n\n### 5. Recovery & Hardening\n- After fixing, review what happened and update this process if needed.\n- Add tests or automation to prevent similar issues.\n- Rotate credentials and check repo/npm security settings.\n\n<br />\n\n---\n\n# Recent Incidents & Fixes\n\n<br />\n\n- None at this time\n\n<br />\n\n---\n\n**If you spot a security issue, please report it via [security](/security) outreach. We’ll respond as quickly as possible.**\n"
  },
  {
    "path": "wiki/privacy/README.md",
    "content": "# Privacy Policy\n\n- Library: Two.js library code does not collect or send user data.\n- Website: This docs site uses Google Analytics (GA4).\n- Fonts: Third party fonts are provided by Adobe through Typekit\n- Data Collected (website): Page views, browser/device info, approximate location; no user accounts.\n<!-- - Legal Bases: GDPR consent (EU/UK), opt-out under CCPA/CPRA (US-CA), similar under LGPD (BR) and PIPEDA (CA). -->\n- Retention: Analytics event data retained for up to 14 months.\n- Data Processors: Google (Analytics). Standard Contractual Clauses apply; see Google’s DPA.\n- Your Rights: Access, deletion, portability (where applicable). To exercise rights, contact [inquiries+two.js@jono.fyi](mailto:inquiries+two.js@jono.fyi).\n- Contact: [inquiries+two.js@jono.fyi](inquiries+two.js@jono.fyi)"
  },
  {
    "path": "wiki/security/README.md",
    "content": "---\npageClass: fine-print\ntitle: Two.js Security Policy\nlang: en-US\n---\n\n# Notes on Safety\n\nWhen using Two.js or any other client-side rendering library we recommend considering:\n\n- Handle untrusted SVGs carefully: Do not load or interpret SVGs from untrusted users without sanitizing. Malicious SVGs can embed external references and scripts. Use an SVG sanitizer and set appropriate `Content-Security-Policy`.\n- Images and external assets: Prefer same-origin or vetted hosts. Disable `allow-scripts` in any embedders/iframes and avoid inline event handlers.\n- CSP recommended defaults: Consider a CSP that restricts scripts to self and trusted CDNs, disallows inline/eval, and sets `object-src 'none'`.\n\n::: tip Note\nThe Two.js library does not collect user data. If you embed Two.js in a site that handles user content, apply your own input validation, rate limiting, and abuse reporting workflow.\n:::\n\n# Security Policy\n\nIf you have discovered a security vulnerability in this project, please report it\nprivately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.\n\n**You may submit the report in the following ways:**\n\n- Github users can privately report security advisories directly [here](https://github.com/jonobr1/two.js/security/advisories/new)\n\n- Send an email to [inquiries+two.js@jono.fyi](mailto:inquiries+two.js@jono.fyi).\n\n**Please provide the following information in your report:**\n\n- The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting)\n- Full paths of source file(s) related to the manifestation of the issue\n- The location of the affected source code (tag/branch/commit or direct URL)\n- Any special configuration required to reproduce the issue\n- Step-by-step instructions to reproduce the issue\n- Proof-of-concept or exploit code (if possible)\n- Impact of the issue, including how an attacker might exploit the issue\n\nThis project is maintained by volunteers on a reasonable-effort basis. As such, we ask that you give us 90 days to work on a fix before public exposure.\n\n<br />\n\n---\n\n<br />\n\n_Two.js conforms to this [Incident Response Plan](https://two.js.org/incident-response-plan) in moments of security risks._"
  }
]