diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..79034149 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,41 @@ +module.exports = { + extends: ['@pixi/eslint-config', 'plugin:prettier/recommended'], + rules: { + 'prettier/prettier': [ + 'warn', + { + semi: true, + trailingComma: 'es5', + singleQuote: true, + printWidth: 180, + tabWidth: 4, + useTabs: false, + }, + ], + + // special rules because most of this code is lifted from spine + // I would love to not have to do this, but I don't have the time nor the energy to fix the entire lib. Milton - 2023 + + eqeqeq: 0, + '@typescript-eslint/no-unused-vars': 0, + '@typescript-eslint/no-empty-function': 0, + 'no-console': 0, + '@typescript-eslint/no-use-before-define': 0, + radix: 0, + 'no-eq-null': 0, + 'no-constant-condition': 0, + 'no-prototype-builtins': 0, + camelcase: 0, + '@typescript-eslint/ban-ts-comment': 0, + 'no-case-declarations': 0, + '@typescript-eslint/no-useless-constructor': 0, + 'consistent-return': 0, + 'no-loop-func': 0, + '@typescript-eslint/ban-types': 0, + '@typescript-eslint/no-empty-interface': 0, + '@typescript-eslint/adjacent-overload-signatures': 0, + 'no-fallthrough': 0, + '@typescript-eslint/no-unused-expressions': 0, + 'max-params': 0, + }, +}; diff --git a/.gitignore b/.gitignore index 91637055..674aaa6c 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ lib dist bundles/*/index.d.ts packages/*/index.d.ts + +# ignore the npmrc +.npmrc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 817889c7..6903870c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,22 +28,21 @@ steps to reproduce, etc. "X isn't working!!!1!" will probably just be closed. ## Making Changes -To build the library you will need to download node.js from [nodejs.org][20]. After it has been installed open a -console and run `npm install -g yarn` to install the global `yarn` executable. +To build the library you will need to download node.js from [nodejs.org][20]. + +This monorepo uses Lerna and NX in the background. After that you can clone the repository and run `npm install` inside the cloned folder. This will install -dependencies necessary for building the project. You can rebuild the project by running `yarn build` in the cloned +dependencies necessary for building the project. You can rebuild the project by running `npm build` in the cloned folder. +Please, before sending your commit make sure that `npm run lint` returns no errors nor warnings. You can use `npm run lint:fix` to automagically fix most issues but you still might need to fix them manually. + Once that is ready, you can make your changes and submit a Pull Request: - **Send Pull Requests to the `master` branch.** All Pull Requests must be sent to the `master` branch, which is where all "bleeding-edge" development takes place. -- **Never commit new builds.** When making a code change you should always run `yarn build` which will rebuild the project -so you can test, *however* please do not commit the new builds placed in `dist/` or your PR will be closed. By default -the build process will output to an ignored folder (`build/`) you should be fine. - - **Only commit relevant changes.** Don't include changes that are not directly relevant to the fix you are making. The more focused a PR is, the faster it will get attention and be merged. Extra files changing only whitespace or trash files will likely get your PR closed. diff --git a/README.md b/README.md index f2aaf7a1..367c61c8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,19 @@ # pixi-spine -Spine 3.7, 3.8, 4.0, 4.1 implementation for PixiJS v5 & v6. +Spine 3.7, 3.8, 4.0, 4.1 implementation for PixiJS. -Typescript definitions are up-to-date with PixiJS v6. +### Versions Compatibility + +| PixiJS | pixi-spine | +|---|---| +| v5.x - v6.x | v3.x | +| v7.x | v4.x | For spine < 3.7 support is limited, but accepting PR's for `runtime-3.7` package. -For previous versions of pixi & typescript definitions - please refer to [README in pixi5](https://github.com/pixijs/pixi-spine/tree/pixi5/#readme) +For previous versions of pixi refer to +- [README in pixi6](https://github.com/pixijs/pixi-spine/tree/pixi6/#readme) +- [README in pixi5](https://github.com/pixijs/pixi-spine/tree/pixi5/#readme) Demos: @@ -18,86 +25,73 @@ https://sbfkcel.github.io/pixi-spine-debug/ ## Basic Usage -Please read this carefully: there are three ways to add this lib to your app. +Please read this carefully: there are many ways to add this lib to your app. -1. Angular, React, Webpack, Rollup - if you know those words, use ES6 bundles -2. Good old ` - -``` - -```js -const animation = new PIXI.spine.Spine(resources.spineCharacter.spineData); -``` +### Browser builds -Unfortunately, there are no typescript definitions for vanilla build on both `pixi` v6 and `pixi-spine` +For browser builds, you will need to grab either the `.js` (for CJS) file or the `.mjs` (for ESM) from the `dist` folder or from your CDN of choice. ### Custom bundle -Main bundle `pixi-spine` weights more than 1 MB. +Main bundle `pixi-spine` weights 374 KB (unzipped). -Bundle `@pixi-spine/all-3.8` weights about 400 KB. +Bundle `@pixi-spine/all-3.8` weights about 165 KB (unzipped). If you want to use different version (3.7) please look how modules `loader-3.8` and `pixi-spine-3.8` are made. -Basically, you have to copy its code in a separate file in your project, and alter imports to corresonding version. +Basically, you have to copy its code in a separate file in your project, and alter imports to corresponding version. For example, here's bundle for 3.8: ```js -import {SpineParser} from '@pixi-spine/loader-3.8'; -export {SpineParser}; +import '@pixi-spine/loader-3.8'; // Side effect install the loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports export * from '@pixi-spine/runtime-3.8'; export * from '@pixi-spine/base'; - -SpineParser.registerLoaderPlugin(); ``` In case author was too lazy to publish`loader-3.7`, you can do the same trick with them, just look in sources of `loader-3.8`. @@ -109,7 +103,7 @@ Read our [docs](examples/index.md). ### Two-color tint Light-dark tint is supported with help of [pixi-heaven](https://github.com/gameofbombs/pixi-heaven) -Currently supported only by UMD build. +Currently supported only by UMD build. (and most likely on PixiJS < 7.x) ```js let spine = new PIXI.heaven.Spine(spineData); @@ -165,27 +159,23 @@ If you want to create your own debugger you can extend `SpineDebugRenderer` or c You will need to have [node][node] setup on your machine. -Make sure you have [rush][rush] installed: - -```bash -npm install -g @microsoft/rush -``` - Then you can install dependencies and build: ```bash -npm run prepare +npm install npm run build ``` -That will output the built all modules. UMD can be found in `./bundles/pixi-spine/dist`. +That will build all packages and bundles. Browser packages are inside `dist` and npm packages are inside `lib` -If you use IntellIJ Idea / Webstorm to navigate the project, take this line and set it in **project settings** / **exclude files** - -``` -packages/*/node_modules;packages/*/compile;bundles/*/node_modules;bundles/*/compile;lib;dist -``` +`npm link` will misbehave because of the monorepo setup. [node]: https://nodejs.org/ [typescript]: https://www.typescriptlang.org/ -[rush]: https://rushjs.io/ + +## Deploying + +If you have enough rights to publish this monorepo, you can publish by running `npm run lernaPublish` +This is so that it runs with the internal npm v8 since npm v9 doesn't play nice with Lerna. + +If for some reason your publish failed, use `npm run lernaPublish:fromPackage` to try to force a publish without creating a new version \ No newline at end of file diff --git a/bundles/all-3.8/package.json b/bundles/all-3.8/package.json index 4d453d54..62f87a81 100644 --- a/bundles/all-3.8/package.json +++ b/bundles/all-3.8/package.json @@ -2,20 +2,50 @@ "name": "@pixi-spine/all-3.8", "version": "3.1.2", "description": "Pixi integration with EsotericSoftware Spine, big, contains runtime for 3.8", - "main": "lib/all-3.8.js", - "module": "lib/all-3.8.es.js", - "bundle": "dist/pixi-spine-3.8.umd.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/pixi-spine-3.8.js", + "bundleModule": "dist/pixi-spine-3.8.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine", + "@pixi-spine/loader-3.8": "PIXI.spine", + "@pixi-spine/loader-base": "PIXI.spine", + "@pixi-spine/runtime-3.8": "PIXI.spine38" + } + }, "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-3.8": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/runtime-3.8": "~3.1.2" + "@pixi-spine/base": "*", + "@pixi-spine/loader-3.8": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-3.8": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,7 +69,8 @@ "Chad Engler ", "Richard Davey ", "Shukant K. Pal ", - "Yevhenii Huselietov " + "Yevhenii Huselietov ", + "Milton Candelero " ], "bugs": { "url": "https://github.com/pixijs/pixi-spine/issues" @@ -47,14 +78,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/bundles/all-3.8/rollup.config.js b/bundles/all-3.8/rollup.config.js deleted file mode 100644 index 7596c9ce..00000000 --- a/bundles/all-3.8/rollup.config.js +++ /dev/null @@ -1,35 +0,0 @@ -const { main } = require('@pixi-spine/rollup-config/main'); -const pkg = require('./package.json'); - -const results = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine', - '@pixi-spine/runtime-3.8': 'PIXI.spine38', - '@pixi-spine/loader-base': 'PIXI.spine', - '@pixi-spine/loader-3.8': 'PIXI.spine', - }, -}); - -// Find all the peer deps. Note: This assumes we have only two levels of peer deps. -let umdDeps = []; -const deps = Object.keys(pkg.dependencies || {}); -for (let dep of deps) { - const p = require(`${dep}/package.json`); - umdDeps = umdDeps.concat(Object.keys(p.peerDependencies || {})); -} - -const license1 = 'is licensed under the MIT License.\n * http://www.opensource.org/licenses/mit-license'; -const licenseSpine = 'is licensed under SPINE-LICENSE\n * http://esotericsoftware.com/spine-runtimes-license'; - -results.forEach((entry) => { - if (entry.output.banner) { - entry.output.banner = entry.output.banner.replace(license1, licenseSpine); - } - if (entry.output.format === 'umd') { - entry.external = entry.external.filter((moduleName) => { - return moduleName.indexOf('@pixi-spine') !== 0; - }).concat(umdDeps); - } -}) - -module.exports = results; diff --git a/bundles/all-3.8/rollup.config.mjs b/bundles/all-3.8/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/bundles/all-3.8/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/bundles/all-3.8/src/index.ts b/bundles/all-3.8/src/index.ts index d536a3ab..b4781b31 100644 --- a/bundles/all-3.8/src/index.ts +++ b/bundles/all-3.8/src/index.ts @@ -1,6 +1,4 @@ -import {SpineParser} from '@pixi-spine/loader-3.8'; -export {SpineParser}; +import '@pixi-spine/loader-3.8'; // Side effect install the loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports export * from '@pixi-spine/runtime-3.8'; export * from '@pixi-spine/base'; - -SpineParser.registerLoaderPlugin(); diff --git a/bundles/all-4.0/package.json b/bundles/all-4.0/package.json index 5cdfd426..07119117 100644 --- a/bundles/all-4.0/package.json +++ b/bundles/all-4.0/package.json @@ -2,20 +2,50 @@ "name": "@pixi-spine/all-4.0", "version": "3.1.2", "description": "Pixi integration with EsotericSoftware Spine, big, contains runtime for 4.0", - "main": "lib/all-4.0.js", - "module": "lib/all-4.0.es.js", - "bundle": "dist/pixi-spine-4.0.umd.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/pixi-spine-4.0.js", + "bundleModule": "dist/pixi-spine-4.0.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine", + "@pixi-spine/loader-4.0": "PIXI.spine", + "@pixi-spine/loader-base": "PIXI.spine", + "@pixi-spine/runtime-4.0": "PIXI.spine40" + } + }, "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-4.0": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/runtime-4.0": "~3.1.2" + "@pixi-spine/base": "*", + "@pixi-spine/loader-4.0": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.0": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,7 +69,8 @@ "Chad Engler ", "Richard Davey ", "Shukant K. Pal ", - "Yevhenii Huselietov " + "Yevhenii Huselietov ", + "Milton Candelero " ], "bugs": { "url": "https://github.com/pixijs/pixi-spine/issues" @@ -47,14 +78,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/bundles/all-4.0/rollup.config.js b/bundles/all-4.0/rollup.config.js deleted file mode 100644 index 373d9da8..00000000 --- a/bundles/all-4.0/rollup.config.js +++ /dev/null @@ -1,36 +0,0 @@ -const { main } = require('@pixi-spine/rollup-config/main'); -const pkg = require('./package.json'); - -const results = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine', - '@pixi-spine/runtime-4.0': 'PIXI.spine40', - '@pixi-spine/loader-base': 'PIXI.spine', - '@pixi-spine/loader-4.0': 'PIXI.spine', - }, -}); - -// Find all the peer deps. Note: This assumes we have only two levels of peer deps. -let umdDeps = []; -const deps = Object.keys(pkg.dependencies || {}); -for (let dep of deps) { - const p = require(`${dep}/package.json`); - umdDeps = umdDeps.concat(Object.keys(p.peerDependencies || {})); -} - - -const license1 = 'is licensed under the MIT License.\n * http://www.opensource.org/licenses/mit-license'; -const licenseSpine = 'is licensed under SPINE-LICENSE\n * http://esotericsoftware.com/spine-runtimes-license'; - -results.forEach((entry) => { - if (entry.output.banner) { - entry.output.banner = entry.output.banner.replace(license1, licenseSpine); - } - if (entry.output.format === 'umd') { - entry.external = entry.external.filter((moduleName) => { - return moduleName.indexOf('@pixi-spine') !== 0; - }).concat(umdDeps); - } -}) - -module.exports = results; diff --git a/bundles/all-4.0/rollup.config.mjs b/bundles/all-4.0/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/bundles/all-4.0/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/bundles/all-4.0/src/index.ts b/bundles/all-4.0/src/index.ts index c3247810..8815fef2 100644 --- a/bundles/all-4.0/src/index.ts +++ b/bundles/all-4.0/src/index.ts @@ -1,6 +1,4 @@ -import {SpineParser} from '@pixi-spine/loader-4.0'; -export {SpineParser}; +import '@pixi-spine/loader-4.0'; // Side effect install the loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports export * from '@pixi-spine/runtime-4.0'; export * from '@pixi-spine/base'; - -SpineParser.registerLoaderPlugin(); diff --git a/bundles/all-4.1/package.json b/bundles/all-4.1/package.json index 7ca53e5f..a6815311 100644 --- a/bundles/all-4.1/package.json +++ b/bundles/all-4.1/package.json @@ -2,20 +2,50 @@ "name": "@pixi-spine/all-4.1", "version": "3.1.2", "description": "Pixi integration with EsotericSoftware Spine, big, contains runtime for 4.1", - "main": "lib/all-4.1.js", - "module": "lib/all-4.1.es.js", - "bundle": "dist/pixi-spine-4.1.umd.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/pixi-spine-4.1.js", + "bundleModule": "dist/pixi-spine-4.1.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine", + "@pixi-spine/loader-4.1": "PIXI.spine", + "@pixi-spine/loader-base": "PIXI.spine", + "@pixi-spine/runtime-4.1": "PIXI.spine41" + } + }, "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-4.1": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/runtime-4.1": "~3.1.2" + "@pixi-spine/base": "*", + "@pixi-spine/loader-4.1": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.1": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,7 +69,8 @@ "Chad Engler ", "Richard Davey ", "Shukant K. Pal ", - "Yevhenii Huselietov " + "Yevhenii Huselietov ", + "Milton Candelero " ], "bugs": { "url": "https://github.com/pixijs/pixi-spine/issues" @@ -47,14 +78,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/bundles/all-4.1/rollup.config.js b/bundles/all-4.1/rollup.config.js deleted file mode 100644 index 614fb404..00000000 --- a/bundles/all-4.1/rollup.config.js +++ /dev/null @@ -1,35 +0,0 @@ -const { main } = require('@pixi-spine/rollup-config/main'); -const pkg = require('./package.json'); - -const results = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine', - '@pixi-spine/runtime-4.0': 'PIXI.spine40', - '@pixi-spine/loader-base': 'PIXI.spine', - '@pixi-spine/loader-4.0': 'PIXI.spine', - }, -}); - -// Find all the peer deps. Note: This assumes we have only two levels of peer deps. -let umdDeps = []; -const deps = Object.keys(pkg.dependencies || {}); -for (let dep of deps) { - const p = require(`${dep}/package.json`); - umdDeps = umdDeps.concat(Object.keys(p.peerDependencies || {})); -} - -const license1 = 'is licensed under the MIT License.\n * http://www.opensource.org/licenses/mit-license'; -const licenseSpine = 'is licensed under SPINE-LICENSE\n * http://esotericsoftware.com/spine-runtimes-license'; - -results.forEach((entry) => { - if (entry.output.banner) { - entry.output.banner = entry.output.banner.replace(license1, licenseSpine); - } - if (entry.output.format === 'umd') { - entry.external = entry.external.filter((moduleName) => { - return moduleName.indexOf('@pixi-spine') !== 0; - }).concat(umdDeps); - } -}) - -module.exports = results; diff --git a/bundles/all-4.1/rollup.config.mjs b/bundles/all-4.1/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/bundles/all-4.1/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/bundles/all-4.1/src/index.ts b/bundles/all-4.1/src/index.ts index 43e347b4..5a4eba9c 100644 --- a/bundles/all-4.1/src/index.ts +++ b/bundles/all-4.1/src/index.ts @@ -1,6 +1,4 @@ -import {SpineParser} from '@pixi-spine/loader-4.1'; -export {SpineParser}; +import '@pixi-spine/loader-4.1'; // Side effect install the loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports export * from '@pixi-spine/runtime-4.1'; export * from '@pixi-spine/base'; - -SpineParser.registerLoaderPlugin(); diff --git a/bundles/pixi-spine/package.json b/bundles/pixi-spine/package.json index b0c2a316..e93baa78 100644 --- a/bundles/pixi-spine/package.json +++ b/bundles/pixi-spine/package.json @@ -2,22 +2,56 @@ "name": "pixi-spine", "version": "3.1.2", "description": "Pixi integration with EsotericSoftware Spine, big, contains all runtimes", - "main": "lib/all.js", - "module": "lib/all.es.js", - "bundle": "dist/pixi-spine.umd.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/pixi-spine.js", + "bundleModule": "dist/pixi-spine.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine", + "@pixi-spine/runtime-3.7": "PIXI.spine37", + "@pixi-spine/runtime-3.8": "PIXI.spine38", + "@pixi-spine/runtime-4.0": "PIXI.spine40", + "@pixi-spine/runtime-4.1": "PIXI.spine41", + "@pixi-spine/loader-base": "PIXI.spine", + "@pixi-spine/loader-uni": "PIXI.spine" + } + }, "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/loader-uni": "~3.1.2", - "@pixi-spine/runtime-3.7": "~3.1.2", - "@pixi-spine/runtime-3.8": "~3.1.2", - "@pixi-spine/runtime-4.1": "~3.1.2" + "@pixi-spine/base": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/loader-uni": "*", + "@pixi-spine/runtime-3.7": "*", + "@pixi-spine/runtime-3.8": "*", + "@pixi-spine/runtime-4.0": "*", + "@pixi-spine/runtime-4.1": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -40,7 +74,8 @@ "Chad Engler ", "Richard Davey ", "Shukant K. Pal ", - "Yevhenii Huselietov " + "Yevhenii Huselietov ", + "Milton Candelero " ], "bugs": { "url": "https://github.com/pixijs/pixi-spine/issues" @@ -48,14 +83,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/bundles/pixi-spine/rollup.config.js b/bundles/pixi-spine/rollup.config.js deleted file mode 100644 index b223ee86..00000000 --- a/bundles/pixi-spine/rollup.config.js +++ /dev/null @@ -1,38 +0,0 @@ -const { main } = require('@pixi-spine/rollup-config/main'); -const pkg = require('./package.json'); - -const results = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine', - '@pixi-spine/runtime-3.7': 'PIXI.spine37', - '@pixi-spine/runtime-3.8': 'PIXI.spine38', - '@pixi-spine/runtime-4.0': 'PIXI.spine40', - '@pixi-spine/loader-base': 'PIXI.spine', - '@pixi-spine/loader-uni': 'PIXI.spine', - }, -}); - -// Find all the peer deps. Note: This assumes we have only two levels of peer deps. -let umdDeps = []; -const deps = Object.keys(pkg.dependencies || {}); -for (let dep of deps) { - const p = require(`${dep}/package.json`); - umdDeps = umdDeps.concat(Object.keys(p.peerDependencies || {})); -} - - -const license1 = 'is licensed under the MIT License.\n * http://www.opensource.org/licenses/mit-license'; -const licenseSpine = 'is licensed under SPINE-LICENSE\n * http://esotericsoftware.com/spine-runtimes-license'; - -results.forEach((entry) => { - if (entry.output.banner) { - entry.output.banner = entry.output.banner.replace(license1, licenseSpine); - } - if (entry.output.format === 'umd') { - entry.external = entry.external.filter((moduleName) => { - return moduleName.indexOf('@pixi-spine') !== 0; - }).concat(umdDeps); - } -}) - -module.exports = results; diff --git a/bundles/pixi-spine/rollup.config.mjs b/bundles/pixi-spine/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/bundles/pixi-spine/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/bundles/pixi-spine/src/index.ts b/bundles/pixi-spine/src/index.ts index 688ba30c..2d537e70 100644 --- a/bundles/pixi-spine/src/index.ts +++ b/bundles/pixi-spine/src/index.ts @@ -1,6 +1,6 @@ -import {Spine, SpineParser} from '@pixi-spine/loader-uni'; -export {Spine, SpineParser}; +import '@pixi-spine/loader-uni'; // Side effect install the loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports +import { Spine } from '@pixi-spine/loader-uni'; +export { Spine }; export * from '@pixi-spine/base'; -export {SkeletonBounds} from '@pixi-spine/runtime-4.1'; - -SpineParser.registerLoaderPlugin(); +export { SkeletonBounds } from '@pixi-spine/runtime-4.1'; diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json deleted file mode 100644 index 9b549b7a..00000000 --- a/common/config/rush/command-line.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", - "commands": [ - { - "commandKind": "global", - "name": "unit-test", - "summary": "Run unit-tests of each project", - "description": "Uses @pixi-build-tools/floss-rush-monorepo to run all test/index.js test suites", - "safeForSimultaneousRushProcesses": false, - "shellCommand": "pnpm test" - }, - { - "commandKind": "bulk", - "name": "build:types", - "summary": "Generate declaration files for each project", - "description": "@microsoft/api-extractor is used to bundle the compiler generated .d.ts files", - "safeForSimultaneousRushProcesses": true, - "enableParallelism": true, - "ignoreMissingScript": true - } - ] -} diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json deleted file mode 100644 index fd054dd8..00000000 --- a/common/config/rush/version-policies.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "definitionName": "lockStepVersion", - "policyName": "PixiSpine", - "version": "3.1.2", - "nextBump": "patch", - "mainProject": "pixi-spine" - } -] diff --git a/examples/change_atlas_extension.md b/examples/change_atlas_extension.md index d4096bb7..65507150 100644 --- a/examples/change_atlas_extension.md +++ b/examples/change_atlas_extension.md @@ -1,5 +1,7 @@ ### How to change atlas file extension (I hate IIS webserver) +#### This example is for PixiJS version < 7.x + ```js var spineLoaderOptions = { metadata: { spineAtlasSuffix: '.txt' } }; PIXI.loader diff --git a/examples/choose_skeleton_scale.md b/examples/choose_skeleton_scale.md index 4419e26f..65ad5387 100644 --- a/examples/choose_skeleton_scale.md +++ b/examples/choose_skeleton_scale.md @@ -1,5 +1,7 @@ ### How to choose skeleton scale +#### This example is for PixiJS version < 7.x + ```js var spineLoaderOptions = { metadata: { spineSkeletonScale: 2.0 } }; PIXI.loader diff --git a/examples/compressed_textures.md b/examples/compressed_textures.md index ded58d63..3691ce37 100644 --- a/examples/compressed_textures.md +++ b/examples/compressed_textures.md @@ -1,5 +1,7 @@ ### How to use compressed textures +#### This example is for PixiJS version < 7.x + ```js PIXI.loader.before(PIXI.compressedTextures.extensionChooser(["@2x.atlas", ".dds"])); var options = { metadata: { spineMetadata: { choice: ["@.5x.atlas", "@2x.atlas"] }, imageMetadata: { choice: [".dds", ".pvr"] } } }; diff --git a/examples/dynamic_texture_atlas.md b/examples/dynamic_texture_atlas.md index 3b60ef60..d74025bf 100644 --- a/examples/dynamic_texture_atlas.md +++ b/examples/dynamic_texture_atlas.md @@ -1,6 +1,8 @@ How to make dynamic texture atlas ================================= +#### This example is for PixiJS version < 7.x + If for some reason you don't want to use Spine's `*.atlas` here's how you might customize atlas loading. ```js diff --git a/examples/hack_texture.md b/examples/hack_texture.md index fd0bc53b..6a6f3899 100644 --- a/examples/hack_texture.md +++ b/examples/hack_texture.md @@ -1,5 +1,7 @@ ### Changing the texture the direct way (hacks) +#### This example is for PixiJS version < 7.x + ```js //let 'spine' be Spine object var spine = new PIXI.spine.Spine(loader.resources['spineBoy'].data); diff --git a/examples/preload_atlas_image.md b/examples/preload_atlas_image.md index 07fde65c..f8068166 100644 --- a/examples/preload_atlas_image.md +++ b/examples/preload_atlas_image.md @@ -1,5 +1,7 @@ ### How to use generated or preloaded base textures +#### This example is for PixiJS version < 7.x + ```js var spineLoaderOptions = { metadata: { image: PIXI.BaseTexture.fromImage("something.jpg") diff --git a/examples/preload_atlas_text.md b/examples/preload_atlas_text.md index 7b6ca5f8..13406e06 100644 --- a/examples/preload_atlas_text.md +++ b/examples/preload_atlas_text.md @@ -1,4 +1,7 @@ ### How to use preload atlas text file from spine2D. + +#### This example is for PixiJS version < 7.x + * Step 1: ```js var atlasLoaderOption = { xhrType: "text"}; diff --git a/examples/preloaded_json.md b/examples/preloaded_json.md index e4c55ac2..1dffdc5c 100644 --- a/examples/preloaded_json.md +++ b/examples/preloaded_json.md @@ -1,5 +1,7 @@ ### How to use pre-loaded json and atlas files +#### This example is for PixiJS version < 7.x + ```js var rawSkeletonData = JSON.parse("$jsondata"); //your skeleton.json file here var rawAtlasData = "$atlasdata"; //your atlas file diff --git a/examples/reuse_texture.md b/examples/reuse_texture.md index 02b5735e..0ff4dbf5 100644 --- a/examples/reuse_texture.md +++ b/examples/reuse_texture.md @@ -1,5 +1,7 @@ ### How to use same texture from multiple atlases +#### This example is for PixiJS version < 7.x + imageLoader uses existing resource for a texture if you specify namePrefix. Do not use this approach if all of your textures are named `skeleton.png` :) diff --git a/examples/single_atlas_multiple_models.md b/examples/single_atlas_multiple_models.md index c24e257c..248bb75a 100644 --- a/examples/single_atlas_multiple_models.md +++ b/examples/single_atlas_multiple_models.md @@ -2,6 +2,8 @@ Suppose you want to use same atlas for several models. You can put all the textu Load it in separate loader, then use atlas for all others. +#### This example is for PixiJS version < 7.x + ```js const preLoader = new PIXI.Loader(); const loader = new PIXI.Loader(); diff --git a/examples/texture_and_sprite_resolution.md b/examples/texture_and_sprite_resolution.md index 12bb0fd2..2ae44c7e 100644 --- a/examples/texture_and_sprite_resolution.md +++ b/examples/texture_and_sprite_resolution.md @@ -1,5 +1,7 @@ ### How to choose resolution +#### This example is for PixiJS version < 7.x + Use with [pixi-compressed-textures.js](https://github.com/pixijs/pixi-compressed-textures) ```js diff --git a/lerna.json b/lerna.json new file mode 100644 index 00000000..2ca2fc7f --- /dev/null +++ b/lerna.json @@ -0,0 +1,4 @@ +{ + "useWorkspaces": true, + "version": "3.1.2" +} \ No newline at end of file diff --git a/nx.json b/nx.json new file mode 100644 index 00000000..e03daab3 --- /dev/null +++ b/nx.json @@ -0,0 +1,51 @@ +{ + "tasksRunnerOptions": { + "default": { + "runner": "nx/tasks-runners/default", + "options": { + "cacheableOperations": [ + "build", + "build:types", + "build:rollup", + "lint", + "lint:fix", + "test" + ] + } + } + }, + "targetDefaults": { + "build": { + "dependsOn": [ + "^build" + ], + "outputs": [ + "{projectRoot}/lib", + "{projectRoot}/dist", + "{projectRoot}/types.d.ts" + ] + }, + "build:types": { + "dependsOn": [ + "^build:types" + ], + "outputs": [ + "{projectRoot}/types.d.ts" + ] + }, + "build:rollup": { + "dependsOn": [ + "^build:rollup" + ], + "outputs": [ + "{projectRoot}/lib", + "{projectRoot}/dist" + ] + }, + "test": { + "dependsOn": [ + "build" + ] + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..5ddf91c0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12567 @@ +{ + "name": "pixi-spine-parent", + "version": "3.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pixi-spine-parent", + "version": "3.0.0", + "license": "SEE SPINE-LICENSE", + "workspaces": [ + "packages/*", + "bundles/*", + "tools/*" + ], + "devDependencies": { + "@microsoft/api-extractor": "^7.33.7", + "@pixi/eslint-config": "^4.0.1", + "@pixi/rollup-plugin-rename-node-modules": "^2.0.0", + "@rollup/plugin-commonjs": "^24.0.0", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-replace": "^5.0.2", + "eslint": "^8.3.1", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.2.1", + "install": "^0.13.0", + "lerna": "^6.4.0", + "npm": "^8.0.0", + "npm-run-all": "^4.1.5", + "prepend": "=1.0.2", + "rimraf": "3.0.2", + "rollup": "^3.9.1", + "rollup-plugin-esbuild": "^5.0.0", + "rollup-plugin-string": "^3.0.0", + "typescript": "~4.9.0" + } + }, + "bundles/all-3.8": { + "name": "@pixi-spine/all-3.8", + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "dependencies": { + "@pixi-spine/base": "*", + "@pixi-spine/loader-3.8": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-3.8": "*" + }, + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" + } + }, + "bundles/all-4.0": { + "name": "@pixi-spine/all-4.0", + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "dependencies": { + "@pixi-spine/base": "*", + "@pixi-spine/loader-4.0": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.0": "*" + }, + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" + } + }, + "bundles/all-4.1": { + "name": "@pixi-spine/all-4.1", + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "dependencies": { + "@pixi-spine/base": "*", + "@pixi-spine/loader-4.1": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.1": "*" + }, + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" + } + }, + "bundles/pixi-spine": { + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "dependencies": { + "@pixi-spine/base": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/loader-uni": "*", + "@pixi-spine/runtime-3.7": "*", + "@pixi-spine/runtime-3.8": "*", + "@pixi-spine/runtime-4.0": "*", + "@pixi-spine/runtime-4.1": "*" + }, + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.0", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true, + "license": "MIT" + }, + "node_modules/@lerna/add": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/bootstrap": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/filter-options": "6.4.1", + "@lerna/npm-conf": "6.4.1", + "@lerna/validation-error": "6.4.1", + "dedent": "^0.7.0", + "npm-package-arg": "8.1.1", + "p-map": "^4.0.0", + "pacote": "^13.6.1", + "semver": "^7.3.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/bootstrap": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/command": "6.4.1", + "@lerna/filter-options": "6.4.1", + "@lerna/has-npm-version": "6.4.1", + "@lerna/npm-install": "6.4.1", + "@lerna/package-graph": "6.4.1", + "@lerna/pulse-till-done": "6.4.1", + "@lerna/rimraf-dir": "6.4.1", + "@lerna/run-lifecycle": "6.4.1", + "@lerna/run-topologically": "6.4.1", + "@lerna/symlink-binary": "6.4.1", + "@lerna/symlink-dependencies": "6.4.1", + "@lerna/validation-error": "6.4.1", + "@npmcli/arborist": "5.3.0", + "dedent": "^0.7.0", + "get-port": "^5.1.1", + "multimatch": "^5.0.0", + "npm-package-arg": "8.1.1", + "npmlog": "^6.0.2", + "p-map": "^4.0.0", + "p-map-series": "^2.1.0", + "p-waterfall": "^2.1.1", + "semver": "^7.3.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/changed": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/collect-updates": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/listable": "6.4.1", + "@lerna/output": "6.4.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/check-working-tree": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/collect-uncommitted": "6.4.1", + "@lerna/describe-ref": "6.4.1", + "@lerna/validation-error": "6.4.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/child-process": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "execa": "^5.0.0", + "strong-log-transformer": "^2.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/clean": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/command": "6.4.1", + "@lerna/filter-options": "6.4.1", + "@lerna/prompt": "6.4.1", + "@lerna/pulse-till-done": "6.4.1", + "@lerna/rimraf-dir": "6.4.1", + "p-map": "^4.0.0", + "p-map-series": "^2.1.0", + "p-waterfall": "^2.1.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/cli": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/global-options": "6.4.1", + "dedent": "^0.7.0", + "npmlog": "^6.0.2", + "yargs": "^16.2.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/collect-uncommitted": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "chalk": "^4.1.0", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/collect-updates": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/describe-ref": "6.4.1", + "minimatch": "^3.0.4", + "npmlog": "^6.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/command": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/package-graph": "6.4.1", + "@lerna/project": "6.4.1", + "@lerna/validation-error": "6.4.1", + "@lerna/write-log-file": "6.4.1", + "clone-deep": "^4.0.1", + "dedent": "^0.7.0", + "execa": "^5.0.0", + "is-ci": "^2.0.0", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/conventional-commits": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/validation-error": "6.4.1", + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-core": "^4.2.4", + "conventional-recommended-bump": "^6.1.0", + "fs-extra": "^9.1.0", + "get-stream": "^6.0.0", + "npm-package-arg": "8.1.1", + "npmlog": "^6.0.2", + "pify": "^5.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/conventional-commits/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/conventional-commits/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/conventional-commits/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/create": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/npm-conf": "6.4.1", + "@lerna/validation-error": "6.4.1", + "dedent": "^0.7.0", + "fs-extra": "^9.1.0", + "init-package-json": "^3.0.2", + "npm-package-arg": "8.1.1", + "p-reduce": "^2.1.0", + "pacote": "^13.6.1", + "pify": "^5.0.0", + "semver": "^7.3.4", + "slash": "^3.0.0", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0", + "yargs-parser": "20.2.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/create-symlink": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cmd-shim": "^5.0.0", + "fs-extra": "^9.1.0", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/create-symlink/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/create-symlink/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/create-symlink/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/create/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/create/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/create/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/describe-ref": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/diff": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/validation-error": "6.4.1", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/exec": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/filter-options": "6.4.1", + "@lerna/profiler": "6.4.1", + "@lerna/run-topologically": "6.4.1", + "@lerna/validation-error": "6.4.1", + "p-map": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/filter-options": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/collect-updates": "6.4.1", + "@lerna/filter-packages": "6.4.1", + "dedent": "^0.7.0", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/filter-packages": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/validation-error": "6.4.1", + "multimatch": "^5.0.0", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/get-npm-exec-opts": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/get-packed": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^9.1.0", + "ssri": "^9.0.1", + "tar": "^6.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/get-packed/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/get-packed/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/get-packed/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/github-client": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@octokit/plugin-enterprise-rest": "^6.0.1", + "@octokit/rest": "^19.0.3", + "git-url-parse": "^13.1.0", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/gitlab-client": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/global-options": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/has-npm-version": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "semver": "^7.3.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/import": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/prompt": "6.4.1", + "@lerna/pulse-till-done": "6.4.1", + "@lerna/validation-error": "6.4.1", + "dedent": "^0.7.0", + "fs-extra": "^9.1.0", + "p-map-series": "^2.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/import/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/import/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/import/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/info": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/command": "6.4.1", + "@lerna/output": "6.4.1", + "envinfo": "^7.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/init": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/project": "6.4.1", + "fs-extra": "^9.1.0", + "p-map": "^4.0.0", + "write-json-file": "^4.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/init/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/init/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/init/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/link": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/command": "6.4.1", + "@lerna/package-graph": "6.4.1", + "@lerna/symlink-dependencies": "6.4.1", + "@lerna/validation-error": "6.4.1", + "p-map": "^4.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/list": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/command": "6.4.1", + "@lerna/filter-options": "6.4.1", + "@lerna/listable": "6.4.1", + "@lerna/output": "6.4.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/listable": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/query-graph": "6.4.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/log-packed": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "byte-size": "^7.0.0", + "columnify": "^1.6.0", + "has-unicode": "^2.0.1", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/npm-conf": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.12", + "pify": "^5.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/npm-dist-tag": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/otplease": "6.4.1", + "npm-package-arg": "8.1.1", + "npm-registry-fetch": "^13.3.0", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/npm-install": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/get-npm-exec-opts": "6.4.1", + "fs-extra": "^9.1.0", + "npm-package-arg": "8.1.1", + "npmlog": "^6.0.2", + "signal-exit": "^3.0.3", + "write-pkg": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/npm-install/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/npm-install/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/npm-install/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/npm-publish": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/otplease": "6.4.1", + "@lerna/run-lifecycle": "6.4.1", + "fs-extra": "^9.1.0", + "libnpmpublish": "^6.0.4", + "npm-package-arg": "8.1.1", + "npmlog": "^6.0.2", + "pify": "^5.0.0", + "read-package-json": "^5.0.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/npm-publish/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/npm-publish/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/npm-publish/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/npm-run-script": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "@lerna/get-npm-exec-opts": "6.4.1", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/otplease": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/prompt": "6.4.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/output": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/pack-directory": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/get-packed": "6.4.1", + "@lerna/package": "6.4.1", + "@lerna/run-lifecycle": "6.4.1", + "@lerna/temp-write": "6.4.1", + "npm-packlist": "^5.1.1", + "npmlog": "^6.0.2", + "tar": "^6.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/package": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^6.2.0", + "npm-package-arg": "8.1.1", + "write-pkg": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/package-graph": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/prerelease-id-from-version": "6.4.1", + "@lerna/validation-error": "6.4.1", + "npm-package-arg": "8.1.1", + "npmlog": "^6.0.2", + "semver": "^7.3.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/prerelease-id-from-version": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/profiler": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^9.1.0", + "npmlog": "^6.0.2", + "upath": "^2.0.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/profiler/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/profiler/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/profiler/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/project": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/package": "6.4.1", + "@lerna/validation-error": "6.4.1", + "cosmiconfig": "^7.0.0", + "dedent": "^0.7.0", + "dot-prop": "^6.0.1", + "glob-parent": "^5.1.1", + "globby": "^11.0.2", + "js-yaml": "^4.1.0", + "load-json-file": "^6.2.0", + "npmlog": "^6.0.2", + "p-map": "^4.0.0", + "resolve-from": "^5.0.0", + "write-json-file": "^4.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/project/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@lerna/project/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/prompt": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "inquirer": "^8.2.4", + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/publish": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/check-working-tree": "6.4.1", + "@lerna/child-process": "6.4.1", + "@lerna/collect-updates": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/describe-ref": "6.4.1", + "@lerna/log-packed": "6.4.1", + "@lerna/npm-conf": "6.4.1", + "@lerna/npm-dist-tag": "6.4.1", + "@lerna/npm-publish": "6.4.1", + "@lerna/otplease": "6.4.1", + "@lerna/output": "6.4.1", + "@lerna/pack-directory": "6.4.1", + "@lerna/prerelease-id-from-version": "6.4.1", + "@lerna/prompt": "6.4.1", + "@lerna/pulse-till-done": "6.4.1", + "@lerna/run-lifecycle": "6.4.1", + "@lerna/run-topologically": "6.4.1", + "@lerna/validation-error": "6.4.1", + "@lerna/version": "6.4.1", + "fs-extra": "^9.1.0", + "libnpmaccess": "^6.0.3", + "npm-package-arg": "8.1.1", + "npm-registry-fetch": "^13.3.0", + "npmlog": "^6.0.2", + "p-map": "^4.0.0", + "p-pipe": "^3.1.0", + "pacote": "^13.6.1", + "semver": "^7.3.4" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/publish/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/publish/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/publish/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/pulse-till-done": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/query-graph": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/package-graph": "6.4.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/resolve-symlink": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^9.1.0", + "npmlog": "^6.0.2", + "read-cmd-shim": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/resolve-symlink/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/resolve-symlink/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/resolve-symlink/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/rimraf-dir": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/child-process": "6.4.1", + "npmlog": "^6.0.2", + "path-exists": "^4.0.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/run": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/command": "6.4.1", + "@lerna/filter-options": "6.4.1", + "@lerna/npm-run-script": "6.4.1", + "@lerna/output": "6.4.1", + "@lerna/profiler": "6.4.1", + "@lerna/run-topologically": "6.4.1", + "@lerna/timer": "6.4.1", + "@lerna/validation-error": "6.4.1", + "fs-extra": "^9.1.0", + "nx": ">=15.4.2 < 16", + "p-map": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/run-lifecycle": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/npm-conf": "6.4.1", + "@npmcli/run-script": "^4.1.7", + "npmlog": "^6.0.2", + "p-queue": "^6.6.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/run-topologically": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/query-graph": "6.4.1", + "p-queue": "^6.6.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/run/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/run/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/run/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/symlink-binary": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/create-symlink": "6.4.1", + "@lerna/package": "6.4.1", + "fs-extra": "^9.1.0", + "p-map": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/symlink-binary/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/symlink-binary/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/symlink-binary/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/symlink-dependencies": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/create-symlink": "6.4.1", + "@lerna/resolve-symlink": "6.4.1", + "@lerna/symlink-binary": "6.4.1", + "fs-extra": "^9.1.0", + "p-map": "^4.0.0", + "p-map-series": "^2.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/symlink-dependencies/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@lerna/symlink-dependencies/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@lerna/symlink-dependencies/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@lerna/temp-write": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.15", + "is-stream": "^2.0.0", + "make-dir": "^3.0.0", + "temp-dir": "^1.0.0", + "uuid": "^8.3.2" + } + }, + "node_modules/@lerna/timer": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/validation-error": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "npmlog": "^6.0.2" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/version": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/check-working-tree": "6.4.1", + "@lerna/child-process": "6.4.1", + "@lerna/collect-updates": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/conventional-commits": "6.4.1", + "@lerna/github-client": "6.4.1", + "@lerna/gitlab-client": "6.4.1", + "@lerna/output": "6.4.1", + "@lerna/prerelease-id-from-version": "6.4.1", + "@lerna/prompt": "6.4.1", + "@lerna/run-lifecycle": "6.4.1", + "@lerna/run-topologically": "6.4.1", + "@lerna/temp-write": "6.4.1", + "@lerna/validation-error": "6.4.1", + "@nrwl/devkit": ">=15.4.2 < 16", + "chalk": "^4.1.0", + "dedent": "^0.7.0", + "load-json-file": "^6.2.0", + "minimatch": "^3.0.4", + "npmlog": "^6.0.2", + "p-map": "^4.0.0", + "p-pipe": "^3.1.0", + "p-reduce": "^2.1.0", + "p-waterfall": "^2.1.1", + "semver": "^7.3.4", + "slash": "^3.0.0", + "write-json-file": "^4.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/write-log-file": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "npmlog": "^6.0.2", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@microsoft/api-extractor": { + "version": "7.33.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.25.3", + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.53.3", + "@rushstack/rig-package": "0.3.17", + "@rushstack/ts-command-line": "4.13.1", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "source-map": "~0.6.1", + "typescript": "~4.8.4" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.25.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.53.3" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "4.8.4", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/arborist": { + "version": "5.3.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/move-file": "^2.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.1.3", + "bin-links": "^3.0.0", + "cacache": "^16.0.6", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.0.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.0", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.1", + "proc-log": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.0", + "treeverse": "^2.0.0", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-package-arg": { + "version": "9.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/map-workspaces": { + "version": "2.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "version": "5.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/metavuln-calculator": { + "version": "3.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "cacache": "^16.0.0", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^13.0.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "4.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@nrwl/cli": { + "version": "15.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "nx": "15.5.1" + } + }, + "node_modules/@nrwl/devkit": { + "version": "15.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@phenomnomnominal/tsquery": "4.1.1", + "ejs": "^3.1.7", + "ignore": "^5.0.4", + "semver": "7.3.4", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "nx": ">= 14 <= 16" + } + }, + "node_modules/@nrwl/devkit/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/devkit/node_modules/semver": { + "version": "7.3.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/tao": { + "version": "15.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "nx": "15.5.1" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@octokit/auth-token": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^8.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/core": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^8.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/endpoint": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^8.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/graphql": { + "version": "5.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^6.0.0", + "@octokit/types": "^8.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "14.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-enterprise-rest": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^8.0.0" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=4" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "6.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^8.0.0", + "deprecation": "^2.3.1" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^8.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/request-error": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^8.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/rest": { + "version": "19.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^4.1.0", + "@octokit/plugin-paginate-rest": "^5.0.0", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^6.7.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/types": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^14.0.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.4", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@phenomnomnominal/tsquery": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "esquery": "^1.0.1" + }, + "peerDependencies": { + "typescript": "^3 || ^4" + } + }, + "node_modules/@pixi-spine/all-3.8": { + "resolved": "bundles/all-3.8", + "link": true + }, + "node_modules/@pixi-spine/all-4.0": { + "resolved": "bundles/all-4.0", + "link": true + }, + "node_modules/@pixi-spine/all-4.1": { + "resolved": "bundles/all-4.1", + "link": true + }, + "node_modules/@pixi-spine/base": { + "resolved": "packages/base", + "link": true + }, + "node_modules/@pixi-spine/loader-3.8": { + "resolved": "packages/loader-3.8", + "link": true + }, + "node_modules/@pixi-spine/loader-4.0": { + "resolved": "packages/loader-4.0", + "link": true + }, + "node_modules/@pixi-spine/loader-4.1": { + "resolved": "packages/loader-4.1", + "link": true + }, + "node_modules/@pixi-spine/loader-base": { + "resolved": "packages/loader-base", + "link": true + }, + "node_modules/@pixi-spine/loader-uni": { + "resolved": "packages/loader-uni", + "link": true + }, + "node_modules/@pixi-spine/rollup-config": { + "resolved": "tools/rollup-config", + "link": true + }, + "node_modules/@pixi-spine/runtime-3.7": { + "resolved": "packages/runtime-3.7", + "link": true + }, + "node_modules/@pixi-spine/runtime-3.8": { + "resolved": "packages/runtime-3.8", + "link": true + }, + "node_modules/@pixi-spine/runtime-4.0": { + "resolved": "packages/runtime-4.0", + "link": true + }, + "node_modules/@pixi-spine/runtime-4.1": { + "resolved": "packages/runtime-4.1", + "link": true + }, + "node_modules/@pixi/assets": { + "version": "7.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/css-font-loading-module": "^0.0.7" + } + }, + "node_modules/@pixi/constants": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/core": { + "version": "7.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@pixi/constants": "7.1.0", + "@pixi/extensions": "7.1.0", + "@pixi/math": "7.1.0", + "@pixi/runner": "7.1.0", + "@pixi/settings": "7.1.0", + "@pixi/ticker": "7.1.0", + "@pixi/utils": "7.1.0", + "@types/offscreencanvas": "^2019.6.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/pixijs" + } + }, + "node_modules/@pixi/display": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/eslint-config": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0", + "typescript": ">=3.8.3" + } + }, + "node_modules/@pixi/extensions": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/graphics": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/math": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/mesh": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/mesh-extras": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/rollup-plugin-rename-node-modules": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^2.0.1", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^3.2.5" + } + }, + "node_modules/@pixi/runner": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/settings": { + "version": "7.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@pixi/constants": "7.1.0", + "@types/css-font-loading-module": "^0.0.7" + } + }, + "node_modules/@pixi/sprite": { + "version": "7.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@pixi/ticker": { + "version": "7.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@pixi/extensions": "7.1.0", + "@pixi/settings": "7.1.0", + "@pixi/utils": "7.1.0" + } + }, + "node_modules/@pixi/utils": { + "version": "7.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@pixi/constants": "7.1.0", + "@pixi/settings": "7.1.0", + "@types/earcut": "^2.1.0", + "earcut": "^2.2.4", + "eventemitter3": "^4.0.0", + "url": "^0.11.0" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "24.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { + "version": "0.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.0", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve/node_modules/resolve": { + "version": "1.22.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace/node_modules/magic-string": { + "version": "0.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library": { + "version": "3.53.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "z-schema": "~5.0.2" + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.3.17", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.17.0", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.13.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/css-font-loading-module": { + "version": "0.0.7", + "license": "MIT", + "peer": true + }, + "node_modules/@types/earcut": { + "version": "2.1.1", + "license": "MIT", + "peer": true + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "12.20.24", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.0", + "license": "MIT", + "peer": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.48.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/type-utils": "5.48.1", + "@typescript-eslint/utils": "5.48.1", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.48.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/typescript-estree": "5.48.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.48.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.48.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.48.1", + "@typescript-eslint/utils": "5.48.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.48.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.48.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.48.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/typescript-estree": "5.48.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.48.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.48.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.0-rc.35", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@zkochan/js-yaml": { + "version": "0.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@zkochan/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.8.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/add-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-differ": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bin-links": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/bin-links/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/byte-size": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache": { + "version": "16.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cmd-shim": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colors": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/columnify": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/compare-func": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/compare-func/node_modules/dot-prop": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/conventional-changelog-angular": { + "version": "5.0.13", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-core": { + "version": "4.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^4.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "2.3.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/conventional-commits-filter": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-recommended-bump": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^2.3.4", + "conventional-commits-filter": "^2.0.7", + "conventional-commits-parser": "^3.2.0", + "git-raw-commits": "^2.0.8", + "git-semver-tags": "^4.1.1", + "meow": "^8.0.0", + "q": "^1.5.1" + }, + "bin": { + "conventional-recommended-bump": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debuglog": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/earcut": { + "version": "2.2.4", + "license": "ISC", + "peer": true + }, + "node_modules/ejs": { + "version": "3.1.8", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.21.1", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.17.0", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.0", + "@esbuild/android-arm64": "0.17.0", + "@esbuild/android-x64": "0.17.0", + "@esbuild/darwin-arm64": "0.17.0", + "@esbuild/darwin-x64": "0.17.0", + "@esbuild/freebsd-arm64": "0.17.0", + "@esbuild/freebsd-x64": "0.17.0", + "@esbuild/linux-arm": "0.17.0", + "@esbuild/linux-arm64": "0.17.0", + "@esbuild/linux-ia32": "0.17.0", + "@esbuild/linux-loong64": "0.17.0", + "@esbuild/linux-mips64el": "0.17.0", + "@esbuild/linux-ppc64": "0.17.0", + "@esbuild/linux-riscv64": "0.17.0", + "@esbuild/linux-s390x": "0.17.0", + "@esbuild/linux-x64": "0.17.0", + "@esbuild/netbsd-x64": "0.17.0", + "@esbuild/openbsd-x64": "0.17.0", + "@esbuild/sunos-x64": "0.17.0", + "@esbuild/win32-arm64": "0.17.0", + "@esbuild/win32-ia32": "0.17.0", + "@esbuild/win32-x64": "0.17.0" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.32.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.6.0", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "license": "MIT" + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.15.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/get-pkg-repo/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/git-raw-commits": { + "version": "2.0.11", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-remote-origin-url/node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/git-semver-tags": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^8.0.0", + "semver": "^6.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/git-up": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-ssh": "^1.4.0", + "parse-url": "^8.1.0" + } + }, + "node_modules/git-url-parse": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "git-up": "^7.0.0" + } + }, + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "dev": true, + "license": "BSD", + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "13.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "dev": true, + "license": "ISC" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "4.7.7", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.2.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "5.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "node_modules/init-package-json": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.0.1", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/init-package-json/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/init-package-json/node_modules/npm-package-arg": { + "version": "9.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/install": { + "version": "0.13.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/internal-slot": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-array-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-module": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ssh": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "protocols": "^2.0.1" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/joycon": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-sdsl": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/just-diff": { + "version": "5.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lerna": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@lerna/add": "6.4.1", + "@lerna/bootstrap": "6.4.1", + "@lerna/changed": "6.4.1", + "@lerna/clean": "6.4.1", + "@lerna/cli": "6.4.1", + "@lerna/command": "6.4.1", + "@lerna/create": "6.4.1", + "@lerna/diff": "6.4.1", + "@lerna/exec": "6.4.1", + "@lerna/filter-options": "6.4.1", + "@lerna/import": "6.4.1", + "@lerna/info": "6.4.1", + "@lerna/init": "6.4.1", + "@lerna/link": "6.4.1", + "@lerna/list": "6.4.1", + "@lerna/publish": "6.4.1", + "@lerna/run": "6.4.1", + "@lerna/validation-error": "6.4.1", + "@lerna/version": "6.4.1", + "@nrwl/devkit": ">=15.4.2 < 16", + "import-local": "^3.0.2", + "inquirer": "^8.2.4", + "npmlog": "^6.0.2", + "nx": ">=15.4.2 < 16", + "typescript": "^3 || ^4" + }, + "bin": { + "lerna": "cli.js" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libnpmaccess": { + "version": "6.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/libnpmaccess/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/libnpmaccess/node_modules/npm-package-arg": { + "version": "9.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/libnpmpublish": { + "version": "6.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^4.0.0", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0", + "semver": "^7.3.7", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/libnpmpublish/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/libnpmpublish/node_modules/normalize-package-data": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/libnpmpublish/node_modules/npm-package-arg": { + "version": "9.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/lines-and-columns": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/load-json-file": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/load-json-file/node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "7.14.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/meow": { + "version": "8.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "2.8.9", + "dev": true, + "license": "ISC" + }, + "node_modules/meow/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg-up": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-infer-owner": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/multimatch": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/multimatch/node_modules/arrify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "license": "ISC" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.6.8", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp": { + "version": "9.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm": { + "version": "8.19.3", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/ci-detect", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/run-script", + "abbrev", + "archy", + "cacache", + "chalk", + "chownr", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "mkdirp", + "mkdirp-infer-owner", + "ms", + "node-gyp", + "nopt", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "opener", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "read-package-json", + "read-package-json-fast", + "readdir-scoped-modules", + "rimraf", + "semver", + "ssri", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^5.6.3", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/config": "^4.2.1", + "@npmcli/fs": "^2.1.0", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.2.1", + "abbrev": "~1.1.1", + "archy": "~1.0.0", + "cacache": "^16.1.3", + "chalk": "^4.1.2", + "chownr": "^2.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.2", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.12", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "graceful-fs": "^4.2.10", + "hosted-git-info": "^5.2.1", + "ini": "^3.0.1", + "init-package-json": "^3.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^6.0.4", + "libnpmdiff": "^4.0.5", + "libnpmexec": "^4.0.14", + "libnpmfund": "^3.0.5", + "libnpmhook": "^8.0.4", + "libnpmorg": "^4.0.4", + "libnpmpack": "^4.1.3", + "libnpmpublish": "^6.0.5", + "libnpmsearch": "^5.0.4", + "libnpmteam": "^4.0.4", + "libnpmversion": "^3.0.7", + "make-fetch-happen": "^10.2.0", + "minimatch": "^5.1.0", + "minipass": "^3.1.6", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^9.1.0", + "nopt": "^6.0.0", + "npm-audit-report": "^3.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.1.0", + "npm-pick-manifest": "^7.0.2", + "npm-profile": "^6.2.0", + "npm-registry-fetch": "^13.3.1", + "npm-user-validate": "^1.0.1", + "npmlog": "^6.0.2", + "opener": "^1.5.2", + "p-map": "^4.0.0", + "pacote": "^13.6.2", + "parse-conflict-json": "^2.0.2", + "proc-log": "^2.0.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^5.0.2", + "read-package-json-fast": "^2.0.3", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.1", + "tar": "^6.1.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^2.0.0", + "validate-npm-package-name": "^4.0.0", + "which": "^2.0.2", + "write-file-atomic": "^4.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "5.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/npm-package-arg": { + "version": "8.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^3.0.6", + "semver": "^7.0.0", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-package-arg/node_modules/builtins": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "3.0.8", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-package-arg/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-package-arg/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/npm-packlist": { + "version": "5.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-bundled": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "7.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^2.0.0", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { + "version": "9.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "13.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/npm-package-arg": { + "version": "9.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/npm/node_modules/@gar/promisify": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "5.6.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/move-file": "^2.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/package-json": "^2.0.0", + "@npmcli/query": "^1.2.0", + "@npmcli/run-script": "^4.1.3", + "bin-links": "^3.0.3", + "cacache": "^16.1.3", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^5.2.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "minimatch": "^5.1.0", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^6.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.0.0", + "npm-pick-manifest": "^7.0.2", + "npm-registry-fetch": "^13.0.0", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.1", + "proc-log": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.0", + "treeverse": "^2.0.0", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/ci-detect": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "4.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^2.0.2", + "ini": "^3.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^6.0.0", + "proc-log": "^2.0.0", + "read-package-json-fast": "^2.0.3", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^16.0.0", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^13.0.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/move-file": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "1.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.1.0", + "postcss-selector-parser": "^6.0.10", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "4.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/npm/node_modules/agentkeepalive": { + "version": "4.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/asap": { + "version": "2.0.6", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/builtins": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "16.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^4.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/debuglog": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/npm/node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/dezalgo": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.12", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/gauge": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "8.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.10", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/has": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/npm/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/ini": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.0.1", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/ip": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^3.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.10.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "5.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.4.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "6.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "4.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/disparity-colors": "^2.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.1.0", + "minimatch": "^5.0.1", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1", + "tar": "^6.1.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "4.0.14", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.6.3", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/fs": "^2.1.1", + "@npmcli/run-script": "^4.2.0", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^9.0.1", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "proc-log": "^2.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "semver": "^7.3.7", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "3.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.6.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "8.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "4.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/run-script": "^4.1.3", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "6.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^4.0.0", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0", + "semver": "^7.3.7", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "5.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/run-script": "^4.1.3", + "json-parse-even-better-errors": "^2.3.1", + "proc-log": "^2.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "7.13.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "10.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "3.3.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/mkdirp-infer-owner": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "5.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^2.0.0", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "13.3.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/npmlog": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/once": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/opener": { + "version": "1.5.2", + "dev": true, + "inBundle": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "13.6.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "0.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "1" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "5.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.3.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.7.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.11", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "9.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.1.11", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/npm/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npmlog": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nx": { + "version": "15.5.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nrwl/cli": "15.5.1", + "@nrwl/tao": "15.5.1", + "@parcel/watcher": "2.0.4", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "^3.0.0-rc.18", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.0.0", + "chalk": "4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^7.0.2", + "dotenv": "~10.0.0", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "glob": "7.1.4", + "ignore": "^5.0.4", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "3.0.5", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "semver": "7.3.4", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "v8-compile-cache": "2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js" + }, + "peerDependencies": { + "@swc-node/register": "^1.4.2", + "@swc/core": "^1.2.173" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/nx/node_modules/chalk": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/nx/node_modules/fast-glob": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nx/node_modules/fs-extra": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/nx/node_modules/glob": { + "version": "7.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nx/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/nx/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/nx/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nx/node_modules/minimatch": { + "version": "3.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nx/node_modules/semver": { + "version": "7.3.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nx/node_modules/tmp": { + "version": "0.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/nx/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/nx/node_modules/yargs": { + "version": "17.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/nx/node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/nx/node_modules/yargs/node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map-series": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-pipe": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-waterfall": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pacote": { + "version": "13.6.2", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pacote/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pacote/node_modules/npm-package-arg": { + "version": "9.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-conflict-json": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-path": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "protocols": "^2.0.0" + } + }, + "node_modules/parse-url": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-path": "^7.0.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pixi-spine": { + "resolved": "bundles/pixi-spine", + "link": true + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/prettier": { + "version": "2.8.3", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/proc-log": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-call-limit": { + "version": "1.0.1", + "dev": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promzard": { + "version": "0.3.0", + "dev": true, + "license": "ISC", + "dependencies": { + "read": "1" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/protocols": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "peer": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/read": { + "version": "1.0.7", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-cmd-shim": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json": { + "version": "5.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/load-json-file": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.10.0", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-esbuild": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "debug": "^4.3.4", + "es-module-lexer": "^1.0.5", + "joycon": "^3.1.1", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.10.1", + "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0" + } + }, + "node_modules/rollup-plugin-string": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "rollup-pluginutils": "^2.4.1" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "dev": true, + "license": "MIT" + }, + "node_modules/run-async": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.3.8", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.7.4", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sort-keys": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-keys/node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "9.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strong-log-transformer": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + }, + "bin": { + "sl-log-transformer": "bin/sl-log-transformer.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "6.1.13", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/temp-dir": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/treeverse": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.4", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unique-filename": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/unique-slug": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/upath": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.11.0", + "license": "MIT", + "peer": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "license": "MIT", + "peer": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/validator": { + "version": "13.7.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/walk-up-path": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-json-file": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^6.0.0", + "graceful-fs": "^4.1.15", + "is-plain-obj": "^2.0.0", + "make-dir": "^3.0.0", + "sort-keys": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8.3" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/write-json-file/node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/write-json-file/node_modules/write-file-atomic": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-pkg": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sort-keys": "^2.0.0", + "type-fest": "^0.4.1", + "write-json-file": "^3.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/write-pkg/node_modules/detect-indent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/write-pkg/node_modules/make-dir": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/write-pkg/node_modules/pify": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/write-pkg/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/write-pkg/node_modules/sort-keys": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/write-pkg/node_modules/type-fest": { + "version": "0.4.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=6" + } + }, + "node_modules/write-pkg/node_modules/write-file-atomic": { + "version": "2.4.3", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/write-pkg/node_modules/write-json-file": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.15", + "make-dir": "^2.1.0", + "pify": "^4.0.1", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "packages/base": { + "name": "@pixi-spine/base", + "version": "3.1.2", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" + } + }, + "packages/loader-3.8": { + "name": "@pixi-spine/loader-3.8", + "version": "3.1.2", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-3.8": "*" + } + }, + "packages/loader-4.0": { + "name": "@pixi-spine/loader-4.0", + "version": "3.1.2", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.0": "*" + } + }, + "packages/loader-4.1": { + "name": "@pixi-spine/loader-4.1", + "version": "3.1.2", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.1": "*" + } + }, + "packages/loader-base": { + "name": "@pixi-spine/loader-base", + "version": "3.1.2", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/assets": " ^7.0.0", + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/extensions": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/runner": "^7.0.0", + "@pixi/settings": "^7.0.0", + "@pixi/ticker": "^7.0.0", + "@pixi/utils": "^7.0.0" + } + }, + "packages/loader-uni": { + "name": "@pixi-spine/loader-uni", + "version": "3.1.2", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-3.7": "*", + "@pixi-spine/runtime-3.8": "*", + "@pixi-spine/runtime-4.1": "*", + "@pixi/assets": " ^7.0.0", + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/extensions": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/runner": "^7.0.0", + "@pixi/settings": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/ticker": "^7.0.0", + "@pixi/utils": "^7.0.0" + } + }, + "packages/runtime-3.7": { + "name": "@pixi-spine/runtime-3.7", + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" + } + }, + "packages/runtime-3.8": { + "name": "@pixi-spine/runtime-3.8", + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" + } + }, + "packages/runtime-4.0": { + "name": "@pixi-spine/runtime-4.0", + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" + } + }, + "packages/runtime-4.1": { + "name": "@pixi-spine/runtime-4.1", + "version": "3.1.2", + "license": "SEE SPINE-LICENSE", + "devDependencies": { + "@pixi-spine/rollup-config": "*" + }, + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" + } + }, + "tools/rollup-config": { + "name": "@pixi-spine/rollup-config", + "version": "1.0.0" + } + } +} diff --git a/package.json b/package.json index af9706df..8d1d6237 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,40 @@ "description": "Parent package for pixi-spine", "main": "index.js", "scripts": { - "prepare": "rush install", - "build:compile": "rush build", - "build:types": "rush build:types", - "build": "npm run build:compile && npm run build:types", - "prepublishOnly": "npm run build", - "test": "cd ./tools/unit-tests && pnpm test && cd ../.." + "build": "lerna run build", + "build:types": "lerna run build:types", + "build:rollup": "lerna run build:rollup", + "lint": "lerna run lint", + "lint:fix": "lerna run lint:fix", + "prepublishOnly": "lerna run build", + "lernaPublish": "lerna publish --no-private", + "lernaPublish:fromPackage": "lerna publish from-package --no-private" + }, + "workspaces": [ + "packages/*", + "bundles/*", + "tools/*" + ], + "devDependencies": { + "@microsoft/api-extractor": "^7.33.7", + "@pixi/eslint-config": "^4.0.1", + "@pixi/rollup-plugin-rename-node-modules": "^2.0.0", + "@rollup/plugin-commonjs": "^24.0.0", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-replace": "^5.0.2", + "eslint": "^8.3.1", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.2.1", + "install": "^0.13.0", + "lerna": "^6.4.0", + "npm": "^8.0.0", + "npm-run-all": "^4.1.5", + "prepend": "=1.0.2", + "rimraf": "3.0.2", + "rollup": "^3.9.1", + "rollup-plugin-esbuild": "^5.0.0", + "rollup-plugin-string": "^3.0.0", + "typescript": "~4.9.0" }, "license": "SEE SPINE-LICENSE" -} +} \ No newline at end of file diff --git a/packages/base/package.json b/packages/base/package.json index b1aaf161..b135bad8 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -2,25 +2,39 @@ "name": "@pixi-spine/base", "version": "3.1.2", "description": "Base of pixi-spine integration, common files for spine runtimes of different versions", - "main": "lib/base.js", - "module": "lib/base.es.js", - "bundle": "dist/base.js", - "namespace": "PIXI.spine", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/base.js", + "bundleModule": "dist/base.mjs", + "globals": {} + }, "peerDependencies": { - "@pixi/constants": "^6.1.0", - "@pixi/core": "^6.1.0", - "@pixi/display": "^6.1.0", - "@pixi/graphics": "^6.1.0", - "@pixi/math": "^6.1.0", - "@pixi/mesh": "^6.1.0", - "@pixi/mesh-extras": "^6.1.0", - "@pixi/sprite": "^6.1.0", - "@pixi/utils": "^6.1.0" + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/utils": "^7.0.0" }, "scripts": { - "build": "rollup -c node_modules/@pixi-spine/rollup-config/index.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node scripts/injectGlobalMixins" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node ../../scripts/injectGlobalMixins", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -42,18 +56,6 @@ }, "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@microsoft/api-extractor": "7.18.4", - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0", - "@pixi/runner": "^6.1.0", - "@pixi/settings": "^6.1.0", - "@pixi/extensions": "^6.1.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/base/rollup.config.mjs b/packages/base/rollup.config.mjs new file mode 100644 index 00000000..e20161c3 --- /dev/null +++ b/packages/base/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from "@pixi-spine/rollup-config"; +import pkg from "./package.json" assert { type: "json" }; + +export default configBuilder(pkg.extensionConfig, pkg); \ No newline at end of file diff --git a/packages/base/src/SpineBase.ts b/packages/base/src/SpineBase.ts index f7e3041a..c22283e8 100644 --- a/packages/base/src/SpineBase.ts +++ b/packages/base/src/SpineBase.ts @@ -1,31 +1,21 @@ -import {AttachmentType} from './core/AttachmentType'; -import {TextureRegion} from './core/TextureRegion'; -import {MathUtils} from './core/Utils'; -import type { - IAnimationState, - IAnimationStateData -} from './core/IAnimation'; -import type { - IAttachment, IClippingAttachment, IMeshAttachment, - IRegionAttachment, - ISkeleton, - ISkeletonData, - ISlot, - IVertexAttachment -} from './core/ISkeleton'; - -import {DRAW_MODES} from '@pixi/constants'; -import {Container, DisplayObject} from '@pixi/display'; -import {Sprite} from '@pixi/sprite'; -import {SimpleMesh} from '@pixi/mesh-extras'; -import {Graphics} from '@pixi/graphics' -import {Rectangle, Polygon, Transform} from '@pixi/math'; -import {hex2rgb, rgb2hex} from '@pixi/utils'; -import type {Texture} from '@pixi/core'; -import {settings} from "./settings"; -import { ISpineDebugRenderer } from './SpineDebugRenderer'; - -let tempRgb = [0, 0, 0]; +import { AttachmentType } from './core/AttachmentType'; +import { TextureRegion } from './core/TextureRegion'; +import { MathUtils } from './core/Utils'; +import type { IAnimationState, IAnimationStateData } from './core/IAnimation'; +import type { IAttachment, IClippingAttachment, IMeshAttachment, IRegionAttachment, ISkeleton, ISkeletonData, ISlot, IVertexAttachment } from './core/ISkeleton'; + +import { DRAW_MODES } from '@pixi/constants'; +import { Container, DisplayObject } from '@pixi/display'; +import { Sprite } from '@pixi/sprite'; +import { SimpleMesh } from '@pixi/mesh-extras'; +import { Graphics } from '@pixi/graphics'; +import { Rectangle, Polygon, Transform } from '@pixi/math'; +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import type { Texture } from '@pixi/core'; +import { settings } from './settings'; +import type { ISpineDebugRenderer } from './SpineDebugRenderer'; + +const tempRgb = [0, 0, 0]; /** * @public @@ -70,11 +60,15 @@ export class SpineMesh extends SimpleMesh implements ISpineDisplayObject { * @memberof spine * @param spineData {object} The spine data loaded from a spine atlas. */ -export abstract class SpineBase - extends Container implements GlobalMixins.Spine { +export abstract class SpineBase< + Skeleton extends ISkeleton, + SkeletonData extends ISkeletonData, + AnimationState extends IAnimationState, + AnimationStateData extends IAnimationStateData + > + extends Container + implements GlobalMixins.Spine +{ tintRgb: ArrayLike; spineData: SkeletonData; skeleton: Skeleton; @@ -90,7 +84,8 @@ export abstract class SpineBase delayLimit) dt = delayLimit; this.state.update(dt); this.state.apply(this.skeleton); - //check we haven't been destroyed via a spine event callback in state update - if (!this.skeleton) + // check we haven't been destroyed via a spine event callback in state update + if (!this.skeleton) { return; + } this.skeleton.updateWorldTransform(); - let slots = this.skeleton.slots; + const slots = this.skeleton.slots; // in case pixi has double tint - let globalClr = (this as any).color; - let light: ArrayLike = null, dark: ArrayLike = null; + const globalClr = (this as any).color; + let light: ArrayLike = null; + let dark: ArrayLike = null; if (globalClr) { light = globalClr.light; @@ -262,9 +261,9 @@ export abstract class SpineBase = []; - //@ts-ignore + // @ts-ignore createGraphics(slot: ISlot, clip: IClippingAttachment) { - let graphics = this.newGraphics(); - let poly = new Polygon([]); + const graphics = this.newGraphics(); + const poly = new Polygon([]); + graphics.clear(); graphics.beginFill(0xffffff, 1); graphics.drawPolygon(poly as any); @@ -626,9 +629,10 @@ export abstract class SpineBase):void; - - /** - * This is called when the `spine.debug` object is set to null or when the spine is destroyed. - */ - unregisterSpine(spine:SpineBase< ISkeleton, ISkeletonData, IAnimationState, IAnimationStateData>):void - - /** - * This is called when the `spine.debug` object is set to a new instance of a debug renderer. - */ - registerSpine(spine:SpineBase< ISkeleton, ISkeletonData, IAnimationState, IAnimationStateData>):void -} - -type DebugDisplayObjects = { - bones: Container; - skeletonXY: Graphics; - regionAttachmentsShape: Graphics; - meshTrianglesLine: Graphics; - meshHullLine: Graphics; - clippingPolygon: Graphics; - boundingBoxesRect: Graphics; - boundingBoxesCircle: Graphics; - boundingBoxesPolygon: Graphics; - pathsCurve: Graphics; - pathsLine: Graphics; - parentDebugContainer: Container; -} - -/** - * This is a debug renderer that uses PixiJS Graphics under the hood. - * @public - */ -export class SpineDebugRenderer implements ISpineDebugRenderer { - private registeredSpines:Map, DebugDisplayObjects> = new Map(); - - public drawDebug: boolean = true; - public drawMeshHull: boolean = true; - public drawMeshTriangles: boolean = true; - public drawBones: boolean = true; - public drawPaths: boolean = true; - public drawBoundingBoxes: boolean = true; - public drawClipping: boolean = true; - public drawRegionAttachments: boolean = true; - - public lineWidth: number = 1; - public regionAttachmentsColor: number = 0x0078ff; - public meshHullColor: number = 0x0078ff; - public meshTrianglesColor: number = 0xffcc00; - public clippingPolygonColor: number = 0xff00ff; - public boundingBoxesRectColor: number = 0x00ff00; - public boundingBoxesPolygonColor: number = 0x00ff00; - public boundingBoxesCircleColor: number = 0x00ff00; - public pathsCurveColor: number = 0xff0000; - public pathsLineColor: number = 0xff00ff; - public skeletonXYColor:number = 0xff0000; - public bonesColor:number = 0x00eecc; - - /** - * The debug is attached by force to each spine object. So we need to create it inside the spine when we get the first update - */ - public registerSpine(spine: SpineBase) { - if (this.registeredSpines.has(spine)) - { - console.warn("SpineDebugRenderer.registerSpine() - this spine is already registered!", spine); - } - const debugDisplayObjects = { - parentDebugContainer: new Container(), - bones: new Container(), - skeletonXY: new Graphics(), - regionAttachmentsShape: new Graphics(), - meshTrianglesLine: new Graphics(), - meshHullLine: new Graphics(), - clippingPolygon: new Graphics(), - boundingBoxesRect: new Graphics(), - boundingBoxesCircle: new Graphics(), - boundingBoxesPolygon: new Graphics(), - pathsCurve: new Graphics(), - pathsLine: new Graphics(), - }; - - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.bones); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.skeletonXY); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.regionAttachmentsShape); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.meshTrianglesLine); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.meshHullLine); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.clippingPolygon); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesRect); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesCircle); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesPolygon); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.pathsCurve); - debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.pathsLine); - - spine.addChild(debugDisplayObjects.parentDebugContainer); - - this.registeredSpines.set(spine, debugDisplayObjects); - } - public renderDebug(spine:SpineBase):void { - - if (!this.registeredSpines.has(spine)) { - // This should never happen. Spines are registered when you assign spine.debug - this.registerSpine(spine); - } - - const debugDisplayObjects = this.registeredSpines.get(spine); - - debugDisplayObjects.skeletonXY.clear(); - debugDisplayObjects.regionAttachmentsShape.clear(); - debugDisplayObjects.meshTrianglesLine.clear(); - debugDisplayObjects.meshHullLine.clear(); - debugDisplayObjects.clippingPolygon.clear(); - debugDisplayObjects.boundingBoxesRect.clear(); - debugDisplayObjects.boundingBoxesCircle.clear(); - debugDisplayObjects.boundingBoxesPolygon.clear(); - debugDisplayObjects.pathsCurve.clear(); - debugDisplayObjects.pathsLine.clear(); - - for (let len = debugDisplayObjects.bones.children.length; len > 0; len--) { - debugDisplayObjects.bones.children[len - 1].destroy({ children: true, texture: true, baseTexture: true }); - } - - const scale = spine.scale.x || spine.scale.y || 1; - const lineWidth = this.lineWidth / scale; - - if (this.drawBones) { - this.drawBonesFunc(spine, debugDisplayObjects, lineWidth, scale); - } - - if (this.drawPaths) { - this.drawPathsFunc(spine, debugDisplayObjects, lineWidth); - } - - if (this.drawBoundingBoxes) { - this.drawBoundingBoxesFunc(spine, debugDisplayObjects, lineWidth); - } - - if (this.drawClipping) { - this.drawClippingFunc(spine, debugDisplayObjects, lineWidth); - } - - if (this.drawMeshHull || this.drawMeshTriangles) { - this.drawMeshHullAndMeshTriangles(spine, debugDisplayObjects, lineWidth); - } - - if (this.drawRegionAttachments) { - this.drawRegionAttachmentsFunc(spine, debugDisplayObjects, lineWidth); - } - } - - private drawBonesFunc(spine:SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth:number, scale:number): void { - const skeleton = spine.skeleton; - const skeletonX = skeleton.x; - const skeletonY = skeleton.y; - const bones = skeleton.bones; - - debugDisplayObjects.skeletonXY.lineStyle(lineWidth, this.skeletonXYColor, 1); - - for (let i = 0, len = bones.length; i < len; i++) { - const bone = bones[i], - boneLen = bone.data.length, - starX = skeletonX + bone.matrix.tx, - starY = skeletonY + bone.matrix.ty, - endX = skeletonX + boneLen * bone.matrix.a + bone.matrix.tx, - endY = skeletonY + boneLen * bone.matrix.b + bone.matrix.ty; - - if (bone.data.name === "root" || bone.data.parent === null) { - continue; - } - - // Triangle calculation formula - // area: A=sqrt((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c))/4 - // alpha: alpha=acos((pow(b, 2)+pow(c, 2)-pow(a, 2))/(2*b*c)) - // beta: beta=acos((pow(a, 2)+pow(c, 2)-pow(b, 2))/(2*a*c)) - // gamma: gamma=acos((pow(a, 2)+pow(b, 2)-pow(c, 2))/(2*a*b)) - - const w = Math.abs(starX - endX), - h = Math.abs(starY - endY), - // a = w, // side length a - a2 = Math.pow(w, 2), // square root of side length a - b = h, // side length b - b2 = Math.pow(h, 2), // square root of side length b - c = Math.sqrt(a2 + b2), // side length c - c2 = Math.pow(c, 2), // square root of side length c - rad = Math.PI / 180, - // A = Math.acos([a2 + c2 - b2] / [2 * a * c]) || 0, // Angle A - // C = Math.acos([a2 + b2 - c2] / [2 * a * b]) || 0, // C angle - B = Math.acos((c2 + b2 - a2) / (2 * b * c)) || 0; // angle of corner B - if (c === 0) { - continue; - } - - const gp = new Graphics(); - debugDisplayObjects.bones.addChild(gp); - - // draw bone - const refRation = c / 50 / scale; - gp.beginFill(this.bonesColor, 1); - gp.drawPolygon(0, 0, 0 - refRation, c - refRation * 3, 0, c - refRation, 0 + refRation, c - refRation * 3); - gp.endFill(); - gp.x = starX; - gp.y = starY; - gp.pivot.y = c; - - // Calculate bone rotation angle - let rotation = 0; - if (starX < endX && starY < endY) { - // bottom right - rotation = -B + 180 * rad; - } else if (starX > endX && starY < endY) { - // bottom left - rotation = 180 * rad + B; - } else if (starX > endX && starY > endY) { - // top left - rotation = -B; - } else if (starX < endX && starY > endY) { - // bottom left - rotation = B; - } else if (starY === endY && starX < endX) { - // To the right - rotation = 90 * rad; - } else if (starY === endY && starX > endX) { - // go left - rotation = -90 * rad; - } else if (starX === endX && starY < endY) { - // down - rotation = 180 * rad; - } else if (starX === endX && starY > endY) { - // up - rotation = 0; - } - gp.rotation = rotation; - - // Draw the starting rotation point of the bone - gp.lineStyle(lineWidth + refRation / 2.4, this.bonesColor, 1); - gp.beginFill(0x000000, 0.6); - gp.drawCircle(0, c, refRation * 1.2); - gp.endFill(); - } - - // Draw the skeleton starting point "X" form - const startDotSize = lineWidth * 3; - debugDisplayObjects.skeletonXY.moveTo(skeletonX - startDotSize, skeletonY - startDotSize); - debugDisplayObjects.skeletonXY.lineTo(skeletonX + startDotSize, skeletonY + startDotSize); - debugDisplayObjects.skeletonXY.moveTo(skeletonX + startDotSize, skeletonY - startDotSize); - debugDisplayObjects.skeletonXY.lineTo(skeletonX - startDotSize, skeletonY + startDotSize); - } - - private drawRegionAttachmentsFunc(spine:SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth:number): void { - const skeleton = spine.skeleton; - const slots = skeleton.slots; - - debugDisplayObjects.regionAttachmentsShape.lineStyle(lineWidth, this.regionAttachmentsColor, 1); - - for (let i = 0, len = slots.length; i < len; i++) { - const slot = slots[i], - attachment = slot.getAttachment(); - if (attachment == null || attachment.type !== AttachmentType.Region) { - continue; - } - - const regionAttachment = attachment as IRegionAttachment & { - computeWorldVertices:(slot: unknown, worldVertices: unknown, offset: unknown, stride: unknown) => void, - updateOffset?:() => void, - }; - - const vertices = new Float32Array(8); - - - regionAttachment?.updateOffset(); // We don't need this on all versions - - regionAttachment.computeWorldVertices(slot, vertices, 0, 2); - debugDisplayObjects.regionAttachmentsShape.drawPolygon(Array.from(vertices.slice(0, 8))); - - } - } - - private drawMeshHullAndMeshTriangles(spine:SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth:number): void { - const skeleton = spine.skeleton; - const slots = skeleton.slots; - - debugDisplayObjects.meshHullLine.lineStyle(lineWidth, this.meshHullColor, 1); - debugDisplayObjects.meshTrianglesLine.lineStyle(lineWidth, this.meshTrianglesColor, 1); - - for (let i = 0, len = slots.length; i < len; i++) { - const slot = slots[i]; - if (!slot.bone.active) { - continue; - } - const attachment = slot.getAttachment(); - if (attachment == null || attachment.type !== AttachmentType.Mesh) { - continue; - } - - const meshAttachment:IMeshAttachment = attachment as IMeshAttachment; - - const vertices = new Float32Array(meshAttachment.worldVerticesLength), - triangles = meshAttachment.triangles; - let hullLength = meshAttachment.hullLength; - meshAttachment.computeWorldVertices(slot, 0, meshAttachment.worldVerticesLength, vertices, 0, 2); - // draw the skinned mesh (triangle) - if (this.drawMeshTriangles) { - for (let i = 0, len = triangles.length; i < len; i += 3) { - const v1 = triangles[i] * 2, - v2 = triangles[i + 1] * 2, - v3 = triangles[i + 2] * 2; - debugDisplayObjects.meshTrianglesLine.moveTo(vertices[v1], vertices[v1 + 1]); - debugDisplayObjects.meshTrianglesLine.lineTo(vertices[v2], vertices[v2 + 1]); - debugDisplayObjects.meshTrianglesLine.lineTo(vertices[v3], vertices[v3 + 1]); - } - } - - // draw skin border - if (this.drawMeshHull && hullLength > 0) { - hullLength = (hullLength >> 1) * 2; - let lastX = vertices[hullLength - 2], - lastY = vertices[hullLength - 1]; - for (let i = 0, len = hullLength; i < len; i += 2) { - const x = vertices[i], - y = vertices[i + 1]; - debugDisplayObjects.meshHullLine.moveTo(x, y); - debugDisplayObjects.meshHullLine.lineTo(lastX, lastY); - lastX = x; - lastY = y; - } - } - } - } - - private drawClippingFunc(spine:SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth:number): void { - const skeleton = spine.skeleton; - const slots = skeleton.slots; - - debugDisplayObjects.clippingPolygon.lineStyle(lineWidth, this.clippingPolygonColor, 1); - for (let i = 0, len = slots.length; i < len; i++) { - const slot = slots[i]; - if (!slot.bone.active) { - continue; - } - const attachment = slot.getAttachment(); - if (attachment == null || attachment.type !== AttachmentType.Clipping) { - continue; - } - - const clippingAttachment: IClippingAttachment = attachment as IClippingAttachment; - - const nn = clippingAttachment.worldVerticesLength, - world = new Float32Array(nn); - clippingAttachment.computeWorldVertices(slot, 0, nn, world, 0, 2); - debugDisplayObjects.clippingPolygon.drawPolygon(Array.from(world)); - } - } - - private drawBoundingBoxesFunc(spine:SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth:number): void { - // draw the total outline of the bounding box - debugDisplayObjects.boundingBoxesRect.lineStyle(lineWidth, this.boundingBoxesRectColor, 5); - - const bounds = new SkeletonBoundsBase(); - bounds.update(spine.skeleton, true); - debugDisplayObjects.boundingBoxesRect.drawRect(bounds.minX, bounds.minY, bounds.getWidth(), bounds.getHeight()); - - const polygons = bounds.polygons, - drawPolygon = (polygonVertices: ArrayLike, _offset: unknown, count: number): void => { - debugDisplayObjects.boundingBoxesPolygon.lineStyle(lineWidth, this.boundingBoxesPolygonColor, 1); - debugDisplayObjects.boundingBoxesPolygon.beginFill(this.boundingBoxesPolygonColor, 0.1); - - if (count < 3) { - throw new Error("Polygon must contain at least 3 vertices"); - } - const paths = [], - dotSize = lineWidth * 2; - for (let i = 0, len = polygonVertices.length; i < len; i += 2) { - const x1 = polygonVertices[i], - y1 = polygonVertices[i + 1]; - - // draw the bounding box node - debugDisplayObjects.boundingBoxesCircle.lineStyle(0); - debugDisplayObjects.boundingBoxesCircle.beginFill(this.boundingBoxesCircleColor); - debugDisplayObjects.boundingBoxesCircle.drawCircle(x1, y1, dotSize); - debugDisplayObjects.boundingBoxesCircle.endFill(); - - paths.push(x1, y1); - } - - // draw the bounding box area - debugDisplayObjects.boundingBoxesPolygon.drawPolygon(paths); - debugDisplayObjects.boundingBoxesPolygon.endFill(); - }; - - for (let i = 0, len = polygons.length; i < len; i++) { - const polygon = polygons[i]; - drawPolygon(polygon, 0, polygon.length); - } - } - - private drawPathsFunc(spine:SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth:number): void { - const skeleton = spine.skeleton; - const slots = skeleton.slots; - - debugDisplayObjects.pathsCurve.lineStyle(lineWidth, this.pathsCurveColor, 1); - debugDisplayObjects.pathsLine.lineStyle(lineWidth, this.pathsLineColor, 1); - - for (let i = 0, len = slots.length; i < len; i++) { - const slot = slots[i]; - if (!slot.bone.active) { - continue; - } - const attachment = slot.getAttachment(); - if (attachment == null || attachment.type !== AttachmentType.Path) { - continue - } - - const pathAttachment = attachment as IVertexAttachment & {closed:boolean}; - let nn = pathAttachment.worldVerticesLength; - const world = new Float32Array(nn); - pathAttachment.computeWorldVertices(slot, 0, nn, world, 0, 2); - let x1 = world[2], - y1 = world[3], - x2 = 0, - y2 = 0; - if (pathAttachment.closed) { - const cx1 = world[0], - cy1 = world[1], - cx2 = world[nn - 2], - cy2 = world[nn - 1]; - x2 = world[nn - 4]; - y2 = world[nn - 3]; - - // curve - debugDisplayObjects.pathsCurve.moveTo(x1, y1); - debugDisplayObjects.pathsCurve.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2); - - // handle - debugDisplayObjects.pathsLine.moveTo(x1, y1); - debugDisplayObjects.pathsLine.lineTo(cx1, cy1); - debugDisplayObjects.pathsLine.moveTo(x2, y2); - debugDisplayObjects.pathsLine.lineTo(cx2, cy2); - } - nn -= 4; - for (let ii = 4; ii < nn; ii += 6) { - const cx1 = world[ii], - cy1 = world[ii + 1], - cx2 = world[ii + 2], - cy2 = world[ii + 3]; - x2 = world[ii + 4]; - y2 = world[ii + 5]; - // curve - debugDisplayObjects.pathsCurve.moveTo(x1, y1); - debugDisplayObjects.pathsCurve.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2); - - // handle - debugDisplayObjects.pathsLine.moveTo(x1, y1); - debugDisplayObjects.pathsLine.lineTo(cx1, cy1); - debugDisplayObjects.pathsLine.moveTo(x2, y2); - debugDisplayObjects.pathsLine.lineTo(cx2, cy2); - x1 = x2; - y1 = y2; - } - } - } - - public unregisterSpine(spine:SpineBase):void{ - if (!this.registeredSpines.has(spine)) { - console.warn("SpineDebugRenderer.unregisterSpine() - spine is not registered, can't unregister!", spine); - } - const debugDisplayObjects = this.registeredSpines.get(spine); - debugDisplayObjects.parentDebugContainer.destroy({baseTexture:true, children:true, texture:true}); - this.registeredSpines.delete(spine); - } -} \ No newline at end of file +import { Container } from '@pixi/display'; +import { Graphics } from '@pixi/graphics'; +import type { IAnimationState, IAnimationStateData } from './core/IAnimation'; +import type { IClippingAttachment, IMeshAttachment, IRegionAttachment, ISkeleton, ISkeletonData, IVertexAttachment } from './core/ISkeleton'; +import type { SpineBase } from './SpineBase'; +import { AttachmentType } from './core/AttachmentType'; +import { SkeletonBoundsBase } from './core/SkeletonBoundsBase'; + +/** + * Make a class that extends from this interface to create your own debug renderer. + * @public + */ +export interface ISpineDebugRenderer { + /** + * This will be called every frame, after the spine has been updated. + */ + renderDebug(spine: SpineBase): void; + + /** + * This is called when the `spine.debug` object is set to null or when the spine is destroyed. + */ + unregisterSpine(spine: SpineBase): void; + + /** + * This is called when the `spine.debug` object is set to a new instance of a debug renderer. + */ + registerSpine(spine: SpineBase): void; +} + +type DebugDisplayObjects = { + bones: Container; + skeletonXY: Graphics; + regionAttachmentsShape: Graphics; + meshTrianglesLine: Graphics; + meshHullLine: Graphics; + clippingPolygon: Graphics; + boundingBoxesRect: Graphics; + boundingBoxesCircle: Graphics; + boundingBoxesPolygon: Graphics; + pathsCurve: Graphics; + pathsLine: Graphics; + parentDebugContainer: Container; +}; + +/** + * This is a debug renderer that uses PixiJS Graphics under the hood. + * @public + */ +export class SpineDebugRenderer implements ISpineDebugRenderer { + private registeredSpines: Map, DebugDisplayObjects> = new Map(); + + public drawDebug = true; + public drawMeshHull = true; + public drawMeshTriangles = true; + public drawBones = true; + public drawPaths = true; + public drawBoundingBoxes = true; + public drawClipping = true; + public drawRegionAttachments = true; + + public lineWidth = 1; + public regionAttachmentsColor = 0x0078ff; + public meshHullColor = 0x0078ff; + public meshTrianglesColor = 0xffcc00; + public clippingPolygonColor = 0xff00ff; + public boundingBoxesRectColor = 0x00ff00; + public boundingBoxesPolygonColor = 0x00ff00; + public boundingBoxesCircleColor = 0x00ff00; + public pathsCurveColor = 0xff0000; + public pathsLineColor = 0xff00ff; + public skeletonXYColor = 0xff0000; + public bonesColor = 0x00eecc; + + /** + * The debug is attached by force to each spine object. So we need to create it inside the spine when we get the first update + */ + public registerSpine(spine: SpineBase) { + if (this.registeredSpines.has(spine)) { + console.warn('SpineDebugRenderer.registerSpine() - this spine is already registered!', spine); + } + const debugDisplayObjects = { + parentDebugContainer: new Container(), + bones: new Container(), + skeletonXY: new Graphics(), + regionAttachmentsShape: new Graphics(), + meshTrianglesLine: new Graphics(), + meshHullLine: new Graphics(), + clippingPolygon: new Graphics(), + boundingBoxesRect: new Graphics(), + boundingBoxesCircle: new Graphics(), + boundingBoxesPolygon: new Graphics(), + pathsCurve: new Graphics(), + pathsLine: new Graphics(), + }; + + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.bones); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.skeletonXY); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.regionAttachmentsShape); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.meshTrianglesLine); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.meshHullLine); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.clippingPolygon); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesRect); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesCircle); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesPolygon); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.pathsCurve); + debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.pathsLine); + + spine.addChild(debugDisplayObjects.parentDebugContainer); + + this.registeredSpines.set(spine, debugDisplayObjects); + } + public renderDebug(spine: SpineBase): void { + if (!this.registeredSpines.has(spine)) { + // This should never happen. Spines are registered when you assign spine.debug + this.registerSpine(spine); + } + + const debugDisplayObjects = this.registeredSpines.get(spine); + + debugDisplayObjects.skeletonXY.clear(); + debugDisplayObjects.regionAttachmentsShape.clear(); + debugDisplayObjects.meshTrianglesLine.clear(); + debugDisplayObjects.meshHullLine.clear(); + debugDisplayObjects.clippingPolygon.clear(); + debugDisplayObjects.boundingBoxesRect.clear(); + debugDisplayObjects.boundingBoxesCircle.clear(); + debugDisplayObjects.boundingBoxesPolygon.clear(); + debugDisplayObjects.pathsCurve.clear(); + debugDisplayObjects.pathsLine.clear(); + + for (let len = debugDisplayObjects.bones.children.length; len > 0; len--) { + debugDisplayObjects.bones.children[len - 1].destroy({ children: true, texture: true, baseTexture: true }); + } + + const scale = spine.scale.x || spine.scale.y || 1; + const lineWidth = this.lineWidth / scale; + + if (this.drawBones) { + this.drawBonesFunc(spine, debugDisplayObjects, lineWidth, scale); + } + + if (this.drawPaths) { + this.drawPathsFunc(spine, debugDisplayObjects, lineWidth); + } + + if (this.drawBoundingBoxes) { + this.drawBoundingBoxesFunc(spine, debugDisplayObjects, lineWidth); + } + + if (this.drawClipping) { + this.drawClippingFunc(spine, debugDisplayObjects, lineWidth); + } + + if (this.drawMeshHull || this.drawMeshTriangles) { + this.drawMeshHullAndMeshTriangles(spine, debugDisplayObjects, lineWidth); + } + + if (this.drawRegionAttachments) { + this.drawRegionAttachmentsFunc(spine, debugDisplayObjects, lineWidth); + } + } + + private drawBonesFunc( + spine: SpineBase, + debugDisplayObjects: DebugDisplayObjects, + lineWidth: number, + scale: number + ): void { + const skeleton = spine.skeleton; + const skeletonX = skeleton.x; + const skeletonY = skeleton.y; + const bones = skeleton.bones; + + debugDisplayObjects.skeletonXY.lineStyle(lineWidth, this.skeletonXYColor, 1); + + for (let i = 0, len = bones.length; i < len; i++) { + const bone = bones[i]; + const boneLen = bone.data.length; + const starX = skeletonX + bone.matrix.tx; + const starY = skeletonY + bone.matrix.ty; + const endX = skeletonX + boneLen * bone.matrix.a + bone.matrix.tx; + const endY = skeletonY + boneLen * bone.matrix.b + bone.matrix.ty; + + if (bone.data.name === 'root' || bone.data.parent === null) { + continue; + } + + // Triangle calculation formula + // area: A=sqrt((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c))/4 + // alpha: alpha=acos((pow(b, 2)+pow(c, 2)-pow(a, 2))/(2*b*c)) + // beta: beta=acos((pow(a, 2)+pow(c, 2)-pow(b, 2))/(2*a*c)) + // gamma: gamma=acos((pow(a, 2)+pow(b, 2)-pow(c, 2))/(2*a*b)) + + const w = Math.abs(starX - endX); + const h = Math.abs(starY - endY); + // a = w, // side length a + const a2 = Math.pow(w, 2); // square root of side length a + const b = h; // side length b + const b2 = Math.pow(h, 2); // square root of side length b + const c = Math.sqrt(a2 + b2); // side length c + const c2 = Math.pow(c, 2); // square root of side length c + const rad = Math.PI / 180; + // A = Math.acos([a2 + c2 - b2] / [2 * a * c]) || 0, // Angle A + // C = Math.acos([a2 + b2 - c2] / [2 * a * b]) || 0, // C angle + const B = Math.acos((c2 + b2 - a2) / (2 * b * c)) || 0; // angle of corner B + + if (c === 0) { + continue; + } + + const gp = new Graphics(); + + debugDisplayObjects.bones.addChild(gp); + + // draw bone + const refRation = c / 50 / scale; + + gp.beginFill(this.bonesColor, 1); + gp.drawPolygon(0, 0, 0 - refRation, c - refRation * 3, 0, c - refRation, 0 + refRation, c - refRation * 3); + gp.endFill(); + gp.x = starX; + gp.y = starY; + gp.pivot.y = c; + + // Calculate bone rotation angle + let rotation = 0; + + if (starX < endX && starY < endY) { + // bottom right + rotation = -B + 180 * rad; + } else if (starX > endX && starY < endY) { + // bottom left + rotation = 180 * rad + B; + } else if (starX > endX && starY > endY) { + // top left + rotation = -B; + } else if (starX < endX && starY > endY) { + // bottom left + rotation = B; + } else if (starY === endY && starX < endX) { + // To the right + rotation = 90 * rad; + } else if (starY === endY && starX > endX) { + // go left + rotation = -90 * rad; + } else if (starX === endX && starY < endY) { + // down + rotation = 180 * rad; + } else if (starX === endX && starY > endY) { + // up + rotation = 0; + } + gp.rotation = rotation; + + // Draw the starting rotation point of the bone + gp.lineStyle(lineWidth + refRation / 2.4, this.bonesColor, 1); + gp.beginFill(0x000000, 0.6); + gp.drawCircle(0, c, refRation * 1.2); + gp.endFill(); + } + + // Draw the skeleton starting point "X" form + const startDotSize = lineWidth * 3; + + debugDisplayObjects.skeletonXY.moveTo(skeletonX - startDotSize, skeletonY - startDotSize); + debugDisplayObjects.skeletonXY.lineTo(skeletonX + startDotSize, skeletonY + startDotSize); + debugDisplayObjects.skeletonXY.moveTo(skeletonX + startDotSize, skeletonY - startDotSize); + debugDisplayObjects.skeletonXY.lineTo(skeletonX - startDotSize, skeletonY + startDotSize); + } + + private drawRegionAttachmentsFunc( + spine: SpineBase, + debugDisplayObjects: DebugDisplayObjects, + lineWidth: number + ): void { + const skeleton = spine.skeleton; + const slots = skeleton.slots; + + debugDisplayObjects.regionAttachmentsShape.lineStyle(lineWidth, this.regionAttachmentsColor, 1); + + for (let i = 0, len = slots.length; i < len; i++) { + const slot = slots[i]; + const attachment = slot.getAttachment(); + + if (attachment == null || attachment.type !== AttachmentType.Region) { + continue; + } + + const regionAttachment = attachment as IRegionAttachment & { + computeWorldVertices: (slot: unknown, worldVertices: unknown, offset: unknown, stride: unknown) => void; + updateOffset?: () => void; + }; + + const vertices = new Float32Array(8); + + regionAttachment?.updateOffset(); // We don't need this on all versions + + regionAttachment.computeWorldVertices(slot, vertices, 0, 2); + debugDisplayObjects.regionAttachmentsShape.drawPolygon(Array.from(vertices.slice(0, 8))); + } + } + + private drawMeshHullAndMeshTriangles( + spine: SpineBase, + debugDisplayObjects: DebugDisplayObjects, + lineWidth: number + ): void { + const skeleton = spine.skeleton; + const slots = skeleton.slots; + + debugDisplayObjects.meshHullLine.lineStyle(lineWidth, this.meshHullColor, 1); + debugDisplayObjects.meshTrianglesLine.lineStyle(lineWidth, this.meshTrianglesColor, 1); + + for (let i = 0, len = slots.length; i < len; i++) { + const slot = slots[i]; + + if (!slot.bone.active) { + continue; + } + const attachment = slot.getAttachment(); + + if (attachment == null || attachment.type !== AttachmentType.Mesh) { + continue; + } + + const meshAttachment: IMeshAttachment = attachment as IMeshAttachment; + + const vertices = new Float32Array(meshAttachment.worldVerticesLength); + const triangles = meshAttachment.triangles; + let hullLength = meshAttachment.hullLength; + + meshAttachment.computeWorldVertices(slot, 0, meshAttachment.worldVerticesLength, vertices, 0, 2); + // draw the skinned mesh (triangle) + if (this.drawMeshTriangles) { + for (let i = 0, len = triangles.length; i < len; i += 3) { + const v1 = triangles[i] * 2; + const v2 = triangles[i + 1] * 2; + const v3 = triangles[i + 2] * 2; + + debugDisplayObjects.meshTrianglesLine.moveTo(vertices[v1], vertices[v1 + 1]); + debugDisplayObjects.meshTrianglesLine.lineTo(vertices[v2], vertices[v2 + 1]); + debugDisplayObjects.meshTrianglesLine.lineTo(vertices[v3], vertices[v3 + 1]); + } + } + + // draw skin border + if (this.drawMeshHull && hullLength > 0) { + hullLength = (hullLength >> 1) * 2; + let lastX = vertices[hullLength - 2]; + let lastY = vertices[hullLength - 1]; + + for (let i = 0, len = hullLength; i < len; i += 2) { + const x = vertices[i]; + const y = vertices[i + 1]; + + debugDisplayObjects.meshHullLine.moveTo(x, y); + debugDisplayObjects.meshHullLine.lineTo(lastX, lastY); + lastX = x; + lastY = y; + } + } + } + } + + private drawClippingFunc(spine: SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void { + const skeleton = spine.skeleton; + const slots = skeleton.slots; + + debugDisplayObjects.clippingPolygon.lineStyle(lineWidth, this.clippingPolygonColor, 1); + for (let i = 0, len = slots.length; i < len; i++) { + const slot = slots[i]; + + if (!slot.bone.active) { + continue; + } + const attachment = slot.getAttachment(); + + if (attachment == null || attachment.type !== AttachmentType.Clipping) { + continue; + } + + const clippingAttachment: IClippingAttachment = attachment as IClippingAttachment; + + const nn = clippingAttachment.worldVerticesLength; + const world = new Float32Array(nn); + + clippingAttachment.computeWorldVertices(slot, 0, nn, world, 0, 2); + debugDisplayObjects.clippingPolygon.drawPolygon(Array.from(world)); + } + } + + private drawBoundingBoxesFunc( + spine: SpineBase, + debugDisplayObjects: DebugDisplayObjects, + lineWidth: number + ): void { + // draw the total outline of the bounding box + debugDisplayObjects.boundingBoxesRect.lineStyle(lineWidth, this.boundingBoxesRectColor, 5); + + const bounds = new SkeletonBoundsBase(); + + bounds.update(spine.skeleton, true); + debugDisplayObjects.boundingBoxesRect.drawRect(bounds.minX, bounds.minY, bounds.getWidth(), bounds.getHeight()); + + const polygons = bounds.polygons; + const drawPolygon = (polygonVertices: ArrayLike, _offset: unknown, count: number): void => { + debugDisplayObjects.boundingBoxesPolygon.lineStyle(lineWidth, this.boundingBoxesPolygonColor, 1); + debugDisplayObjects.boundingBoxesPolygon.beginFill(this.boundingBoxesPolygonColor, 0.1); + + if (count < 3) { + throw new Error('Polygon must contain at least 3 vertices'); + } + const paths = []; + const dotSize = lineWidth * 2; + + for (let i = 0, len = polygonVertices.length; i < len; i += 2) { + const x1 = polygonVertices[i]; + const y1 = polygonVertices[i + 1]; + + // draw the bounding box node + debugDisplayObjects.boundingBoxesCircle.lineStyle(0); + debugDisplayObjects.boundingBoxesCircle.beginFill(this.boundingBoxesCircleColor); + debugDisplayObjects.boundingBoxesCircle.drawCircle(x1, y1, dotSize); + debugDisplayObjects.boundingBoxesCircle.endFill(); + + paths.push(x1, y1); + } + + // draw the bounding box area + debugDisplayObjects.boundingBoxesPolygon.drawPolygon(paths); + debugDisplayObjects.boundingBoxesPolygon.endFill(); + }; + + for (let i = 0, len = polygons.length; i < len; i++) { + const polygon = polygons[i]; + + drawPolygon(polygon, 0, polygon.length); + } + } + + private drawPathsFunc(spine: SpineBase, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void { + const skeleton = spine.skeleton; + const slots = skeleton.slots; + + debugDisplayObjects.pathsCurve.lineStyle(lineWidth, this.pathsCurveColor, 1); + debugDisplayObjects.pathsLine.lineStyle(lineWidth, this.pathsLineColor, 1); + + for (let i = 0, len = slots.length; i < len; i++) { + const slot = slots[i]; + + if (!slot.bone.active) { + continue; + } + const attachment = slot.getAttachment(); + + if (attachment == null || attachment.type !== AttachmentType.Path) { + continue; + } + + const pathAttachment = attachment as IVertexAttachment & { closed: boolean }; + let nn = pathAttachment.worldVerticesLength; + const world = new Float32Array(nn); + + pathAttachment.computeWorldVertices(slot, 0, nn, world, 0, 2); + let x1 = world[2]; + let y1 = world[3]; + let x2 = 0; + let y2 = 0; + + if (pathAttachment.closed) { + const cx1 = world[0]; + const cy1 = world[1]; + const cx2 = world[nn - 2]; + const cy2 = world[nn - 1]; + + x2 = world[nn - 4]; + y2 = world[nn - 3]; + + // curve + debugDisplayObjects.pathsCurve.moveTo(x1, y1); + debugDisplayObjects.pathsCurve.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2); + + // handle + debugDisplayObjects.pathsLine.moveTo(x1, y1); + debugDisplayObjects.pathsLine.lineTo(cx1, cy1); + debugDisplayObjects.pathsLine.moveTo(x2, y2); + debugDisplayObjects.pathsLine.lineTo(cx2, cy2); + } + nn -= 4; + for (let ii = 4; ii < nn; ii += 6) { + const cx1 = world[ii]; + const cy1 = world[ii + 1]; + const cx2 = world[ii + 2]; + const cy2 = world[ii + 3]; + + x2 = world[ii + 4]; + y2 = world[ii + 5]; + // curve + debugDisplayObjects.pathsCurve.moveTo(x1, y1); + debugDisplayObjects.pathsCurve.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2); + + // handle + debugDisplayObjects.pathsLine.moveTo(x1, y1); + debugDisplayObjects.pathsLine.lineTo(cx1, cy1); + debugDisplayObjects.pathsLine.moveTo(x2, y2); + debugDisplayObjects.pathsLine.lineTo(cx2, cy2); + x1 = x2; + y1 = y2; + } + } + } + + public unregisterSpine(spine: SpineBase): void { + if (!this.registeredSpines.has(spine)) { + console.warn("SpineDebugRenderer.unregisterSpine() - spine is not registered, can't unregister!", spine); + } + const debugDisplayObjects = this.registeredSpines.get(spine); + + debugDisplayObjects.parentDebugContainer.destroy({ baseTexture: true, children: true, texture: true }); + this.registeredSpines.delete(spine); + } +} diff --git a/packages/base/src/core/AttachmentType.ts b/packages/base/src/core/AttachmentType.ts index 8b536bd3..8a14d2eb 100644 --- a/packages/base/src/core/AttachmentType.ts +++ b/packages/base/src/core/AttachmentType.ts @@ -1,7 +1,12 @@ - /** * @public */ export enum AttachmentType { - Region, BoundingBox, Mesh, LinkedMesh, Path, Point, Clipping + Region, + BoundingBox, + Mesh, + LinkedMesh, + Path, + Point, + Clipping, } diff --git a/packages/base/src/core/BinaryInput.ts b/packages/base/src/core/BinaryInput.ts index 97099e10..d52f9c78 100644 --- a/packages/base/src/core/BinaryInput.ts +++ b/packages/base/src/core/BinaryInput.ts @@ -2,76 +2,85 @@ * @public */ export class BinaryInput { - constructor (data: Uint8Array, public strings = new Array(), private index: number = 0, private buffer = new DataView(data.buffer)) { - } + constructor(data: Uint8Array, public strings = new Array(), private index: number = 0, private buffer = new DataView(data.buffer)) {} - readByte (): number { + readByte(): number { return this.buffer.getInt8(this.index++); } - readUnsignedByte (): number { + readUnsignedByte(): number { return this.buffer.getUint8(this.index++); } - readShort (): number { - let value = this.buffer.getInt16(this.index); + readShort(): number { + const value = this.buffer.getInt16(this.index); + this.index += 2; + return value; } - readInt32 (): number { - let value = this.buffer.getInt32(this.index) + readInt32(): number { + const value = this.buffer.getInt32(this.index); + this.index += 4; + return value; } - readInt (optimizePositive: boolean) { + readInt(optimizePositive: boolean) { let b = this.readByte(); - let result = b & 0x7F; + let result = b & 0x7f; + if ((b & 0x80) != 0) { b = this.readByte(); - result |= (b & 0x7F) << 7; + result |= (b & 0x7f) << 7; if ((b & 0x80) != 0) { b = this.readByte(); - result |= (b & 0x7F) << 14; + result |= (b & 0x7f) << 14; if ((b & 0x80) != 0) { b = this.readByte(); - result |= (b & 0x7F) << 21; + result |= (b & 0x7f) << 21; if ((b & 0x80) != 0) { b = this.readByte(); - result |= (b & 0x7F) << 28; + result |= (b & 0x7f) << 28; } } } } - return optimizePositive ? result : ((result >>> 1) ^ -(result & 1)); + + return optimizePositive ? result : (result >>> 1) ^ -(result & 1); } - readStringRef (): string | null { - let index = this.readInt(true); + readStringRef(): string | null { + const index = this.readInt(true); + return index == 0 ? null : this.strings[index - 1]; } - readString (): string | null { + readString(): string | null { let byteCount = this.readInt(true); + switch (byteCount) { case 0: return null; case 1: - return ""; + return ''; } byteCount--; - let chars = ""; - for (let i = 0; i < byteCount;) { - let b = this.readUnsignedByte(); + let chars = ''; + + for (let i = 0; i < byteCount; ) { + const b = this.readUnsignedByte(); + switch (b >> 4) { case 12: case 13: - chars += String.fromCharCode(((b & 0x1F) << 6 | this.readByte() & 0x3F)); + chars += String.fromCharCode(((b & 0x1f) << 6) | (this.readByte() & 0x3f)); i += 2; break; case 14: - chars += String.fromCharCode(((b & 0x0F) << 12 | (this.readByte() & 0x3F) << 6 | this.readByte() & 0x3F)); + chars += String.fromCharCode(((b & 0x0f) << 12) | ((this.readByte() & 0x3f) << 6) | (this.readByte() & 0x3f)); i += 3; break; default: @@ -79,16 +88,19 @@ export class BinaryInput { i++; } } + return chars; } - readFloat (): number { - let value = this.buffer.getFloat32(this.index); + readFloat(): number { + const value = this.buffer.getFloat32(this.index); + this.index += 4; + return value; } - readBoolean (): boolean { + readBoolean(): boolean { return this.readByte() != 0; } } diff --git a/packages/base/src/core/IAnimation.ts b/packages/base/src/core/IAnimation.ts index 74292755..10257e5f 100644 --- a/packages/base/src/core/IAnimation.ts +++ b/packages/base/src/core/IAnimation.ts @@ -1,5 +1,5 @@ -import {ISkeleton, ISkeletonData} from './ISkeleton'; -import type {Map} from './Utils'; +import type { ISkeleton, ISkeletonData } from './ISkeleton'; +import type { Map } from './Utils'; // Those enums were moved from Animation.ts of spine 3.8 and 4.0 @@ -30,7 +30,7 @@ export enum MixBlend { * `add` is intended for animations layered on top of others, not for the first animations applied. Properties * keyed by additive animations must be set manually or by another animation before applying the additive animations, else * the property values will increase continually. */ - add + add, } /** Indicates whether a timeline's `alpha` is mixing out over time toward 0 (the setup or current pose value) or @@ -40,7 +40,8 @@ export enum MixBlend { * @public * */ export enum MixDirection { - mixIn, mixOut + mixIn, + mixOut, } /** @@ -55,7 +56,7 @@ export interface IAnimation { /** * @public */ - export interface IAnimationState { +export interface IAnimationState { data: AnimationStateData; tracks: ITrackEntry[]; listeners: IAnimationStateListener[]; @@ -64,60 +65,68 @@ export interface IAnimation { update(dt: number): void; apply(skeleton: ISkeleton): boolean; - setAnimation (trackIndex: number, animationName: string, loop: boolean): ITrackEntry; - addAnimation (trackIndex: number, animationName: string, loop: boolean, delay: number): ITrackEntry; - addEmptyAnimation (trackIndex: number, mixDuration: number, delay: number): ITrackEntry; - setEmptyAnimation (trackIndex: number, mixDuration: number): ITrackEntry; - setEmptyAnimations (mixDuration: number): void; + setAnimation(trackIndex: number, animationName: string, loop: boolean): ITrackEntry; + addAnimation(trackIndex: number, animationName: string, loop: boolean, delay: number): ITrackEntry; + addEmptyAnimation(trackIndex: number, mixDuration: number, delay: number): ITrackEntry; + setEmptyAnimation(trackIndex: number, mixDuration: number): ITrackEntry; + setEmptyAnimations(mixDuration: number): void; hasAnimation(animationName: string): boolean; - addListener (listener: IAnimationStateListener): void; - removeListener (listener: IAnimationStateListener): void; - clearListeners (): void; - clearTracks (): void; - clearTrack (index: number): void; + addListener(listener: IAnimationStateListener): void; + removeListener(listener: IAnimationStateListener): void; + clearListeners(): void; + clearTracks(): void; + clearTrack(index: number): void; } /** * @public */ - export interface IAnimationStateData { +export interface IAnimationStateData { skeletonData: SkeletonData; animationToMixTime: Map; defaultMix: number; - setMix (fromName: string, toName: string, duration: number): void; - setMixWith (from: Animation, to: Animation, duration: number): void; - getMix (from: Animation, to: Animation): number; + setMix(fromName: string, toName: string, duration: number): void; + setMixWith(from: Animation, to: Animation, duration: number): void; + getMix(from: Animation, to: Animation): number; } /** * @public */ - export interface IAnimationStateListener { - start? (entry: ITrackEntry): void; - interrupt? (entry: ITrackEntry): void; - end? (entry: ITrackEntry): void; - dispose? (entry: ITrackEntry): void; - complete? (entry: ITrackEntry): void; - event? (entry: ITrackEntry, event: IEvent): void; +export interface IAnimationStateListener { + start?(entry: ITrackEntry): void; + interrupt?(entry: ITrackEntry): void; + end?(entry: ITrackEntry): void; + dispose?(entry: ITrackEntry): void; + complete?(entry: ITrackEntry): void; + event?(entry: ITrackEntry, event: IEvent): void; } /** * @public */ -export interface ITimeline { -} +export interface ITimeline {} /** * @public */ - export interface ITrackEntry { +export interface ITrackEntry { trackIndex: number; loop: boolean; animationEnd: number; listener: IAnimationStateListener; - delay: number; trackTime: number; trackLast: number; nextTrackLast: number; trackEnd: number; timeScale: number; - alpha: number; mixTime: number; mixDuration: number; interruptAlpha: number; totalAlpha: number; + delay: number; + trackTime: number; + trackLast: number; + nextTrackLast: number; + trackEnd: number; + timeScale: number; + alpha: number; + mixTime: number; + mixDuration: number; + interruptAlpha: number; + totalAlpha: number; } /** diff --git a/packages/base/src/core/IConstraint.ts b/packages/base/src/core/IConstraint.ts index c7242d00..c606d7f3 100644 --- a/packages/base/src/core/IConstraint.ts +++ b/packages/base/src/core/IConstraint.ts @@ -1,4 +1,3 @@ - // These enums were moved from PathConstraintData.ts of spine 3.7, 3.8 and 4.0 /** Controls how the first bone is positioned along the path. @@ -7,7 +6,8 @@ * @public * */ export enum PositionMode { - Fixed, Percent + Fixed, + Percent, } /** Controls how bones are rotated, translated, and scaled to match the path. @@ -16,7 +16,9 @@ export enum PositionMode { * @public * */ export enum RotateMode { - Tangent, Chain, ChainScale + Tangent, + Chain, + ChainScale, } /** diff --git a/packages/base/src/core/ISkeleton.ts b/packages/base/src/core/ISkeleton.ts index a4f27087..ff04619e 100644 --- a/packages/base/src/core/ISkeleton.ts +++ b/packages/base/src/core/ISkeleton.ts @@ -1,23 +1,23 @@ -import {AttachmentType} from './AttachmentType'; -import {IAnimation, IEventData} from "./IAnimation"; -import {IIkConstraintData, IPathConstraintData, ITransformConstraintData} from './IConstraint'; -import type {Color, Vector2, Map} from './Utils'; -import type {TextureRegion} from './TextureRegion'; +import type { AttachmentType } from './AttachmentType'; +import type { IAnimation, IEventData } from './IAnimation'; +import type { IIkConstraintData, IPathConstraintData, ITransformConstraintData } from './IConstraint'; +import type { Color, Vector2, Map } from './Utils'; +import type { TextureRegion } from './TextureRegion'; -import type {Matrix} from '@pixi/math'; -import {BLEND_MODES} from '@pixi/constants'; +import type { Matrix } from '@pixi/math'; +import type { BLEND_MODES } from '@pixi/constants'; // This enum was moved from BoneData.ts of spine 3.7, 3.8 and 4.0 /** Determines how a bone inherits world transforms from parent bones. * @public * */ - export enum TransformMode { +export enum TransformMode { Normal, OnlyTranslation, NoRotationOrReflection, NoScale, - NoScaleOrReflection + NoScaleOrReflection, } /** @@ -26,7 +26,7 @@ import {BLEND_MODES} from '@pixi/constants'; export interface IBone { data: IBoneData; matrix: Matrix; - active:boolean; + active: boolean; } /** @@ -36,7 +36,7 @@ export interface ISkin { name: string; attachments: Array>; - getAttachment (slotIndex: number, name: string): IAttachment | null; + getAttachment(slotIndex: number, name: string): IAttachment | null; } /** @@ -75,7 +75,7 @@ export interface IHasTextureRegion { export interface ISequence { id: number; regions: TextureRegion[]; - apply (slot: ISlot, attachment: IHasTextureRegion): void; + apply(slot: ISlot, attachment: IHasTextureRegion): void; } /** @@ -101,7 +101,13 @@ export interface IClippingAttachment extends IVertexAttachment { export interface IRegionAttachment extends IAttachment { region: TextureRegion; color: Color; - x, y, scaleX, scaleY, rotation, width, height: number; + x; + y; + scaleX; + scaleY; + rotation; + width; + height: number; } /** @@ -110,9 +116,9 @@ export interface IRegionAttachment extends IAttachment { export interface IMeshAttachment extends IVertexAttachment { region: TextureRegion; color: Color; - regionUVs: Float32Array, - triangles: number[], - hullLength:number, + regionUVs: Float32Array; + triangles: number[]; + hullLength: number; } /** @@ -176,31 +182,28 @@ export interface ISlot { /** * @public */ -export interface ISkeleton { - bones: Bone[] - slots: Slot[] - drawOrder: Slot[] +export interface ISkeleton { + bones: Bone[]; + slots: Slot[]; + drawOrder: Slot[]; skin: Skin; data: SkeletonData; x: number; // added for debug purposes y: number; // added for debug purposes - updateWorldTransform (): void; - setToSetupPose (): void; - findSlotIndex (slotName: string): number; - getAttachmentByName (slotName: string, attachmentName: string): IAttachment; - - setBonesToSetupPose (): void; - setSlotsToSetupPose (): void; - findBone (boneName: string): Bone; - findSlot (slotName: string): Slot; - findBoneIndex (boneName: string): number; - findSlotIndex (slotName: string): number; - setSkinByName (skinName: string): void; - setAttachment (slotName: string, attachmentName: string): void; - getBounds (offset: Vector2, size: Vector2, temp: Array): void; + updateWorldTransform(): void; + setToSetupPose(): void; + findSlotIndex(slotName: string): number; + getAttachmentByName(slotName: string, attachmentName: string): IAttachment; + + setBonesToSetupPose(): void; + setSlotsToSetupPose(): void; + findBone(boneName: string): Bone; + findSlot(slotName: string): Slot; + findBoneIndex(boneName: string): number; + findSlotIndex(slotName: string): number; + setSkinByName(skinName: string): void; + setAttachment(slotName: string, attachmentName: string): void; + getBounds(offset: Vector2, size: Vector2, temp: Array): void; } /** @@ -213,14 +216,16 @@ export interface ISkeletonParser { /** * @public */ -export interface ISkeletonData { +export interface ISkeletonData< + BoneData extends IBoneData = IBoneData, + SlotData extends ISlotData = ISlotData, + Skin extends ISkin = ISkin, + Animation extends IAnimation = IAnimation, + EventData extends IEventData = IEventData, + IkConstraintData extends IIkConstraintData = IIkConstraintData, + TransformConstraintData extends ITransformConstraintData = ITransformConstraintData, + PathConstraintData extends IPathConstraintData = IPathConstraintData +> { name: string; bones: BoneData[]; slots: SlotData[]; @@ -239,12 +244,12 @@ export interface ISkeletonData { - +export class SkeletonBoundsBase { /** The left edge of the axis aligned bounding box. */ minX = 0; @@ -26,35 +25,37 @@ export class SkeletonBoundsBase (); - private polygonPool = new Pool(() => { - return Utils.newFloatArray(16); - }); + private polygonPool = new Pool(() => Utils.newFloatArray(16)); /** Clears any previous polygons, finds all visible bounding box attachments, and computes the world vertices for each bounding * box's polygon. * @param updateAabb If true, the axis aligned bounding box containing all the polygons is computed. If false, the * SkeletonBounds AABB methods will always return true. */ - update (skeleton: ISkeleton, updateAabb: boolean) { - if (!skeleton) throw new Error("skeleton cannot be null."); - let boundingBoxes = this.boundingBoxes; - let polygons = this.polygons; - let polygonPool = this.polygonPool; - let slots = skeleton.slots; - let slotCount = slots.length; + update(skeleton: ISkeleton, updateAabb: boolean) { + if (!skeleton) throw new Error('skeleton cannot be null.'); + const boundingBoxes = this.boundingBoxes; + const polygons = this.polygons; + const polygonPool = this.polygonPool; + const slots = skeleton.slots; + const slotCount = slots.length; boundingBoxes.length = 0; polygonPool.freeAll(polygons); polygons.length = 0; for (let i = 0; i < slotCount; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (!slot.bone.active) continue; - let attachment = slot.getAttachment(); + const attachment = slot.getAttachment(); + if (attachment != null && attachment.type === AttachmentType.BoundingBox) { - let boundingBox = attachment as BoundingBoxAttachment; + const boundingBox = attachment as BoundingBoxAttachment; + boundingBoxes.push(boundingBox); let polygon = polygonPool.obtain() as NumberArrayLike; + if (polygon.length != boundingBox.worldVerticesLength) { polygon = Utils.newFloatArray(boundingBox.worldVerticesLength); } @@ -73,15 +74,21 @@ export class SkeletonBoundsBase = this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY; } /** Returns true if the axis aligned bounding box intersects the line segment. */ - aabbIntersectsSegment (x1: number, y1: number, x2: number, y2: number) { - let minX = this.minX; - let minY = this.minY; - let maxX = this.maxX; - let maxY = this.maxY; - if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) + aabbIntersectsSegment(x1: number, y1: number, x2: number, y2: number) { + const minX = this.minX; + const minY = this.minY; + const maxX = this.maxX; + const maxY = this.maxY; + + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) { return false; - let m = (y2 - y1) / (x2 - x1); + } + const m = (y2 - y1) / (x2 - x1); let y = m * (minX - x1) + y1; + if (y > minY && y < maxY) return true; y = m * (maxX - x1) + y1; if (y > minY && y < maxY) return true; let x = (minY - y1) / m + x1; + if (x > minX && x < maxX) return true; x = (maxY - y1) / m + x1; if (x > minX && x < maxX) return true; + return false; } /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ - aabbIntersectsSkeleton (bounds: SkeletonBoundsBase) { + aabbIntersectsSkeleton(bounds: SkeletonBoundsBase) { return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY; } /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more - * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. + * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. * Cannot be done here because BoundingBoxAttachment is not a thing yet*/ - containsPoint (x: number, y: number): BoundingBoxAttachment | null { - let polygons = this.polygons; - for (let i = 0, n = polygons.length; i < n; i++) + containsPoint(x: number, y: number): BoundingBoxAttachment | null { + const polygons = this.polygons; + + for (let i = 0, n = polygons.length; i < n; i++) { if (this.containsPointPolygon(polygons[i], x, y)) return this.boundingBoxes[i]; + } + return null; } /** Returns true if the polygon contains the point. */ - containsPointPolygon (polygon: NumberArrayLike, x: number, y: number) { - let vertices = polygon; - let nn = polygon.length; + containsPointPolygon(polygon: NumberArrayLike, x: number, y: number) { + const vertices = polygon; + const nn = polygon.length; let prevIndex = nn - 2; let inside = false; + for (let ii = 0; ii < nn; ii += 2) { - let vertexY = vertices[ii + 1]; - let prevY = vertices[prevIndex + 1]; + const vertexY = vertices[ii + 1]; + const prevY = vertices[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { - let vertexX = vertices[ii]; - if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside; + const vertexX = vertices[ii]; + + if (vertexX + ((y - vertexY) / (prevY - vertexY)) * (vertices[prevIndex] - vertexX) < x) inside = !inside; } prevIndex = ii; } + return inside; } /** Returns the first bounding box attachment that contains any part of the line segment, or null. When doing many checks, it * is usually more efficient to only call this method if {@link #aabbIntersectsSegment()} returns * true. */ - intersectsSegment (x1: number, y1: number, x2: number, y2: number) { - let polygons = this.polygons; - for (let i = 0, n = polygons.length; i < n; i++) + intersectsSegment(x1: number, y1: number, x2: number, y2: number) { + const polygons = this.polygons; + + for (let i = 0, n = polygons.length; i < n; i++) { if (this.intersectsSegmentPolygon(polygons[i], x1, y1, x2, y2)) return this.boundingBoxes[i]; + } + return null; } /** Returns true if the polygon contains any part of the line segment. */ - intersectsSegmentPolygon (polygon: NumberArrayLike, x1: number, y1: number, x2: number, y2: number) { - let vertices = polygon; - let nn = polygon.length; + intersectsSegmentPolygon(polygon: NumberArrayLike, x1: number, y1: number, x2: number, y2: number) { + const vertices = polygon; + const nn = polygon.length; + + const width12 = x1 - x2; + const height12 = y1 - y2; + const det1 = x1 * y2 - y1 * x2; + let x3 = vertices[nn - 2]; + let y3 = vertices[nn - 1]; - let width12 = x1 - x2, height12 = y1 - y2; - let det1 = x1 * y2 - y1 * x2; - let x3 = vertices[nn - 2], y3 = vertices[nn - 1]; for (let ii = 0; ii < nn; ii += 2) { - let x4 = vertices[ii], y4 = vertices[ii + 1]; - let det2 = x3 * y4 - y3 * x4; - let width34 = x3 - x4, height34 = y3 - y4; - let det3 = width12 * height34 - height12 * width34; - let x = (det1 * width34 - width12 * det2) / det3; + const x4 = vertices[ii]; + const y4 = vertices[ii + 1]; + const det2 = x3 * y4 - y3 * x4; + const width34 = x3 - x4; + const height34 = y3 - y4; + const det3 = width12 * height34 - height12 * width34; + const x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { - let y = (det1 * height34 - height12 * det2) / det3; + const y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true; } x3 = x4; y3 = y4; } + return false; } /** Returns the polygon for the specified bounding box, or null. */ - getPolygon (boundingBox: BoundingBoxAttachment) { - if (!boundingBox) throw new Error("boundingBox cannot be null."); - let index = this.boundingBoxes.indexOf(boundingBox); + getPolygon(boundingBox: BoundingBoxAttachment) { + if (!boundingBox) throw new Error('boundingBox cannot be null.'); + const index = this.boundingBoxes.indexOf(boundingBox); + return index == -1 ? null : this.polygons[index]; } /** The width of the axis aligned bounding box. */ - getWidth () { + getWidth() { return this.maxX - this.minX; } /** The height of the axis aligned bounding box. */ - getHeight () { + getHeight() { return this.maxY - this.minY; } } diff --git a/packages/base/src/core/TextureAtlas.ts b/packages/base/src/core/TextureAtlas.ts index 2c9f2568..833803b5 100644 --- a/packages/base/src/core/TextureAtlas.ts +++ b/packages/base/src/core/TextureAtlas.ts @@ -1,10 +1,10 @@ -import {SCALE_MODES, MIPMAP_MODES, ALPHA_MODES} from '@pixi/constants'; -import {Texture} from '@pixi/core'; -import {Rectangle} from '@pixi/math'; -import {TextureRegion, TextureWrap, TextureFilter, filterFromString} from './TextureRegion'; -import {Map, Disposable} from './Utils'; +import { SCALE_MODES, MIPMAP_MODES, ALPHA_MODES } from '@pixi/constants'; +import { Texture } from '@pixi/core'; +import { Rectangle } from '@pixi/math'; +import { TextureRegion, TextureWrap, TextureFilter, filterFromString } from './TextureRegion'; +import type { Map, Disposable } from './Utils'; -import type {BaseTexture} from '@pixi/core'; +import type { BaseTexture } from '@pixi/core'; class RegionFields { x = 0; @@ -32,8 +32,9 @@ export class TextureAtlas implements Disposable { } addTexture(name: string, texture: Texture) { - let pages = this.pages; + const pages = this.pages; let page: TextureAtlasPage = null; + for (let i = 0; i < pages.length; i++) { if (pages[i].baseTexture === texture.baseTexture) { page = pages[i]; @@ -43,27 +44,30 @@ export class TextureAtlas implements Disposable { if (page === null) { page = new TextureAtlasPage(); page.name = 'texturePage'; - let baseTexture = texture.baseTexture; + const baseTexture = texture.baseTexture; + page.width = baseTexture.realWidth; page.height = baseTexture.realHeight; page.baseTexture = baseTexture; - //those fields are not relevant in Pixi + // those fields are not relevant in Pixi page.minFilter = page.magFilter = TextureFilter.Nearest; page.uWrap = TextureWrap.ClampToEdge; page.vWrap = TextureWrap.ClampToEdge; pages.push(page); } - let region = new TextureAtlasRegion(); + const region = new TextureAtlasRegion(); + region.name = name; region.page = page; region.texture = texture; region.index = -1; this.regions.push(region); + return region; } addTextureHash(textures: Map, stripExtension: boolean) { - for (let key in textures) { + for (const key in textures) { if (textures.hasOwnProperty(key)) { this.addTexture(stripExtension && key.indexOf('.') !== -1 ? key.substr(0, key.lastIndexOf('.')) : key, textures[key]); } @@ -75,82 +79,92 @@ export class TextureAtlas implements Disposable { } private load(atlasText: string, textureLoader: (path: string, loaderFunction: (tex: BaseTexture) => any) => any, callback: (obj: TextureAtlas) => any) { - if (textureLoader == null) - throw new Error("textureLoader cannot be null."); + if (textureLoader == null) { + throw new Error('textureLoader cannot be null.'); + } - let reader = new TextureAtlasReader(atlasText); - let entry = new Array(4); + const reader = new TextureAtlasReader(atlasText); + const entry = new Array(4); let page: TextureAtlasPage = null; - let pageFields: Map = {}; + const pageFields: Map = {}; let region: RegionFields = null; - pageFields["size"] = () => { + + pageFields.size = () => { page.width = parseInt(entry[1]); page.height = parseInt(entry[2]); }; - pageFields["format"] = () => { + pageFields.format = () => { // page.format = Format[tuple[0]]; we don't need format in WebGL }; - pageFields["filter"] = () => { + pageFields.filter = () => { page.minFilter = filterFromString(entry[1]); page.magFilter = filterFromString(entry[2]); }; - pageFields["repeat"] = () => { + pageFields.repeat = () => { if (entry[1].indexOf('x') != -1) page.uWrap = TextureWrap.Repeat; if (entry[1].indexOf('y') != -1) page.vWrap = TextureWrap.Repeat; }; - pageFields["pma"] = () => { - page.pma = entry[1] == "true"; + pageFields.pma = () => { + page.pma = entry[1] == 'true'; }; - let regionFields: Map = {}; - regionFields["xy"] = () => { // Deprecated, use bounds. + const regionFields: Map = {}; + + regionFields.xy = () => { + // Deprecated, use bounds. region.x = parseInt(entry[1]); region.y = parseInt(entry[2]); }; - regionFields["size"] = () => { // Deprecated, use bounds. + regionFields.size = () => { + // Deprecated, use bounds. region.width = parseInt(entry[1]); region.height = parseInt(entry[2]); }; - regionFields["bounds"] = () => { + regionFields.bounds = () => { region.x = parseInt(entry[1]); region.y = parseInt(entry[2]); region.width = parseInt(entry[3]); region.height = parseInt(entry[4]); }; - regionFields["offset"] = () => { // Deprecated, use offsets. + regionFields.offset = () => { + // Deprecated, use offsets. region.offsetX = parseInt(entry[1]); region.offsetY = parseInt(entry[2]); }; - regionFields["orig"] = () => { // Deprecated, use offsets. + regionFields.orig = () => { + // Deprecated, use offsets. region.originalWidth = parseInt(entry[1]); region.originalHeight = parseInt(entry[2]); }; - regionFields["offsets"] = () => { + regionFields.offsets = () => { region.offsetX = parseInt(entry[1]); region.offsetY = parseInt(entry[2]); region.originalWidth = parseInt(entry[3]); region.originalHeight = parseInt(entry[4]); }; - regionFields["rotate"] = () => { - let rotateValue = entry[1]; + regionFields.rotate = () => { + const rotateValue = entry[1]; let rotate = 0; - if (rotateValue.toLocaleLowerCase() == "true") { + + if (rotateValue.toLocaleLowerCase() == 'true') { rotate = 6; - } else if (rotateValue.toLocaleLowerCase() == "false") { + } else if (rotateValue.toLocaleLowerCase() == 'false') { rotate = 0; } else { rotate = ((720 - parseFloat(rotateValue)) % 360) / 45; } region.rotate = rotate; }; - regionFields["index"] = () => { + regionFields.index = () => { region.index = parseInt(entry[1]); }; let line = reader.readLine(); // Ignore empty lines before first entry. - while (line != null && line.trim().length == 0) + + while (line != null && line.trim().length == 0) { line = reader.readLine(); + } // Header entries. while (true) { if (line == null || line.trim().length == 0) break; @@ -158,7 +172,7 @@ export class TextureAtlas implements Disposable { line = reader.readLine(); } - let iterateParser = () => { + const iterateParser = () => { while (true) { if (line == null) { return callback && callback(this); @@ -171,8 +185,9 @@ export class TextureAtlas implements Disposable { page.name = line.trim(); while (true) { - if (reader.readEntry(entry, line = reader.readLine()) == 0) break; - let field: Function = pageFields[entry[0]]; + if (reader.readEntry(entry, (line = reader.readLine())) == 0) break; + const field: Function = pageFields[entry[0]]; + if (field) field(); } this.pages.push(page); @@ -180,10 +195,11 @@ export class TextureAtlas implements Disposable { textureLoader(page.name, (texture: BaseTexture) => { if (texture === null) { this.pages.splice(this.pages.indexOf(page), 1); + return callback && callback(null); } page.baseTexture = texture; - //TODO: set scaleMode and mipmapMode from spine + // TODO: set scaleMode and mipmapMode from spine if (page.pma) { texture.alphaMode = ALPHA_MODES.PMA; } @@ -196,7 +212,9 @@ export class TextureAtlas implements Disposable { page.width = texture.realWidth; page.height = texture.realHeight; if (!page.width || !page.height) { - console.log("ERROR spine atlas page " + page.name + ": meshes wont work if you dont specify size in atlas (http://www.html5gamedevs.com/topic/18888-pixi-spines-and-meshes/?p=107121)"); + console.log( + `ERROR spine atlas page ${page.name}: meshes wont work if you dont specify size in atlas (http://www.html5gamedevs.com/topic/18888-pixi-spines-and-meshes/?p=107121)` + ); } } iterateParser(); @@ -204,26 +222,32 @@ export class TextureAtlas implements Disposable { break; } else { region = new RegionFields(); - let atlasRegion = new TextureAtlasRegion(); + const atlasRegion = new TextureAtlasRegion(); + atlasRegion.name = line; atlasRegion.page = page; let names: string[] = null; let values: number[][] = null; + while (true) { - let count = reader.readEntry(entry, line = reader.readLine()); + const count = reader.readEntry(entry, (line = reader.readLine())); + if (count == 0) break; - let field: Function = regionFields[entry[0]]; - if (field) + const field: Function = regionFields[entry[0]]; + + if (field) { field(); - else { + } else { if (names == null) { names = []; - values = [] + values = []; } names.push(entry[0]); - let entryValues: number[] = []; - for (let i = 0; i < count; i++) + const entryValues: number[] = []; + + for (let i = 0; i < count; i++) { entryValues.push(parseInt(entry[i + 1])); + } values.push(entryValues); } } @@ -232,7 +256,8 @@ export class TextureAtlas implements Disposable { region.originalHeight = region.height; } - let resolution = page.baseTexture.resolution; + const resolution = page.baseTexture.resolution; + region.x /= resolution; region.y /= resolution; region.width /= resolution; @@ -243,10 +268,10 @@ export class TextureAtlas implements Disposable { region.offsetY /= resolution; const swapWH = region.rotate % 4 !== 0; - let frame = new Rectangle(region.x, region.y, swapWH ? region.height : region.width, swapWH ? region.width : region.height); + const frame = new Rectangle(region.x, region.y, swapWH ? region.height : region.width, swapWH ? region.width : region.height); - let orig = new Rectangle(0, 0, region.originalWidth, region.originalHeight); - let trim = new Rectangle(region.offsetX, region.originalHeight - region.height - region.offsetY, region.width, region.height); + const orig = new Rectangle(0, 0, region.originalWidth, region.originalHeight); + const trim = new Rectangle(region.offsetX, region.originalHeight - region.height - region.offsetY, region.width, region.height); atlasRegion.texture = new Texture(atlasRegion.page.baseTexture, frame, orig, trim, region.rotate); atlasRegion.index = region.index; @@ -266,6 +291,7 @@ export class TextureAtlas implements Disposable { return this.regions[i]; } } + return null; } @@ -281,30 +307,35 @@ export class TextureAtlas implements Disposable { */ class TextureAtlasReader { lines: Array; - index: number = 0; + index = 0; constructor(text: string) { this.lines = text.split(/\r\n|\r|\n/); } readLine(): string { - if (this.index >= this.lines.length) + if (this.index >= this.lines.length) { return null; + } + return this.lines[this.index++]; } - readEntry (entry: string[], line: string): number { + readEntry(entry: string[], line: string): number { if (line == null) return 0; line = line.trim(); if (line.length == 0) return 0; - let colon = line.indexOf(':'); + const colon = line.indexOf(':'); + if (colon == -1) return 0; entry[0] = line.substr(0, colon).trim(); - for (let i = 1, lastMatch = colon + 1;; i++) { - let comma = line.indexOf(',', lastMatch); + for (let i = 1, lastMatch = colon + 1; ; i++) { + const comma = line.indexOf(',', lastMatch); + if (comma == -1) { entry[i] = line.substr(lastMatch).trim(); + return i; } entry[i] = line.substr(lastMatch, comma - lastMatch).trim(); @@ -329,8 +360,9 @@ export class TextureAtlasPage { pma: boolean; public setFilters() { - let tex = this.baseTexture; - let filter = this.minFilter; + const tex = this.baseTexture; + const filter = this.minFilter; + if (filter == TextureFilter.Linear) { tex.scaleMode = SCALE_MODES.LINEAR; } else if (this.minFilter == TextureFilter.Nearest) { diff --git a/packages/base/src/core/TextureRegion.ts b/packages/base/src/core/TextureRegion.ts index 0469bd82..a867220d 100644 --- a/packages/base/src/core/TextureRegion.ts +++ b/packages/base/src/core/TextureRegion.ts @@ -1,32 +1,43 @@ - -import { Texture } from '@pixi/core'; -import { Rectangle } from '@pixi/math'; +import type { Texture } from '@pixi/core'; +import type { Rectangle } from '@pixi/math'; /** * @public */ -export function filterFromString (text: string): TextureFilter { +export function filterFromString(text: string): TextureFilter { switch (text.toLowerCase()) { - case "nearest": return TextureFilter.Nearest; - case "linear": return TextureFilter.Linear; - case "mipmap": return TextureFilter.MipMap; - case "mipmapnearestnearest": return TextureFilter.MipMapNearestNearest; - case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest; - case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear; - case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear; - default: throw new Error(`Unknown texture filter ${text}`); + case 'nearest': + return TextureFilter.Nearest; + case 'linear': + return TextureFilter.Linear; + case 'mipmap': + return TextureFilter.MipMap; + case 'mipmapnearestnearest': + return TextureFilter.MipMapNearestNearest; + case 'mipmaplinearnearest': + return TextureFilter.MipMapLinearNearest; + case 'mipmapnearestlinear': + return TextureFilter.MipMapNearestLinear; + case 'mipmaplinearlinear': + return TextureFilter.MipMapLinearLinear; + default: + throw new Error(`Unknown texture filter ${text}`); } } /** * @public */ -export function wrapFromString (text: string): TextureWrap { +export function wrapFromString(text: string): TextureWrap { switch (text.toLowerCase()) { - case "mirroredtepeat": return TextureWrap.MirroredRepeat; - case "clamptoedge": return TextureWrap.ClampToEdge; - case "repeat": return TextureWrap.Repeat; - default: throw new Error(`Unknown texture wrap ${text}`); + case 'mirroredtepeat': + return TextureWrap.MirroredRepeat; + case 'clamptoedge': + return TextureWrap.ClampToEdge; + case 'repeat': + return TextureWrap.Repeat; + default: + throw new Error(`Unknown texture wrap ${text}`); } } @@ -40,7 +51,7 @@ export enum TextureFilter { MipMapNearestNearest = 9984, // WebGLRenderingContext.NEAREST_MIPMAP_NEAREST MipMapLinearNearest = 9985, // WebGLRenderingContext.LINEAR_MIPMAP_NEAREST MipMapNearestLinear = 9986, // WebGLRenderingContext.NEAREST_MIPMAP_LINEAR - MipMapLinearLinear = 9987 // WebGLRenderingContext.LINEAR_MIPMAP_LINEAR + MipMapLinearLinear = 9987, // WebGLRenderingContext.LINEAR_MIPMAP_LINEAR } /** @@ -49,7 +60,7 @@ export enum TextureFilter { export enum TextureWrap { MirroredRepeat = 33648, // WebGLRenderingContext.MIRRORED_REPEAT ClampToEdge = 33071, // WebGLRenderingContext.CLAMP_TO_EDGE - Repeat = 10497 // WebGLRenderingContext.REPEAT + Repeat = 10497, // WebGLRenderingContext.REPEAT } /** @@ -58,7 +69,7 @@ export enum TextureWrap { export class TextureRegion { texture: Texture; - //thats for overrides + // thats for overrides size: Rectangle = null; names: string[] = null; @@ -68,17 +79,21 @@ export class TextureRegion { get width(): number { const tex = this.texture; + if (tex.trim) { return tex.trim.width; } + return tex.orig.width; } get height(): number { const tex = this.texture; + if (tex.trim) { return tex.trim.height; } + return tex.orig.height; } @@ -100,6 +115,7 @@ export class TextureRegion { get offsetX(): number { const tex = this.texture; + return tex.trim ? tex.trim.x : 0; } @@ -110,11 +126,13 @@ export class TextureRegion { get pixiOffsetY(): number { const tex = this.texture; + return tex.trim ? tex.trim.y : 0; } get spineOffsetY(): number { - let tex = this.texture; + const tex = this.texture; + return this.originalHeight - this.height - (tex.trim ? tex.trim.y : 0); } diff --git a/packages/base/src/core/Utils.ts b/packages/base/src/core/Utils.ts index 944693a4..4879b1e4 100644 --- a/packages/base/src/core/Utils.ts +++ b/packages/base/src/core/Utils.ts @@ -1,17 +1,9 @@ -import {ISkeleton} from "./ISkeleton"; +import type { ISkeleton } from './ISkeleton'; - -let fround_polyfill = (function(array) { - return function(x: number) { - return array[0] = x, array[0]; - }; -})(new Float32Array(1)); - -let fround: (value: number) => number = - (Math as any).fround || fround_polyfill; /** * @public */ + export interface Map { [key: string]: T; } @@ -29,21 +21,23 @@ export interface StringMap { export class IntSet { array = new Array(); - add (value: number): boolean { - let contains = this.contains(value); + add(value: number): boolean { + const contains = this.contains(value); + this.array[value | 0] = value | 0; + return !contains; } - contains (value: number) { + contains(value: number) { return this.array[value | 0] != undefined; } - remove (value: number) { + remove(value: number) { this.array[value | 0] = undefined; } - clear () { + clear() { this.array.length = 0; } } @@ -55,28 +49,34 @@ export class StringSet { entries: StringMap = {}; size = 0; - add (value: string): boolean { - let contains = this.entries[value]; + add(value: string): boolean { + const contains = this.entries[value]; + this.entries[value] = true; if (!contains) { this.size++; + return true; } + return false; } - addAll (values: string[]): boolean { - let oldSize = this.size; - for (var i = 0, n = values.length; i < n; i++) + addAll(values: string[]): boolean { + const oldSize = this.size; + + for (let i = 0, n = values.length; i < n; i++) { this.add(values[i]); + } + return oldSize != this.size; } - contains (value: string) { + contains(value: string) { return this.entries[value]; } - clear () { + clear() { this.entries = {}; this.size = 0; } @@ -94,14 +94,14 @@ export interface NumberArrayLike { * @public */ export interface Disposable { - dispose (): void; + dispose(): void; } /** * @public */ export interface Restorable { - restore (): void; + restore(): void; } /** @@ -114,43 +114,46 @@ export class Color { public static BLUE = new Color(0, 0, 1, 1); public static MAGENTA = new Color(1, 0, 1, 1); - constructor (public r: number = 0, public g: number = 0, public b: number = 0, public a: number = 0) { - } + constructor(public r: number = 0, public g: number = 0, public b: number = 0, public a: number = 0) {} - set (r: number, g: number, b: number, a: number) { + set(r: number, g: number, b: number, a: number) { this.r = r; this.g = g; this.b = b; this.a = a; + return this.clamp(); } - setFromColor (c: Color) { + setFromColor(c: Color) { this.r = c.r; this.g = c.g; this.b = c.b; this.a = c.a; + return this; } - setFromString (hex: string) { + setFromString(hex: string) { hex = hex.charAt(0) == '#' ? hex.substr(1) : hex; this.r = parseInt(hex.substr(0, 2), 16) / 255; this.g = parseInt(hex.substr(2, 2), 16) / 255; this.b = parseInt(hex.substr(4, 2), 16) / 255; this.a = hex.length != 8 ? 1 : parseInt(hex.substr(6, 2), 16) / 255; + return this; } - add (r: number, g: number, b: number, a: number) { + add(r: number, g: number, b: number, a: number) { this.r += r; this.g += g; this.b += b; this.a += a; + return this.clamp(); } - clamp () { + clamp() { if (this.r < 0) this.r = 0; else if (this.r > 1) this.r = 1; @@ -162,23 +165,24 @@ export class Color { if (this.a < 0) this.a = 0; else if (this.a > 1) this.a = 1; + return this; } - static rgba8888ToColor (color: Color, value: number) { + static rgba8888ToColor(color: Color, value: number) { color.r = ((value & 0xff000000) >>> 24) / 255; color.g = ((value & 0x00ff0000) >>> 16) / 255; color.b = ((value & 0x0000ff00) >>> 8) / 255; - color.a = ((value & 0x000000ff)) / 255; + color.a = (value & 0x000000ff) / 255; } - static rgb888ToColor (color: Color, value: number) { + static rgb888ToColor(color: Color, value: number) { color.r = ((value & 0x00ff0000) >>> 16) / 255; color.g = ((value & 0x0000ff00) >>> 8) / 255; - color.b = ((value & 0x000000ff)) / 255; + color.b = (value & 0x000000ff) / 255; } - static fromString (hex: string): Color { + static fromString(hex: string): Color { return new Color().setFromString(hex); } } @@ -194,45 +198,49 @@ export class MathUtils { static degreesToRadians = MathUtils.PI / 180; static degRad = MathUtils.degreesToRadians; - static clamp (value: number, min: number, max: number) { + static clamp(value: number, min: number, max: number) { if (value < min) return min; if (value > max) return max; + return value; } - static cosDeg (degrees: number) { + static cosDeg(degrees: number) { return Math.cos(degrees * MathUtils.degRad); } - static sinDeg (degrees: number) { + static sinDeg(degrees: number) { return Math.sin(degrees * MathUtils.degRad); } - static signum (value: number): number { - return value > 0 ? 1 : value < 0 ? -1 : 0; + static signum(value: number): number { + return Math.sign(value); } - static toInt (x: number) { + static toInt(x: number) { return x > 0 ? Math.floor(x) : Math.ceil(x); } - static cbrt (x: number) { - let y = Math.pow(Math.abs(x), 1 / 3); + static cbrt(x: number) { + const y = Math.pow(Math.abs(x), 1 / 3); + return x < 0 ? -y : y; } - static randomTriangular (min: number, max: number): number { + static randomTriangular(min: number, max: number): number { return MathUtils.randomTriangularWith(min, max, (min + max) * 0.5); } - static randomTriangularWith (min: number, max: number, mode: number): number { - let u = Math.random(); - let d = max - min; + static randomTriangularWith(min: number, max: number, mode: number): number { + const u = Math.random(); + const d = max - min; + if (u <= (mode - min) / d) return min + Math.sqrt(u * d * (mode - min)); + return max - Math.sqrt((1 - u) * d * (max - mode)); } - static isPowerOfTwo (value: number) { + static isPowerOfTwo(value: number) { return value && (value & (value - 1)) === 0; } } @@ -241,8 +249,8 @@ export class MathUtils { * @public */ export abstract class Interpolation { - protected abstract applyInternal (a: number): number; - apply (start: number, end: number, a: number): number { + protected abstract applyInternal(a: number): number; + apply(start: number, end: number, a: number): number { return start + (end - start) * this.applyInternal(a); } } @@ -253,13 +261,14 @@ export abstract class Interpolation { export class Pow extends Interpolation { protected power = 2; - constructor (power: number) { + constructor(power: number) { super(); this.power = power; } - applyInternal (a: number): number { + applyInternal(a: number): number { if (a <= 0.5) return Math.pow(a * 2, this.power) / 2; + return Math.pow((a - 1) * 2, this.power) / (this.power % 2 == 0 ? -2 : 2) + 1; } } @@ -268,11 +277,7 @@ export class Pow extends Interpolation { * @public */ export class PowOut extends Pow { - constructor (power: number) { - super(power); - } - - applyInternal (a: number): number { + applyInternal(a: number): number { return Math.pow(a - 1, this.power) * (this.power % 2 == 0 ? -1 : 1) + 1; } } @@ -281,80 +286,90 @@ export class PowOut extends Pow { * @public */ export class Utils { - static SUPPORTS_TYPED_ARRAYS = typeof (Float32Array) !== "undefined"; + static SUPPORTS_TYPED_ARRAYS = typeof Float32Array !== 'undefined'; - static arrayCopy (source: ArrayLike, sourceStart: number, dest: ArrayLike, destStart: number, numElements: number) { + static arrayCopy(source: ArrayLike, sourceStart: number, dest: ArrayLike, destStart: number, numElements: number) { for (let i = sourceStart, j = destStart; i < sourceStart + numElements; i++, j++) { dest[j] = source[i]; } } - static arrayFill (array: ArrayLike, fromIndex: number, toIndex: number, value: T) { - for (let i = fromIndex; i < toIndex; i++) + static arrayFill(array: ArrayLike, fromIndex: number, toIndex: number, value: T) { + for (let i = fromIndex; i < toIndex; i++) { array[i] = value; + } } - static setArraySize (array: Array, size: number, value: any = 0): Array { - let oldSize = array.length; + static setArraySize(array: Array, size: number, value: any = 0): Array { + const oldSize = array.length; + if (oldSize == size) return array; array.length = size; if (oldSize < size) { for (let i = oldSize; i < size; i++) array[i] = value; } + return array; } - static ensureArrayCapacity (array: Array, size: number, value: any = 0): Array { + static ensureArrayCapacity(array: Array, size: number, value: any = 0): Array { if (array.length >= size) return array; + return Utils.setArraySize(array, size, value); } - static newArray (size: number, defaultValue: T): Array { - let array = new Array(size); + static newArray(size: number, defaultValue: T): Array { + const array = new Array(size); + for (let i = 0; i < size; i++) array[i] = defaultValue; + return array; } - static newFloatArray (size: number): NumberArrayLike { - if (Utils.SUPPORTS_TYPED_ARRAYS) - return new Float32Array(size) - else { - let array = new Array(size); - for (let i = 0; i < array.length; i++) array[i] = 0; - return array; + static newFloatArray(size: number): NumberArrayLike { + if (Utils.SUPPORTS_TYPED_ARRAYS) { + return new Float32Array(size); } + + const array = new Array(size); + + for (let i = 0; i < array.length; i++) array[i] = 0; + + return array; } - static newShortArray (size: number): NumberArrayLike { - if (Utils.SUPPORTS_TYPED_ARRAYS) - return new Int16Array(size) - else { - let array = new Array(size); - for (let i = 0; i < array.length; i++) array[i] = 0; - return array; + static newShortArray(size: number): NumberArrayLike { + if (Utils.SUPPORTS_TYPED_ARRAYS) { + return new Int16Array(size); } + + const array = new Array(size); + + for (let i = 0; i < array.length; i++) array[i] = 0; + + return array; } - static toFloatArray (array: Array) { + static toFloatArray(array: Array) { return Utils.SUPPORTS_TYPED_ARRAYS ? new Float32Array(array) : array; } - static toSinglePrecision (value: number) { - return Utils.SUPPORTS_TYPED_ARRAYS ? fround(value) : value; + static toSinglePrecision(value: number) { + return Utils.SUPPORTS_TYPED_ARRAYS ? Math.fround(value) : value; } // This function is used to fix WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109 - static webkit602BugfixHelper (alpha: number, blend: any) { - - } + static webkit602BugfixHelper(alpha: number, blend: any) {} - static contains (array: Array, element: T, identity = true) { - for (var i = 0; i < array.length; i++) + static contains(array: Array, element: T, identity = true) { + for (let i = 0; i < array.length; i++) { if (array[i] == element) return true; + } + return false; } - static enumValue (type: any, name: string) { + static enumValue(type: any, name: string) { return type[name[0].toUpperCase() + name.slice(1)]; } } @@ -365,9 +380,10 @@ export class Utils { export class DebugUtils { static logBones(skeleton: ISkeleton) { for (let i = 0; i < skeleton.bones.length; i++) { - let bone = skeleton.bones[i]; - let mat = bone.matrix; - console.log(bone.data.name + ", " + mat.a + ", " + mat.b + ", " + mat.c + ", " + mat.d + ", " + mat.tx + ", " + mat.ty); + const bone = skeleton.bones[i]; + const mat = bone.matrix; + + console.log(`${bone.data.name}, ${mat.a}, ${mat.b}, ${mat.c}, ${mat.d}, ${mat.tx}, ${mat.ty}`); } } } @@ -379,25 +395,26 @@ export class Pool { private items = new Array(); private instantiator: () => T; - constructor (instantiator: () => T) { + constructor(instantiator: () => T) { this.instantiator = instantiator; } - obtain () { + obtain() { return this.items.length > 0 ? this.items.pop() : this.instantiator(); } - free (item: T) { + free(item: T) { if ((item as any).reset) (item as any).reset(); this.items.push(item); } - freeAll (items: ArrayLike) { - for (let i = 0; i < items.length; i++) + freeAll(items: ArrayLike) { + for (let i = 0; i < items.length; i++) { this.free(items[i]); + } } - clear () { + clear() { this.items.length = 0; } } @@ -406,27 +423,30 @@ export class Pool { * @public */ export class Vector2 { - constructor (public x = 0, public y = 0) { - } + constructor(public x = 0, public y = 0) {} - set (x: number, y: number): Vector2 { + set(x: number, y: number): Vector2 { this.x = x; this.y = y; + return this; } - length () { - let x = this.x; - let y = this.y; + length() { + const x = this.x; + const y = this.y; + return Math.sqrt(x * x + y * y); } - normalize () { - let len = this.length(); + normalize() { + const len = this.length(); + if (len != 0) { this.x /= len; this.y /= len; } + return this; } } @@ -444,8 +464,9 @@ export class TimeKeeper { private frameCount = 0; private frameTime = 0; - update () { - let now = Date.now() / 1000; + update() { + const now = Date.now() / 1000; + this.delta = now - this.lastTime; this.frameTime += this.delta; this.totalTime += this.delta; @@ -479,32 +500,36 @@ export class WindowedMean { mean = 0; dirty = true; - constructor (windowSize: number = 32) { + constructor(windowSize = 32) { this.values = new Array(windowSize); } - hasEnoughData () { + hasEnoughData() { return this.addedValues >= this.values.length; } - addValue (value: number) { + addValue(value: number) { if (this.addedValues < this.values.length) this.addedValues++; this.values[this.lastValue++] = value; if (this.lastValue > this.values.length - 1) this.lastValue = 0; this.dirty = true; } - getMean () { + getMean() { if (this.hasEnoughData()) { if (this.dirty) { let mean = 0; - for (let i = 0; i < this.values.length; i++) + + for (let i = 0; i < this.values.length; i++) { mean += this.values[i]; + } this.mean = mean / this.values.length; this.dirty = false; } + return this.mean; } + return 0; } } diff --git a/packages/base/src/index.ts b/packages/base/src/index.ts index a37aa6a8..839db03c 100644 --- a/packages/base/src/index.ts +++ b/packages/base/src/index.ts @@ -1,3 +1,5 @@ +/* eslint-disable spaced-comment */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// export * from './core/AttachmentType'; export * from './core/BinaryInput'; diff --git a/packages/base/src/polyfills.ts b/packages/base/src/polyfills.ts deleted file mode 100644 index c9fa809f..00000000 --- a/packages/base/src/polyfills.ts +++ /dev/null @@ -1,13 +0,0 @@ -interface Math { - fround(n: number): number; -} - -(() => { - if (!Math.fround) { - Math.fround = Math.fround = (function(array) { - return function(x: number) { - return array[0] = x, array[0]; - }; - })(new Float32Array(1)); - } -})(); diff --git a/packages/base/src/settings.ts b/packages/base/src/settings.ts index 64754402..e2a4d36a 100644 --- a/packages/base/src/settings.ts +++ b/packages/base/src/settings.ts @@ -1,7 +1,7 @@ /** * @public */ -export let settings = { +export const settings = { yDown: true, /** * pixi-spine gives option to not fail at certain parsing errors @@ -18,4 +18,4 @@ export let settings = { * past Spine.globalDelayLimit */ GLOBAL_DELAY_LIMIT: 0, -} +}; diff --git a/packages/loader-3.8/package.json b/packages/loader-3.8/package.json index dad6cf20..db2acb79 100644 --- a/packages/loader-3.8/package.json +++ b/packages/loader-3.8/package.json @@ -2,22 +2,35 @@ "name": "@pixi-spine/loader-3.8", "version": "3.1.2", "description": "Pixi loader middleware for spine 3.8 models", - "main": "lib/loader-3.8.js", - "module": "lib/loader-3.8.es.js", - "bundle": "dist/loader-3.8.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", - "peerDependencies": { - "@pixi/loaders": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/loader-3.8.js", + "bundleModule": "dist/loader-3.8.mjs", + "globals": { + "@pixi-spine/runtime-3.8": "PIXI.spine38", + "@pixi-spine/loader-base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/runtime-3.8": "~3.1.2" + "peerDependencies": { + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-3.8": "*" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node scripts/injectGlobalMixins" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node ../../scripts/injectGlobalMixins", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,15 +52,6 @@ }, "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-build-tools/rollup-configurator": "^1.0.11", - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/loader-3.8/rollup.config.js b/packages/loader-3.8/rollup.config.js deleted file mode 100644 index 07ced353..00000000 --- a/packages/loader-3.8/rollup.config.js +++ /dev/null @@ -1,12 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine.base', - '@pixi-spine/runtime-3.8': 'PIXI.spine', - '@pixi-spine/loader-base': 'PIXI.spine', - }, -}); - - - diff --git a/packages/loader-3.8/rollup.config.mjs b/packages/loader-3.8/rollup.config.mjs new file mode 100644 index 00000000..e20161c3 --- /dev/null +++ b/packages/loader-3.8/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from "@pixi-spine/rollup-config"; +import pkg from "./package.json" assert { type: "json" }; + +export default configBuilder(pkg.extensionConfig, pkg); \ No newline at end of file diff --git a/packages/loader-3.8/src/index.ts b/packages/loader-3.8/src/index.ts index 3977b159..f51eea57 100644 --- a/packages/loader-3.8/src/index.ts +++ b/packages/loader-3.8/src/index.ts @@ -1,13 +1,17 @@ +/* eslint-disable spaced-comment */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -import {AbstractSpineParser} from '@pixi-spine/loader-base'; -import {ISkeletonParser, TextureAtlas} from '@pixi-spine/base'; -import {LoaderResource, Loader} from "@pixi/loaders"; -import {AtlasAttachmentLoader, SkeletonBinary, SkeletonJson} from "@pixi-spine/runtime-3.8"; + +import '@pixi-spine/loader-base'; // Side effect install atlas loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports +import { ISpineResource, SpineLoaderAbstract } from '@pixi-spine/loader-base'; +import type { ISkeletonParser, TextureAtlas } from '@pixi-spine/base'; +import { AtlasAttachmentLoader, SkeletonBinary, SkeletonData, SkeletonJson } from '@pixi-spine/runtime-3.8'; /** - * @public + * @internal */ -export class SpineParser extends AbstractSpineParser { +class SpineParser extends SpineLoaderAbstract { createBinaryParser(): ISkeletonParser { return new SkeletonBinary(null); } @@ -16,17 +20,16 @@ export class SpineParser extends AbstractSpineParser { return new SkeletonJson(null); } - parseData(resource: LoaderResource, parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): void { + parseData(parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): ISpineResource { const parserCast = parser as SkeletonBinary | SkeletonJson; parserCast.attachmentLoader = new AtlasAttachmentLoader(atlas); - resource.spineData = parserCast.readSkeletonData(dataToParse); - resource.spineAtlas = atlas; - } - static use = new SpineParser().genMiddleware().use; - - static registerLoaderPlugin() { - Loader.registerPlugin(SpineParser); + return { + spineData: parserCast.readSkeletonData(dataToParse), + spineAtlas: atlas, + }; } } + +new SpineParser().installLoader(); diff --git a/packages/loader-4.0/package.json b/packages/loader-4.0/package.json index 19d9b02f..2bbec1ce 100644 --- a/packages/loader-4.0/package.json +++ b/packages/loader-4.0/package.json @@ -2,24 +2,35 @@ "name": "@pixi-spine/loader-4.0", "version": "3.1.2", "description": "Pixi loader middleware for spine 4.0 models", - "main": "lib/loader-4.0.js", - "module": "lib/loader-4.0.es.js", - "bundle": "dist/loader-4.0.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", - "peerDependencies": { - "@pixi/core": "^6.1.0", - "@pixi/display": "^6.1.0", - "@pixi/loaders": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/loader-4.0.js", + "bundleModule": "dist/loader-4.0.mjs", + "globals": { + "@pixi-spine/runtime-4.0": "PIXI.spine40", + "@pixi-spine/loader-base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/runtime-4.0": "~3.1.2" + "peerDependencies": { + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.0": "*" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node scripts/injectGlobalMixins" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node ../../scripts/injectGlobalMixins", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -41,14 +52,6 @@ }, "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/loader-4.0/rollup.config.js b/packages/loader-4.0/rollup.config.js deleted file mode 100644 index 6e7455ef..00000000 --- a/packages/loader-4.0/rollup.config.js +++ /dev/null @@ -1,9 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine.base', - '@pixi-spine/runtime-3.8': 'PIXI.spine', - '@pixi-spine/loader-base': 'PIXI.spine', - }, -}); diff --git a/packages/loader-4.0/rollup.config.mjs b/packages/loader-4.0/rollup.config.mjs new file mode 100644 index 00000000..e20161c3 --- /dev/null +++ b/packages/loader-4.0/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from "@pixi-spine/rollup-config"; +import pkg from "./package.json" assert { type: "json" }; + +export default configBuilder(pkg.extensionConfig, pkg); \ No newline at end of file diff --git a/packages/loader-4.0/src/index.ts b/packages/loader-4.0/src/index.ts index 2df7cc42..a08ef838 100644 --- a/packages/loader-4.0/src/index.ts +++ b/packages/loader-4.0/src/index.ts @@ -1,13 +1,17 @@ +/* eslint-disable spaced-comment */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -import {AbstractSpineParser} from '@pixi-spine/loader-base'; -import {ISkeletonParser, TextureAtlas} from '@pixi-spine/base'; -import {LoaderResource, Loader} from "@pixi/loaders"; -import {AtlasAttachmentLoader, SkeletonBinary, SkeletonJson} from "@pixi-spine/runtime-4.0"; + +import '@pixi-spine/loader-base'; // Side effect install atlas loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports +import { ISpineResource, SpineLoaderAbstract } from '@pixi-spine/loader-base'; +import type { ISkeletonParser, TextureAtlas } from '@pixi-spine/base'; +import { AtlasAttachmentLoader, SkeletonBinary, SkeletonData, SkeletonJson } from '@pixi-spine/runtime-4.0'; /** - * @public + * @internal */ -export class SpineParser extends AbstractSpineParser { +class SpineParser extends SpineLoaderAbstract { createBinaryParser(): ISkeletonParser { return new SkeletonBinary(null); } @@ -16,17 +20,16 @@ export class SpineParser extends AbstractSpineParser { return new SkeletonJson(null); } - parseData(resource: LoaderResource, parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): void { + parseData(parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): ISpineResource { const parserCast = parser as SkeletonBinary | SkeletonJson; parserCast.attachmentLoader = new AtlasAttachmentLoader(atlas); - resource.spineData = parserCast.readSkeletonData(dataToParse); - resource.spineAtlas = atlas; - } - static use = new SpineParser().genMiddleware().use; - - static registerLoaderPlugin() { - Loader.registerPlugin(SpineParser); + return { + spineData: parserCast.readSkeletonData(dataToParse), + spineAtlas: atlas, + }; } } + +new SpineParser().installLoader(); diff --git a/packages/loader-4.1/package.json b/packages/loader-4.1/package.json index 9cf87833..4d086606 100644 --- a/packages/loader-4.1/package.json +++ b/packages/loader-4.1/package.json @@ -2,24 +2,35 @@ "name": "@pixi-spine/loader-4.1", "version": "3.1.2", "description": "Pixi loader middleware for spine 4.1 models", - "main": "lib/loader-4.1.js", - "module": "lib/loader-4.1.es.js", - "bundle": "dist/loader-4.1.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", - "peerDependencies": { - "@pixi/core": "^6.1.0", - "@pixi/display": "^6.1.0", - "@pixi/loaders": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/loader-4.1.js", + "bundleModule": "dist/loader-4.1.mjs", + "globals": { + "@pixi-spine/runtime-4.1": "PIXI.spine41", + "@pixi-spine/loader-base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/runtime-4.1": "~3.1.2" + "peerDependencies": { + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-4.1": "*" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node scripts/injectGlobalMixins" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node ../../scripts/injectGlobalMixins", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -41,14 +52,6 @@ }, "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/loader-4.1/rollup.config.js b/packages/loader-4.1/rollup.config.js deleted file mode 100644 index 6e7455ef..00000000 --- a/packages/loader-4.1/rollup.config.js +++ /dev/null @@ -1,9 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine.base', - '@pixi-spine/runtime-3.8': 'PIXI.spine', - '@pixi-spine/loader-base': 'PIXI.spine', - }, -}); diff --git a/packages/loader-4.1/rollup.config.mjs b/packages/loader-4.1/rollup.config.mjs new file mode 100644 index 00000000..e20161c3 --- /dev/null +++ b/packages/loader-4.1/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from "@pixi-spine/rollup-config"; +import pkg from "./package.json" assert { type: "json" }; + +export default configBuilder(pkg.extensionConfig, pkg); \ No newline at end of file diff --git a/packages/loader-4.1/src/index.ts b/packages/loader-4.1/src/index.ts index b7782bee..3c57ff27 100644 --- a/packages/loader-4.1/src/index.ts +++ b/packages/loader-4.1/src/index.ts @@ -1,13 +1,17 @@ +/* eslint-disable spaced-comment */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -import {AbstractSpineParser} from '@pixi-spine/loader-base'; -import {ISkeletonParser, TextureAtlas} from '@pixi-spine/base'; -import {LoaderResource, Loader} from "@pixi/loaders"; -import {AtlasAttachmentLoader, SkeletonBinary, SkeletonJson} from "@pixi-spine/runtime-4.1"; + +import '@pixi-spine/loader-base'; // Side effect install atlas loader +// eslint-disable-next-line @typescript-eslint/no-duplicate-imports +import { ISpineResource, SpineLoaderAbstract } from '@pixi-spine/loader-base'; +import type { ISkeletonParser, TextureAtlas } from '@pixi-spine/base'; +import { AtlasAttachmentLoader, SkeletonBinary, SkeletonData, SkeletonJson } from '@pixi-spine/runtime-4.1'; /** - * @public + * @internal */ -export class SpineParser extends AbstractSpineParser { +class SpineParser extends SpineLoaderAbstract { createBinaryParser(): ISkeletonParser { return new SkeletonBinary(null); } @@ -16,17 +20,16 @@ export class SpineParser extends AbstractSpineParser { return new SkeletonJson(null); } - parseData(resource: LoaderResource, parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): void { + parseData(parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): ISpineResource { const parserCast = parser as SkeletonBinary | SkeletonJson; parserCast.attachmentLoader = new AtlasAttachmentLoader(atlas); - resource.spineData = parserCast.readSkeletonData(dataToParse); - resource.spineAtlas = atlas; - } - static use = new SpineParser().genMiddleware().use; - - static registerLoaderPlugin() { - Loader.registerPlugin(SpineParser); + return { + spineData: parserCast.readSkeletonData(dataToParse), + spineAtlas: atlas, + }; } } + +new SpineParser().installLoader(); diff --git a/packages/loader-base/package.json b/packages/loader-base/package.json index 859016bb..db2745fa 100644 --- a/packages/loader-base/package.json +++ b/packages/loader-base/package.json @@ -2,22 +2,42 @@ "name": "@pixi-spine/loader-base", "version": "3.1.2", "description": "Pixi loader middleware base", - "main": "lib/loader-base.js", - "module": "lib/loader-base.es.js", - "bundle": "dist/loader-base.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", - "peerDependencies": { - "@pixi/constants": "^6.1.0", - "@pixi/core": "^6.1.0", - "@pixi/loaders": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/loader-base.js", + "bundleModule": "dist/loader-base.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2" + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/assets": " ^7.0.0", + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/extensions": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/runner": "^7.0.0", + "@pixi/settings": "^7.0.0", + "@pixi/ticker": "^7.0.0", + "@pixi/utils": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node scripts/injectGlobalMixins" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node ../../scripts/injectGlobalMixins", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,14 +59,6 @@ }, "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/loader-base/rollup.config.js b/packages/loader-base/rollup.config.js deleted file mode 100644 index 71cbae50..00000000 --- a/packages/loader-base/rollup.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine', - }, -}); diff --git a/packages/loader-base/rollup.config.mjs b/packages/loader-base/rollup.config.mjs new file mode 100644 index 00000000..e20161c3 --- /dev/null +++ b/packages/loader-base/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from "@pixi-spine/rollup-config"; +import pkg from "./package.json" assert { type: "json" }; + +export default configBuilder(pkg.extensionConfig, pkg); \ No newline at end of file diff --git a/packages/loader-base/src/SpineLoaderAbstract.ts b/packages/loader-base/src/SpineLoaderAbstract.ts new file mode 100644 index 00000000..de54a14d --- /dev/null +++ b/packages/loader-base/src/SpineLoaderAbstract.ts @@ -0,0 +1,183 @@ +import { ISkeletonData, ISkeletonParser, TextureAtlas } from '@pixi-spine/base'; +import { AssetExtension, checkExtension, LoadAsset, Loader, LoaderParserPriority } from '@pixi/assets'; +import { BaseTexture, extensions, ExtensionType, settings, Texture, utils } from '@pixi/core'; +import { makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject } from './atlasLoader'; + +type SPINEJSON = any; +type SPINEBINARY = ArrayBuffer; + +function isJson(resource: unknown): resource is SPINEJSON { + return resource.hasOwnProperty('bones'); +} + +function isBuffer(resource: unknown): resource is SPINEBINARY { + return resource instanceof ArrayBuffer; +} + +/** + * This abstract class is used to create a spine loader specifically for a needed version + * @public + */ +export abstract class SpineLoaderAbstract { + constructor() {} + + abstract createJsonParser(): ISkeletonParser; + + abstract createBinaryParser(): ISkeletonParser; + + abstract parseData(parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): ISpineResource; + + public installLoader(): any { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const spineAdapter = this; + const spineLoaderExtension: AssetExtension, ISpineMetadata> = { + extension: ExtensionType.Asset, + + loader: { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Normal, + }, + + // #region Downloading skel buffer data + test(url) { + return checkExtension(url, '.skel'); + }, + + async load(url: string): Promise { + const response = await settings.ADAPTER.fetch(url); + + const buffer = await response.arrayBuffer(); + + return buffer as SPINEBINARY; + }, + // #endregion + + // #region Parsing spine data + testParse(asset: unknown, options: LoadAsset): Promise { + const isJsonSpineModel = checkExtension(options.src, '.json') && isJson(asset); + const isBinarySpineModel = checkExtension(options.src, '.skel') && isBuffer(asset); + + // From 6.x loader. If the atlas is strictly false we bail + const isMetadataAngry = options.data?.spineAtlas === false; + + return Promise.resolve((isJsonSpineModel && !isMetadataAngry) || isBinarySpineModel); + }, + + async parse(asset: SPINEJSON | SPINEBINARY, loadAsset, loader): Promise> { + const fileExt = utils.path.extname(loadAsset.src).toLowerCase(); + const fileName = utils.path.basename(loadAsset.src, fileExt); + let basePath = utils.path.dirname(loadAsset.src); + + if (basePath && basePath.lastIndexOf('/') !== basePath.length - 1) { + basePath += '/'; + } + + const isJsonSpineModel = checkExtension(loadAsset.src, '.json') && isJson(asset); + // const isBinarySpineModel = fileExt === 'slel' && isBuffer(asset); + + let parser: ISkeletonParser = null; + let dataToParse = asset; + + if (isJsonSpineModel) { + parser = spineAdapter.createJsonParser(); + } else { + parser = spineAdapter.createBinaryParser(); + dataToParse = new Uint8Array(asset); + } + + const metadata = (loadAsset.data || {}) as ISpineMetadata; + const metadataSkeletonScale = metadata?.spineSkeletonScale ?? null; + + if (metadataSkeletonScale) { + parser.scale = metadataSkeletonScale; + } + + // if metadataAtlas is a TextureAtlas, use it directly + const metadataAtlas: TextureAtlas = metadata.spineAtlas as TextureAtlas; + + if (metadataAtlas && metadataAtlas.pages) { + return spineAdapter.parseData(parser, metadataAtlas, dataToParse); + } + + // if for some odd reason, you dumped the text information of the atlas into the metadata... + const textAtlas = metadata.atlasRawData; + + if (textAtlas) { + let auxResolve = null; + let auxReject = null; + const atlasPromise = new Promise((resolve, reject) => { + auxResolve = resolve; + auxReject = reject; + }); + const atlas = new TextureAtlas(textAtlas, makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject(loader, basePath, metadata.imageMetadata), (newAtlas) => { + if (!newAtlas) { + auxReject('Something went terribly wrong loading a spine .atlas file\nMost likely your texture failed to load.'); + } + auxResolve(atlas); + }); + const textureAtlas = await atlasPromise; + + return spineAdapter.parseData(parser, textureAtlas, dataToParse); + } + + // Maybe you told us where to find the file? (I sure hope you remembered to add the .atlas extension) + let atlasPath = metadata.spineAtlasFile; + + // Finally, if no information at all about the atlas, we guess the atlas file name + if (!atlasPath) { + atlasPath = `${basePath + fileName}.atlas`; + } + + const textureAtlas = await loader.load({ src: atlasPath, data: metadata, alias: metadata.spineAtlasAlias }); + + return spineAdapter.parseData(parser, textureAtlas, dataToParse); + }, + + // #endregion + + // unload(asset: ISpineResource, loadAsset, loader) { + // ??? + // }, + }, + } as AssetExtension, ISpineMetadata>; + + extensions.add(spineLoaderExtension); + + return spineLoaderExtension; + } +} + +/** + * The final spineData+spineAtlas object that can be used to create a Spine. + * @public + */ +export interface ISpineResource { + spineData: SKD; + spineAtlas: TextureAtlas; +} + +/** + * Metadata for loading spine assets + * @public + */ +export interface ISpineMetadata { + // Passed directly to Spine's SkeletonJson/BinaryParser + spineSkeletonScale?: number; + // If you already have a TextureAtlas, you can pass it directly + spineAtlas?: Partial; + // If you are going to download an .atlas file, you can specify an alias here for cache/future lookup + spineAtlasAlias?: string[]; + // If you want to use a custom .atlas file, you can specify the path here. **It must be a .atlas file or you need your own parser!** + spineAtlasFile?: string; + // If for some reason, you have the raw text content of an .atlas file, and want to use it dump it here + atlasRawData?: string; + // If you are hardcore and can write your own loader function to load the textures for the atlas, you can pass it here + imageLoader?: (loader: Loader, path: string) => (path: string, callback: (tex: BaseTexture) => any) => any; + // If you are downloading an .atlas file, this metadata will go to the Texture loader + imageMetadata?: any; + // If you already have atlas pages loaded as pixi textures and want to use that to create the atlas, you can pass them here + images?: Record; + // If your spine only uses one atlas page and you have it as a pixi texture, you can pass it here + image?: Texture | BaseTexture; +} diff --git a/packages/loader-base/src/atlasLoader.ts b/packages/loader-base/src/atlasLoader.ts new file mode 100644 index 00000000..fa98d750 --- /dev/null +++ b/packages/loader-base/src/atlasLoader.ts @@ -0,0 +1,108 @@ +import { TextureAtlas } from '@pixi-spine/base'; +import { type AssetExtension, LoaderParserPriority, LoadAsset, Loader, checkExtension } from '@pixi/assets'; +import { BaseTexture, extensions, ExtensionType, settings, Texture, utils } from '@pixi/core'; +import type { ISpineMetadata } from './SpineLoaderAbstract'; + +type RawAtlas = string; + +const spineTextureAtlasLoader: AssetExtension = { + extension: ExtensionType.Asset, + + // cache: { + // test: (asset: RawAtlas | TextureAtlas) => asset instanceof TextureAtlas, + // getCacheableAssets: (keys: string[], asset: RawAtlas | TextureAtlas) => getCacheableAssets(keys, asset), + // }, + + loader: { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Normal, + }, + + test(url: string): boolean { + return checkExtension(url, '.atlas'); + }, + + async load(url: string): Promise { + const response = await settings.ADAPTER.fetch(url); + + const txt = await response.text(); + + return txt as RawAtlas; + }, + + testParse(asset: unknown, options: LoadAsset): Promise { + const isExtensionRight = checkExtension(options.src, '.atlas'); + const isString = typeof asset === 'string'; + + return Promise.resolve(isExtensionRight && isString); + }, + + async parse(asset: RawAtlas, options: LoadAsset, loader: Loader): Promise { + const metadata: ISpineMetadata = options.data; + let basePath = utils.path.dirname(options.src); + + if (basePath && basePath.lastIndexOf('/') !== basePath.length - 1) { + basePath += '/'; + } + + let resolve = null; + let reject = null; + const retPromise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + + // Retval is going to be a texture atlas. However we need to wait for it's callback to resolve this promise. + let retval; + const resolveCallback = (newAtlas: TextureAtlas): void => { + if (!newAtlas) { + reject('Something went terribly wrong loading a spine .atlas file\nMost likely your texture failed to load.'); + } + resolve(retval); + }; + + // if we have an already loaded pixi image in the image field, use that. + if (metadata.image || metadata.images) { + // merge the objects + const pages = Object.assign(metadata.image ? { default: metadata.image } : {}, metadata.images); + + retval = new TextureAtlas( + asset as RawAtlas, + (line: any, callback: any) => { + const page = pages[line] || (pages.default as any); + + if (page && page.baseTexture) callback(page.baseTexture); + else callback(page); + }, + resolveCallback + ); + } else { + // We don't have ready to use pixi textures, we need to load them now! + retval = new TextureAtlas(asset as RawAtlas, makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject(loader, basePath, metadata.imageMetadata), resolveCallback); + } + + return (await retPromise) as TextureAtlas; + }, + + unload(atlas: TextureAtlas) { + atlas.dispose(); + }, + }, +} as AssetExtension; + +/** + * Ugly function to promisify the spine texture atlas loader function. + * @public + */ +export const makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject = (loader: Loader, atlasBasePath: string, imageMetadata: any) => { + return async (pageName: string, textureLoadedCallback: (tex: BaseTexture) => any): Promise => { + const url = utils.path.join(...atlasBasePath.split(utils.path.sep), pageName); + + const texture = await loader.load({ src: url, data: imageMetadata }); + + textureLoadedCallback(texture.baseTexture); + }; +}; + +extensions.add(spineTextureAtlasLoader); diff --git a/packages/loader-base/src/index.ts b/packages/loader-base/src/index.ts index 6b582a59..9714210a 100644 --- a/packages/loader-base/src/index.ts +++ b/packages/loader-base/src/index.ts @@ -1,2 +1,8 @@ +/* eslint-disable spaced-comment */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -export * from './loaders'; + +import './atlasLoader'; // side effect install atlas loader + +export * from './atlasLoader'; +export * from './SpineLoaderAbstract'; diff --git a/packages/loader-base/src/loaders.ts b/packages/loader-base/src/loaders.ts deleted file mode 100644 index 5568acf4..00000000 --- a/packages/loader-base/src/loaders.ts +++ /dev/null @@ -1,201 +0,0 @@ -import {IResourceMetadata, Loader, LoaderResource} from "@pixi/loaders"; -import {BaseTexture, Texture} from "@pixi/core"; -import {ISkeletonParser, TextureAtlas} from "@pixi-spine/base"; -import {ALPHA_MODES} from "@pixi/constants"; - -function isJson(resource: LoaderResource) { - return resource.type === LoaderResource.TYPE.JSON; -} - -function isBuffer(resource: LoaderResource) { - return resource.xhrType === (LoaderResource as any).XHR_RESPONSE_TYPE.BUFFER; -} - -LoaderResource.setExtensionXhrType('skel', LoaderResource.XHR_RESPONSE_TYPE.BUFFER); - -/** - * @public - */ -export abstract class AbstractSpineParser { - abstract createJsonParser(): ISkeletonParser; - - abstract createBinaryParser(): ISkeletonParser; - - abstract parseData(resource: LoaderResource, parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): void; - - genMiddleware() { - const self = this; - - return { - use(this: Loader, resource: LoaderResource, next: () => any) { - // skip if no data, its not json, or it isn't atlas data - if (!resource.data) { - return next(); - } - - const isJsonSpineModel = isJson(resource) && resource.data.bones; - const isBinarySpineModel = isBuffer(resource) && (resource.extension === 'skel' || resource.metadata - && (resource.metadata as any).spineMetadata); - - if (!isJsonSpineModel && !isBinarySpineModel) { - return next(); - } - - let parser: ISkeletonParser = null; - let dataToParse = resource.data; - - if (isJsonSpineModel) { - parser = self.createJsonParser(); - } else { - parser = self.createBinaryParser(); - if (resource.data instanceof ArrayBuffer) { - dataToParse = new Uint8Array(resource.data); - } - } - - const metadata = (resource.metadata || {}) as IResourceMetadata; - const metadataSkeletonScale = metadata ? (metadata as any).spineSkeletonScale : null; - - if (metadataSkeletonScale) { - parser.scale = metadataSkeletonScale; - } - - const metadataAtlas = metadata.spineAtlas; - if (metadataAtlas === false) { - return next(); - } - if (metadataAtlas && metadataAtlas.pages) { - self.parseData(resource, parser, metadataAtlas, dataToParse); - return next(); - } - - const metadataAtlasSuffix = metadata.spineAtlasSuffix || '.atlas'; - - /** - * use a bit of hackery to load the atlas file, here we assume that the .json, .atlas and .png files - * that correspond to the spine file are in the same base URL and that the .json and .atlas files - * have the same name - */ - let atlasPath = resource.url; - let queryStringPos = atlasPath.indexOf('?'); - if (queryStringPos > 0) { - //remove querystring - atlasPath = atlasPath.substr(0, queryStringPos) - } - atlasPath = atlasPath.substr(0, atlasPath.lastIndexOf('.')) + metadataAtlasSuffix; -// use atlas path as a params. (no need to use same atlas file name with json file name) - if (metadata.spineAtlasFile) { - atlasPath = metadata.spineAtlasFile; - } - -//remove the baseUrl - atlasPath = atlasPath.replace(this.baseUrl, ''); - - const atlasOptions = { - crossOrigin: resource.crossOrigin, - xhrType: LoaderResource.XHR_RESPONSE_TYPE.TEXT, - metadata: metadata.spineMetadata || null, - parentResource: resource - }; - const imageOptions = { - crossOrigin: resource.crossOrigin, - metadata: metadata.imageMetadata || null, - parentResource: resource - }; - let baseUrl = resource.url.substr(0, resource.url.lastIndexOf('/') + 1); -//remove the baseUrl - baseUrl = baseUrl.replace(this.baseUrl, ''); - - const namePrefix = metadata.imageNamePrefix || (resource.name + '_atlas_page_'); - - const adapter = metadata.images ? staticImageLoader(metadata.images) - : metadata.image ? staticImageLoader({'default': metadata.image}) - : metadata.imageLoader ? metadata.imageLoader(this, namePrefix, baseUrl, imageOptions) - : imageLoaderAdapter(this, namePrefix, baseUrl, imageOptions); - - function createSkeletonWithRawAtlas(rawData: string) { - new TextureAtlas(rawData, adapter, function(spineAtlas) { - if (spineAtlas) { - self.parseData(resource, parser, spineAtlas, dataToParse); - } - next(); - }); - } - - if (metadata.atlasRawData) { - createSkeletonWithRawAtlas(metadata.atlasRawData) - } else { - this.add(resource.name + '_atlas', atlasPath, atlasOptions, function (atlasResource: any) { - if (!atlasResource.error) { - createSkeletonWithRawAtlas(atlasResource.data); - } else { - next(); - } - }); - } - } - } - } -} - -/** - * @public - */ -export function imageLoaderAdapter(loader: any, namePrefix: any, baseUrl: any, imageOptions: any) { - if (baseUrl && baseUrl.lastIndexOf('/') !== (baseUrl.length - 1)) { - baseUrl += '/'; - } - return function (line: string, callback: (baseTexture: BaseTexture) => any) { - const name = namePrefix + line; - const url = baseUrl + line; - - const cachedResource = loader.resources[name]; - if (cachedResource) { - const done = () => { - callback(cachedResource.texture.baseTexture) - } - if (cachedResource.texture) { - done(); - } else { - cachedResource.onAfterMiddleware.add(done); - } - } else { - loader.add(name, url, imageOptions, (resource: LoaderResource) => { - if (!resource.error) { - if (line.indexOf('-pma.') >= 0) { - resource.texture.baseTexture.alphaMode = ALPHA_MODES.PMA; - } - - callback(resource.texture.baseTexture); - } else { - callback(null); - } - }); - } - } -} - -/** - * @public - */ -export function syncImageLoaderAdapter(baseUrl: any, crossOrigin: any) { - if (baseUrl && baseUrl.lastIndexOf('/') !== (baseUrl.length - 1)) { - baseUrl += '/'; - } - return function (line: any, callback: any) { - callback(BaseTexture.from(line, crossOrigin)); - } -} - -/** - * @public - */ -export function staticImageLoader(pages: { [key: string]: (BaseTexture | Texture) }) { - return function (line: any, callback: any) { - let page = pages[line] || pages['default'] as any; - if (page && page.baseTexture) - callback(page.baseTexture); - else - callback(page); - } -} diff --git a/packages/loader-uni/package.json b/packages/loader-uni/package.json index c1ca479a..fa195393 100644 --- a/packages/loader-uni/package.json +++ b/packages/loader-uni/package.json @@ -2,23 +2,54 @@ "name": "@pixi-spine/loader-uni", "version": "3.1.2", "description": "Pixi integration with EsotericSoftware Spine, big, contains all runtimes", - "main": "lib/loader-uni.js", - "module": "lib/loader-uni.es.js", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "namespace": "PIXI.spine", - "peerDependencies": { - "@pixi/loaders": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine", + "bundle": "dist/loader-uni.js", + "bundleModule": "dist/loader-uni.mjs", + "globals": { + "@pixi-spine/loader-base": "PIXI.spine", + "@pixi-spine/runtime-3.7": "PIXI.spine37", + "@pixi-spine/runtime-3.8": "PIXI.spine38", + "@pixi-spine/runtime-4.1": "PIXI.spine41" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2", - "@pixi-spine/loader-base": "~3.1.2", - "@pixi-spine/runtime-3.7": "~3.1.2", - "@pixi-spine/runtime-3.8": "~3.1.2", - "@pixi-spine/runtime-4.1": "~3.1.2" + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi-spine/loader-base": "*", + "@pixi-spine/runtime-3.7": "*", + "@pixi-spine/runtime-3.8": "*", + "@pixi-spine/runtime-4.1": "*", + "@pixi/assets": " ^7.0.0", + "@pixi/constants": "^7.0.0", + "@pixi/core": "^7.0.0", + "@pixi/display": "^7.0.0", + "@pixi/extensions": "^7.0.0", + "@pixi/graphics": "^7.0.0", + "@pixi/math": "^7.0.0", + "@pixi/mesh": "^7.0.0", + "@pixi/mesh-extras": "^7.0.0", + "@pixi/runner": "^7.0.0", + "@pixi/settings": "^7.0.0", + "@pixi/sprite": "^7.0.0", + "@pixi/ticker": "^7.0.0", + "@pixi/utils": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node scripts/injectGlobalMixins" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run && node ../../scripts/injectGlobalMixins", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -40,14 +71,6 @@ }, "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "prepend": "=1.0.2", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/loader-uni/rollup.config.js b/packages/loader-uni/rollup.config.js deleted file mode 100644 index 137ad9b4..00000000 --- a/packages/loader-uni/rollup.config.js +++ /dev/null @@ -1,8 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine', - '@pixi-spine/loader-base': 'PIXI.spine', - }, -}); diff --git a/packages/loader-uni/rollup.config.mjs b/packages/loader-uni/rollup.config.mjs new file mode 100644 index 00000000..e20161c3 --- /dev/null +++ b/packages/loader-uni/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from "@pixi-spine/rollup-config"; +import pkg from "./package.json" assert { type: "json" }; + +export default configBuilder(pkg.extensionConfig, pkg); \ No newline at end of file diff --git a/packages/loader-uni/src/Spine.ts b/packages/loader-uni/src/Spine.ts index 9ecb3343..14db8e96 100644 --- a/packages/loader-uni/src/Spine.ts +++ b/packages/loader-uni/src/Spine.ts @@ -1,17 +1,13 @@ -import {IAnimationState, IAnimationStateData, ISkeleton, ISkeletonData, SpineBase} from "@pixi-spine/base"; -import * as spine38 from "@pixi-spine/runtime-3.8"; -import * as spine37 from "@pixi-spine/runtime-3.7"; -import * as spine41 from "@pixi-spine/runtime-4.1"; -import {detectSpineVersion, SPINE_VERSION} from "./versions"; +import { IAnimationState, IAnimationStateData, ISkeleton, ISkeletonData, SpineBase } from '@pixi-spine/base'; +import * as spine38 from '@pixi-spine/runtime-3.8'; +import * as spine37 from '@pixi-spine/runtime-3.7'; +import * as spine41 from '@pixi-spine/runtime-4.1'; +import { detectSpineVersion, SPINE_VERSION } from './versions'; /** * @public */ -export class Spine extends SpineBase { - +export class Spine extends SpineBase { createSkeleton(spineData: ISkeletonData) { const ver = detectSpineVersion(spineData.version); let spine: any = null; @@ -26,7 +22,8 @@ export class Spine extends SpineBase { createBinaryParser(): ISkeletonParser { return new UniBinaryParser(); } @@ -96,15 +104,12 @@ export class SpineParser extends AbstractSpineParser { return new UniJsonParser(); } - parseData(resource: LoaderResource, parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): void { - const parserCast = parser as (UniBinaryParser | UniJsonParser); - resource.spineData = parserCast.readSkeletonData(atlas, dataToParse); - resource.spineAtlas = atlas; - } - - static use = new SpineParser().genMiddleware().use; + parseData(parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): ISpineResource { + const parserCast = parser as UniBinaryParser | UniJsonParser; - static registerLoaderPlugin() { - Loader.registerPlugin(SpineParser); + return { + spineData: parserCast.readSkeletonData(atlas, dataToParse), + spineAtlas: atlas, + }; } } diff --git a/packages/loader-uni/src/index.ts b/packages/loader-uni/src/index.ts index 93d4f849..78d73e0e 100644 --- a/packages/loader-uni/src/index.ts +++ b/packages/loader-uni/src/index.ts @@ -1,4 +1,10 @@ +/* eslint-disable spaced-comment */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -export * from './SpineLoader'; +import '@pixi-spine/loader-base'; // Side effect install atlas loader +import { SpineLoader } from './SpineLoader'; export * from './Spine'; export * from './versions'; + +// Install the loader +new SpineLoader().installLoader(); diff --git a/packages/loader-uni/src/versions.ts b/packages/loader-uni/src/versions.ts index 63ac438b..4a051089 100644 --- a/packages/loader-uni/src/versions.ts +++ b/packages/loader-uni/src/versions.ts @@ -14,7 +14,7 @@ export enum SPINE_VERSION { */ export function detectSpineVersion(version: string): SPINE_VERSION { const ver3 = version.substr(0, 3); - const verNum = Math.floor(+ver3 * 10 + 1e-3); + const verNum = Math.floor(Number(ver3) * 10 + 1e-3); if (ver3 === '3.7') { return SPINE_VERSION.VER37; @@ -32,5 +32,6 @@ export function detectSpineVersion(version: string): SPINE_VERSION { if (verNum < SPINE_VERSION.VER37) { return SPINE_VERSION.VER37; } + return SPINE_VERSION.UNKNOWN; } diff --git a/packages/runtime-3.7/package.json b/packages/runtime-3.7/package.json index d878ba23..47d2245d 100644 --- a/packages/runtime-3.7/package.json +++ b/packages/runtime-3.7/package.json @@ -2,21 +2,35 @@ "name": "@pixi-spine/runtime-3.7", "version": "3.1.2", "description": "Pixi runtime for spine 3.7 models", - "main": "lib/runtime-3.7.js", - "module": "lib/runtime-3.7.es.js", - "bundle": "dist/runtime-3.7.js", - "namespace": "PIXI.spine37", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "peerDependencies": { - "@pixi/constants": "^6.1.0", - "@pixi/math": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine37", + "bundle": "dist/runtime-3.7.js", + "bundleModule": "dist/runtime-3.7.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2" + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,13 +53,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/runtime-3.7/rollup.config.js b/packages/runtime-3.7/rollup.config.js deleted file mode 100644 index 71cbae50..00000000 --- a/packages/runtime-3.7/rollup.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine', - }, -}); diff --git a/packages/runtime-3.7/rollup.config.mjs b/packages/runtime-3.7/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/packages/runtime-3.7/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/packages/runtime-3.7/src/Spine.ts b/packages/runtime-3.7/src/Spine.ts index 6faecd39..b3a2b909 100644 --- a/packages/runtime-3.7/src/Spine.ts +++ b/packages/runtime-3.7/src/Spine.ts @@ -1,8 +1,8 @@ -import {SpineBase} from '@pixi-spine/base'; -import {Skeleton} from "./core/Skeleton"; -import {SkeletonData} from "./core/SkeletonData"; -import {AnimationState} from "./core/AnimationState"; -import {AnimationStateData} from "./core/AnimationStateData"; +import { SpineBase } from '@pixi-spine/base'; +import { Skeleton } from './core/Skeleton'; +import type { SkeletonData } from './core/SkeletonData'; +import { AnimationState } from './core/AnimationState'; +import { AnimationStateData } from './core/AnimationStateData'; /** * @public diff --git a/packages/runtime-3.7/src/core/Animation.ts b/packages/runtime-3.7/src/core/Animation.ts index c6be1d9b..52f08010 100644 --- a/packages/runtime-3.7/src/core/Animation.ts +++ b/packages/runtime-3.7/src/core/Animation.ts @@ -1,11 +1,11 @@ -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; -import {Attachment, VertexAttachment} from "./attachments"; -import {ArrayLike, MathUtils, Utils, MixBlend, MixDirection, IAnimation, ITimeline} from '@pixi-spine/base'; -import {Slot} from "./Slot"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; +import { Attachment, VertexAttachment } from './attachments'; +import { ArrayLike, MathUtils, Utils, MixBlend, MixDirection, IAnimation, ITimeline } from '@pixi-spine/base'; +import type { Slot } from './Slot'; +import type { IkConstraint } from './IkConstraint'; +import type { TransformConstraint } from './TransformConstraint'; +import type { PathConstraint } from './PathConstraint'; /** * @public @@ -15,45 +15,45 @@ export class Animation implements IAnimation { timelines: Array; duration: number; - constructor (name: string, timelines: Array, duration: number) { - if (name == null) throw new Error("name cannot be null."); - if (timelines == null) throw new Error("timelines cannot be null."); + constructor(name: string, timelines: Array, duration: number) { + if (name == null) throw new Error('name cannot be null.'); + if (timelines == null) throw new Error('timelines cannot be null.'); this.name = name; this.timelines = timelines; this.duration = duration; } - apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - if (skeleton == null) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + if (skeleton == null) throw new Error('skeleton cannot be null.'); if (loop && this.duration != 0) { time %= this.duration; if (lastTime > 0) lastTime %= this.duration; } - let timelines = this.timelines; - for (let i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); + const timelines = this.timelines; + + for (let i = 0, n = timelines.length; i < n; i++) timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); } - static binarySearch (values: ArrayLike, target: number, step: number = 1) { + static binarySearch(values: ArrayLike, target: number, step = 1) { let low = 0; let high = values.length / step - 2; + if (high == 0) return step; let current = high >>> 1; + while (true) { - if (values[(current + 1) * step] <= target) - low = current + 1; - else - high = current; + if (values[(current + 1) * step] <= target) low = current + 1; + else high = current; if (low == high) return (low + 1) * step; current = (low + high) >>> 1; } } - static linearSearch (values: ArrayLike, target: number, step: number) { - for (let i = 0, last = values.length - step; i <= last; i += step) - if (values[i] > target) return i; + static linearSearch(values: ArrayLike, target: number, step: number) { + for (let i = 0, last = values.length - step; i <= last; i += step) if (values[i] > target) return i; + return -1; } } @@ -62,73 +62,94 @@ export class Animation implements IAnimation { * @public */ export interface Timeline extends ITimeline { - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; - getPropertyId (): number; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; + getPropertyId(): number; } /** * @public */ export enum TimelineType { - rotate, translate, scale, shear, - attachment, color, deform, - event, drawOrder, - ikConstraint, transformConstraint, - pathConstraintPosition, pathConstraintSpacing, pathConstraintMix, - twoColor + rotate, + translate, + scale, + shear, + attachment, + color, + deform, + event, + drawOrder, + ikConstraint, + transformConstraint, + pathConstraintPosition, + pathConstraintSpacing, + pathConstraintMix, + twoColor, } /** * @public */ export abstract class CurveTimeline implements Timeline { - static LINEAR = 0; static STEPPED = 1; static BEZIER = 2; + static LINEAR = 0; + static STEPPED = 1; + static BEZIER = 2; static BEZIER_SIZE = 10 * 2 - 1; private curves: ArrayLike; // type, x, y, ... abstract getPropertyId(): number; - constructor (frameCount: number) { - if (frameCount <= 0) throw new Error("frameCount must be > 0: " + frameCount); + constructor(frameCount: number) { + if (frameCount <= 0) throw new Error(`frameCount must be > 0: ${frameCount}`); this.curves = Utils.newFloatArray((frameCount - 1) * CurveTimeline.BEZIER_SIZE); } - getFrameCount () { + getFrameCount() { return this.curves.length / CurveTimeline.BEZIER_SIZE + 1; } - setLinear (frameIndex: number) { + setLinear(frameIndex: number) { this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.LINEAR; } - setStepped (frameIndex: number) { + setStepped(frameIndex: number) { this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.STEPPED; } - getCurveType (frameIndex: number): number { - let index = frameIndex * CurveTimeline.BEZIER_SIZE; + getCurveType(frameIndex: number): number { + const index = frameIndex * CurveTimeline.BEZIER_SIZE; + if (index == this.curves.length) return CurveTimeline.LINEAR; - let type = this.curves[index]; + const type = this.curves[index]; + if (type == CurveTimeline.LINEAR) return CurveTimeline.LINEAR; if (type == CurveTimeline.STEPPED) return CurveTimeline.STEPPED; + return CurveTimeline.BEZIER; } /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of * the difference between the keyframe's values. */ - setCurve (frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) { - let tmpx = (-cx1 * 2 + cx2) * 0.03, tmpy = (-cy1 * 2 + cy2) * 0.03; - let dddfx = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006; - let ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; - let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667; + setCurve(frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) { + const tmpx = (-cx1 * 2 + cx2) * 0.03; + const tmpy = (-cy1 * 2 + cy2) * 0.03; + const dddfx = ((cx1 - cx2) * 3 + 1) * 0.006; + const dddfy = ((cy1 - cy2) * 3 + 1) * 0.006; + let ddfx = tmpx * 2 + dddfx; + let ddfy = tmpy * 2 + dddfy; + let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667; + let dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667; let i = frameIndex * CurveTimeline.BEZIER_SIZE; - let curves = this.curves; + const curves = this.curves; + curves[i++] = CurveTimeline.BEZIER; - let x = dfx, y = dfy; + let x = dfx; + let y = dfy; + for (let n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; @@ -141,19 +162,23 @@ export abstract class CurveTimeline implements Timeline { } } - getCurvePercent (frameIndex: number, percent: number) { + getCurvePercent(frameIndex: number, percent: number) { percent = MathUtils.clamp(percent, 0, 1); - let curves = this.curves; + const curves = this.curves; let i = frameIndex * CurveTimeline.BEZIER_SIZE; - let type = curves[i]; + const type = curves[i]; + if (type == CurveTimeline.LINEAR) return percent; if (type == CurveTimeline.STEPPED) return 0; i++; let x = 0; + for (let start = i, n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) { x = curves[i]; if (x >= percent) { - let prevX: number, prevY: number; + let prevX: number; + let prevY: number; + if (i == start) { prevX = 0; prevY = 0; @@ -161,14 +186,16 @@ export abstract class CurveTimeline implements Timeline { prevX = curves[i - 2]; prevY = curves[i - 1]; } - return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + + return prevY + ((curves[i + 1] - prevY) * (percent - prevX)) / (x - prevX); } } - let y = curves[i - 1]; - return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + const y = curves[i - 1]; + + return y + ((1 - y) * (percent - x)) / (1 - x); // Last point is 1,1. } - abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; + abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; } /** @@ -176,46 +203,53 @@ export abstract class CurveTimeline implements Timeline { */ export class RotateTimeline extends CurveTimeline { static ENTRIES = 2; - static PREV_TIME = -2; static PREV_ROTATION = -1; + static PREV_TIME = -2; + static PREV_ROTATION = -1; static ROTATION = 1; boneIndex: number; frames: ArrayLike; // time, degrees, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount << 1); } - getPropertyId () { + getPropertyId() { return (TimelineType.rotate << 24) + this.boneIndex; } /** Sets the time and angle of the specified keyframe. */ - setFrame (frameIndex: number, time: number, degrees: number) { + setFrame(frameIndex: number, time: number, degrees: number) { frameIndex <<= 1; this.frames[frameIndex] = time; this.frames[frameIndex + RotateTimeline.ROTATION] = degrees; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation; + return; case MixBlend.first: - let r = bone.data.rotation - bone.rotation; + const r = bone.data.rotation - bone.rotation; + bone.rotation += (r - (16384 - ((16384.499999999996 - r / 360) | 0)) * 360) * alpha; } + return; } - if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { // Time is after last frame. + if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { + // Time is after last frame. let r = frames[frames.length + RotateTimeline.PREV_ROTATION]; + switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation + r * alpha; @@ -227,17 +261,18 @@ export class RotateTimeline extends CurveTimeline { case MixBlend.add: bone.rotation += r * alpha; } + return; } // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); - let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent((frame >> 1) - 1, - 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); + const frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); + const prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; + const frameTime = frames[frame]; + const percent = this.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); let r = frames[frame + RotateTimeline.ROTATION] - prevRotation; + r = prevRotation + (r - (16384 - ((16384.499999999996 - r / 360) | 0)) * 360) * percent; switch (blend) { case MixBlend.setup: @@ -257,58 +292,67 @@ export class RotateTimeline extends CurveTimeline { */ export class TranslateTimeline extends CurveTimeline { static ENTRIES = 3; - static PREV_TIME = -3; static PREV_X = -2; static PREV_Y = -1; - static X = 1; static Y = 2; + static PREV_TIME = -3; + static PREV_X = -2; + static PREV_Y = -1; + static X = 1; + static Y = 2; boneIndex: number; frames: ArrayLike; // time, x, y, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * TranslateTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.translate << 24) + this.boneIndex; } /** Sets the time and value of the specified keyframe. */ - setFrame (frameIndex: number, time: number, x: number, y: number) { + setFrame(frameIndex: number, time: number, x: number, y: number) { frameIndex *= TranslateTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + TranslateTimeline.X] = x; this.frames[frameIndex + TranslateTimeline.Y] = y; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.x = bone.data.x; bone.y = bone.data.y; + return; case MixBlend.first: bone.x += (bone.data.x - bone.x) * alpha; bone.y += (bone.data.y - bone.y) * alpha; } + return; } - let x = 0, y = 0; - if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { // Time is after last frame. + let x = 0; + let y = 0; + + if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { + // Time is after last frame. x = frames[frames.length + TranslateTimeline.PREV_X]; y = frames[frames.length + TranslateTimeline.PREV_Y]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES); + x = frames[frame + TranslateTimeline.PREV_X]; y = frames[frame + TranslateTimeline.PREV_Y]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime)); x += (frames[frame + TranslateTimeline.X] - x) * percent; y += (frames[frame + TranslateTimeline.Y] - y) * percent; @@ -334,43 +378,49 @@ export class TranslateTimeline extends CurveTimeline { * @public */ export class ScaleTimeline extends TranslateTimeline { - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.scale << 24) + this.boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleX = bone.data.scaleX; bone.scaleY = bone.data.scaleY; + return; case MixBlend.first: bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; } + return; } - let x = 0, y = 0; - if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { // Time is after last frame. + let x = 0; + let y = 0; + + if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { + // Time is after last frame. x = frames[frames.length + ScaleTimeline.PREV_X] * bone.data.scaleX; y = frames[frames.length + ScaleTimeline.PREV_Y] * bone.data.scaleY; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES); + x = frames[frame + ScaleTimeline.PREV_X]; y = frames[frame + ScaleTimeline.PREV_Y]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime)); x = (x + (frames[frame + ScaleTimeline.X] - x) * percent) * bone.data.scaleX; y = (y + (frames[frame + ScaleTimeline.Y] - y) * percent) * bone.data.scaleY; @@ -384,7 +434,9 @@ export class ScaleTimeline extends TranslateTimeline { bone.scaleY = y; } } else { - let bx = 0, by = 0; + let bx = 0; + let by = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -436,43 +488,49 @@ export class ScaleTimeline extends TranslateTimeline { * @public */ export class ShearTimeline extends TranslateTimeline { - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.shear << 24) + this.boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX; bone.shearY = bone.data.shearY; + return; case MixBlend.first: bone.shearX += (bone.data.shearX - bone.shearX) * alpha; bone.shearY += (bone.data.shearY - bone.shearY) * alpha; } + return; } - let x = 0, y = 0; - if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { // Time is after last frame. + let x = 0; + let y = 0; + + if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { + // Time is after last frame. x = frames[frames.length + ShearTimeline.PREV_X]; y = frames[frames.length + ShearTimeline.PREV_Y]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES); + x = frames[frame + ShearTimeline.PREV_X]; y = frames[frame + ShearTimeline.PREV_Y]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime)); x = x + (frames[frame + ShearTimeline.X] - x) * percent; y = y + (frames[frame + ShearTimeline.Y] - y) * percent; @@ -499,23 +557,30 @@ export class ShearTimeline extends TranslateTimeline { */ export class ColorTimeline extends CurveTimeline { static ENTRIES = 5; - static PREV_TIME = -5; static PREV_R = -4; static PREV_G = -3; static PREV_B = -2; static PREV_A = -1; - static R = 1; static G = 2; static B = 3; static A = 4; + static PREV_TIME = -5; + static PREV_R = -4; + static PREV_G = -3; + static PREV_B = -2; + static PREV_A = -1; + static R = 1; + static G = 2; + static B = 3; + static A = 4; slotIndex: number; frames: ArrayLike; // time, r, g, b, a, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * ColorTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.color << 24) + this.slotIndex; } /** Sets the time and value of the specified keyframe. */ - setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number) { + setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number) { frameIndex *= ColorTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + ColorTimeline.R] = r; @@ -524,49 +589,59 @@ export class ColorTimeline extends CurveTimeline { this.frames[frameIndex + ColorTimeline.A] = a; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: slot.color.setFromColor(slot.data.color); + return; case MixBlend.first: - let color = slot.color, setup = slot.data.color; - color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, - (setup.a - color.a) * alpha); + const color = slot.color; + const setup = slot.data.color; + + color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, (setup.a - color.a) * alpha); } + return; } - let r = 0, g = 0, b = 0, a = 0; - if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { // Time is after last frame. - let i = frames.length; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + + if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { + // Time is after last frame. + const i = frames.length; + r = frames[i + ColorTimeline.PREV_R]; g = frames[i + ColorTimeline.PREV_G]; b = frames[i + ColorTimeline.PREV_B]; a = frames[i + ColorTimeline.PREV_A]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES); + r = frames[frame + ColorTimeline.PREV_R]; g = frames[frame + ColorTimeline.PREV_G]; b = frames[frame + ColorTimeline.PREV_B]; a = frames[frame + ColorTimeline.PREV_A]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime)); r += (frames[frame + ColorTimeline.R] - r) * percent; g += (frames[frame + ColorTimeline.G] - g) * percent; b += (frames[frame + ColorTimeline.B] - b) * percent; a += (frames[frame + ColorTimeline.A] - a) * percent; } - if (alpha == 1) - slot.color.set(r, g, b, a); + if (alpha == 1) slot.color.set(r, g, b, a); else { - let color = slot.color; + const color = slot.color; + if (blend == MixBlend.setup) color.setFromColor(slot.data.color); color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); } @@ -578,24 +653,36 @@ export class ColorTimeline extends CurveTimeline { */ export class TwoColorTimeline extends CurveTimeline { static ENTRIES = 8; - static PREV_TIME = -8; static PREV_R = -7; static PREV_G = -6; static PREV_B = -5; static PREV_A = -4; - static PREV_R2 = -3; static PREV_G2 = -2; static PREV_B2 = -1; - static R = 1; static G = 2; static B = 3; static A = 4; static R2 = 5; static G2 = 6; static B2 = 7; + static PREV_TIME = -8; + static PREV_R = -7; + static PREV_G = -6; + static PREV_B = -5; + static PREV_A = -4; + static PREV_R2 = -3; + static PREV_G2 = -2; + static PREV_B2 = -1; + static R = 1; + static G = 2; + static B = 3; + static A = 4; + static R2 = 5; + static G2 = 6; + static B2 = 7; slotIndex: number; frames: ArrayLike; // time, r, g, b, a, r2, g2, b2, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * TwoColorTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.twoColor << 24) + this.slotIndex; } /** Sets the time and value of the specified keyframe. */ - setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { + setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { frameIndex *= TwoColorTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + TwoColorTimeline.R] = r; @@ -607,27 +694,42 @@ export class TwoColorTimeline extends CurveTimeline { this.frames[frameIndex + TwoColorTimeline.B2] = b2; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: slot.color.setFromColor(slot.data.color); slot.darkColor.setFromColor(slot.data.darkColor); + return; case MixBlend.first: - let light = slot.color, dark = slot.darkColor, setupLight = slot.data.color, setupDark = slot.data.darkColor; - light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, - (setupLight.a - light.a) * alpha); + const light = slot.color; + const dark = slot.darkColor; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + + light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); dark.add((setupDark.r - dark.r) * alpha, (setupDark.g - dark.g) * alpha, (setupDark.b - dark.b) * alpha, 0); } + return; } - let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0; - if (time >= frames[frames.length - TwoColorTimeline.ENTRIES]) { // Time is after last frame. - let i = frames.length; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + let r2 = 0; + let g2 = 0; + let b2 = 0; + + if (time >= frames[frames.length - TwoColorTimeline.ENTRIES]) { + // Time is after last frame. + const i = frames.length; + r = frames[i + TwoColorTimeline.PREV_R]; g = frames[i + TwoColorTimeline.PREV_G]; b = frames[i + TwoColorTimeline.PREV_B]; @@ -637,7 +739,8 @@ export class TwoColorTimeline extends CurveTimeline { b2 = frames[i + TwoColorTimeline.PREV_B2]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, TwoColorTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, TwoColorTimeline.ENTRIES); + r = frames[frame + TwoColorTimeline.PREV_R]; g = frames[frame + TwoColorTimeline.PREV_G]; b = frames[frame + TwoColorTimeline.PREV_B]; @@ -645,9 +748,8 @@ export class TwoColorTimeline extends CurveTimeline { r2 = frames[frame + TwoColorTimeline.PREV_R2]; g2 = frames[frame + TwoColorTimeline.PREV_G2]; b2 = frames[frame + TwoColorTimeline.PREV_B2]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / TwoColorTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + TwoColorTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / TwoColorTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + TwoColorTimeline.PREV_TIME] - frameTime)); r += (frames[frame + TwoColorTimeline.R] - r) * percent; g += (frames[frame + TwoColorTimeline.G] - g) * percent; @@ -661,7 +763,9 @@ export class TwoColorTimeline extends CurveTimeline { slot.color.set(r, g, b, a); slot.darkColor.set(r2, g2, b2, 1); } else { - let light = slot.color, dark = slot.darkColor; + const light = slot.color; + const dark = slot.darkColor; + if (blend == MixBlend.setup) { light.setFromColor(slot.data.color); dark.setFromColor(slot.data.darkColor); @@ -677,58 +781,65 @@ export class TwoColorTimeline extends CurveTimeline { */ export class AttachmentTimeline implements Timeline { slotIndex: number; - frames: ArrayLike // time, ... + frames: ArrayLike; // time, ... attachmentNames: Array; - constructor (frameCount: number) { + constructor(frameCount: number) { this.frames = Utils.newFloatArray(frameCount); this.attachmentNames = new Array(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.attachment << 24) + this.slotIndex; } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time and value of the specified keyframe. */ - setFrame (frameIndex: number, time: number, attachmentName: string) { + setFrame(frameIndex: number, time: number, attachmentName: string) { this.frames[frameIndex] = time; this.attachmentNames[frameIndex] = attachmentName; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (direction == MixDirection.mixOut && blend == MixBlend.setup) { - let attachmentName = slot.data.attachmentName; + const attachmentName = slot.data.attachmentName; + slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); + return; } - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) { - let attachmentName = slot.data.attachmentName; + const attachmentName = slot.data.attachmentName; + slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); } + return; } let frameIndex = 0; - if (time >= frames[frames.length - 1]) // Time is after last frame. + + if (time >= frames[frames.length - 1]) + // Time is after last frame. frameIndex = frames.length - 1; - else - frameIndex = Animation.binarySearch(frames, time, 1) - 1; + else frameIndex = Animation.binarySearch(frames, time, 1) - 1; - let attachmentName = this.attachmentNames[frameIndex]; - skeleton.slots[this.slotIndex] - .setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); + const attachmentName = this.attachmentNames[frameIndex]; + + skeleton.slots[this.slotIndex].setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); } } -let zeros : ArrayLike = null; +let zeros: ArrayLike = null; /** * @public @@ -739,78 +850,88 @@ export class DeformTimeline extends CurveTimeline { frames: ArrayLike; // time, ... frameVertices: Array>; - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount); this.frameVertices = new Array>(frameCount); if (zeros == null) zeros = Utils.newFloatArray(64); } - getPropertyId () { - return (TimelineType.deform << 27) + + this.attachment.id + this.slotIndex; + getPropertyId() { + return (TimelineType.deform << 27) + Number(this.attachment.id) + this.slotIndex; } /** Sets the time of the specified keyframe. */ - setFrame (frameIndex: number, time: number, vertices: ArrayLike) { + setFrame(frameIndex: number, time: number, vertices: ArrayLike) { this.frames[frameIndex] = time; this.frameVertices[frameIndex] = vertices; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot: Slot = skeleton.slots[this.slotIndex]; - let slotAttachment: Attachment = slot.getAttachment(); + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot: Slot = skeleton.slots[this.slotIndex]; + const slotAttachment: Attachment = slot.getAttachment(); + if (!(slotAttachment instanceof VertexAttachment) || !(slotAttachment).applyDeform(this.attachment)) return; - let verticesArray: Array = slot.attachmentVertices; + const verticesArray: Array = slot.attachmentVertices; + if (verticesArray.length == 0) blend = MixBlend.setup; - let frameVertices = this.frameVertices; - let vertexCount = frameVertices[0].length; + const frameVertices = this.frameVertices; + const vertexCount = frameVertices[0].length; + + const frames = this.frames; - let frames = this.frames; if (time < frames[0]) { - let vertexAttachment = slotAttachment; + const vertexAttachment = slotAttachment; + switch (blend) { case MixBlend.setup: verticesArray.length = 0; + return; case MixBlend.first: if (alpha == 1) { verticesArray.length = 0; break; } - let vertices: Array = Utils.setArraySize(verticesArray, vertexCount); + const vertices: Array = Utils.setArraySize(verticesArray, vertexCount); + if (vertexAttachment.bones == null) { // Unweighted vertex positions. - let setupVertices = vertexAttachment.vertices; - for (let i = 0; i < vertexCount; i++) - vertices[i] += (setupVertices[i] - vertices[i]) * alpha; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) vertices[i] += (setupVertices[i] - vertices[i]) * alpha; } else { // Weighted deform offsets. alpha = 1 - alpha; - for (let i = 0; i < vertexCount; i++) - vertices[i] *= alpha; + for (let i = 0; i < vertexCount; i++) vertices[i] *= alpha; } } + return; } - let vertices: Array = Utils.setArraySize(verticesArray, vertexCount); - if (time >= frames[frames.length - 1]) { // Time is after last frame. - let lastVertices = frameVertices[frames.length - 1]; + const vertices: Array = Utils.setArraySize(verticesArray, vertexCount); + + if (time >= frames[frames.length - 1]) { + // Time is after last frame. + const lastVertices = frameVertices[frames.length - 1]; + if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { vertices[i] += lastVertices[i] - setupVertices[i]; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - vertices[i] += lastVertices[i]; + for (let i = 0; i < vertexCount; i++) vertices[i] += lastVertices[i]; } } else { Utils.arrayCopy(lastVertices, 0, vertices, 0, vertexCount); @@ -818,88 +939,101 @@ export class DeformTimeline extends CurveTimeline { } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let setup = setupVertices[i]; + const setup = setupVertices[i]; + vertices[i] = setup + (lastVertices[i] - setup) * alpha; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - vertices[i] = lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) vertices[i] = lastVertices[i] * alpha; } break; } case MixBlend.first: case MixBlend.replace: - for (let i = 0; i < vertexCount; i++) - vertices[i] += (lastVertices[i] - vertices[i]) * alpha; + for (let i = 0; i < vertexCount; i++) vertices[i] += (lastVertices[i] - vertices[i]) * alpha; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { vertices[i] += (lastVertices[i] - setupVertices[i]) * alpha; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - vertices[i] += lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) vertices[i] += lastVertices[i] * alpha; } } } + return; } // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time); - let prevVertices = frameVertices[frame - 1]; - let nextVertices = frameVertices[frame]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); + const frame = Animation.binarySearch(frames, time); + const prevVertices = frameVertices[frame - 1]; + const nextVertices = frameVertices[frame]; + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + vertices[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i]; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + vertices[i] += prev + (nextVertices[i] - prev) * percent; } } } else { for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; } } } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i], setup = setupVertices[i]; + const prev = prevVertices[i]; + const setup = setupVertices[i]; + vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -908,23 +1042,28 @@ export class DeformTimeline extends CurveTimeline { case MixBlend.first: case MixBlend.replace: for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; } break; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -940,51 +1079,54 @@ export class EventTimeline implements Timeline { frames: ArrayLike; // time, ... events: Array; - constructor (frameCount: number) { + constructor(frameCount: number) { this.frames = Utils.newFloatArray(frameCount); this.events = new Array(frameCount); } - getPropertyId () { + getPropertyId() { return TimelineType.event << 24; } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time of the specified keyframe. */ - setFrame (frameIndex: number, event: Event) { + setFrame(frameIndex: number, event: Event) { this.frames[frameIndex] = event.time; this.events[frameIndex] = event; } /** Fires events for frames > lastTime and <= time. */ - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { if (firedEvents == null) return; - let frames = this.frames; - let frameCount = this.frames.length; + const frames = this.frames; + const frameCount = this.frames.length; - if (lastTime > time) { // Fire events after last time for looped animations. + if (lastTime > time) { + // Fire events after last time for looped animations. this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, blend, direction); lastTime = -1; - } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + } else if (lastTime >= frames[frameCount - 1]) + // Last time is after last frame. return; if (time < frames[0]) return; // Time is before first frame. let frame = 0; - if (lastTime < frames[0]) - frame = 0; + + if (lastTime < frames[0]) frame = 0; else { frame = Animation.binarySearch(frames, lastTime); - let frameTime = frames[frame]; - while (frame > 0) { // Fire multiple events with the same frame. + const frameTime = frames[frame]; + + while (frame > 0) { + // Fire multiple events with the same frame. if (frames[frame - 1] != frameTime) break; frame--; } } - for (; frame < frameCount && time >= frames[frame]; frame++) - firedEvents.push(this.events[frame]); + for (; frame < frameCount && time >= frames[frame]; frame++) firedEvents.push(this.events[frame]); } } @@ -995,52 +1137,56 @@ export class DrawOrderTimeline implements Timeline { frames: ArrayLike; // time, ... drawOrders: Array>; - constructor (frameCount: number) { + constructor(frameCount: number) { this.frames = Utils.newFloatArray(frameCount); this.drawOrders = new Array>(frameCount); } - getPropertyId () { + getPropertyId() { return TimelineType.drawOrder << 24; } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time of the specified keyframe. * @param drawOrder May be null to use bind pose draw order. */ - setFrame (frameIndex: number, time: number, drawOrder: Array) { + setFrame(frameIndex: number, time: number, drawOrder: Array) { this.frames[frameIndex] = time; this.drawOrders[frameIndex] = drawOrder; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let drawOrder: Array = skeleton.drawOrder; - let slots: Array = skeleton.slots; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const drawOrder: Array = skeleton.drawOrder; + const slots: Array = skeleton.slots; + if (direction == MixDirection.mixOut && blend == MixBlend.setup) { Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } let frame = 0; - if (time >= frames[frames.length - 1]) // Time is after last frame. + + if (time >= frames[frames.length - 1]) + // Time is after last frame. frame = frames.length - 1; - else - frame = Animation.binarySearch(frames, time) - 1; + else frame = Animation.binarySearch(frames, time) - 1; - let drawOrderToSetupIndex = this.drawOrders[frame]; - if (drawOrderToSetupIndex == null) - Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length); + const drawOrderToSetupIndex = this.drawOrders[frame]; + + if (drawOrderToSetupIndex == null) Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length); else { - for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) - drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) drawOrder[i] = slots[drawOrderToSetupIndex[i]]; } } } @@ -1050,23 +1196,30 @@ export class DrawOrderTimeline implements Timeline { */ export class IkConstraintTimeline extends CurveTimeline { static ENTRIES = 5; - static PREV_TIME = -5; static PREV_MIX = -4; static PREV_BEND_DIRECTION = -3; static PREV_COMPRESS = -2; static PREV_STRETCH = -1; - static MIX = 1; static BEND_DIRECTION = 2; static COMPRESS = 3; static STRETCH = 4; + static PREV_TIME = -5; + static PREV_MIX = -4; + static PREV_BEND_DIRECTION = -3; + static PREV_COMPRESS = -2; + static PREV_STRETCH = -1; + static MIX = 1; + static BEND_DIRECTION = 2; + static COMPRESS = 3; + static STRETCH = 4; ikConstraintIndex: number; frames: ArrayLike; // time, mix, bendDirection, compress, stretch, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * IkConstraintTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.ikConstraint << 24) + this.ikConstraintIndex; } /** Sets the time, mix and bend direction of the specified keyframe. */ - setFrame (frameIndex: number, time: number, mix: number, bendDirection: number, compress: boolean, stretch: boolean) { + setFrame(frameIndex: number, time: number, mix: number, bendDirection: number, compress: boolean, stretch: boolean) { frameIndex *= IkConstraintTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + IkConstraintTimeline.MIX] = mix; @@ -1075,9 +1228,10 @@ export class IkConstraintTimeline extends CurveTimeline { this.frames[frameIndex + IkConstraintTimeline.STRETCH] = stretch ? 1 : 0; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: @@ -1085,6 +1239,7 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.bendDirection = constraint.data.bendDirection; constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; + return; case MixBlend.first: constraint.mix += (constraint.data.mix - constraint.mix) * alpha; @@ -1092,10 +1247,12 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } + return; } - if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { // Time is after last frame. + if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { + // Time is after last frame. if (blend == MixBlend.setup) { constraint.mix = constraint.data.mix + (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.data.mix) * alpha; if (direction == MixDirection.mixOut) { @@ -1103,7 +1260,7 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } else { - constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION] + constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION]; constraint.compress = frames[frames.length + IkConstraintTimeline.PREV_COMPRESS] != 0; constraint.stretch = frames[frames.length + IkConstraintTimeline.PREV_STRETCH] != 0; } @@ -1115,15 +1272,15 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.stretch = frames[frames.length + IkConstraintTimeline.PREV_STRETCH] != 0; } } + return; } // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES); - let mix = frames[frame + IkConstraintTimeline.PREV_MIX]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime)); + const frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES); + const mix = frames[frame + IkConstraintTimeline.PREV_MIX]; + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime)); if (blend == MixBlend.setup) { constraint.mix = constraint.data.mix + (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.data.mix) * alpha; @@ -1152,23 +1309,30 @@ export class IkConstraintTimeline extends CurveTimeline { */ export class TransformConstraintTimeline extends CurveTimeline { static ENTRIES = 5; - static PREV_TIME = -5; static PREV_ROTATE = -4; static PREV_TRANSLATE = -3; static PREV_SCALE = -2; static PREV_SHEAR = -1; - static ROTATE = 1; static TRANSLATE = 2; static SCALE = 3; static SHEAR = 4; + static PREV_TIME = -5; + static PREV_ROTATE = -4; + static PREV_TRANSLATE = -3; + static PREV_SCALE = -2; + static PREV_SHEAR = -1; + static ROTATE = 1; + static TRANSLATE = 2; + static SCALE = 3; + static SHEAR = 4; transformConstraintIndex: number; frames: ArrayLike; // time, rotate mix, translate mix, scale mix, shear mix, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * TransformConstraintTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.transformConstraint << 24) + this.transformConstraintIndex; } /** Sets the time and mixes of the specified keyframe. */ - setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number) { + setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number) { frameIndex *= TransformConstraintTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + TransformConstraintTimeline.ROTATE] = rotateMix; @@ -1177,18 +1341,21 @@ export class TransformConstraintTimeline extends CurveTimeline { this.frames[frameIndex + TransformConstraintTimeline.SHEAR] = shearMix; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; - let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; if (time < frames[0]) { - let data = constraint.data; + const data = constraint.data; + switch (blend) { case MixBlend.setup: constraint.rotateMix = data.rotateMix; constraint.translateMix = data.translateMix; constraint.scaleMix = data.scaleMix; constraint.shearMix = data.shearMix; + return; case MixBlend.first: constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha; @@ -1196,26 +1363,36 @@ export class TransformConstraintTimeline extends CurveTimeline { constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha; constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha; } + return; } - let rotate = 0, translate = 0, scale = 0, shear = 0; - if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { // Time is after last frame. - let i = frames.length; + let rotate = 0; + let translate = 0; + let scale = 0; + let shear = 0; + + if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { + // Time is after last frame. + const i = frames.length; + rotate = frames[i + TransformConstraintTimeline.PREV_ROTATE]; translate = frames[i + TransformConstraintTimeline.PREV_TRANSLATE]; scale = frames[i + TransformConstraintTimeline.PREV_SCALE]; shear = frames[i + TransformConstraintTimeline.PREV_SHEAR]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES); + rotate = frames[frame + TransformConstraintTimeline.PREV_ROTATE]; translate = frames[frame + TransformConstraintTimeline.PREV_TRANSLATE]; scale = frames[frame + TransformConstraintTimeline.PREV_SCALE]; shear = frames[frame + TransformConstraintTimeline.PREV_SHEAR]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / TransformConstraintTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / TransformConstraintTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime) + ); rotate += (frames[frame + TransformConstraintTimeline.ROTATE] - rotate) * percent; translate += (frames[frame + TransformConstraintTimeline.TRANSLATE] - translate) * percent; @@ -1223,7 +1400,8 @@ export class TransformConstraintTimeline extends CurveTimeline { shear += (frames[frame + TransformConstraintTimeline.SHEAR] - shear) * percent; } if (blend == MixBlend.setup) { - let data = constraint.data; + const data = constraint.data; + constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha; constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha; constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha; @@ -1242,60 +1420,67 @@ export class TransformConstraintTimeline extends CurveTimeline { */ export class PathConstraintPositionTimeline extends CurveTimeline { static ENTRIES = 2; - static PREV_TIME = -2; static PREV_VALUE = -1; + static PREV_TIME = -2; + static PREV_VALUE = -1; static VALUE = 1; pathConstraintIndex: number; frames: ArrayLike; // time, position, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * PathConstraintPositionTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.pathConstraintPosition << 24) + this.pathConstraintIndex; } /** Sets the time and value of the specified keyframe. */ - setFrame (frameIndex: number, time: number, value: number) { + setFrame(frameIndex: number, time: number, value: number) { frameIndex *= PathConstraintPositionTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + PathConstraintPositionTimeline.VALUE] = value; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.position = constraint.data.position; + return; case MixBlend.first: constraint.position += (constraint.data.position - constraint.position) * alpha; } + return; } let position = 0; - if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) // Time is after last frame. + + if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) + // Time is after last frame. position = frames[frames.length + PathConstraintPositionTimeline.PREV_VALUE]; else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES); + position = frames[frame + PathConstraintPositionTimeline.PREV_VALUE]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / PathConstraintPositionTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / PathConstraintPositionTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime) + ); position += (frames[frame + PathConstraintPositionTimeline.VALUE] - position) * percent; } - if (blend == MixBlend.setup) - constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; - else - constraint.position += (position - constraint.position) * alpha; + if (blend == MixBlend.setup) constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; + else constraint.position += (position - constraint.position) * alpha; } } @@ -1303,46 +1488,52 @@ export class PathConstraintPositionTimeline extends CurveTimeline { * @public */ export class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline { - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.pathConstraintSpacing << 24) + this.pathConstraintIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.spacing = constraint.data.spacing; + return; case MixBlend.first: constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha; } + return; } let spacing = 0; - if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) // Time is after last frame. + + if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) + // Time is after last frame. spacing = frames[frames.length + PathConstraintSpacingTimeline.PREV_VALUE]; else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES); + spacing = frames[frame + PathConstraintSpacingTimeline.PREV_VALUE]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / PathConstraintSpacingTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / PathConstraintSpacingTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime) + ); spacing += (frames[frame + PathConstraintSpacingTimeline.VALUE] - spacing) * percent; } - if (blend == MixBlend.setup) - constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; - else - constraint.spacing += (spacing - constraint.spacing) * alpha; + if (blend == MixBlend.setup) constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; + else constraint.spacing += (spacing - constraint.spacing) * alpha; } } @@ -1351,59 +1542,70 @@ export class PathConstraintSpacingTimeline extends PathConstraintPositionTimelin */ export class PathConstraintMixTimeline extends CurveTimeline { static ENTRIES = 3; - static PREV_TIME = -3; static PREV_ROTATE = -2; static PREV_TRANSLATE = -1; - static ROTATE = 1; static TRANSLATE = 2; + static PREV_TIME = -3; + static PREV_ROTATE = -2; + static PREV_TRANSLATE = -1; + static ROTATE = 1; + static TRANSLATE = 2; pathConstraintIndex: number; frames: ArrayLike; // time, rotate mix, translate mix, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * PathConstraintMixTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.pathConstraintMix << 24) + this.pathConstraintIndex; } /** Sets the time and mixes of the specified keyframe. */ - setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number) { + setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number) { frameIndex *= PathConstraintMixTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + PathConstraintMixTimeline.ROTATE] = rotateMix; this.frames[frameIndex + PathConstraintMixTimeline.TRANSLATE] = translateMix; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.rotateMix = constraint.data.rotateMix; constraint.translateMix = constraint.data.translateMix; + return; case MixBlend.first: constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha; constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha; } + return; } - let rotate = 0, translate = 0; - if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { // Time is after last frame. + let rotate = 0; + let translate = 0; + + if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { + // Time is after last frame. rotate = frames[frames.length + PathConstraintMixTimeline.PREV_ROTATE]; translate = frames[frames.length + PathConstraintMixTimeline.PREV_TRANSLATE]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES); + rotate = frames[frame + PathConstraintMixTimeline.PREV_ROTATE]; translate = frames[frame + PathConstraintMixTimeline.PREV_TRANSLATE]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / PathConstraintMixTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / PathConstraintMixTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime) + ); rotate += (frames[frame + PathConstraintMixTimeline.ROTATE] - rotate) * percent; translate += (frames[frame + PathConstraintMixTimeline.TRANSLATE] - translate) * percent; diff --git a/packages/runtime-3.7/src/core/AnimationState.ts b/packages/runtime-3.7/src/core/AnimationState.ts index 1dc59695..05feb070 100644 --- a/packages/runtime-3.7/src/core/AnimationState.ts +++ b/packages/runtime-3.7/src/core/AnimationState.ts @@ -1,29 +1,14 @@ -import { - IAnimationState, - IAnimationStateListener, - ITrackEntry, - MathUtils, - MixBlend, - MixDirection, - Pool, - IntSet, - Utils -} from "@pixi-spine/base"; -import { - Animation, - AttachmentTimeline, - DrawOrderTimeline, - RotateTimeline, Timeline -} from './Animation'; -import {AnimationStateData} from "./AnimationStateData"; -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; +import { IAnimationState, IAnimationStateListener, ITrackEntry, MathUtils, MixBlend, MixDirection, Pool, IntSet, Utils } from '@pixi-spine/base'; +import { Animation, AttachmentTimeline, DrawOrderTimeline, RotateTimeline, Timeline } from './Animation'; +import type { AnimationStateData } from './AnimationStateData'; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; /** * @public */ export class AnimationState implements IAnimationState { - static emptyAnimation = new Animation("", [], 0); + static emptyAnimation = new Animation('', [], 0); static SUBSEQUENT = 0; static FIRST = 1; static HOLD = 2; @@ -40,15 +25,17 @@ export class AnimationState implements IAnimationState { trackEntryPool = new Pool(() => new TrackEntry()); - constructor (data: AnimationStateData) { + constructor(data: AnimationStateData) { this.data = data; } - update (delta: number) { + update(delta: number) { delta *= this.timeScale; - let tracks = this.tracks; + const tracks = this.tracks; + for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (current == null) continue; current.animationLast = current.nextAnimationLast; @@ -64,9 +51,11 @@ export class AnimationState implements IAnimationState { } let next = current.next; + if (next != null) { // When the next entry's delay is passed, change to the next entry, preserving leftover time. - let nextTime = current.trackLast - next.delay; + const nextTime = current.trackLast - next.delay; + if (nextTime >= 0) { next.delay = 0; next.trackTime = current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale; @@ -87,6 +76,7 @@ export class AnimationState implements IAnimationState { if (current.mixingFrom != null && this.updateMixingFrom(current, delta)) { // End mixing from entries once all have completed. let from = current.mixingFrom; + current.mixingFrom = null; if (from != null) from.mixingTo = null; while (from != null) { @@ -101,11 +91,12 @@ export class AnimationState implements IAnimationState { this.queue.drain(); } - updateMixingFrom (to: TrackEntry, delta: number): boolean { - let from = to.mixingFrom; + updateMixingFrom(to: TrackEntry, delta: number): boolean { + const from = to.mixingFrom; + if (from == null) return true; - let finished = this.updateMixingFrom(from, delta); + const finished = this.updateMixingFrom(from, delta); from.animationLast = from.nextAnimationLast; from.trackLast = from.nextTrackLast; @@ -119,39 +110,43 @@ export class AnimationState implements IAnimationState { to.interruptAlpha = from.interruptAlpha; this.queue.end(from); } + return finished; } from.trackTime += delta * from.timeScale; to.mixTime += delta; + return false; } - apply (skeleton: Skeleton) : boolean { - if (skeleton == null) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton): boolean { + if (skeleton == null) throw new Error('skeleton cannot be null.'); if (this.animationsChanged) this._animationsChanged(); - let events = this.events; - let tracks = this.tracks; + const events = this.events; + const tracks = this.tracks; let applied = false; for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (current == null || current.delay > 0) continue; applied = true; - let blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; + const blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; // Apply mixing from entries first. let mix = current.alpha; - if (current.mixingFrom != null) - mix *= this.applyMixingFrom(current, skeleton, blend); - else if (current.trackTime >= current.trackEnd && current.next == null) - mix = 0; + + if (current.mixingFrom != null) mix *= this.applyMixingFrom(current, skeleton, blend); + else if (current.trackTime >= current.trackEnd && current.next == null) mix = 0; // Apply current entry. - let animationLast = current.animationLast, animationTime = current.getAnimationTime(); - let timelineCount = current.animation.timelines.length; - let timelines = current.animation.timelines; + const animationLast = current.animationLast; + const animationTime = current.getAnimationTime(); + const timelineCount = current.animation.timelines.length; + const timelines = current.animation.timelines; + if ((i == 0 && mix == 1) || blend == MixBlend.add) { for (let ii = 0; ii < timelineCount; ii++) { // Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets @@ -161,15 +156,17 @@ export class AnimationState implements IAnimationState { timelines[ii].apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.mixIn); } } else { - let timelineMode = current.timelineMode; + const timelineMode = current.timelineMode; + + const firstFrame = current.timelinesRotation.length == 0; - let firstFrame = current.timelinesRotation.length == 0; if (firstFrame) Utils.setArraySize(current.timelinesRotation, timelineCount << 1, null); - let timelinesRotation = current.timelinesRotation; + const timelinesRotation = current.timelinesRotation; for (let ii = 0; ii < timelineCount; ii++) { - let timeline = timelines[ii]; - let timelineBlend = timelineMode[ii] == AnimationState.SUBSEQUENT ? blend : MixBlend.setup; + const timeline = timelines[ii]; + const timelineBlend = timelineMode[ii] == AnimationState.SUBSEQUENT ? blend : MixBlend.setup; + if (timeline instanceof RotateTimeline) { this.applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); } else { @@ -186,15 +183,19 @@ export class AnimationState implements IAnimationState { } this.queue.drain(); + return applied; } - applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { - let from = to.mixingFrom; + applyMixingFrom(to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { + const from = to.mixingFrom; + if (from.mixingFrom != null) this.applyMixingFrom(from, skeleton, blend); let mix = 0; - if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes. + + if (to.mixDuration == 0) { + // Single frame mix to undo mixingFrom changes. mix = 1; if (blend == MixBlend.first) blend = MixBlend.setup; } else { @@ -203,29 +204,34 @@ export class AnimationState implements IAnimationState { if (blend != MixBlend.first) blend = from.mixBlend; } - let events = mix < from.eventThreshold ? this.events : null; - let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; - let animationLast = from.animationLast, animationTime = from.getAnimationTime(); - let timelineCount = from.animation.timelines.length; - let timelines = from.animation.timelines; - let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix); + const events = mix < from.eventThreshold ? this.events : null; + const attachments = mix < from.attachmentThreshold; + const drawOrder = mix < from.drawOrderThreshold; + const animationLast = from.animationLast; + const animationTime = from.getAnimationTime(); + const timelineCount = from.animation.timelines.length; + const timelines = from.animation.timelines; + const alphaHold = from.alpha * to.interruptAlpha; + const alphaMix = alphaHold * (1 - mix); + if (blend == MixBlend.add) { - for (let i = 0; i < timelineCount; i++) - timelines[i].apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.mixOut); + for (let i = 0; i < timelineCount; i++) timelines[i].apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.mixOut); } else { - let timelineMode = from.timelineMode; - let timelineHoldMix = from.timelineHoldMix; + const timelineMode = from.timelineMode; + const timelineHoldMix = from.timelineHoldMix; + + const firstFrame = from.timelinesRotation.length == 0; - let firstFrame = from.timelinesRotation.length == 0; if (firstFrame) Utils.setArraySize(from.timelinesRotation, timelineCount << 1, null); - let timelinesRotation = from.timelinesRotation; + const timelinesRotation = from.timelinesRotation; from.totalAlpha = 0; for (let i = 0; i < timelineCount; i++) { - let timeline = timelines[i]; + const timeline = timelines[i]; let direction = MixDirection.mixOut; let timelineBlend: MixBlend; let alpha = 0; + switch (timelineMode[i]) { case AnimationState.SUBSEQUENT: if (!attachments && timeline instanceof AttachmentTimeline) continue; @@ -243,13 +249,13 @@ export class AnimationState implements IAnimationState { break; default: timelineBlend = MixBlend.setup; - let holdMix = timelineHoldMix[i]; + const holdMix = timelineHoldMix[i]; + alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration); break; } from.totalAlpha += alpha; - if (timeline instanceof RotateTimeline) - this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame); + if (timeline instanceof RotateTimeline) this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame); else { // This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109 Utils.webkit602BugfixHelper(alpha, blend); @@ -273,20 +279,21 @@ export class AnimationState implements IAnimationState { return mix; } - applyRotateTimeline (timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend, - timelinesRotation: Array, i: number, firstFrame: boolean) { - + applyRotateTimeline(timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend, timelinesRotation: Array, i: number, firstFrame: boolean) { if (firstFrame) timelinesRotation[i] = 0; if (alpha == 1) { timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn); + return; } - let rotateTimeline = timeline as RotateTimeline; - let frames = rotateTimeline.frames; - let bone = skeleton.bones[rotateTimeline.boneIndex]; - let r1 = 0, r2 = 0; + const rotateTimeline = timeline as RotateTimeline; + const frames = rotateTimeline.frames; + const bone = skeleton.bones[rotateTimeline.boneIndex]; + let r1 = 0; + let r2 = 0; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: @@ -299,15 +306,15 @@ export class AnimationState implements IAnimationState { } } else { r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation; - if (time >= frames[frames.length - RotateTimeline.ENTRIES]) // Time is after last frame. + if (time >= frames[frames.length - RotateTimeline.ENTRIES]) + // Time is after last frame. r2 = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION]; else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); - let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; - let frameTime = frames[frame]; - let percent = rotateTimeline.getCurvePercent((frame >> 1) - 1, - 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); + const frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); + const prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; + const frameTime = frames[frame]; + const percent = rotateTimeline.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation; r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360; @@ -317,12 +324,16 @@ export class AnimationState implements IAnimationState { } // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. - let total = 0, diff = r2 - r1; + let total = 0; + let diff = r2 - r1; + diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360; if (diff == 0) { total = timelinesRotation[i]; } else { - let lastTotal = 0, lastDiff = 0; + let lastTotal = 0; + let lastDiff = 0; + if (firstFrame) { lastTotal = 0; lastDiff = diff; @@ -330,14 +341,16 @@ export class AnimationState implements IAnimationState { lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. lastDiff = timelinesRotation[i + 1]; // Difference between bones. } - let current = diff > 0, dir = lastTotal >= 0; + const current = diff > 0; + let dir = lastTotal >= 0; // Detect cross at 0 (not 180). + if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) { // A cross after a 360 rotation is a loop. if (Math.abs(lastTotal) > 180) lastTotal += 360 * MathUtils.signum(lastTotal); dir = current; } - total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. + total = diff + lastTotal - (lastTotal % 360); // Store loops as part of lastTotal. if (dir != current) total += 360 * MathUtils.signum(lastTotal); timelinesRotation[i] = total; } @@ -346,16 +359,20 @@ export class AnimationState implements IAnimationState { bone.rotation = r1 - (16384 - ((16384.499999999996 - r1 / 360) | 0)) * 360; } - queueEvents (entry: TrackEntry, animationTime: number) { - let animationStart = entry.animationStart, animationEnd = entry.animationEnd; - let duration = animationEnd - animationStart; - let trackLastWrapped = entry.trackLast % duration; + queueEvents(entry: TrackEntry, animationTime: number) { + const animationStart = entry.animationStart; + const animationEnd = entry.animationEnd; + const duration = animationEnd - animationStart; + const trackLastWrapped = entry.trackLast % duration; // Queue events before complete. - let events = this.events; - let i = 0, n = events.length; + const events = this.events; + let i = 0; + const n = events.length; + for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < trackLastWrapped) break; if (event.time > animationEnd) continue; // Discard events outside animation start/end. this.queue.event(entry, event); @@ -363,33 +380,34 @@ export class AnimationState implements IAnimationState { // Queue complete if completed a loop iteration or the animation. let complete = false; - if (entry.loop) - complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; - else - complete = animationTime >= animationEnd && entry.animationLast < animationEnd; + + if (entry.loop) complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; + else complete = animationTime >= animationEnd && entry.animationLast < animationEnd; if (complete) this.queue.complete(entry); // Queue events after complete. for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < animationStart) continue; // Discard events outside animation start/end. this.queue.event(entry, events[i]); } } - clearTracks () { - let oldDrainDisabled = this.queue.drainDisabled; + clearTracks() { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; - for (let i = 0, n = this.tracks.length; i < n; i++) - this.clearTrack(i); + for (let i = 0, n = this.tracks.length; i < n; i++) this.clearTrack(i); this.tracks.length = 0; this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); } - clearTrack (trackIndex: number) { + clearTrack(trackIndex: number) { if (trackIndex >= this.tracks.length) return; - let current = this.tracks[trackIndex]; + const current = this.tracks[trackIndex]; + if (current == null) return; this.queue.end(current); @@ -397,8 +415,10 @@ export class AnimationState implements IAnimationState { this.disposeNext(current); let entry = current; + while (true) { - let from = entry.mixingFrom; + const from = entry.mixingFrom; + if (from == null) break; this.queue.end(from); entry.mixingFrom = null; @@ -411,8 +431,9 @@ export class AnimationState implements IAnimationState { this.queue.drain(); } - setCurrent (index: number, current: TrackEntry, interrupt: boolean) { - let from = this.expandToIndex(index); + setCurrent(index: number, current: TrackEntry, interrupt: boolean) { + const from = this.expandToIndex(index); + this.tracks[index] = current; if (from != null) { @@ -422,8 +443,7 @@ export class AnimationState implements IAnimationState { current.mixTime = 0; // Store the interrupted mix percentage. - if (from.mixingFrom != null && from.mixDuration > 0) - current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); + if (from.mixingFrom != null && from.mixDuration > 0) current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in. } @@ -431,16 +451,19 @@ export class AnimationState implements IAnimationState { this.queue.start(current); } - setAnimation (trackIndex: number, animationName: string, loop: boolean) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (animation == null) throw new Error("Animation not found: " + animationName); + setAnimation(trackIndex: number, animationName: string, loop: boolean) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (animation == null) throw new Error(`Animation not found: ${animationName}`); + return this.setAnimationWith(trackIndex, animation, loop); } - setAnimationWith (trackIndex: number, animation: Animation, loop: boolean) { - if (animation == null) throw new Error("animation cannot be null."); + setAnimationWith(trackIndex: number, animation: Animation, loop: boolean) { + if (animation == null) throw new Error('animation cannot be null.'); let interrupt = true; let current = this.expandToIndex(trackIndex); + if (current != null) { if (current.nextTrackLast == -1) { // Don't mix from an entry that was never applied. @@ -450,31 +473,34 @@ export class AnimationState implements IAnimationState { this.disposeNext(current); current = current.mixingFrom; interrupt = false; - } else - this.disposeNext(current); + } else this.disposeNext(current); } - let entry = this.trackEntry(trackIndex, animation, loop, current); + const entry = this.trackEntry(trackIndex, animation, loop, current); + this.setCurrent(trackIndex, entry, interrupt); this.queue.drain(); + return entry; } - addAnimation (trackIndex: number, animationName: string, loop: boolean, delay: number) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (animation == null) throw new Error("Animation not found: " + animationName); + addAnimation(trackIndex: number, animationName: string, loop: boolean, delay: number) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (animation == null) throw new Error(`Animation not found: ${animationName}`); + return this.addAnimationWith(trackIndex, animation, loop, delay); } - addAnimationWith (trackIndex: number, animation: Animation, loop: boolean, delay: number) { - if (animation == null) throw new Error("animation cannot be null."); + addAnimationWith(trackIndex: number, animation: Animation, loop: boolean, delay: number) { + if (animation == null) throw new Error('animation cannot be null.'); let last = this.expandToIndex(trackIndex); + if (last != null) { - while (last.next != null) - last = last.next; + while (last.next != null) last = last.next; } - let entry = this.trackEntry(trackIndex, animation, loop, last); + const entry = this.trackEntry(trackIndex, animation, loop, last); if (last == null) { this.setCurrent(trackIndex, entry, true); @@ -482,57 +508,64 @@ export class AnimationState implements IAnimationState { } else { last.next = entry; if (delay <= 0) { - let duration = last.animationEnd - last.animationStart; + const duration = last.animationEnd - last.animationStart; + if (duration != 0) { - if (last.loop) - delay += duration * (1 + ((last.trackTime / duration) | 0)); - else - delay += Math.max(duration, last.trackTime); + if (last.loop) delay += duration * (1 + ((last.trackTime / duration) | 0)); + else delay += Math.max(duration, last.trackTime); delay -= this.data.getMix(last.animation, animation); - } else - delay = last.trackTime; + } else delay = last.trackTime; } } entry.delay = delay; + return entry; } - setEmptyAnimation (trackIndex: number, mixDuration: number) { - let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false); + setEmptyAnimation(trackIndex: number, mixDuration: number) { + const entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false); + entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } - addEmptyAnimation (trackIndex: number, mixDuration: number, delay: number) { + addEmptyAnimation(trackIndex: number, mixDuration: number, delay: number) { if (delay <= 0) delay -= mixDuration; - let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay); + const entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay); + entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } - setEmptyAnimations (mixDuration: number) { - let oldDrainDisabled = this.queue.drainDisabled; + setEmptyAnimations(mixDuration: number) { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; for (let i = 0, n = this.tracks.length; i < n; i++) { - let current = this.tracks[i]; + const current = this.tracks[i]; + if (current != null) this.setEmptyAnimation(current.trackIndex, mixDuration); } this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); } - expandToIndex (index: number) { + expandToIndex(index: number) { if (index < this.tracks.length) return this.tracks[index]; Utils.ensureArrayCapacity(this.tracks, index - this.tracks.length + 1, null); this.tracks.length = index + 1; + return null; } - trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) { - let entry = this.trackEntryPool.obtain(); + trackEntry(trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) { + const entry = this.trackEntryPool.obtain(); + entry.trackIndex = trackIndex; entry.animation = animation; entry.loop = loop; @@ -558,11 +591,13 @@ export class AnimationState implements IAnimationState { entry.interruptAlpha = 1; entry.mixTime = 0; entry.mixDuration = last == null ? 0 : this.data.getMix(last.animation, animation); + return entry; } - disposeNext (entry: TrackEntry) { + disposeNext(entry: TrackEntry) { let next = entry.next; + while (next != null) { this.queue.dispose(next); next = next.next; @@ -570,132 +605,139 @@ export class AnimationState implements IAnimationState { entry.next = null; } - _animationsChanged () { + _animationsChanged() { this.animationsChanged = false; this.propertyIDs.clear(); for (let i = 0, n = this.tracks.length; i < n; i++) { let entry = this.tracks[i]; + if (entry == null) continue; - while (entry.mixingFrom != null) - entry = entry.mixingFrom; + while (entry.mixingFrom != null) entry = entry.mixingFrom; do { if (entry.mixingFrom == null || entry.mixBlend != MixBlend.add) this.setTimelineModes(entry); entry = entry.mixingTo; - } while (entry != null) + } while (entry != null); } } - setTimelineModes (entry: TrackEntry) { - let to = entry.mixingTo; - let timelines = entry.animation.timelines; - let timelinesCount = entry.animation.timelines.length; - let timelineMode = Utils.setArraySize(entry.timelineMode, timelinesCount); + setTimelineModes(entry: TrackEntry) { + const to = entry.mixingTo; + const timelines = entry.animation.timelines; + const timelinesCount = entry.animation.timelines.length; + const timelineMode = Utils.setArraySize(entry.timelineMode, timelinesCount); + entry.timelineHoldMix.length = 0; - let timelineDipMix = Utils.setArraySize(entry.timelineHoldMix, timelinesCount); - let propertyIDs = this.propertyIDs; + const timelineDipMix = Utils.setArraySize(entry.timelineHoldMix, timelinesCount); + const propertyIDs = this.propertyIDs; if (to != null && to.holdPrevious) { for (let i = 0; i < timelinesCount; i++) { propertyIDs.add(timelines[i].getPropertyId()); timelineMode[i] = AnimationState.HOLD; } + return; } - outer: - for (let i = 0; i < timelinesCount; i++) { - let id = timelines[i].getPropertyId(); - if (!propertyIDs.add(id)) - timelineMode[i] = AnimationState.SUBSEQUENT; - else if (to == null || !this.hasTimeline(to, id)) - timelineMode[i] = AnimationState.FIRST; - else { - for (let next = to.mixingTo; next != null; next = next.mixingTo) { - if (this.hasTimeline(next, id)) continue; - if (entry.mixDuration > 0) { - timelineMode[i] = AnimationState.HOLD_MIX; - timelineDipMix[i] = next; - continue outer; - } - break; + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < timelinesCount; i++) { + const id = timelines[i].getPropertyId(); + + if (!propertyIDs.add(id)) timelineMode[i] = AnimationState.SUBSEQUENT; + else if (to == null || !this.hasTimeline(to, id)) timelineMode[i] = AnimationState.FIRST; + else { + for (let next = to.mixingTo; next != null; next = next.mixingTo) { + if (this.hasTimeline(next, id)) continue; + if (entry.mixDuration > 0) { + timelineMode[i] = AnimationState.HOLD_MIX; + timelineDipMix[i] = next; + // eslint-disable-next-line no-labels + continue outer; } - timelineMode[i] = AnimationState.HOLD; + break; } + timelineMode[i] = AnimationState.HOLD; } + } } - hasTimeline (entry: TrackEntry, id: number) : boolean { - let timelines = entry.animation.timelines; - for (let i = 0, n = timelines.length; i < n; i++) - if (timelines[i].getPropertyId() == id) return true; + hasTimeline(entry: TrackEntry, id: number): boolean { + const timelines = entry.animation.timelines; + + for (let i = 0, n = timelines.length; i < n; i++) if (timelines[i].getPropertyId() == id) return true; + return false; } - getCurrent (trackIndex: number) { + getCurrent(trackIndex: number) { if (trackIndex >= this.tracks.length) return null; + return this.tracks[trackIndex]; } - addListener (listener: AnimationStateListener) { - if (listener == null) throw new Error("listener cannot be null."); + addListener(listener: AnimationStateListener) { + if (listener == null) throw new Error('listener cannot be null.'); this.listeners.push(listener); } /** Removes the listener added with {@link #addListener(AnimationStateListener)}. */ - removeListener (listener: AnimationStateListener) { - let index = this.listeners.indexOf(listener); + removeListener(listener: AnimationStateListener) { + const index = this.listeners.indexOf(listener); + if (index >= 0) this.listeners.splice(index, 1); } - clearListeners () { + clearListeners() { this.listeners.length = 0; } - clearListenerNotifications () { + clearListenerNotifications() { this.queue.clear(); } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; onEnd: (trackIndex: number) => any; - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; setAnimationByName(trackIndex: number, animationName: string, loop: boolean) { if (!AnimationState.deprecatedWarning1) { AnimationState.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on.'); } this.setAnimation(trackIndex, animationName, loop); } - private static deprecatedWarning2: boolean = false; + private static deprecatedWarning2 = false; addAnimationByName(trackIndex: number, animationName: string, loop: boolean, delay: number) { if (!AnimationState.deprecatedWarning2) { AnimationState.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on.'); } this.addAnimation(trackIndex, animationName, loop, delay); } - private static deprecatedWarning3: boolean = false; + private static deprecatedWarning3 = false; hasAnimation(animationName: string): boolean { - let animation = this.data.skeletonData.findAnimation(animationName); + const animation = this.data.skeletonData.findAnimation(animationName); + return animation !== null; } hasAnimationByName(animationName: string): boolean { if (!AnimationState.deprecatedWarning3) { AnimationState.deprecatedWarning3 = true; - console.warn("Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on.'); } + return this.hasAnimation(animationName); } } @@ -705,21 +747,37 @@ export class AnimationState implements IAnimationState { */ export class TrackEntry implements ITrackEntry { animation: Animation; - next: TrackEntry; mixingFrom: TrackEntry; mixingTo: TrackEntry; + next: TrackEntry; + mixingFrom: TrackEntry; + mixingTo: TrackEntry; listener: AnimationStateListener; trackIndex: number; loop: boolean; holdPrevious: boolean; - eventThreshold: number; attachmentThreshold: number; drawOrderThreshold: number; - animationStart: number; animationEnd: number; animationLast: number; nextAnimationLast: number; - delay: number; trackTime: number; trackLast: number; nextTrackLast: number; trackEnd: number; timeScale: number; - alpha: number; mixTime: number; mixDuration: number; interruptAlpha: number; totalAlpha: number; + eventThreshold: number; + attachmentThreshold: number; + drawOrderThreshold: number; + animationStart: number; + animationEnd: number; + animationLast: number; + nextAnimationLast: number; + delay: number; + trackTime: number; + trackLast: number; + nextTrackLast: number; + trackEnd: number; + timeScale: number; + alpha: number; + mixTime: number; + mixDuration: number; + interruptAlpha: number; + totalAlpha: number; mixBlend = MixBlend.replace; timelineMode = new Array(); timelineHoldMix = new Array(); timelinesRotation = new Array(); - reset () { + reset() { this.next = null; this.mixingFrom = null; this.mixingTo = null; @@ -730,12 +788,15 @@ export class TrackEntry implements ITrackEntry { this.timelinesRotation.length = 0; } - getAnimationTime () { + getAnimationTime() { if (this.loop) { - let duration = this.animationEnd - this.animationStart; + const duration = this.animationEnd - this.animationStart; + if (duration == 0) return this.animationStart; + return (this.trackTime % duration) + this.animationStart; } + return Math.min(this.trackTime + this.animationStart, this.animationEnd); } @@ -744,35 +805,36 @@ export class TrackEntry implements ITrackEntry { this.nextAnimationLast = animationLast; } - isComplete () { + isComplete() { return this.trackTime >= this.animationEnd - this.animationStart; } - resetRotationDirections () { + resetRotationDirections() { this.timelinesRotation.length = 0; } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; onEnd: (trackIndex: number) => any; - private static deprecatedWarning1: Boolean = false; - private static deprecatedWarning2: Boolean = false; + private static deprecatedWarning1 = false; + private static deprecatedWarning2 = false; get time() { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } + return this.trackTime; } set time(value: number) { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } this.trackTime = value; } @@ -780,15 +842,16 @@ export class TrackEntry implements ITrackEntry { get endTime() { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } + return this.trackTime; } set endTime(value: number) { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } this.trackTime = value; } @@ -810,103 +873,103 @@ export class EventQueue { this.animState = animState; } - start (entry: TrackEntry) { + start(entry: TrackEntry) { this.objects.push(EventType.start); this.objects.push(entry); this.animState.animationsChanged = true; } - interrupt (entry: TrackEntry) { + interrupt(entry: TrackEntry) { this.objects.push(EventType.interrupt); this.objects.push(entry); } - end (entry: TrackEntry) { + end(entry: TrackEntry) { this.objects.push(EventType.end); this.objects.push(entry); this.animState.animationsChanged = true; } - dispose (entry: TrackEntry) { + dispose(entry: TrackEntry) { this.objects.push(EventType.dispose); this.objects.push(entry); } - complete (entry: TrackEntry) { + complete(entry: TrackEntry) { this.objects.push(EventType.complete); this.objects.push(entry); } - event (entry: TrackEntry, event: Event) { + event(entry: TrackEntry, event: Event) { this.objects.push(EventType.event); this.objects.push(entry); this.objects.push(event); } - private static deprecatedWarning1: Boolean = false; + private static deprecatedWarning1 = false; deprecateStuff() { if (!EventQueue.deprecatedWarning1) { EventQueue.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: onComplete, onStart, onEnd, onEvent art deprecated, please use listeners from now on. 'state.addListener({ complete: function(track, event) { } })'"); + console.warn( + "Spine Deprecation Warning: onComplete, onStart, onEnd, onEvent art deprecated, please use listeners from now on. 'state.addListener({ complete: function(track, event) { } })'" + ); } + return true; } - drain () { + drain() { if (this.drainDisabled) return; this.drainDisabled = true; - let objects = this.objects; - let listeners = this.animState.listeners; + const objects = this.objects; + const listeners = this.animState.listeners; for (let i = 0; i < objects.length; i += 2) { - let type = objects[i] as EventType; - let entry = objects[i + 1] as TrackEntry; + const type = objects[i] as EventType; + const entry = objects[i + 1] as TrackEntry; + switch (type) { case EventType.start: if (entry.listener != null && entry.listener.start) entry.listener.start(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].start) listeners[ii].start(entry); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].start) listeners[ii].start(entry); + // deprecation entry.onStart && this.deprecateStuff() && entry.onStart(entry.trackIndex); this.animState.onStart && this.deprecateStuff() && this.deprecateStuff && this.animState.onStart(entry.trackIndex); break; case EventType.interrupt: if (entry.listener != null && entry.listener.interrupt) entry.listener.interrupt(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].interrupt) listeners[ii].interrupt(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].interrupt) listeners[ii].interrupt(entry); break; case EventType.end: if (entry.listener != null && entry.listener.end) entry.listener.end(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].end) listeners[ii].end(entry); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].end) listeners[ii].end(entry); + // deprecation entry.onEnd && this.deprecateStuff() && entry.onEnd(entry.trackIndex); this.animState.onEnd && this.deprecateStuff() && this.animState.onEnd(entry.trackIndex); // Fall through. case EventType.dispose: if (entry.listener != null && entry.listener.dispose) entry.listener.dispose(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].dispose) listeners[ii].dispose(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].dispose) listeners[ii].dispose(entry); this.animState.trackEntryPool.free(entry); break; case EventType.complete: if (entry.listener != null && entry.listener.complete) entry.listener.complete(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].complete) listeners[ii].complete(entry); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].complete) listeners[ii].complete(entry); + // deprecation + + const count = MathUtils.toInt(entry.loopsCount()); - let count = MathUtils.toInt(entry.loopsCount()) ; entry.onComplete && this.deprecateStuff() && entry.onComplete(entry.trackIndex, count); this.animState.onComplete && this.deprecateStuff() && this.animState.onComplete(entry.trackIndex, count); break; case EventType.event: - let event = objects[i++ + 2] as Event; + const event = objects[i++ + 2] as Event; + if (entry.listener != null && entry.listener.event) entry.listener.event(entry, event); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].event) listeners[ii].event(entry, event); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].event) listeners[ii].event(entry, event); + // deprecation entry.onEvent && this.deprecateStuff() && entry.onEvent(entry.trackIndex, event); this.animState.onEvent && this.deprecateStuff() && this.animState.onEvent(entry.trackIndex, event); break; @@ -917,7 +980,7 @@ export class EventQueue { this.drainDisabled = false; } - clear () { + clear() { this.objects.length = 0; } } @@ -926,7 +989,12 @@ export class EventQueue { * @public */ export enum EventType { - start, interrupt, end, dispose, complete, event + start, + interrupt, + end, + dispose, + complete, + event, } /** @@ -934,45 +1002,39 @@ export enum EventType { */ export interface AnimationStateListener extends IAnimationStateListener { /** Invoked when this entry has been set as the current entry. */ - start? (entry: TrackEntry): void; + start?(entry: TrackEntry): void; /** Invoked when another entry has replaced this entry as the current entry. This entry may continue being applied for * mixing. */ - interrupt? (entry: TrackEntry): void; + interrupt?(entry: TrackEntry): void; /** Invoked when this entry is no longer the current entry and will never be applied again. */ - end? (entry: TrackEntry): void; + end?(entry: TrackEntry): void; /** Invoked when this entry will be disposed. This may occur without the entry ever being set as the current entry. * References to the entry should not be kept after dispose is called, as it may be destroyed or reused. */ - dispose? (entry: TrackEntry): void; + dispose?(entry: TrackEntry): void; /** Invoked every time this entry's animation completes a loop. */ - complete? (entry: TrackEntry): void; + complete?(entry: TrackEntry): void; /** Invoked when this entry's animation triggers an event. */ - event? (entry: TrackEntry, event: Event): void; + event?(entry: TrackEntry, event: Event): void; } /** * @public */ export abstract class AnimationStateAdapter2 implements AnimationStateListener { - start (entry: TrackEntry) { - } + start(entry: TrackEntry) {} - interrupt (entry: TrackEntry) { - } + interrupt(entry: TrackEntry) {} - end (entry: TrackEntry) { - } + end(entry: TrackEntry) {} - dispose (entry: TrackEntry) { - } + dispose(entry: TrackEntry) {} - complete (entry: TrackEntry) { - } + complete(entry: TrackEntry) {} - event (entry: TrackEntry, event: Event) { - } + event(entry: TrackEntry, event: Event) {} } diff --git a/packages/runtime-3.7/src/core/AnimationStateData.ts b/packages/runtime-3.7/src/core/AnimationStateData.ts index 1750d2e5..9c442cb3 100644 --- a/packages/runtime-3.7/src/core/AnimationStateData.ts +++ b/packages/runtime-3.7/src/core/AnimationStateData.ts @@ -1,6 +1,6 @@ -import {SkeletonData} from "./SkeletonData"; -import {IAnimationStateData, Map} from '@pixi-spine/base'; -import type {Animation} from './Animation'; +import type { SkeletonData } from './SkeletonData'; +import type { IAnimationStateData, Map } from '@pixi-spine/base'; +import type { Animation } from './Animation'; /** * @public @@ -11,38 +11,42 @@ export class AnimationStateData implements IAnimationStateData 0.0001) { s = Math.abs(pa * pd - pb * pc) / s; pb = pc * s; @@ -130,12 +141,13 @@ export class Bone implements Updatable, IBone { pc = 0; prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg; } - let rx = rotation + shearX - prx; - let ry = rotation + shearY - prx + 90; - let la = MathUtils.cosDeg(rx) * scaleX; - let lb = MathUtils.cosDeg(ry) * scaleY; - let lc = MathUtils.sinDeg(rx) * scaleX; - let ld = MathUtils.sinDeg(ry) * scaleY; + const rx = rotation + shearX - prx; + const ry = rotation + shearY - prx + 90; + const la = MathUtils.cosDeg(rx) * scaleX; + const lb = MathUtils.cosDeg(ry) * scaleY; + const lc = MathUtils.sinDeg(rx) * scaleX; + const ld = MathUtils.sinDeg(ry) * scaleY; + m.a = pa * la - pb * lc; m.c = pa * lb - pb * ld; m.b = pc * la + pd * lc; @@ -144,28 +156,29 @@ export class Bone implements Updatable, IBone { } case TransformMode.NoScale: case TransformMode.NoScaleOrReflection: { - let cos = MathUtils.cosDeg(rotation); - let sin = MathUtils.sinDeg(rotation); + const cos = MathUtils.cosDeg(rotation); + const sin = MathUtils.sinDeg(rotation); let za = (pa * cos + pb * sin) / sx; let zc = (pc * cos + pd * sin) / sy; let s = Math.sqrt(za * za + zc * zc); + if (s > 0.00001) s = 1 / s; za *= s; zc *= s; s = Math.sqrt(za * za + zc * zc); if ( - this.data.transformMode == TransformMode.NoScale - && (pa * pd - pb * pc < 0) != (settings.yDown? - (this.skeleton.scaleX < 0 != this.skeleton.scaleY > 0) : - (this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0)) - ) s = -s; - let r = Math.PI / 2 + Math.atan2(zc, za); - let zb = Math.cos(r) * s; - let zd = Math.sin(r) * s; - let la = MathUtils.cosDeg(shearX) * scaleX; - let lb = MathUtils.cosDeg(90 + shearY) * scaleY; - let lc = MathUtils.sinDeg(shearX) * scaleX; - let ld = MathUtils.sinDeg(90 + shearY) * scaleY; + this.data.transformMode == TransformMode.NoScale && + pa * pd - pb * pc < 0 != (settings.yDown ? this.skeleton.scaleX < 0 != this.skeleton.scaleY > 0 : this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0) + ) + s = -s; + const r = Math.PI / 2 + Math.atan2(zc, za); + const zb = Math.cos(r) * s; + const zd = Math.sin(r) * s; + const la = MathUtils.cosDeg(shearX) * scaleX; + const lb = MathUtils.cosDeg(90 + shearY) * scaleY; + const lc = MathUtils.sinDeg(shearX) * scaleX; + const ld = MathUtils.sinDeg(90 + shearY) * scaleY; + m.a = za * la + zb * lc; m.c = za * lb + zb * ld; m.b = zc * la + zd * lc; @@ -180,7 +193,8 @@ export class Bone implements Updatable, IBone { } setToSetupPose() { - let data = this.data; + const data = this.data; + this.x = data.x; this.y = data.y; this.rotation = data.rotation; @@ -199,12 +213,14 @@ export class Bone implements Updatable, IBone { } getWorldScaleX() { - let m = this.matrix; + const m = this.matrix; + return Math.sqrt(m.a * m.a + m.c * m.c); } getWorldScaleY() { - let m = this.matrix; + const m = this.matrix; + return Math.sqrt(m.b * m.b + m.d * m.d); } @@ -214,8 +230,9 @@ export class Bone implements Updatable, IBone { * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. */ updateAppliedTransform() { this.appliedValid = true; - let parent = this.parent; - let m = this.matrix; + const parent = this.parent; + const m = this.matrix; + if (parent == null) { this.ax = m.tx; this.ay = m.ty; @@ -224,25 +241,30 @@ export class Bone implements Updatable, IBone { this.ascaleY = Math.sqrt(m.c * m.c + m.d * m.d); this.ashearX = 0; this.ashearY = Math.atan2(m.a * m.c + m.b * m.d, m.a * m.d - m.b * m.c) * MathUtils.radDeg; + return; } - let pm = parent.matrix; - let pid = 1 / (pm.a * pm.d - pm.b * pm.c); - let dx = m.tx - pm.tx, dy = m.ty - pm.ty; - this.ax = (dx * pm.d * pid - dy * pm.c * pid); - this.ay = (dy * pm.a * pid - dx * pm.b * pid); - let ia = pid * pm.d; - let id = pid * pm.a; - let ib = pid * pm.c; - let ic = pid * pm.b; - let ra = ia * m.a - ib * m.b; - let rb = ia * m.c - ib * m.d; - let rc = id * m.b - ic * m.a; - let rd = id * m.d - ic * m.c; + const pm = parent.matrix; + const pid = 1 / (pm.a * pm.d - pm.b * pm.c); + const dx = m.tx - pm.tx; + const dy = m.ty - pm.ty; + + this.ax = dx * pm.d * pid - dy * pm.c * pid; + this.ay = dy * pm.a * pid - dx * pm.b * pid; + const ia = pid * pm.d; + const id = pid * pm.a; + const ib = pid * pm.c; + const ic = pid * pm.b; + const ra = ia * m.a - ib * m.b; + const rb = ia * m.c - ib * m.d; + const rc = id * m.b - ic * m.a; + const rd = id * m.d - ic * m.c; + this.ashearX = 0; this.ascaleX = Math.sqrt(ra * ra + rc * rc); if (this.ascaleX > 0.0001) { - let det = ra * rd - rb * rc; + const det = ra * rd - rb * rc; + this.ascaleY = det / this.ascaleX; this.ashearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg; this.arotation = Math.atan2(rc, ra) * MathUtils.radDeg; @@ -255,39 +277,57 @@ export class Bone implements Updatable, IBone { } worldToLocal(world: Vector2) { - let m = this.matrix; - let a = m.a, b = m.c, c = m.b, d = m.d; - let invDet = 1 / (a * d - b * c); - let x = world.x - m.tx, y = world.y - m.ty; - world.x = (x * d * invDet - y * b * invDet); - world.y = (y * a * invDet - x * c * invDet); + const m = this.matrix; + const a = m.a; + const b = m.c; + const c = m.b; + const d = m.d; + const invDet = 1 / (a * d - b * c); + const x = world.x - m.tx; + const y = world.y - m.ty; + + world.x = x * d * invDet - y * b * invDet; + world.y = y * a * invDet - x * c * invDet; + return world; } localToWorld(local: Vector2) { - let m = this.matrix; - let x = local.x, y = local.y; + const m = this.matrix; + const x = local.x; + const y = local.y; + local.x = x * m.a + y * m.c + m.tx; local.y = x * m.b + y * m.d + m.ty; + return local; } - worldToLocalRotation (worldRotation: number) { - let sin = MathUtils.sinDeg(worldRotation), cos = MathUtils.cosDeg(worldRotation); - let mat = this.matrix; + worldToLocalRotation(worldRotation: number) { + const sin = MathUtils.sinDeg(worldRotation); + const cos = MathUtils.cosDeg(worldRotation); + const mat = this.matrix; + return Math.atan2(mat.a * sin - mat.b * cos, mat.d * cos - mat.c * sin) * MathUtils.radDeg; } - localToWorldRotation (localRotation: number) { - let sin = MathUtils.sinDeg(localRotation), cos = MathUtils.cosDeg(localRotation); - let mat = this.matrix; + localToWorldRotation(localRotation: number) { + const sin = MathUtils.sinDeg(localRotation); + const cos = MathUtils.cosDeg(localRotation); + const mat = this.matrix; + return Math.atan2(cos * mat.b + sin * mat.d, cos * mat.a + sin * mat.c) * MathUtils.radDeg; } - rotateWorld (degrees: number) { - let mat = this.matrix; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees); + rotateWorld(degrees: number) { + const mat = this.matrix; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + const cos = MathUtils.cosDeg(degrees); + const sin = MathUtils.sinDeg(degrees); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; diff --git a/packages/runtime-3.7/src/core/BoneData.ts b/packages/runtime-3.7/src/core/BoneData.ts index 982f423d..7bfbd96b 100644 --- a/packages/runtime-3.7/src/core/BoneData.ts +++ b/packages/runtime-3.7/src/core/BoneData.ts @@ -1,4 +1,4 @@ -import { TransformMode } from "@pixi-spine/base"; +import { TransformMode } from '@pixi-spine/base'; /** * @public @@ -18,8 +18,8 @@ export class BoneData { transformMode = TransformMode.Normal; constructor(index: number, name: string, parent: BoneData) { - if (index < 0) throw new Error("index must be >= 0."); - if (name == null) throw new Error("name cannot be null."); + if (index < 0) throw new Error('index must be >= 0.'); + if (name == null) throw new Error('name cannot be null.'); this.index = index; this.name = name; this.parent = parent; diff --git a/packages/runtime-3.7/src/core/Constraint.ts b/packages/runtime-3.7/src/core/Constraint.ts index e64661dd..e9e8feae 100644 --- a/packages/runtime-3.7/src/core/Constraint.ts +++ b/packages/runtime-3.7/src/core/Constraint.ts @@ -1,4 +1,4 @@ -import {Updatable} from "./Updatable"; +import type { Updatable } from './Updatable'; /** * @public diff --git a/packages/runtime-3.7/src/core/Event.ts b/packages/runtime-3.7/src/core/Event.ts index ac8acd9e..55010bac 100644 --- a/packages/runtime-3.7/src/core/Event.ts +++ b/packages/runtime-3.7/src/core/Event.ts @@ -1,5 +1,5 @@ -import {EventData} from "./EventData"; -import {IEvent} from "@pixi-spine/base"; +import type { EventData } from './EventData'; +import type { IEvent } from '@pixi-spine/base'; /** * @public @@ -13,9 +13,8 @@ export class Event implements IEvent { volume: number; balance: number; - constructor(time: number, data: EventData) { - if (data == null) throw new Error("data cannot be null."); + if (data == null) throw new Error('data cannot be null.'); this.time = time; this.data = data; } diff --git a/packages/runtime-3.7/src/core/EventData.ts b/packages/runtime-3.7/src/core/EventData.ts index 6951be45..47a270a2 100644 --- a/packages/runtime-3.7/src/core/EventData.ts +++ b/packages/runtime-3.7/src/core/EventData.ts @@ -1,4 +1,4 @@ -import {IEventData} from "@pixi-spine/base"; +import type { IEventData } from '@pixi-spine/base'; /** * @public @@ -12,7 +12,7 @@ export class EventData implements IEventData { volume: number; balance: number; - constructor (name: string) { + constructor(name: string) { this.name = name; } } diff --git a/packages/runtime-3.7/src/core/IkConstraint.ts b/packages/runtime-3.7/src/core/IkConstraint.ts index 26bffa92..35479d6c 100644 --- a/packages/runtime-3.7/src/core/IkConstraint.ts +++ b/packages/runtime-3.7/src/core/IkConstraint.ts @@ -1,8 +1,8 @@ -import {Constraint} from "./Constraint"; -import {IkConstraintData} from "./IkConstraintData"; -import {Bone} from "./Bone"; -import {Skeleton} from "./Skeleton"; -import {MathUtils} from "@pixi-spine/base"; +import type { Constraint } from './Constraint'; +import type { IkConstraintData } from './IkConstraintData'; +import type { Bone } from './Bone'; +import type { Skeleton } from './Skeleton'; +import { MathUtils } from '@pixi-spine/base'; /** * @public @@ -16,9 +16,9 @@ export class IkConstraint implements Constraint { stretch = false; mix = 1; - constructor (data: IkConstraintData, skeleton: Skeleton) { - if (data == null) throw new Error("data cannot be null."); - if (skeleton == null) throw new Error("skeleton cannot be null."); + constructor(data: IkConstraintData, skeleton: Skeleton) { + if (data == null) throw new Error('data cannot be null.'); + if (skeleton == null) throw new Error('skeleton cannot be null.'); this.data = data; this.mix = data.mix; this.bendDirection = data.bendDirection; @@ -26,22 +26,22 @@ export class IkConstraint implements Constraint { this.stretch = data.stretch; this.bones = new Array(); - for (let i = 0; i < data.bones.length; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0; i < data.bones.length; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findBone(data.target.name); } - getOrder () { + getOrder() { return this.data.order; } - apply () { + apply() { this.update(); } - update () { - let target = this.target; - let bones = this.bones; + update() { + const target = this.target; + const bones = this.bones; + switch (bones.length) { case 1: this.apply1(bones[0], target.worldX, target.worldY, this.compress, this.stretch, this.data.uniform, this.mix); @@ -54,43 +54,58 @@ export class IkConstraint implements Constraint { /** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world * coordinate system. */ - apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { + apply1(bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { if (!bone.appliedValid) bone.updateAppliedTransform(); - let p = bone.parent.matrix; - let id = 1 / (p.a * p.d - p.b * p.c); - let x = targetX - p.tx, y = targetY - p.ty; - let tx = (x * p.d - y * p.c) * id - bone.ax, ty = (y * p.a - x * p.b) * id - bone.ay; + const p = bone.parent.matrix; + const id = 1 / (p.a * p.d - p.b * p.c); + const x = targetX - p.tx; + const y = targetY - p.ty; + const tx = (x * p.d - y * p.c) * id - bone.ax; + const ty = (y * p.a - x * p.b) * id - bone.ay; let rotationIK = Math.atan2(ty, tx) * MathUtils.radDeg - bone.ashearX - bone.arotation; + if (bone.ascaleX < 0) rotationIK += 180; - if (rotationIK > 180) - rotationIK -= 360; + if (rotationIK > 180) rotationIK -= 360; else if (rotationIK < -180) rotationIK += 360; - let sx = bone.ascaleX, sy = bone.ascaleY; + let sx = bone.ascaleX; + let sy = bone.ascaleY; + if (compress || stretch) { - let b = bone.data.length * sx, dd = Math.sqrt(tx * tx + ty * ty); - if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) { - let s = (dd / b - 1) * alpha + 1; + const b = bone.data.length * sx; + const dd = Math.sqrt(tx * tx + ty * ty); + + if ((compress && dd < b) || (stretch && dd > b && b > 0.0001)) { + const s = (dd / b - 1) * alpha + 1; + sx *= s; if (uniform) sy *= s; } } - bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, - bone.ashearY); + bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY); } /** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The * target is specified in the world coordinate system. * @param child A direct descendant of the parent bone. */ - apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, alpha: number) { + apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, alpha: number) { if (alpha == 0) { child.updateWorldTransform(); + return; } if (!parent.appliedValid) parent.updateAppliedTransform(); if (!child.appliedValid) child.updateAppliedTransform(); - let px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX; - let pmat = parent.matrix; - let os1 = 0, os2 = 0, s2 = 0; + const px = parent.ax; + const py = parent.ay; + let psx = parent.ascaleX; + let sx = psx; + let psy = parent.ascaleY; + let csx = child.ascaleX; + const pmat = parent.matrix; + let os1 = 0; + let os2 = 0; + let s2 = 0; + if (psx < 0) { psx = -psx; os1 = 180; @@ -106,10 +121,17 @@ export class IkConstraint implements Constraint { if (csx < 0) { csx = -csx; os2 = 180; - } else - os2 = 0; - let cx = child.ax, cy = 0, cwx = 0, cwy = 0, a = pmat.a, b = pmat.c, c = pmat.b, d = pmat.d; - let u = Math.abs(psx - psy) <= 0.0001; + } else os2 = 0; + const cx = child.ax; + let cy = 0; + let cwx = 0; + let cwy = 0; + let a = pmat.a; + let b = pmat.c; + let c = pmat.b; + let d = pmat.d; + const u = Math.abs(psx - psy) <= 0.0001; + if (!u) { cy = 0; cwx = a * cx + pmat.tx; @@ -119,91 +141,117 @@ export class IkConstraint implements Constraint { cwx = a * cx + b * cy + pmat.tx; cwy = c * cx + d * cy + pmat.ty; } - let pp = parent.parent.matrix; + const pp = parent.parent.matrix; + a = pp.a; b = pp.c; c = pp.b; d = pp.d; - let id = 1 / (a * d - b * c), x = targetX - pp.tx, y = targetY - pp.ty; - let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py, dd = tx * tx + ty * ty; + const id = 1 / (a * d - b * c); + let x = targetX - pp.tx; + let y = targetY - pp.ty; + const tx = (x * d - y * b) * id - px; + const ty = (y * a - x * c) * id - py; + const dd = tx * tx + ty * ty; + x = cwx - pp.tx; y = cwy - pp.ty; - let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; - let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1 = 0, a2 = 0; - outer: - if (u) { - l2 *= psx; - let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); - if (cos < -1) - cos = -1; - else if (cos > 1) { - cos = 1; - if (stretch && l1 + l2 > 0.0001) sx *= (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; - } - a2 = Math.acos(cos) * bendDir; - a = l1 + l2 * cos; - b = l2 * Math.sin(a2); - a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); - } else { - a = psx * l2; - b = psy * l2; - let aa = a * a, bb = b * b, ta = Math.atan2(ty, tx); - c = bb * l1 * l1 + aa * dd - aa * bb; - let c1 = -2 * bb * l1, c2 = bb - aa; - d = c1 * c1 - 4 * c2 * c; - if (d >= 0) { - let q = Math.sqrt(d); - if (c1 < 0) q = -q; - q = -(c1 + q) / 2; - let r0 = q / c2, r1 = c / q; - let r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; - if (r * r <= dd) { - y = Math.sqrt(dd - r * r) * bendDir; - a1 = ta - Math.atan2(y, r); - a2 = Math.atan2(y / psy, (r - l1) / psx); - break outer; - } + const dx = (x * d - y * b) * id - px; + const dy = (y * a - x * c) * id - py; + const l1 = Math.sqrt(dx * dx + dy * dy); + let l2 = child.data.length * csx; + let a1 = 0; + let a2 = 0; + + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: if (u) { + l2 *= psx; + let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); + + if (cos < -1) cos = -1; + else if (cos > 1) { + cos = 1; + if (stretch && l1 + l2 > 0.0001) sx *= (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; + } + a2 = Math.acos(cos) * bendDir; + a = l1 + l2 * cos; + b = l2 * Math.sin(a2); + a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); + } else { + a = psx * l2; + b = psy * l2; + const aa = a * a; + const bb = b * b; + const ta = Math.atan2(ty, tx); + + c = bb * l1 * l1 + aa * dd - aa * bb; + const c1 = -2 * bb * l1; + const c2 = bb - aa; + + d = c1 * c1 - 4 * c2 * c; + if (d >= 0) { + let q = Math.sqrt(d); + + if (c1 < 0) q = -q; + q = -(c1 + q) / 2; + const r0 = q / c2; + const r1 = c / q; + const r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; + + if (r * r <= dd) { + y = Math.sqrt(dd - r * r) * bendDir; + a1 = ta - Math.atan2(y, r); + a2 = Math.atan2(y / psy, (r - l1) / psx); + // eslint-disable-next-line no-labels + break outer; } - let minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0; - let maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; - c = -a * l1 / (aa - bb); - if (c >= -1 && c <= 1) { - c = Math.acos(c); - x = a * Math.cos(c) + l1; - y = b * Math.sin(c); - d = x * x + y * y; - if (d < minDist) { - minAngle = c; - minDist = d; - minX = x; - minY = y; - } - if (d > maxDist) { - maxAngle = c; - maxDist = d; - maxX = x; - maxY = y; - } + } + let minAngle = MathUtils.PI; + let minX = l1 - a; + let minDist = minX * minX; + let minY = 0; + let maxAngle = 0; + let maxX = l1 + a; + let maxDist = maxX * maxX; + let maxY = 0; + + c = (-a * l1) / (aa - bb); + if (c >= -1 && c <= 1) { + c = Math.acos(c); + x = a * Math.cos(c) + l1; + y = b * Math.sin(c); + d = x * x + y * y; + if (d < minDist) { + minAngle = c; + minDist = d; + minX = x; + minY = y; } - if (dd <= (minDist + maxDist) / 2) { - a1 = ta - Math.atan2(minY * bendDir, minX); - a2 = minAngle * bendDir; - } else { - a1 = ta - Math.atan2(maxY * bendDir, maxX); - a2 = maxAngle * bendDir; + if (d > maxDist) { + maxAngle = c; + maxDist = d; + maxX = x; + maxY = y; } } - let os = Math.atan2(cy, cx) * s2; + if (dd <= (minDist + maxDist) / 2) { + a1 = ta - Math.atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } else { + a1 = ta - Math.atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + const os = Math.atan2(cy, cx) * s2; let rotation = parent.arotation; + a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation; - if (a1 > 180) - a1 -= 360; + if (a1 > 180) a1 -= 360; else if (a1 < -180) a1 += 360; parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0); rotation = child.arotation; a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation; - if (a2 > 180) - a2 -= 360; + if (a2 > 180) a2 -= 360; else if (a2 < -180) a2 += 360; child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); } diff --git a/packages/runtime-3.7/src/core/IkConstraintData.ts b/packages/runtime-3.7/src/core/IkConstraintData.ts index 1790b7c4..56e4058b 100644 --- a/packages/runtime-3.7/src/core/IkConstraintData.ts +++ b/packages/runtime-3.7/src/core/IkConstraintData.ts @@ -1,4 +1,4 @@ -import {BoneData} from "./BoneData"; +import type { BoneData } from './BoneData'; /** * @public @@ -14,7 +14,7 @@ export class IkConstraintData { uniform = false; mix = 1; - constructor (name: string) { + constructor(name: string) { this.name = name; } } diff --git a/packages/runtime-3.7/src/core/PathConstraint.ts b/packages/runtime-3.7/src/core/PathConstraint.ts index 16fbf982..31461b9d 100644 --- a/packages/runtime-3.7/src/core/PathConstraint.ts +++ b/packages/runtime-3.7/src/core/PathConstraint.ts @@ -1,34 +1,41 @@ -import {PathAttachment} from "./attachments"; -import {Constraint} from "./Constraint"; -import {PathConstraintData, SpacingMode} from "./PathConstraintData"; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Skeleton} from "./Skeleton"; -import {MathUtils, PositionMode, RotateMode, Utils} from "@pixi-spine/base"; +import { PathAttachment } from './attachments'; +import type { Constraint } from './Constraint'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import type { Bone } from './Bone'; +import type { Slot } from './Slot'; +import type { Skeleton } from './Skeleton'; +import { MathUtils, PositionMode, RotateMode, Utils } from '@pixi-spine/base'; /** * @public */ export class PathConstraint implements Constraint { - static NONE = -1; static BEFORE = -2; static AFTER = -3; + static NONE = -1; + static BEFORE = -2; + static AFTER = -3; static epsilon = 0.00001; data: PathConstraintData; bones: Array; target: Slot; - position = 0; spacing = 0; rotateMix = 0; translateMix = 0; + position = 0; + spacing = 0; + rotateMix = 0; + translateMix = 0; - spaces = new Array(); positions = new Array(); - world = new Array(); curves = new Array(); lengths = new Array(); + spaces = new Array(); + positions = new Array(); + world = new Array(); + curves = new Array(); + lengths = new Array(); segments = new Array(); - constructor (data: PathConstraintData, skeleton: Skeleton) { - if (data == null) throw new Error("data cannot be null."); - if (skeleton == null) throw new Error("skeleton cannot be null."); + constructor(data: PathConstraintData, skeleton: Skeleton) { + if (data == null) throw new Error('data cannot be null.'); + if (skeleton == null) throw new Error('skeleton cannot be null.'); this.data = data; this.bones = new Array(); - for (let i = 0, n = data.bones.length; i < n; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0, n = data.bones.length; i < n; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findSlot(data.target.name); this.position = data.position; this.spacing = data.spacing; @@ -36,68 +43,93 @@ export class PathConstraint implements Constraint { this.translateMix = data.translateMix; } - apply () { + apply() { this.update(); } - update () { - let attachment = this.target.getAttachment(); + update() { + const attachment = this.target.getAttachment(); + if (!(attachment instanceof PathAttachment)) return; - let rotateMix = this.rotateMix, translateMix = this.translateMix; - let translate = translateMix > 0, rotate = rotateMix > 0; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const translate = translateMix > 0; + const rotate = rotateMix > 0; + if (!translate && !rotate) return; - let data = this.data; - let spacingMode = data.spacingMode; - let lengthSpacing = spacingMode == SpacingMode.Length; - let rotateMode = data.rotateMode; - let tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale; - let boneCount = this.bones.length, spacesCount = tangents ? boneCount : boneCount + 1; - let bones = this.bones; - let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array = null; - let spacing = this.spacing; + const data = this.data; + const spacingMode = data.spacingMode; + const lengthSpacing = spacingMode == SpacingMode.Length; + const rotateMode = data.rotateMode; + const tangents = rotateMode == RotateMode.Tangent; + const scale = rotateMode == RotateMode.ChainScale; + const boneCount = this.bones.length; + const spacesCount = tangents ? boneCount : boneCount + 1; + const bones = this.bones; + const spaces = Utils.setArraySize(this.spaces, spacesCount); + let lengths: Array = null; + const spacing = this.spacing; + if (scale || lengthSpacing) { if (scale) lengths = Utils.setArraySize(this.lengths, boneCount); - for (let i = 0, n = spacesCount - 1; i < n;) { - let bone = bones[i]; - let setupLength = bone.data.length; + for (let i = 0, n = spacesCount - 1; i < n; ) { + const bone = bones[i]; + const setupLength = bone.data.length; + if (setupLength < PathConstraint.epsilon) { if (scale) lengths[i] = 0; spaces[++i] = 0; } else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; - let length = Math.sqrt(x * x + y * y); + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + const length = Math.sqrt(x * x + y * y); + if (scale) lengths[i] = length; - spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + spaces[++i] = ((lengthSpacing ? setupLength + spacing : spacing) * length) / setupLength; } } } else { - for (let i = 1; i < spacesCount; i++) - spaces[i] = spacing; + for (let i = 1; i < spacesCount; i++) spaces[i] = spacing; } - let positions = this.computeWorldPositions(attachment, spacesCount, tangents, - data.positionMode == PositionMode.Percent, spacingMode == SpacingMode.Percent); - let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; + const positions = this.computeWorldPositions( + attachment, + spacesCount, + tangents, + data.positionMode == PositionMode.Percent, + spacingMode == SpacingMode.Percent + ); + let boneX = positions[0]; + let boneY = positions[1]; + let offsetRotation = data.offsetRotation; let tip = false; - if (offsetRotation == 0) - tip = rotateMode == RotateMode.Chain; + + if (offsetRotation == 0) tip = rotateMode == RotateMode.Chain; else { tip = false; - let p = this.target.bone.matrix; + const p = this.target.bone.matrix; + offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.degRad : -MathUtils.degRad; } for (let i = 0, p = 3; i < boneCount; i++, p += 3) { - let bone = bones[i]; - let mat = bone.matrix; + const bone = bones[i]; + const mat = bone.matrix; + mat.tx += (boneX - mat.tx) * translateMix; mat.ty += (boneY - mat.ty) * translateMix; - let x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; + const x = positions[p]; + const y = positions[p + 1]; + const dx = x - boneX; + const dy = y - boneY; + if (scale) { - let length = lengths[i]; + const length = lengths[i]; + if (length != 0) { - let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; + const s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; + mat.a *= s; mat.b *= s; } @@ -105,26 +137,32 @@ export class PathConstraint implements Constraint { boneX = x; boneY = y; if (rotate) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d, r = 0, cos = 0, sin = 0; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let r = 0; + let cos = 0; + let sin = 0; + if (tangents) - r = positions[p - 1]; - else if (spaces[i + 1] == 0) - r = positions[p + 2]; - else - r = Math.atan2(dy, dx); + if (tangents) r = positions[p - 1]; + else if (spaces[i + 1] == 0) r = positions[p + 2]; + else r = Math.atan2(dy, dx); r -= Math.atan2(c, a); if (tip) { cos = Math.cos(r); sin = Math.sin(r); - let length = bone.data.length; + const length = bone.data.length; + boneX += (length * (cos * a - sin * c) - dx) * rotateMix; boneY += (length * (sin * a + cos * c) - dy) * rotateMix; } else { r += offsetRotation; } - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= rotateMix; cos = Math.cos(r); @@ -138,26 +176,31 @@ export class PathConstraint implements Constraint { } } - computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean, - percentSpacing: boolean) { - let target = this.target; + computeWorldPositions(path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean, percentSpacing: boolean) { + const target = this.target; let position = this.position; - let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array = null; - let closed = path.closed; - let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE; + const spaces = this.spaces; + const out = Utils.setArraySize(this.positions, spacesCount * 3 + 2); + let world: Array = null; + const closed = path.closed; + let verticesLength = path.worldVerticesLength; + let curveCount = verticesLength / 6; + let prevCurve = PathConstraint.NONE; if (!path.constantSpeed) { - let lengths = path.lengths; + const lengths = path.lengths; + curveCount -= closed ? 1 : 2; - let pathLength = lengths[curveCount]; + const pathLength = lengths[curveCount]; + if (percentPosition) position *= pathLength; if (percentSpacing) { - for (let i = 0; i < spacesCount; i++) - spaces[i] *= pathLength; + for (let i = 0; i < spacesCount; i++) spaces[i] *= pathLength; } world = Utils.setArraySize(this.world, 8); for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i]; + const space = spaces[i]; + position += space; let p = position; @@ -182,13 +225,14 @@ export class PathConstraint implements Constraint { } // Determine curve containing position. - for (;; curve++) { - let length = lengths[curve]; + for (; ; curve++) { + const length = lengths[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = lengths[curve - 1]; + const prev = lengths[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -198,12 +242,11 @@ export class PathConstraint implements Constraint { if (closed && curve == curveCount) { path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); path.computeWorldVertices(target, 0, 4, world, 4, 2); - } else - path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); + } else path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); } - this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, - tangents || (i > 0 && space == 0)); + this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0)); } + return out; } @@ -223,10 +266,25 @@ export class PathConstraint implements Constraint { } // Curve lengths. - let curves = Utils.setArraySize(this.curves, curveCount); + const curves = Utils.setArraySize(this.curves, curveCount); let pathLength = 0; - let x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; - let tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0; + let x1 = world[0]; + let y1 = world[1]; + let cx1 = 0; + let cy1 = 0; + let cx2 = 0; + let cy2 = 0; + let x2 = 0; + let y2 = 0; + let tmpx = 0; + let tmpy = 0; + let dddfx = 0; + let dddfy = 0; + let ddfx = 0; + let ddfy = 0; + let dfx = 0; + let dfy = 0; + for (let i = 0, w = 2; i < curveCount; i++, w += 6) { cx1 = world[w]; cy1 = world[w + 1]; @@ -260,14 +318,15 @@ export class PathConstraint implements Constraint { } if (percentPosition) position *= pathLength; if (percentSpacing) { - for (let i = 0; i < spacesCount; i++) - spaces[i] *= pathLength; + for (let i = 0; i < spacesCount; i++) spaces[i] *= pathLength; } - let segments = this.segments; + const segments = this.segments; let curveLength = 0; + for (let i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i]; + const space = spaces[i]; + position += space; let p = position; @@ -284,13 +343,14 @@ export class PathConstraint implements Constraint { } // Determine curve containing position. - for (;; curve++) { - let length = curves[curve]; + for (; ; curve++) { + const length = curves[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = curves[curve - 1]; + const prev = curves[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -300,6 +360,7 @@ export class PathConstraint implements Constraint { if (curve != prevCurve) { prevCurve = curve; let ii = curve * 6; + x1 = world[ii]; y1 = world[ii + 1]; cx1 = world[ii + 2]; @@ -339,48 +400,81 @@ export class PathConstraint implements Constraint { // Weight by segment length. p *= curveLength; - for (;; segment++) { - let length = segments[segment]; + for (; ; segment++) { + const length = segments[segment]; + if (p > length) continue; - if (segment == 0) - p /= length; + if (segment == 0) p /= length; else { - let prev = segments[segment - 1]; + const prev = segments[segment - 1]; + p = segment + (p - prev) / (length - prev); } break; } this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0)); } + return out; } - addBeforePosition (p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx); + addBeforePosition(p: number, temp: Array, i: number, out: Array, o: number) { + const x1 = temp[i]; + const y1 = temp[i + 1]; + const dx = temp[i + 2] - x1; + const dy = temp[i + 3] - y1; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } - addAfterPosition (p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx); + addAfterPosition(p: number, temp: Array, i: number, out: Array, o: number) { + const x1 = temp[i + 2]; + const y1 = temp[i + 3]; + const dx = x1 - temp[i]; + const dy = y1 - temp[i + 1]; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } - addCurvePosition (p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, - out: Array, o: number, tangents: boolean) { + addCurvePosition( + p: number, + x1: number, + y1: number, + cx1: number, + cy1: number, + cx2: number, + cy2: number, + x2: number, + y2: number, + out: Array, + o: number, + tangents: boolean + ) { if (p == 0 || isNaN(p)) p = 0.0001; - let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; - let ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; - let x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + const tt = p * p; + const ttt = tt * p; + const u = 1 - p; + const uu = u * u; + const uuu = uu * u; + const ut = u * p; + const ut3 = ut * 3; + const uut3 = u * ut3; + const utt3 = ut3 * p; + const x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt; + const y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + out[o] = x; out[o + 1] = y; if (tangents) out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); } - getOrder () { + getOrder() { return this.data.order; } } diff --git a/packages/runtime-3.7/src/core/PathConstraintData.ts b/packages/runtime-3.7/src/core/PathConstraintData.ts index 0f1ef4ae..060a47b3 100644 --- a/packages/runtime-3.7/src/core/PathConstraintData.ts +++ b/packages/runtime-3.7/src/core/PathConstraintData.ts @@ -1,6 +1,6 @@ -import type {SlotData} from "./SlotData"; -import type {BoneData} from "./BoneData"; -import { RotateMode, PositionMode, IPathConstraintData } from "@pixi-spine/base"; +import type { SlotData } from './SlotData'; +import type { BoneData } from './BoneData'; +import type { RotateMode, PositionMode, IPathConstraintData } from '@pixi-spine/base'; /** * @public @@ -28,5 +28,7 @@ export class PathConstraintData implements IPathConstraintData { * @public */ export enum SpacingMode { - Length, Fixed, Percent + Length, + Fixed, + Percent, } diff --git a/packages/runtime-3.7/src/core/Skeleton.ts b/packages/runtime-3.7/src/core/Skeleton.ts index f51435fb..138d5b2c 100644 --- a/packages/runtime-3.7/src/core/Skeleton.ts +++ b/packages/runtime-3.7/src/core/Skeleton.ts @@ -1,13 +1,13 @@ -import {Attachment, RegionAttachment, MeshAttachment, PathAttachment} from './attachments'; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Updatable} from "./Updatable"; -import {SkeletonData} from "./SkeletonData"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; -import {Skin} from "./Skin"; -import {Color, Utils, Vector2, ISkeleton} from "@pixi-spine/base"; +import { Attachment, RegionAttachment, MeshAttachment, PathAttachment } from './attachments'; +import { Bone } from './Bone'; +import { Slot } from './Slot'; +import type { Updatable } from './Updatable'; +import type { SkeletonData } from './SkeletonData'; +import { IkConstraint } from './IkConstraint'; +import { TransformConstraint } from './TransformConstraint'; +import { PathConstraint } from './PathConstraint'; +import type { Skin } from './Skin'; +import { Color, Utils, Vector2, ISkeleton } from '@pixi-spine/base'; /** * @public @@ -25,21 +25,24 @@ export class Skeleton implements ISkeleton { skin: Skin; color: Color; time = 0; - scaleX = 1; scaleY = 1; - x = 0; y = 0; + scaleX = 1; + scaleY = 1; + x = 0; + y = 0; - constructor (data: SkeletonData) { - if (data == null) throw new Error("data cannot be null."); + constructor(data: SkeletonData) { + if (data == null) throw new Error('data cannot be null.'); this.data = data; this.bones = new Array(); for (let i = 0; i < data.bones.length; i++) { - let boneData = data.bones[i]; + const boneData = data.bones[i]; let bone: Bone; - if (boneData.parent == null) - bone = new Bone(boneData, this, null); + + if (boneData.parent == null) bone = new Bone(boneData, this, null); else { - let parent = this.bones[boneData.parent.index]; + const parent = this.bones[boneData.parent.index]; + bone = new Bone(boneData, this, parent); parent.children.push(bone); } @@ -49,28 +52,32 @@ export class Skeleton implements ISkeleton { this.slots = new Array(); this.drawOrder = new Array(); for (let i = 0; i < data.slots.length; i++) { - let slotData = data.slots[i]; - let bone = this.bones[slotData.boneData.index]; - let slot = new Slot(slotData, bone); + const slotData = data.slots[i]; + const bone = this.bones[slotData.boneData.index]; + const slot = new Slot(slotData, bone); + this.slots.push(slot); this.drawOrder.push(slot); } this.ikConstraints = new Array(); for (let i = 0; i < data.ikConstraints.length; i++) { - let ikConstraintData = data.ikConstraints[i]; + const ikConstraintData = data.ikConstraints[i]; + this.ikConstraints.push(new IkConstraint(ikConstraintData, this)); } this.transformConstraints = new Array(); for (let i = 0; i < data.transformConstraints.length; i++) { - let transformConstraintData = data.transformConstraints[i]; + const transformConstraintData = data.transformConstraints[i]; + this.transformConstraints.push(new TransformConstraint(transformConstraintData, this)); } this.pathConstraints = new Array(); for (let i = 0; i < data.pathConstraints.length; i++) { - let pathConstraintData = data.pathConstraints[i]; + const pathConstraintData = data.pathConstraints[i]; + this.pathConstraints.push(new PathConstraint(pathConstraintData, this)); } @@ -78,61 +85,72 @@ export class Skeleton implements ISkeleton { this.updateCache(); } - updateCache () { - let updateCache = this._updateCache; + updateCache() { + const updateCache = this._updateCache; + updateCache.length = 0; this.updateCacheReset.length = 0; - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - bones[i].sorted = false; + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) bones[i].sorted = false; // IK first, lowest hierarchy depth first. - let ikConstraints = this.ikConstraints; - let transformConstraints = this.transformConstraints; - let pathConstraints = this.pathConstraints; - let ikCount = ikConstraints.length, transformCount = transformConstraints.length, pathCount = pathConstraints.length; - let constraintCount = ikCount + transformCount + pathCount; - - outer: - for (let i = 0; i < constraintCount; i++) { - for (let ii = 0; ii < ikCount; ii++) { - let constraint = ikConstraints[ii]; - if (constraint.data.order == i) { - this.sortIkConstraint(constraint); - continue outer; - } + const ikConstraints = this.ikConstraints; + const transformConstraints = this.transformConstraints; + const pathConstraints = this.pathConstraints; + const ikCount = ikConstraints.length; + const transformCount = transformConstraints.length; + const pathCount = pathConstraints.length; + const constraintCount = ikCount + transformCount + pathCount; + + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < constraintCount; i++) { + for (let ii = 0; ii < ikCount; ii++) { + const constraint = ikConstraints[ii]; + + if (constraint.data.order == i) { + this.sortIkConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < transformCount; ii++) { - let constraint = transformConstraints[ii]; - if (constraint.data.order == i) { - this.sortTransformConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < transformCount; ii++) { + const constraint = transformConstraints[ii]; + + if (constraint.data.order == i) { + this.sortTransformConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < pathCount; ii++) { - let constraint = pathConstraints[ii]; - if (constraint.data.order == i) { - this.sortPathConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < pathCount; ii++) { + const constraint = pathConstraints[ii]; + + if (constraint.data.order == i) { + this.sortPathConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } } + } - for (let i = 0, n = bones.length; i < n; i++) - this.sortBone(bones[i]); + for (let i = 0, n = bones.length; i < n; i++) this.sortBone(bones[i]); } - sortIkConstraint (constraint: IkConstraint) { - let target = constraint.target; + sortIkConstraint(constraint: IkConstraint) { + const target = constraint.target; + this.sortBone(target); - let constrained = constraint.bones; - let parent = constrained[0]; + const constrained = constraint.bones; + const parent = constrained[0]; + this.sortBone(parent); if (constrained.length > 1) { - let child = constrained[constrained.length - 1]; + const child = constrained[constrained.length - 1]; + if (!(this._updateCache.indexOf(child) > -1)) this.updateCacheReset.push(child); } @@ -142,40 +160,40 @@ export class Skeleton implements ISkeleton { constrained[constrained.length - 1].sorted = true; } - sortPathConstraint (constraint: PathConstraint) { - let slot = constraint.target; - let slotIndex = slot.data.index; - let slotBone = slot.bone; + sortPathConstraint(constraint: PathConstraint) { + const slot = constraint.target; + const slotIndex = slot.data.index; + const slotBone = slot.bone; + if (this.skin != null) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone); - if (this.data.defaultSkin != null && this.data.defaultSkin != this.skin) - this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); - for (let i = 0, n = this.data.skins.length; i < n; i++) - this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + if (this.data.defaultSkin != null && this.data.defaultSkin != this.skin) this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); + for (let i = 0, n = this.data.skins.length; i < n; i++) this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + + const attachment = slot.getAttachment(); - let attachment = slot.getAttachment(); if (attachment instanceof PathAttachment) this.sortPathConstraintAttachmentWith(attachment, slotBone); - let constrained = constraint.bones; - let boneCount = constrained.length; - for (let i = 0; i < boneCount; i++) - this.sortBone(constrained[i]); + const constrained = constraint.bones; + const boneCount = constrained.length; + + for (let i = 0; i < boneCount; i++) this.sortBone(constrained[i]); this._updateCache.push(constraint); - for (let i = 0; i < boneCount; i++) - this.sortReset(constrained[i].children); - for (let i = 0; i < boneCount; i++) - constrained[i].sorted = true; + for (let i = 0; i < boneCount; i++) this.sortReset(constrained[i].children); + for (let i = 0; i < boneCount; i++) constrained[i].sorted = true; } - sortTransformConstraint (constraint: TransformConstraint) { + sortTransformConstraint(constraint: TransformConstraint) { this.sortBone(constraint.target); - let constrained = constraint.bones; - let boneCount = constrained.length; + const constrained = constraint.bones; + const boneCount = constrained.length; + if (constraint.data.local) { for (let i = 0; i < boneCount; i++) { - let child = constrained[i]; + const child = constrained[i]; + this.sortBone(child.parent); if (!(this._updateCache.indexOf(child) > -1)) this.updateCacheReset.push(child); } @@ -187,59 +205,65 @@ export class Skeleton implements ISkeleton { this._updateCache.push(constraint); - for (let ii = 0; ii < boneCount; ii++) - this.sortReset(constrained[ii].children); - for (let ii = 0; ii < boneCount; ii++) - constrained[ii].sorted = true; + for (let ii = 0; ii < boneCount; ii++) this.sortReset(constrained[ii].children); + for (let ii = 0; ii < boneCount; ii++) constrained[ii].sorted = true; } - sortPathConstraintAttachment (skin: Skin, slotIndex: number, slotBone: Bone) { - let attachments = skin.attachments[slotIndex]; + sortPathConstraintAttachment(skin: Skin, slotIndex: number, slotBone: Bone) { + const attachments = skin.attachments[slotIndex]; + if (!attachments) return; - for (let key in attachments) { + for (const key in attachments) { this.sortPathConstraintAttachmentWith(attachments[key], slotBone); } } - sortPathConstraintAttachmentWith (attachment: Attachment, slotBone: Bone) { + sortPathConstraintAttachmentWith(attachment: Attachment, slotBone: Bone) { if (!(attachment instanceof PathAttachment)) return; - let pathBones = (attachment).bones; - if (pathBones == null) - this.sortBone(slotBone); + const pathBones = (attachment).bones; + + if (pathBones == null) this.sortBone(slotBone); else { - let bones = this.bones; + const bones = this.bones; let i = 0; + while (i < pathBones.length) { - let boneCount = pathBones[i++]; + const boneCount = pathBones[i++]; + for (let n = i + boneCount; i < n; i++) { - let boneIndex = pathBones[i]; + const boneIndex = pathBones[i]; + this.sortBone(bones[boneIndex]); } } } } - sortBone (bone: Bone) { + sortBone(bone: Bone) { if (bone.sorted) return; - let parent = bone.parent; + const parent = bone.parent; + if (parent != null) this.sortBone(parent); bone.sorted = true; this._updateCache.push(bone); } - sortReset (bones: Array) { + sortReset(bones: Array) { for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (bone.sorted) this.sortReset(bone.children); bone.sorted = false; } } /** Updates the world transform for each bone and applies constraints. */ - updateWorldTransform () { - let updateCacheReset = this.updateCacheReset; + updateWorldTransform() { + const updateCacheReset = this.updateCacheReset; + for (let i = 0, n = updateCacheReset.length; i < n; i++) { - let bone = updateCacheReset[i] as Bone; + const bone = updateCacheReset[i] as Bone; + bone.ax = bone.x; bone.ay = bone.y; bone.arotation = bone.rotation; @@ -249,44 +273,50 @@ export class Skeleton implements ISkeleton { bone.ashearY = bone.shearY; bone.appliedValid = true; } - let updateCache = this._updateCache; - for (let i = 0, n = updateCache.length; i < n; i++) - updateCache[i].update(); + const updateCache = this._updateCache; + + for (let i = 0, n = updateCache.length; i < n; i++) updateCache[i].update(); } /** Sets the bones, constraints, and slots to their setup pose values. */ - setToSetupPose () { + setToSetupPose() { this.setBonesToSetupPose(); this.setSlotsToSetupPose(); } /** Sets the bones and constraints to their setup pose values. */ - setBonesToSetupPose () { - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - bones[i].setToSetupPose(); + setBonesToSetupPose() { + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) bones[i].setToSetupPose(); + + const ikConstraints = this.ikConstraints; - let ikConstraints = this.ikConstraints; for (let i = 0, n = ikConstraints.length; i < n; i++) { - let constraint = ikConstraints[i]; + const constraint = ikConstraints[i]; + constraint.bendDirection = constraint.data.bendDirection; constraint.mix = constraint.data.mix; } - let transformConstraints = this.transformConstraints; + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; - let data = constraint.data; + const constraint = transformConstraints[i]; + const data = constraint.data; + constraint.rotateMix = data.rotateMix; constraint.translateMix = data.translateMix; constraint.scaleMix = data.scaleMix; constraint.shearMix = data.shearMix; } - let pathConstraints = this.pathConstraints; + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; - let data = constraint.data; + const constraint = pathConstraints[i]; + const data = constraint.data; + constraint.position = data.position; constraint.spacing = data.spacing; constraint.rotateMix = data.rotateMix; @@ -294,64 +324,74 @@ export class Skeleton implements ISkeleton { } } - setSlotsToSetupPose () { - let slots = this.slots; + setSlotsToSetupPose() { + const slots = this.slots; + Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length); - for (let i = 0, n = slots.length; i < n; i++) - slots[i].setToSetupPose(); + for (let i = 0, n = slots.length; i < n; i++) slots[i].setToSetupPose(); } /** @return May return null. */ - getRootBone () { + getRootBone() { if (this.bones.length == 0) return null; + return this.bones[0]; } /** @return May be null. */ - findBone (boneName: string) { - if (boneName == null) throw new Error("boneName cannot be null."); - let bones = this.bones; + findBone(boneName: string) { + if (boneName == null) throw new Error('boneName cannot be null.'); + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (bone.data.name == boneName) return bone; } + return null; } /** @return -1 if the bone was not found. */ - findBoneIndex (boneName: string) { - if (boneName == null) throw new Error("boneName cannot be null."); - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - if (bones[i].data.name == boneName) return i; + findBoneIndex(boneName: string) { + if (boneName == null) throw new Error('boneName cannot be null.'); + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) if (bones[i].data.name == boneName) return i; + return -1; } /** @return May be null. */ - findSlot (slotName: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; + findSlot(slotName: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) return slot; } + return null; } /** @return -1 if the bone was not found. */ - findSlotIndex (slotName: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; - for (let i = 0, n = slots.length; i < n; i++) - if (slots[i].data.name == slotName) return i; + findSlotIndex(slotName: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + + for (let i = 0, n = slots.length; i < n; i++) if (slots[i].data.name == slotName) return i; + return -1; } /** Sets a skin by name. * @see #setSkin(Skin) */ - setSkinByName (skinName: string) { - let skin = this.data.findSkin(skinName); - if (skin == null) throw new Error("Skin not found: " + skinName); + setSkinByName(skinName: string) { + const skin = this.data.findSkin(skinName); + + if (skin == null) throw new Error(`Skin not found: ${skinName}`); this.setSkin(skin); } @@ -359,17 +399,19 @@ export class Skeleton implements ISkeleton { * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was no * old skin, each slot's setup mode attachment is attached from the new skin. * @param newSkin May be null. */ - setSkin (newSkin: Skin | null) { + setSkin(newSkin: Skin | null) { if (newSkin != null) { - if (this.skin != null) - newSkin.attachAll(this, this.skin); + if (this.skin != null) newSkin.attachAll(this, this.skin); else { - let slots = this.slots; + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; - let name = slot.data.attachmentName; + const slot = slots[i]; + const name = slot.data.attachmentName; + if (name != null) { - let attachment: Attachment = newSkin.getAttachment(i, name); + const attachment: Attachment = newSkin.getAttachment(i, name); + if (attachment != null) slot.setAttachment(attachment); } } @@ -379,71 +421,85 @@ export class Skeleton implements ISkeleton { } /** @return May be null. */ - getAttachmentByName (slotName: string, attachmentName: string): Attachment { + getAttachmentByName(slotName: string, attachmentName: string): Attachment { return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName); } /** @return May be null. */ - getAttachment (slotIndex: number, attachmentName: string): Attachment { - if (attachmentName == null) throw new Error("attachmentName cannot be null."); + getAttachment(slotIndex: number, attachmentName: string): Attachment { + if (attachmentName == null) throw new Error('attachmentName cannot be null.'); if (this.skin != null) { - let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName); + const attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment != null) return attachment; } if (this.data.defaultSkin != null) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; } /** @param attachmentName May be null. */ - setAttachment (slotName: string, attachmentName: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; + setAttachment(slotName: string, attachmentName: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) { let attachment: Attachment = null; + if (attachmentName != null) { attachment = this.getAttachment(i, attachmentName); - if (attachment == null) - throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName); + if (attachment == null) throw new Error(`Attachment not found: ${attachmentName}, for slot: ${slotName}`); } slot.setAttachment(attachment); + return; } } - throw new Error("Slot not found: " + slotName); + throw new Error(`Slot not found: ${slotName}`); } /** @return May be null. */ - findIkConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let ikConstraints = this.ikConstraints; + findIkConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const ikConstraints = this.ikConstraints; + for (let i = 0, n = ikConstraints.length; i < n; i++) { - let ikConstraint = ikConstraints[i]; + const ikConstraint = ikConstraints[i]; + if (ikConstraint.data.name == constraintName) return ikConstraint; } + return null; } /** @return May be null. */ - findTransformConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let transformConstraints = this.transformConstraints; + findTransformConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; + const constraint = transformConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } /** @return May be null. */ - findPathConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let pathConstraints = this.pathConstraints; + findPathConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; + const constraint = pathConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } @@ -451,29 +507,37 @@ export class Skeleton implements ISkeleton { * @param offset The distance from the skeleton origin to the bottom left corner of the AABB. * @param size The width and height of the AABB. * @param temp Working memory */ - getBounds (offset: Vector2, size: Vector2, temp: Array) { - if (offset == null) throw new Error("offset cannot be null."); - if (size == null) throw new Error("size cannot be null."); - let drawOrder = this.drawOrder; - let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; + getBounds(offset: Vector2, size: Vector2, temp: Array) { + if (offset == null) throw new Error('offset cannot be null.'); + if (size == null) throw new Error('size cannot be null.'); + const drawOrder = this.drawOrder; + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + let maxX = Number.NEGATIVE_INFINITY; + let maxY = Number.NEGATIVE_INFINITY; + for (let i = 0, n = drawOrder.length; i < n; i++) { - let slot = drawOrder[i]; + const slot = drawOrder[i]; let verticesLength = 0; let vertices: ArrayLike = null; - let attachment = slot.getAttachment(); + const attachment = slot.getAttachment(); + if (attachment instanceof RegionAttachment) { verticesLength = 8; vertices = Utils.setArraySize(temp, verticesLength, 0); (attachment).computeWorldVertices(slot.bone, vertices, 0, 2); } else if (attachment instanceof MeshAttachment) { - let mesh = (attachment); + const mesh = attachment; + verticesLength = mesh.worldVerticesLength; vertices = Utils.setArraySize(temp, verticesLength, 0); mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); } if (vertices != null) { for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) { - let x = vertices[ii], y = vertices[ii + 1]; + const x = vertices[ii]; + const y = vertices[ii + 1]; + minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); @@ -485,7 +549,7 @@ export class Skeleton implements ISkeleton { size.set(maxX - minX, maxY - minY); } - update (delta: number) { + update(delta: number) { this.time += delta; } @@ -496,7 +560,7 @@ export class Skeleton implements ISkeleton { set flipX(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleX = value ? 1.0 : -1.0; } @@ -508,10 +572,10 @@ export class Skeleton implements ISkeleton { set flipY(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleY = value ? 1.0 : -1.0; } - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; } diff --git a/packages/runtime-3.7/src/core/SkeletonBounds.ts b/packages/runtime-3.7/src/core/SkeletonBounds.ts index cce4ffe0..126d81aa 100644 --- a/packages/runtime-3.7/src/core/SkeletonBounds.ts +++ b/packages/runtime-3.7/src/core/SkeletonBounds.ts @@ -1,8 +1,8 @@ -import {BoundingBoxAttachment} from "./attachments"; -import {SkeletonBoundsBase} from "@pixi-spine/base"; +import type { BoundingBoxAttachment } from './attachments'; +import { SkeletonBoundsBase } from '@pixi-spine/base'; /** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are * provided along with convenience methods for doing hit detection. * @public * */ - export class SkeletonBounds extends SkeletonBoundsBase{}; \ No newline at end of file +export class SkeletonBounds extends SkeletonBoundsBase {} diff --git a/packages/runtime-3.7/src/core/SkeletonData.ts b/packages/runtime-3.7/src/core/SkeletonData.ts index 956c05f7..bbb8dfc5 100644 --- a/packages/runtime-3.7/src/core/SkeletonData.ts +++ b/packages/runtime-3.7/src/core/SkeletonData.ts @@ -1,12 +1,12 @@ -import type {ISkeletonData} from "@pixi-spine/base"; -import type {Animation} from "./Animation"; -import {BoneData} from "./BoneData"; -import {SlotData} from "./SlotData"; -import {Skin} from "./Skin"; -import {EventData} from "./EventData"; -import {IkConstraintData} from "./IkConstraintData"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {PathConstraintData} from "./PathConstraintData"; +import type { ISkeletonData } from '@pixi-spine/base'; +import type { Animation } from './Animation'; +import type { BoneData } from './BoneData'; +import type { SlotData } from './SlotData'; +import type { Skin } from './Skin'; +import type { EventData } from './EventData'; +import type { IkConstraintData } from './IkConstraintData'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { PathConstraintData } from './PathConstraintData'; /** * @public @@ -32,106 +32,133 @@ export class SkeletonData implements ISkeletonData(); - constructor (attachmentLoader: AttachmentLoader) { + constructor(attachmentLoader: AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - readSkeletonData (json: string | any): SkeletonData { - let scale = this.scale; - let skeletonData = new SkeletonData(); - let root = typeof(json) === "string" ? JSON.parse(json) : json; + readSkeletonData(json: string | any): SkeletonData { + const scale = this.scale; + const skeletonData = new SkeletonData(); + const root = typeof json === 'string' ? JSON.parse(json) : json; // Skeleton - let skeletonMap = root.skeleton; + const skeletonMap = root.skeleton; + if (skeletonMap != null) { skeletonData.hash = skeletonMap.hash; skeletonData.version = skeletonMap.spine; @@ -58,24 +63,26 @@ export class SkeletonJson { // Bones if (root.bones) { for (let i = 0; i < root.bones.length; i++) { - let boneMap = root.bones[i]; + const boneMap = root.bones[i]; let parent: BoneData = null; - let parentName: string = this.getValue(boneMap, "parent", null); + const parentName: string = this.getValue(boneMap, 'parent', null); + if (parentName != null) { parent = skeletonData.findBone(parentName); - if (parent == null) throw new Error("Parent bone not found: " + parentName); + if (parent == null) throw new Error(`Parent bone not found: ${parentName}`); } - let data = new BoneData(skeletonData.bones.length, boneMap.name, parent); - data.length = this.getValue(boneMap, "length", 0) * scale; - data.x = this.getValue(boneMap, "x", 0) * scale; - data.y = this.getValue(boneMap, "y", 0) * scale; - data.rotation = this.getValue(boneMap, "rotation", 0); - data.scaleX = this.getValue(boneMap, "scaleX", 1); - data.scaleY = this.getValue(boneMap, "scaleY", 1); - data.shearX = this.getValue(boneMap, "shearX", 0); - data.shearY = this.getValue(boneMap, "shearY", 0); - data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, "transform", "normal")); + const data = new BoneData(skeletonData.bones.length, boneMap.name, parent); + + data.length = this.getValue(boneMap, 'length', 0) * scale; + data.x = this.getValue(boneMap, 'x', 0) * scale; + data.y = this.getValue(boneMap, 'y', 0) * scale; + data.rotation = this.getValue(boneMap, 'rotation', 0); + data.scaleX = this.getValue(boneMap, 'scaleX', 1); + data.scaleY = this.getValue(boneMap, 'scaleY', 1); + data.shearX = this.getValue(boneMap, 'shearX', 0); + data.shearY = this.getValue(boneMap, 'shearY', 0); + data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, 'transform', 'normal')); skeletonData.bones.push(data); } @@ -84,24 +91,27 @@ export class SkeletonJson { // Slots. if (root.slots) { for (let i = 0; i < root.slots.length; i++) { - let slotMap = root.slots[i]; - let slotName: string = slotMap.name; - let boneName: string = slotMap.bone; - let boneData = skeletonData.findBone(boneName); - if (boneData == null) throw new Error("Slot bone not found: " + boneName); - let data = new SlotData(skeletonData.slots.length, slotName, boneData); - - let color: string = this.getValue(slotMap, "color", null); + const slotMap = root.slots[i]; + const slotName: string = slotMap.name; + const boneName: string = slotMap.bone; + const boneData = skeletonData.findBone(boneName); + + if (boneData == null) throw new Error(`Slot bone not found: ${boneName}`); + const data = new SlotData(skeletonData.slots.length, slotName, boneData); + + const color: string = this.getValue(slotMap, 'color', null); + if (color != null) data.color.setFromString(color); - let dark: string = this.getValue(slotMap, "dark", null); + const dark: string = this.getValue(slotMap, 'dark', null); + if (dark != null) { data.darkColor = new Color(1, 1, 1, 1); data.darkColor.setFromString(dark); } - data.attachmentName = this.getValue(slotMap, "attachment", null); - data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, "blend", "normal")); + data.attachmentName = this.getValue(slotMap, 'attachment', null); + data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, 'blend', 'normal')); skeletonData.slots.push(data); } } @@ -109,23 +119,26 @@ export class SkeletonJson { // IK constraints if (root.ik) { for (let i = 0; i < root.ik.length; i++) { - let constraintMap = root.ik[i]; - let data = new IkConstraintData(constraintMap.name); - data.order = this.getValue(constraintMap, "order", 0); + const constraintMap = root.ik[i]; + const data = new IkConstraintData(constraintMap.name); + + data.order = this.getValue(constraintMap, 'order', 0); for (let j = 0; j < constraintMap.bones.length; j++) { - let boneName = constraintMap.bones[j]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("IK bone not found: " + boneName); + const boneName = constraintMap.bones[j]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`IK bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findBone(targetName); - if (data.target == null) throw new Error("IK target bone not found: " + targetName); + if (data.target == null) throw new Error(`IK target bone not found: ${targetName}`); - data.bendDirection = this.getValue(constraintMap, "bendPositive", true) ? 1 : -1; - data.mix = this.getValue(constraintMap, "mix", 1); + data.bendDirection = this.getValue(constraintMap, 'bendPositive', true) ? 1 : -1; + data.mix = this.getValue(constraintMap, 'mix', 1); skeletonData.ikConstraints.push(data); } @@ -134,34 +147,37 @@ export class SkeletonJson { // Transform constraints. if (root.transform) { for (let i = 0; i < root.transform.length; i++) { - let constraintMap = root.transform[i]; - let data = new TransformConstraintData(constraintMap.name); - data.order = this.getValue(constraintMap, "order", 0); + const constraintMap = root.transform[i]; + const data = new TransformConstraintData(constraintMap.name); + + data.order = this.getValue(constraintMap, 'order', 0); for (let j = 0; j < constraintMap.bones.length; j++) { - let boneName = constraintMap.bones[j]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); + const boneName = constraintMap.bones[j]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`Transform constraint bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findBone(targetName); - if (data.target == null) throw new Error("Transform constraint target bone not found: " + targetName); - - data.local = this.getValue(constraintMap, "local", false); - data.relative = this.getValue(constraintMap, "relative", false); - data.offsetRotation = this.getValue(constraintMap, "rotation", 0); - data.offsetX = this.getValue(constraintMap, "x", 0) * scale; - data.offsetY = this.getValue(constraintMap, "y", 0) * scale; - data.offsetScaleX = this.getValue(constraintMap, "scaleX", 0); - data.offsetScaleY = this.getValue(constraintMap, "scaleY", 0); - data.offsetShearY = this.getValue(constraintMap, "shearY", 0); - - data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); - data.translateMix = this.getValue(constraintMap, "translateMix", 1); - data.scaleMix = this.getValue(constraintMap, "scaleMix", 1); - data.shearMix = this.getValue(constraintMap, "shearMix", 1); + if (data.target == null) throw new Error(`Transform constraint target bone not found: ${targetName}`); + + data.local = this.getValue(constraintMap, 'local', false); + data.relative = this.getValue(constraintMap, 'relative', false); + data.offsetRotation = this.getValue(constraintMap, 'rotation', 0); + data.offsetX = this.getValue(constraintMap, 'x', 0) * scale; + data.offsetY = this.getValue(constraintMap, 'y', 0) * scale; + data.offsetScaleX = this.getValue(constraintMap, 'scaleX', 0); + data.offsetScaleY = this.getValue(constraintMap, 'scaleY', 0); + data.offsetShearY = this.getValue(constraintMap, 'shearY', 0); + + data.rotateMix = this.getValue(constraintMap, 'rotateMix', 1); + data.translateMix = this.getValue(constraintMap, 'translateMix', 1); + data.scaleMix = this.getValue(constraintMap, 'scaleMix', 1); + data.shearMix = this.getValue(constraintMap, 'shearMix', 1); skeletonData.transformConstraints.push(data); } @@ -170,31 +186,34 @@ export class SkeletonJson { // Path constraints. if (root.path) { for (let i = 0; i < root.path.length; i++) { - let constraintMap = root.path[i]; - let data = new PathConstraintData(constraintMap.name); - data.order = this.getValue(constraintMap, "order", 0); + const constraintMap = root.path[i]; + const data = new PathConstraintData(constraintMap.name); + + data.order = this.getValue(constraintMap, 'order', 0); for (let j = 0; j < constraintMap.bones.length; j++) { - let boneName = constraintMap.bones[j]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); + const boneName = constraintMap.bones[j]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`Transform constraint bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findSlot(targetName); - if (data.target == null) throw new Error("Path target slot not found: " + targetName); + if (data.target == null) throw new Error(`Path target slot not found: ${targetName}`); - data.positionMode = SkeletonJson.positionModeFromString(this.getValue(constraintMap, "positionMode", "percent")); - data.spacingMode = SkeletonJson.spacingModeFromString(this.getValue(constraintMap, "spacingMode", "length")); - data.rotateMode = SkeletonJson.rotateModeFromString(this.getValue(constraintMap, "rotateMode", "tangent")); - data.offsetRotation = this.getValue(constraintMap, "rotation", 0); - data.position = this.getValue(constraintMap, "position", 0); + data.positionMode = SkeletonJson.positionModeFromString(this.getValue(constraintMap, 'positionMode', 'percent')); + data.spacingMode = SkeletonJson.spacingModeFromString(this.getValue(constraintMap, 'spacingMode', 'length')); + data.rotateMode = SkeletonJson.rotateModeFromString(this.getValue(constraintMap, 'rotateMode', 'tangent')); + data.offsetRotation = this.getValue(constraintMap, 'rotation', 0); + data.position = this.getValue(constraintMap, 'position', 0); if (data.positionMode == PositionMode.Fixed) data.position *= scale; - data.spacing = this.getValue(constraintMap, "spacing", 0); + data.spacing = this.getValue(constraintMap, 'spacing', 0); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale; - data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); - data.translateMix = this.getValue(constraintMap, "translateMix", 1); + data.rotateMix = this.getValue(constraintMap, 'rotateMix', 1); + data.translateMix = this.getValue(constraintMap, 'translateMix', 1); skeletonData.pathConstraints.push(data); } @@ -202,47 +221,54 @@ export class SkeletonJson { // Skins. if (root.skins) { - for (let skinName in root.skins) { - let skinMap = root.skins[skinName] - let skin = new Skin(skinName); - for (let slotName in skinMap) { - let slotIndex = skeletonData.findSlotIndex(slotName); - if (slotIndex == -1) throw new Error("Slot not found: " + slotName); - let slotMap = skinMap[slotName]; - for (let entryName in slotMap) { - let attachment = this.readAttachment(slotMap[entryName], skin, slotIndex, entryName, skeletonData); + for (const skinName in root.skins) { + const skinMap = root.skins[skinName]; + const skin = new Skin(skinName); + + for (const slotName in skinMap) { + const slotIndex = skeletonData.findSlotIndex(slotName); + + if (slotIndex == -1) throw new Error(`Slot not found: ${slotName}`); + const slotMap = skinMap[slotName]; + + for (const entryName in slotMap) { + const attachment = this.readAttachment(slotMap[entryName], skin, slotIndex, entryName, skeletonData); + if (attachment != null) skin.addAttachment(slotIndex, entryName, attachment); } } skeletonData.skins.push(skin); - if (skin.name == "default") skeletonData.defaultSkin = skin; + if (skin.name == 'default') skeletonData.defaultSkin = skin; } } // Linked meshes. for (let i = 0, n = this.linkedMeshes.length; i < n; i++) { - let linkedMesh = this.linkedMeshes[i]; - let skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); - if (skin == null) throw new Error("Skin not found: " + linkedMesh.skin); - let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); - if (parent == null) throw new Error("Parent mesh not found: " + linkedMesh.parent); - linkedMesh.mesh.setParentMesh( parent); - //linkedMesh.mesh.updateUVs(); + const linkedMesh = this.linkedMeshes[i]; + const skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + + if (skin == null) throw new Error(`Skin not found: ${linkedMesh.skin}`); + const parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + + if (parent == null) throw new Error(`Parent mesh not found: ${linkedMesh.parent}`); + linkedMesh.mesh.setParentMesh(parent); + // linkedMesh.mesh.updateUVs(); } this.linkedMeshes.length = 0; // Events. if (root.events) { - for (let eventName in root.events) { - let eventMap = root.events[eventName]; - let data = new EventData(eventName); - data.intValue = this.getValue(eventMap, "int", 0); - data.floatValue = this.getValue(eventMap, "float", 0); - data.stringValue = this.getValue(eventMap, "string", ""); - data.audioPath = this.getValue(eventMap, "audio", null); + for (const eventName in root.events) { + const eventMap = root.events[eventName]; + const data = new EventData(eventName); + + data.intValue = this.getValue(eventMap, 'int', 0); + data.floatValue = this.getValue(eventMap, 'float', 0); + data.stringValue = this.getValue(eventMap, 'string', ''); + data.audioPath = this.getValue(eventMap, 'audio', null); if (data.audioPath != null) { - data.volume = this.getValue(eventMap, "volume", 1); - data.balance = this.getValue(eventMap, "balance", 0); + data.volume = this.getValue(eventMap, 'volume', 1); + data.balance = this.getValue(eventMap, 'balance', 0); } skeletonData.events.push(data); } @@ -250,8 +276,9 @@ export class SkeletonJson { // Animations. if (root.animations) { - for (let animationName in root.animations) { - let animationMap = root.animations[animationName]; + for (const animationName in root.animations) { + const animationMap = root.animations[animationName]; + this.readAnimation(animationMap, animationName, skeletonData); } } @@ -259,134 +286,165 @@ export class SkeletonJson { return skeletonData; } - readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment { - let scale = this.scale; - name = this.getValue(map, "name", name); + readAttachment(map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment { + const scale = this.scale; + + name = this.getValue(map, 'name', name); - let type = this.getValue(map, "type", "region"); + const type = this.getValue(map, 'type', 'region'); switch (type) { - case "region": { - let path = this.getValue(map, "path", name); - let region = this.attachmentLoader.newRegionAttachment(skin, name, path); + case 'region': { + const path = this.getValue(map, 'path', name); + const region = this.attachmentLoader.newRegionAttachment(skin, name, path); + if (region == null) return null; region.path = path; - region.x = this.getValue(map, "x", 0) * scale; - region.y = this.getValue(map, "y", 0) * scale; - region.scaleX = this.getValue(map, "scaleX", 1); - region.scaleY = this.getValue(map, "scaleY", 1); - region.rotation = this.getValue(map, "rotation", 0); + region.x = this.getValue(map, 'x', 0) * scale; + region.y = this.getValue(map, 'y', 0) * scale; + region.scaleX = this.getValue(map, 'scaleX', 1); + region.scaleY = this.getValue(map, 'scaleY', 1); + region.rotation = this.getValue(map, 'rotation', 0); region.width = map.width * scale; region.height = map.height * scale; - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) region.color.setFromString(color); - //region.updateOffset(); + // region.updateOffset(); return region; } - case "boundingbox": { - let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + case 'boundingbox': { + const box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + if (box == null) return null; this.readVertices(map, box, map.vertexCount << 1); - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) box.color.setFromString(color); + return box; } - case "mesh": - case "linkedmesh": { - let path = this.getValue(map, "path", name); - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + case 'mesh': + case 'linkedmesh': { + const path = this.getValue(map, 'path', name); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (mesh == null) return null; mesh.path = path; - let color = this.getValue(map, "color", null); + const color = this.getValue(map, 'color', null); + if (color != null) mesh.color.setFromString(color); - let parent: string = this.getValue(map, "parent", null); + const parent: string = this.getValue(map, 'parent', null); + if (parent != null) { - mesh.inheritDeform = this.getValue(map, "deform", true); - this.linkedMeshes.push(new LinkedMesh(mesh, this.getValue(map, "skin", null), slotIndex, parent)); + mesh.inheritDeform = this.getValue(map, 'deform', true); + this.linkedMeshes.push(new LinkedMesh(mesh, this.getValue(map, 'skin', null), slotIndex, parent)); + return mesh; } - let uvs: Array = map.uvs; + const uvs: Array = map.uvs; + this.readVertices(map, mesh, uvs.length); mesh.triangles = map.triangles; mesh.regionUVs = new Float32Array(uvs); - //mesh.updateUVs(); + // mesh.updateUVs(); + + mesh.hullLength = this.getValue(map, 'hull', 0) * 2; - mesh.hullLength = this.getValue(map, "hull", 0) * 2; return mesh; } - case "path": { - let path = this.attachmentLoader.newPathAttachment(skin, name); + case 'path': { + const path = this.attachmentLoader.newPathAttachment(skin, name); + if (path == null) return null; - path.closed = this.getValue(map, "closed", false); - path.constantSpeed = this.getValue(map, "constantSpeed", true); + path.closed = this.getValue(map, 'closed', false); + path.constantSpeed = this.getValue(map, 'constantSpeed', true); + + const vertexCount = map.vertexCount; - let vertexCount = map.vertexCount; this.readVertices(map, path, vertexCount << 1); - let lengths: Array = Utils.newArray(vertexCount / 3, 0); - for (let i = 0; i < map.lengths.length; i++) - lengths[i] = map.lengths[i] * scale; + const lengths: Array = Utils.newArray(vertexCount / 3, 0); + + for (let i = 0; i < map.lengths.length; i++) lengths[i] = map.lengths[i] * scale; path.lengths = lengths; - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) path.color.setFromString(color); + return path; } - case "point": { - let point = this.attachmentLoader.newPointAttachment(skin, name); + case 'point': { + const point = this.attachmentLoader.newPointAttachment(skin, name); + if (point == null) return null; - point.x = this.getValue(map, "x", 0) * scale; - point.y = this.getValue(map, "y", 0) * scale; - point.rotation = this.getValue(map, "rotation", 0); + point.x = this.getValue(map, 'x', 0) * scale; + point.y = this.getValue(map, 'y', 0) * scale; + point.rotation = this.getValue(map, 'rotation', 0); + + const color = this.getValue(map, 'color', null); - let color = this.getValue(map, "color", null); if (color != null) point.color.setFromString(color); + return point; } - case "clipping": { - let clip = this.attachmentLoader.newClippingAttachment(skin, name); + case 'clipping': { + const clip = this.attachmentLoader.newClippingAttachment(skin, name); + if (clip == null) return null; - let end = this.getValue(map, "end", null); + const end = this.getValue(map, 'end', null); + if (end != null) { - let slot = skeletonData.findSlot(end); - if (slot == null) throw new Error("Clipping end slot not found: " + end); + const slot = skeletonData.findSlot(end); + + if (slot == null) throw new Error(`Clipping end slot not found: ${end}`); clip.endSlot = slot; } - let vertexCount = map.vertexCount; + const vertexCount = map.vertexCount; + this.readVertices(map, clip, vertexCount << 1); - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) clip.color.setFromString(color); + return clip; } } + return null; } - readVertices (map: any, attachment: VertexAttachment, verticesLength: number) { - let scale = this.scale; + readVertices(map: any, attachment: VertexAttachment, verticesLength: number) { + const scale = this.scale; + attachment.worldVerticesLength = verticesLength; - let vertices: Array = map.vertices; + const vertices: Array = map.vertices; + if (verticesLength == vertices.length) { - let scaledVertices = Utils.toFloatArray(vertices); + const scaledVertices = Utils.toFloatArray(vertices); + if (scale != 1) { - for (let i = 0, n = vertices.length; i < n; i++) - scaledVertices[i] *= scale; + for (let i = 0, n = vertices.length; i < n; i++) scaledVertices[i] *= scale; } attachment.vertices = scaledVertices; + return; } - let weights = new Array(); - let bones = new Array(); - for (let i = 0, n = vertices.length; i < n;) { - let boneCount = vertices[i++]; + const weights = new Array(); + const bones = new Array(); + + for (let i = 0, n = vertices.length; i < n; ) { + const boneCount = vertices[i++]; + bones.push(boneCount); for (let nn = i + boneCount * 4; i < nn; i += 4) { bones.push(vertices[i]); @@ -399,55 +457,65 @@ export class SkeletonJson { attachment.vertices = Utils.toFloatArray(weights); } - readAnimation (map: any, name: string, skeletonData: SkeletonData) { - let scale = this.scale; - let timelines = new Array(); + readAnimation(map: any, name: string, skeletonData: SkeletonData) { + const scale = this.scale; + const timelines = new Array(); let duration = 0; // Slot timelines. if (map.slots) { - for (let slotName in map.slots) { - let slotMap = map.slots[slotName]; - let slotIndex = skeletonData.findSlotIndex(slotName); - if (slotIndex == -1) throw new Error("Slot not found: " + slotName); - for (let timelineName in slotMap) { - let timelineMap = slotMap[timelineName]; - if (timelineName == "attachment") { - let timeline = new AttachmentTimeline(timelineMap.length); + for (const slotName in map.slots) { + const slotMap = map.slots[slotName]; + const slotIndex = skeletonData.findSlotIndex(slotName); + + if (slotIndex == -1) throw new Error(`Slot not found: ${slotName}`); + for (const timelineName in slotMap) { + const timelineMap = slotMap[timelineName]; + + if (timelineName == 'attachment') { + const timeline = new AttachmentTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; + const valueMap = timelineMap[i]; + timeline.setFrame(frameIndex++, valueMap.time, valueMap.name); } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); - } else if (timelineName == "color") { - let timeline = new ColorTimeline(timelineMap.length); + } else if (timelineName == 'color') { + const timeline = new ColorTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - let color = new Color(); - color.setFromString(valueMap.color || "ffffffff"); + const valueMap = timelineMap[i]; + const color = new Color(); + + color.setFromString(valueMap.color || 'ffffffff'); timeline.setFrame(frameIndex, valueMap.time, color.r, color.g, color.b, color.a); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * ColorTimeline.ENTRIES]); + } else if (timelineName == 'twoColor') { + const timeline = new TwoColorTimeline(timelineMap.length); - } else if (timelineName == "twoColor") { - let timeline = new TwoColorTimeline(timelineMap.length); timeline.slotIndex = slotIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - let light = new Color(); - let dark = new Color(); + const valueMap = timelineMap[i]; + const light = new Color(); + const dark = new Color(); + light.setFromString(valueMap.light); dark.setFromString(valueMap.dark); timeline.setFrame(frameIndex, valueMap.time, light.r, light.g, light.b, light.a, dark.r, dark.g, dark.b); @@ -456,42 +524,43 @@ export class SkeletonJson { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TwoColorTimeline.ENTRIES]); - - } else - throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } else throw new Error(`Invalid timeline type for a slot: ${timelineName} (${slotName})`); } } } // Bone timelines. if (map.bones) { - for (let boneName in map.bones) { - let boneMap = map.bones[boneName]; - let boneIndex = skeletonData.findBoneIndex(boneName); - if (boneIndex == -1) throw new Error("Bone not found: " + boneName); - for (let timelineName in boneMap) { - let timelineMap = boneMap[timelineName]; - if (timelineName === "rotate") { - let timeline = new RotateTimeline(timelineMap.length); + for (const boneName in map.bones) { + const boneMap = map.bones[boneName]; + const boneIndex = skeletonData.findBoneIndex(boneName); + + if (boneIndex == -1) throw new Error(`Bone not found: ${boneName}`); + for (const timelineName in boneMap) { + const timelineMap = boneMap[timelineName]; + + if (timelineName === 'rotate') { + const timeline = new RotateTimeline(timelineMap.length); + timeline.boneIndex = boneIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; + const valueMap = timelineMap[i]; + timeline.setFrame(frameIndex, valueMap.time, valueMap.angle); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * RotateTimeline.ENTRIES]); - - } else if (timelineName === "translate" || timelineName === "scale" || timelineName === "shear") { + } else if (timelineName === 'translate' || timelineName === 'scale' || timelineName === 'shear') { let timeline: TranslateTimeline = null; let timelineScale = 1; - if (timelineName === "scale") - timeline = new ScaleTimeline(timelineMap.length); - else if (timelineName === "shear") - timeline = new ShearTimeline(timelineMap.length); + + if (timelineName === 'scale') timeline = new ScaleTimeline(timelineMap.length); + else if (timelineName === 'shear') timeline = new ShearTimeline(timelineMap.length); else { timeline = new TranslateTimeline(timelineMap.length); timelineScale = scale; @@ -499,34 +568,44 @@ export class SkeletonJson { timeline.boneIndex = boneIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - let x = this.getValue(valueMap, "x", 0), y = this.getValue(valueMap, "y", 0); + const valueMap = timelineMap[i]; + const x = this.getValue(valueMap, 'x', 0); + const y = this.getValue(valueMap, 'y', 0); + timeline.setFrame(frameIndex, valueMap.time, x * timelineScale, y * timelineScale); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TranslateTimeline.ENTRIES]); - - } else - throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } else throw new Error(`Invalid timeline type for a bone: ${timelineName} (${boneName})`); } } } // IK constraint timelines. if (map.ik) { - for (let constraintName in map.ik) { - let constraintMap = map.ik[constraintName]; - let constraint = skeletonData.findIkConstraint(constraintName); - let timeline = new IkConstraintTimeline(constraintMap.length); + for (const constraintName in map.ik) { + const constraintMap = map.ik[constraintName]; + const constraint = skeletonData.findIkConstraint(constraintName); + const timeline = new IkConstraintTimeline(constraintMap.length); + timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(constraint); let frameIndex = 0; + for (let i = 0; i < constraintMap.length; i++) { - let valueMap = constraintMap[i]; - timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "mix", 1), - this.getValue(valueMap, "bendPositive", true) ? 1 : -1, this.getValue(valueMap, "compress", false), this.getValue(valueMap, "stretch", false)); + const valueMap = constraintMap[i]; + + timeline.setFrame( + frameIndex, + valueMap.time, + this.getValue(valueMap, 'mix', 1), + this.getValue(valueMap, 'bendPositive', true) ? 1 : -1, + this.getValue(valueMap, 'compress', false), + this.getValue(valueMap, 'stretch', false) + ); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -537,38 +616,50 @@ export class SkeletonJson { // Transform constraint timelines. if (map.transform) { - for (let constraintName in map.transform) { - let constraintMap = map.transform[constraintName]; - let constraint = skeletonData.findTransformConstraint(constraintName); - let timeline = new TransformConstraintTimeline(constraintMap.length); + for (const constraintName in map.transform) { + const constraintMap = map.transform[constraintName]; + const constraint = skeletonData.findTransformConstraint(constraintName); + const timeline = new TransformConstraintTimeline(constraintMap.length); + timeline.transformConstraintIndex = skeletonData.transformConstraints.indexOf(constraint); let frameIndex = 0; + for (let i = 0; i < constraintMap.length; i++) { - let valueMap = constraintMap[i]; - timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1), - this.getValue(valueMap, "translateMix", 1), this.getValue(valueMap, "scaleMix", 1), this.getValue(valueMap, "shearMix", 1)); + const valueMap = constraintMap[i]; + + timeline.setFrame( + frameIndex, + valueMap.time, + this.getValue(valueMap, 'rotateMix', 1), + this.getValue(valueMap, 'translateMix', 1), + this.getValue(valueMap, 'scaleMix', 1), + this.getValue(valueMap, 'shearMix', 1) + ); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); - duration = Math.max(duration, - timeline.frames[(timeline.getFrameCount() - 1) * TransformConstraintTimeline.ENTRIES]); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TransformConstraintTimeline.ENTRIES]); } } // Path constraint timelines. if (map.paths) { - for (let constraintName in map.paths) { - let constraintMap = map.paths[constraintName]; - let index = skeletonData.findPathConstraintIndex(constraintName); - if (index == -1) throw new Error("Path constraint not found: " + constraintName); - let data = skeletonData.pathConstraints[index]; - for (let timelineName in constraintMap) { - let timelineMap = constraintMap[timelineName]; - if (timelineName === "position" || timelineName === "spacing") { + for (const constraintName in map.paths) { + const constraintMap = map.paths[constraintName]; + const index = skeletonData.findPathConstraintIndex(constraintName); + + if (index == -1) throw new Error(`Path constraint not found: ${constraintName}`); + const data = skeletonData.pathConstraints[index]; + + for (const timelineName in constraintMap) { + const timelineMap = constraintMap[timelineName]; + + if (timelineName === 'position' || timelineName === 'spacing') { let timeline: PathConstraintPositionTimeline = null; let timelineScale = 1; - if (timelineName === "spacing") { + + if (timelineName === 'spacing') { timeline = new PathConstraintSpacingTimeline(timelineMap.length); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale; } else { @@ -577,29 +668,31 @@ export class SkeletonJson { } timeline.pathConstraintIndex = index; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; + const valueMap = timelineMap[i]; + timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, timelineName, 0) * timelineScale); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); - duration = Math.max(duration, - timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintPositionTimeline.ENTRIES]); - } else if (timelineName === "mix") { - let timeline = new PathConstraintMixTimeline(timelineMap.length); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintPositionTimeline.ENTRIES]); + } else if (timelineName === 'mix') { + const timeline = new PathConstraintMixTimeline(timelineMap.length); + timeline.pathConstraintIndex = index; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1), - this.getValue(valueMap, "translateMix", 1)); + const valueMap = timelineMap[i]; + + timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, 'rotateMix', 1), this.getValue(valueMap, 'translateMix', 1)); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); - duration = Math.max(duration, - timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintMixTimeline.ENTRIES]); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintMixTimeline.ENTRIES]); } } } @@ -607,49 +700,54 @@ export class SkeletonJson { // Deform timelines. if (map.deform) { - for (let deformName in map.deform) { - let deformMap = map.deform[deformName]; - let skin = skeletonData.findSkin(deformName); + for (const deformName in map.deform) { + const deformMap = map.deform[deformName]; + const skin = skeletonData.findSkin(deformName); + if (skin == null) { if (settings.FAIL_ON_NON_EXISTING_SKIN) { - throw new Error("Skin not found: " + deformName); + throw new Error(`Skin not found: ${deformName}`); } else { continue; } - } for (let slotName in deformMap) { - let slotMap = deformMap[slotName]; - let slotIndex = skeletonData.findSlotIndex(slotName); - if (slotIndex == -1) throw new Error("Slot not found: " + slotMap.name); - for (let timelineName in slotMap) { - let timelineMap = slotMap[timelineName]; - let attachment = skin.getAttachment(slotIndex, timelineName); - if (attachment == null) throw new Error("Deform attachment not found: " + timelineMap.name); - let weighted = attachment.bones != null; - let vertices = attachment.vertices; - let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; - - let timeline = new DeformTimeline(timelineMap.length); + } + for (const slotName in deformMap) { + const slotMap = deformMap[slotName]; + const slotIndex = skeletonData.findSlotIndex(slotName); + + if (slotIndex == -1) throw new Error(`Slot not found: ${slotMap.name}`); + for (const timelineName in slotMap) { + const timelineMap = slotMap[timelineName]; + const attachment = skin.getAttachment(slotIndex, timelineName); + + if (attachment == null) throw new Error(`Deform attachment not found: ${timelineMap.name}`); + const weighted = attachment.bones != null; + const vertices = attachment.vertices; + const deformLength = weighted ? (vertices.length / 3) * 2 : vertices.length; + + const timeline = new DeformTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; timeline.attachment = attachment; let frameIndex = 0; + for (let j = 0; j < timelineMap.length; j++) { - let valueMap = timelineMap[j]; + const valueMap = timelineMap[j]; let deform: ArrayLike; - let verticesValue: Array = this.getValue(valueMap, "vertices", null); - if (verticesValue == null) - deform = weighted ? Utils.newFloatArray(deformLength) : vertices; + const verticesValue: Array = this.getValue(valueMap, 'vertices', null); + + if (verticesValue == null) deform = weighted ? Utils.newFloatArray(deformLength) : vertices; else { deform = Utils.newFloatArray(deformLength); - let start = this.getValue(valueMap, "offset", 0); + const start = this.getValue(valueMap, 'offset', 0); + Utils.arrayCopy(verticesValue, 0, deform, start, verticesValue.length); if (scale != 1) { - for (let i = start, n = i + verticesValue.length; i < n; i++) - deform[i] *= scale; + for (let i = start, n = i + verticesValue.length; i < n; i++) deform[i] *= scale; } if (!weighted) { - for (let i = 0; i < deformLength; i++) - deform[i] += vertices[i]; + for (let i = 0; i < deformLength; i++) deform[i] += vertices[i]; } } @@ -666,35 +764,38 @@ export class SkeletonJson { // Draw order timeline. let drawOrderNode = map.drawOrder; + if (drawOrderNode == null) drawOrderNode = map.draworder; if (drawOrderNode != null) { - let timeline = new DrawOrderTimeline(drawOrderNode.length); - let slotCount = skeletonData.slots.length; + const timeline = new DrawOrderTimeline(drawOrderNode.length); + const slotCount = skeletonData.slots.length; let frameIndex = 0; + for (let j = 0; j < drawOrderNode.length; j++) { - let drawOrderMap = drawOrderNode[j]; + const drawOrderMap = drawOrderNode[j]; let drawOrder: Array = null; - let offsets = this.getValue(drawOrderMap, "offsets", null); + const offsets = this.getValue(drawOrderMap, 'offsets', null); + if (offsets != null) { drawOrder = Utils.newArray(slotCount, -1); - let unchanged = Utils.newArray(slotCount - offsets.length, 0); - let originalIndex = 0, unchangedIndex = 0; + const unchanged = Utils.newArray(slotCount - offsets.length, 0); + let originalIndex = 0; + let unchangedIndex = 0; + for (let i = 0; i < offsets.length; i++) { - let offsetMap = offsets[i]; - let slotIndex = skeletonData.findSlotIndex(offsetMap.slot); - if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap.slot); + const offsetMap = offsets[i]; + const slotIndex = skeletonData.findSlotIndex(offsetMap.slot); + + if (slotIndex == -1) throw new Error(`Slot not found: ${offsetMap.slot}`); // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + offsetMap.offset] = originalIndex++; } // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. - for (let i = slotCount - 1; i >= 0; i--) - if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; + for (let i = slotCount - 1; i >= 0; i--) if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; } timeline.setFrame(frameIndex++, drawOrderMap.time, drawOrder); } @@ -704,19 +805,22 @@ export class SkeletonJson { // Event timeline. if (map.events) { - let timeline = new EventTimeline(map.events.length); + const timeline = new EventTimeline(map.events.length); let frameIndex = 0; + for (let i = 0; i < map.events.length; i++) { - let eventMap = map.events[i]; - let eventData = skeletonData.findEvent(eventMap.name); - if (eventData == null) throw new Error("Event not found: " + eventMap.name); - let event = new Event(Utils.toSinglePrecision(eventMap.time), eventData); - event.intValue = this.getValue(eventMap, "int", eventData.intValue); - event.floatValue = this.getValue(eventMap, "float", eventData.floatValue); - event.stringValue = this.getValue(eventMap, "string", eventData.stringValue); + const eventMap = map.events[i]; + const eventData = skeletonData.findEvent(eventMap.name); + + if (eventData == null) throw new Error(`Event not found: ${eventMap.name}`); + const event = new Event(Utils.toSinglePrecision(eventMap.time), eventData); + + event.intValue = this.getValue(eventMap, 'int', eventData.intValue); + event.floatValue = this.getValue(eventMap, 'float', eventData.floatValue); + event.stringValue = this.getValue(eventMap, 'string', eventData.stringValue); if (event.data.audioPath != null) { - event.volume = this.getValue(eventMap, "volume", 1); - event.balance = this.getValue(eventMap, "balance", 0); + event.volume = this.getValue(eventMap, 'volume', 1); + event.balance = this.getValue(eventMap, 'balance', 0); } timeline.setFrame(frameIndex++, event); } @@ -725,75 +829,76 @@ export class SkeletonJson { } if (isNaN(duration)) { - throw new Error("Error while parsing animation, duration is NaN"); + throw new Error('Error while parsing animation, duration is NaN'); } skeletonData.animations.push(new Animation(name, timelines, duration)); } - readCurve (map: any, timeline: CurveTimeline, frameIndex: number) { + readCurve(map: any, timeline: CurveTimeline, frameIndex: number) { if (!map.curve) return; - if (map.curve === "stepped") - timeline.setStepped(frameIndex); + if (map.curve === 'stepped') timeline.setStepped(frameIndex); else if (Object.prototype.toString.call(map.curve) === '[object Array]') { - let curve: Array = map.curve; + const curve: Array = map.curve; + timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); } } - getValue (map: any, prop: string, defaultValue: any) { + getValue(map: any, prop: string, defaultValue: any) { return map[prop] !== undefined ? map[prop] : defaultValue; } - static blendModeFromString (str: string) { + static blendModeFromString(str: string) { str = str.toLowerCase(); - if (str == "normal") return BLEND_MODES.NORMAL; - if (str == "additive") return BLEND_MODES.ADD; - if (str == "multiply") return BLEND_MODES.MULTIPLY; - if (str == "screen") return BLEND_MODES.SCREEN; + if (str == 'normal') return BLEND_MODES.NORMAL; + if (str == 'additive') return BLEND_MODES.ADD; + if (str == 'multiply') return BLEND_MODES.MULTIPLY; + if (str == 'screen') return BLEND_MODES.SCREEN; throw new Error(`Unknown blend mode: ${str}`); } - static positionModeFromString (str: string) { + static positionModeFromString(str: string) { str = str.toLowerCase(); - if (str == "fixed") return PositionMode.Fixed; - if (str == "percent") return PositionMode.Percent; + if (str == 'fixed') return PositionMode.Fixed; + if (str == 'percent') return PositionMode.Percent; throw new Error(`Unknown position mode: ${str}`); } - static spacingModeFromString (str: string) { + static spacingModeFromString(str: string) { str = str.toLowerCase(); - if (str == "length") return SpacingMode.Length; - if (str == "fixed") return SpacingMode.Fixed; - if (str == "percent") return SpacingMode.Percent; + if (str == 'length') return SpacingMode.Length; + if (str == 'fixed') return SpacingMode.Fixed; + if (str == 'percent') return SpacingMode.Percent; throw new Error(`Unknown position mode: ${str}`); } - static rotateModeFromString (str: string) { + static rotateModeFromString(str: string) { str = str.toLowerCase(); - if (str == "tangent") return RotateMode.Tangent; - if (str == "chain") return RotateMode.Chain; - if (str == "chainscale") return RotateMode.ChainScale; + if (str == 'tangent') return RotateMode.Tangent; + if (str == 'chain') return RotateMode.Chain; + if (str == 'chainscale') return RotateMode.ChainScale; throw new Error(`Unknown rotate mode: ${str}`); } static transformModeFromString(str: string) { str = str.toLowerCase(); - if (str == "normal") return TransformMode.Normal; - if (str == "onlytranslation") return TransformMode.OnlyTranslation; - if (str == "norotationorreflection") return TransformMode.NoRotationOrReflection; - if (str == "noscale") return TransformMode.NoScale; - if (str == "noscaleorreflection") return TransformMode.NoScaleOrReflection; + if (str == 'normal') return TransformMode.Normal; + if (str == 'onlytranslation') return TransformMode.OnlyTranslation; + if (str == 'norotationorreflection') return TransformMode.NoRotationOrReflection; + if (str == 'noscale') return TransformMode.NoScale; + if (str == 'noscaleorreflection') return TransformMode.NoScaleOrReflection; throw new Error(`Unknown transform mode: ${str}`); } } class LinkedMesh { - parent: string; skin: string; + parent: string; + skin: string; slotIndex: number; mesh: MeshAttachment; - constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string) { + constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; diff --git a/packages/runtime-3.7/src/core/Skin.ts b/packages/runtime-3.7/src/core/Skin.ts index 76f1dff2..a458fce8 100644 --- a/packages/runtime-3.7/src/core/Skin.ts +++ b/packages/runtime-3.7/src/core/Skin.ts @@ -1,7 +1,7 @@ -import {Attachment} from './attachments'; -import {Skeleton} from "./Skeleton"; +import type { Attachment } from './attachments'; +import type { Skeleton } from './Skeleton'; -import type {Map, ISkin} from '@pixi-spine/base'; +import type { Map, ISkin } from '@pixi-spine/base'; /** * @public @@ -11,13 +11,14 @@ export class Skin implements ISkin { attachments = new Array>(); constructor(name: string) { - if (name == null) throw new Error("name cannot be null."); + if (name == null) throw new Error('name cannot be null.'); this.name = name; } addAttachment(slotIndex: number, name: string, attachment: Attachment) { - if (attachment == null) throw new Error("attachment cannot be null."); - let attachments = this.attachments; + if (attachment == null) throw new Error('attachment cannot be null.'); + const attachments = this.attachments; + if (slotIndex >= attachments.length) attachments.length = slotIndex + 1; if (!attachments[slotIndex]) attachments[slotIndex] = {}; attachments[slotIndex][name] = attachment; @@ -25,22 +26,28 @@ export class Skin implements ISkin { /** @return May be null. */ getAttachment(slotIndex: number, name: string): Attachment { - let dictionary = this.attachments[slotIndex]; + const dictionary = this.attachments[slotIndex]; + return dictionary ? dictionary[name] : null; } /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ attachAll(skeleton: Skeleton, oldSkin: Skin) { let slotIndex = 0; + for (let i = 0; i < skeleton.slots.length; i++) { - let slot = skeleton.slots[i]; - let slotAttachment = slot.getAttachment(); + const slot = skeleton.slots[i]; + const slotAttachment = slot.getAttachment(); + if (slotAttachment && slotIndex < oldSkin.attachments.length) { - let dictionary = oldSkin.attachments[slotIndex]; - for (let key in dictionary) { - let skinAttachment: Attachment = dictionary[key]; + const dictionary = oldSkin.attachments[slotIndex]; + + for (const key in dictionary) { + const skinAttachment: Attachment = dictionary[key]; + if (slotAttachment == skinAttachment) { - let attachment = this.getAttachment(slotIndex, key); + const attachment = this.getAttachment(slotIndex, key); + if (attachment != null) slot.setAttachment(attachment); break; } diff --git a/packages/runtime-3.7/src/core/Slot.ts b/packages/runtime-3.7/src/core/Slot.ts index fd78d15c..5344cafb 100644 --- a/packages/runtime-3.7/src/core/Slot.ts +++ b/packages/runtime-3.7/src/core/Slot.ts @@ -1,8 +1,8 @@ -import {Color, ISlot} from '@pixi-spine/base'; +import { Color, ISlot } from '@pixi-spine/base'; -import type {Attachment} from './attachments/Attachment'; -import type {Bone} from './Bone'; -import type {SlotData} from './SlotData'; +import type { Attachment } from './attachments/Attachment'; +import type { Bone } from './Bone'; +import type { SlotData } from './SlotData'; /** * @public @@ -10,7 +10,7 @@ import type {SlotData} from './SlotData'; export class Slot implements ISlot { blendMode: number; - //this is canon + // this is canon data: SlotData; bone: Bone; color: Color; @@ -19,9 +19,9 @@ export class Slot implements ISlot { private attachmentTime: number; attachmentVertices = new Array(); - constructor (data: SlotData, bone: Bone) { - if (data == null) throw new Error("data cannot be null."); - if (bone == null) throw new Error("bone cannot be null."); + constructor(data: SlotData, bone: Bone) { + if (data == null) throw new Error('data cannot be null.'); + if (bone == null) throw new Error('bone cannot be null.'); this.data = data; this.bone = bone; this.color = new Color(); @@ -32,33 +32,32 @@ export class Slot implements ISlot { } /** @return May be null. */ - getAttachment (): Attachment { + getAttachment(): Attachment { return this.attachment; } /** Sets the attachment and if it changed, resets {@link #getAttachmentTime()} and clears {@link #getAttachmentVertices()}. * @param attachment May be null. */ - setAttachment (attachment: Attachment) { + setAttachment(attachment: Attachment) { if (this.attachment == attachment) return; this.attachment = attachment; this.attachmentTime = this.bone.skeleton.time; this.attachmentVertices.length = 0; } - setAttachmentTime (time: number) { + setAttachmentTime(time: number) { this.attachmentTime = this.bone.skeleton.time - time; } /** Returns the time since the attachment was set. */ - getAttachmentTime (): number { + getAttachmentTime(): number { return this.bone.skeleton.time - this.attachmentTime; } - setToSetupPose () { + setToSetupPose() { this.color.setFromColor(this.data.color); if (this.darkColor != null) this.darkColor.setFromColor(this.data.darkColor); - if (this.data.attachmentName == null) - this.attachment = null; + if (this.data.attachmentName == null) this.attachment = null; else { this.attachment = null; this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName)); diff --git a/packages/runtime-3.7/src/core/SlotData.ts b/packages/runtime-3.7/src/core/SlotData.ts index 9a40db86..4d5ca4ef 100644 --- a/packages/runtime-3.7/src/core/SlotData.ts +++ b/packages/runtime-3.7/src/core/SlotData.ts @@ -1,8 +1,8 @@ -import {Color} from '@pixi-spine/base'; +import { Color } from '@pixi-spine/base'; -import type {ISlotData} from '@pixi-spine/base'; -import type {BLEND_MODES} from '@pixi/constants'; -import {BoneData} from "./BoneData"; +import type { ISlotData } from '@pixi-spine/base'; +import type { BLEND_MODES } from '@pixi/constants'; +import type { BoneData } from './BoneData'; /** * @public @@ -16,10 +16,10 @@ export class SlotData implements ISlotData { attachmentName: string; blendMode: BLEND_MODES; - constructor (index: number, name: string, boneData: BoneData) { - if (index < 0) throw new Error("index must be >= 0."); - if (name == null) throw new Error("name cannot be null."); - if (boneData == null) throw new Error("boneData cannot be null."); + constructor(index: number, name: string, boneData: BoneData) { + if (index < 0) throw new Error('index must be >= 0.'); + if (name == null) throw new Error('name cannot be null.'); + if (boneData == null) throw new Error('boneData cannot be null.'); this.index = index; this.name = name; this.boneData = boneData; diff --git a/packages/runtime-3.7/src/core/TransformConstraint.ts b/packages/runtime-3.7/src/core/TransformConstraint.ts index 5e9545e3..e2227185 100644 --- a/packages/runtime-3.7/src/core/TransformConstraint.ts +++ b/packages/runtime-3.7/src/core/TransformConstraint.ts @@ -1,8 +1,8 @@ -import {Constraint} from "./Constraint"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {Bone} from "./Bone"; -import {MathUtils, Vector2} from "@pixi-spine/base"; -import {Skeleton} from "./Skeleton"; +import type { Constraint } from './Constraint'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { Bone } from './Bone'; +import { MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Skeleton } from './Skeleton'; /** * @public @@ -18,16 +18,15 @@ export class TransformConstraint implements Constraint { temp = new Vector2(); constructor(data: TransformConstraintData, skeleton: Skeleton) { - if (data == null) throw new Error("data cannot be null."); - if (skeleton == null) throw new Error("skeleton cannot be null."); + if (data == null) throw new Error('data cannot be null.'); + if (skeleton == null) throw new Error('skeleton cannot be null.'); this.data = data; this.rotateMix = data.rotateMix; this.translateMix = data.translateMix; this.scaleMix = data.scaleMix; this.shearMix = data.shearMix; this.bones = new Array(); - for (let i = 0; i < data.bones.length; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0; i < data.bones.length; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findBone(data.target.name); } @@ -37,43 +36,46 @@ export class TransformConstraint implements Constraint { update() { if (this.data.local) { - if (this.data.relative) - this.applyRelativeLocal(); - else - this.applyAbsoluteLocal(); - - } else { - if (this.data.relative) - this.applyRelativeWorld(); - else - this.applyAbsoluteWorld(); - } + if (this.data.relative) this.applyRelativeLocal(); + else this.applyAbsoluteLocal(); + } else if (this.data.relative) this.applyRelativeWorld(); + else this.applyAbsoluteWorld(); } applyAbsoluteWorld() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; - let targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect; - let offsetShearY = this.data.offsetShearY * degRadReflect; - let bones = this.bones; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + const targetMat = target.matrix; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; let modified = false; - let mat = bone.matrix; + const mat = bone.matrix; if (rotateMix != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) - r += MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; r *= rotateMix; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -82,7 +84,8 @@ export class TransformConstraint implements Constraint { } if (translateMix != 0) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += (temp.x - mat.tx) * translateMix; mat.ty += (temp.y - mat.ty) * translateMix; @@ -92,6 +95,7 @@ export class TransformConstraint implements Constraint { if (scaleMix > 0) { let s = Math.sqrt(mat.a * mat.a + mat.b * mat.b); let ts = Math.sqrt(ta * ta + tc * tc); + if (s > 0.00001) s = (s + (ts - s + this.data.offsetScaleX) * scaleMix) / s; mat.a *= s; mat.b *= s; @@ -104,15 +108,16 @@ export class TransformConstraint implements Constraint { } if (shearMix > 0) { - let b = mat.c, d = mat.d; - let by = Math.atan2(d, b); + const b = mat.c; + const d = mat.d; + const by = Math.atan2(d, b); let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(mat.b, mat.a)); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) - r += MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; r = by + (r + offsetShearY) * shearMix; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; modified = true; @@ -123,28 +128,39 @@ export class TransformConstraint implements Constraint { } applyRelativeWorld() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; - let targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect, - offsetShearY = this.data.offsetShearY * degRadReflect; - let bones = this.bones; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + const targetMat = target.matrix; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; let modified = false; - let mat = bone.matrix; + const mat = bone.matrix; if (rotateMix != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; else if (r < -MathUtils.PI) r += MathUtils.PI2; r *= rotateMix; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -153,7 +169,8 @@ export class TransformConstraint implements Constraint { } if (translateMix != 0) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += temp.x * translateMix; mat.ty += temp.y * translateMix; @@ -162,6 +179,7 @@ export class TransformConstraint implements Constraint { if (scaleMix > 0) { let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * scaleMix + 1; + mat.a *= s; mat.b *= s; s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * scaleMix + 1; @@ -172,12 +190,15 @@ export class TransformConstraint implements Constraint { if (shearMix > 0) { let r = Math.atan2(td, tb) - Math.atan2(tc, ta); - if (r > MathUtils.PI) - r -= MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; else if (r < -MathUtils.PI) r += MathUtils.PI2; - let b = mat.c, d = mat.d; + const b = mat.c; + const d = mat.d; + r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * shearMix; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; modified = true; @@ -188,37 +209,50 @@ export class TransformConstraint implements Constraint { } applyAbsoluteLocal() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + if (!target.appliedValid) target.updateAppliedTransform(); - let bones = this.bones; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (!bone.appliedValid) bone.updateAppliedTransform(); let rotation = bone.arotation; + if (rotateMix != 0) { let r = target.arotation - rotation + this.data.offsetRotation; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; rotation += r * rotateMix; } - let x = bone.ax, y = bone.ay; + let x = bone.ax; + let y = bone.ay; + if (translateMix != 0) { x += (target.ax - x + this.data.offsetX) * translateMix; y += (target.ay - y + this.data.offsetY) * translateMix; } - let scaleX = bone.ascaleX, scaleY = bone.ascaleY; + let scaleX = bone.ascaleX; + let scaleY = bone.ascaleY; + if (scaleMix > 0) { if (scaleX > 0.00001) scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * scaleMix) / scaleX; if (scaleY > 0.00001) scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * scaleMix) / scaleY; } - let shearY = bone.ashearY; + const shearY = bone.ashearY; + if (shearMix > 0) { let r = target.ashearY - shearY + this.data.offsetShearY; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; bone.shearY += r * shearMix; } @@ -228,31 +262,42 @@ export class TransformConstraint implements Constraint { } applyRelativeLocal() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + if (!target.appliedValid) target.updateAppliedTransform(); - let bones = this.bones; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (!bone.appliedValid) bone.updateAppliedTransform(); let rotation = bone.arotation; + if (rotateMix != 0) rotation += (target.arotation + this.data.offsetRotation) * rotateMix; - let x = bone.ax, y = bone.ay; + let x = bone.ax; + let y = bone.ay; + if (translateMix != 0) { x += (target.ax + this.data.offsetX) * translateMix; y += (target.ay + this.data.offsetY) * translateMix; } - let scaleX = bone.ascaleX, scaleY = bone.ascaleY; + let scaleX = bone.ascaleX; + let scaleY = bone.ascaleY; + if (scaleMix > 0) { - if (scaleX > 0.00001) scaleX *= ((target.ascaleX - 1 + this.data.offsetScaleX) * scaleMix) + 1; - if (scaleY > 0.00001) scaleY *= ((target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix) + 1; + if (scaleX > 0.00001) scaleX *= (target.ascaleX - 1 + this.data.offsetScaleX) * scaleMix + 1; + if (scaleY > 0.00001) scaleY *= (target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix + 1; } let shearY = bone.ashearY; + if (shearMix > 0) shearY += (target.ashearY + this.data.offsetShearY) * shearMix; bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); diff --git a/packages/runtime-3.7/src/core/TransformConstraintData.ts b/packages/runtime-3.7/src/core/TransformConstraintData.ts index 4dbd9eba..4115564e 100644 --- a/packages/runtime-3.7/src/core/TransformConstraintData.ts +++ b/packages/runtime-3.7/src/core/TransformConstraintData.ts @@ -1,4 +1,4 @@ -import {BoneData} from './BoneData'; +import type { BoneData } from './BoneData'; /** * @public @@ -8,13 +8,21 @@ export class TransformConstraintData { order = 0; bones = new Array(); target: BoneData; - rotateMix = 0; translateMix = 0; scaleMix = 0; shearMix = 0; - offsetRotation = 0; offsetX = 0; offsetY = 0; offsetScaleX = 0; offsetScaleY = 0; offsetShearY = 0; + rotateMix = 0; + translateMix = 0; + scaleMix = 0; + shearMix = 0; + offsetRotation = 0; + offsetX = 0; + offsetY = 0; + offsetScaleX = 0; + offsetScaleY = 0; + offsetShearY = 0; relative = false; local = false; - constructor (name: string) { - if (name == null) throw new Error("name cannot be null."); + constructor(name: string) { + if (name == null) throw new Error('name cannot be null.'); this.name = name; } } diff --git a/packages/runtime-3.7/src/core/VertexEffect.ts b/packages/runtime-3.7/src/core/VertexEffect.ts index a3a9cc9f..98ef8cf1 100644 --- a/packages/runtime-3.7/src/core/VertexEffect.ts +++ b/packages/runtime-3.7/src/core/VertexEffect.ts @@ -1,5 +1,5 @@ -import type {Skeleton} from "./Skeleton"; -import type {Color, Vector2} from "@pixi-spine/base"; +import type { Skeleton } from './Skeleton'; +import type { Color, Vector2 } from '@pixi-spine/base'; /** * @public diff --git a/packages/runtime-3.7/src/core/attachments/Attachment.ts b/packages/runtime-3.7/src/core/attachments/Attachment.ts index 40c8ea0d..2a36eaaa 100644 --- a/packages/runtime-3.7/src/core/attachments/Attachment.ts +++ b/packages/runtime-3.7/src/core/attachments/Attachment.ts @@ -1,7 +1,6 @@ -import {AttachmentType} from '@pixi-spine/base'; -import type {IAttachment, ArrayLike} from '@pixi-spine/base'; +import type { IAttachment, ArrayLike, AttachmentType } from '@pixi-spine/base'; -import type {Slot} from '../Slot'; +import type { Slot } from '../Slot'; /** * @public @@ -11,7 +10,7 @@ export abstract class Attachment implements IAttachment { type: AttachmentType; constructor(name: string) { - if (name == null) throw new Error("name cannot be null."); + if (name == null) throw new Error('name cannot be null.'); this.name = name; } } @@ -42,38 +41,55 @@ export abstract class VertexAttachment extends Attachment { * @param offset The worldVertices index to begin writing values. */ computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: ArrayLike, offset: number, stride: number) { count = offset + (count >> 1) * stride; - let skeleton = slot.bone.skeleton; - let deformArray = slot.attachmentVertices; + const skeleton = slot.bone.skeleton; + const deformArray = slot.attachmentVertices; let vertices = this.vertices; - let bones = this.bones; + const bones = this.bones; + if (bones == null) { if (deformArray.length > 0) vertices = deformArray; - let mat = slot.bone.matrix; - let x = mat.tx; - let y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const mat = slot.bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + for (let v = start, w = offset; w < count; v += 2, w += stride) { - let vx = vertices[v], vy = vertices[v + 1]; + const vx = vertices[v]; + const vy = vertices[v + 1]; + worldVertices[w] = vx * a + vy * b + x; worldVertices[w + 1] = vx * c + vy * d + y; } + return; } - let v = 0, skip = 0; + let v = 0; + let skip = 0; + for (let i = 0; i < start; i += 2) { - let n = bones[v]; + const n = bones[v]; + v += n + 1; skip += n; } - let skeletonBones = skeleton.bones; + const skeletonBones = skeleton.bones; + if (deformArray.length == 0) { for (let w = offset, b = skip * 3; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b]; + const vy = vertices[b + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } @@ -81,15 +97,20 @@ export abstract class VertexAttachment extends Attachment { worldVertices[w + 1] = wy; } } else { - let deform = deformArray; + const deform = deformArray; + for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3, f += 2) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], - weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b] + deform[f]; + const vy = vertices[b + 1] + deform[f + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } diff --git a/packages/runtime-3.7/src/core/attachments/AttachmentLoader.ts b/packages/runtime-3.7/src/core/attachments/AttachmentLoader.ts index 522c2f37..21cb94e4 100644 --- a/packages/runtime-3.7/src/core/attachments/AttachmentLoader.ts +++ b/packages/runtime-3.7/src/core/attachments/AttachmentLoader.ts @@ -1,23 +1,23 @@ -import {Skin} from '../Skin'; -import type {RegionAttachment} from './RegionAttachment'; -import type {MeshAttachment} from './MeshAttachment'; -import type {BoundingBoxAttachment} from './BoundingBoxAttachment'; -import type {PathAttachment} from './PathAttachment'; -import type {PointAttachment} from './PointAttachment'; -import type {ClippingAttachment} from './ClippingAttachment'; +import type { Skin } from '../Skin'; +import type { RegionAttachment } from './RegionAttachment'; +import type { MeshAttachment } from './MeshAttachment'; +import type { BoundingBoxAttachment } from './BoundingBoxAttachment'; +import type { PathAttachment } from './PathAttachment'; +import type { PointAttachment } from './PointAttachment'; +import type { ClippingAttachment } from './ClippingAttachment'; /** * @public */ export interface AttachmentLoader { /** @return May be null to not load an attachment. */ - newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment; + newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment; /** @return May be null to not load an attachment. */ - newMeshAttachment (skin: Skin, name: string, path: string): MeshAttachment; + newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment; /** @return May be null to not load an attachment. */ - newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment; + newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment; /** @return May be null to not load an attachment */ newPathAttachment(skin: Skin, name: string): PathAttachment; diff --git a/packages/runtime-3.7/src/core/attachments/BoundingBoxAttachment.ts b/packages/runtime-3.7/src/core/attachments/BoundingBoxAttachment.ts index d2a42696..89eb1219 100644 --- a/packages/runtime-3.7/src/core/attachments/BoundingBoxAttachment.ts +++ b/packages/runtime-3.7/src/core/attachments/BoundingBoxAttachment.ts @@ -1,5 +1,5 @@ -import {VertexAttachment} from './Attachment'; -import {AttachmentType, Color} from '@pixi-spine/base'; +import { VertexAttachment } from './Attachment'; +import { AttachmentType, Color } from '@pixi-spine/base'; /** * @public diff --git a/packages/runtime-3.7/src/core/attachments/ClippingAttachment.ts b/packages/runtime-3.7/src/core/attachments/ClippingAttachment.ts index ddcca0b9..556adaad 100644 --- a/packages/runtime-3.7/src/core/attachments/ClippingAttachment.ts +++ b/packages/runtime-3.7/src/core/attachments/ClippingAttachment.ts @@ -1,6 +1,6 @@ -import {VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IClippingAttachment} from '@pixi-spine/base'; -import type {SlotData} from '../SlotData'; +import { VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IClippingAttachment } from '@pixi-spine/base'; +import type { SlotData } from '../SlotData'; /** * @public diff --git a/packages/runtime-3.7/src/core/attachments/MeshAttachment.ts b/packages/runtime-3.7/src/core/attachments/MeshAttachment.ts index 57adfe2f..c45291e9 100644 --- a/packages/runtime-3.7/src/core/attachments/MeshAttachment.ts +++ b/packages/runtime-3.7/src/core/attachments/MeshAttachment.ts @@ -1,5 +1,5 @@ -import {VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IMeshAttachment, TextureRegion} from '@pixi-spine/base'; +import { VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IMeshAttachment, TextureRegion } from '@pixi-spine/base'; /** * @public @@ -9,7 +9,8 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment region: TextureRegion; path: string; - regionUVs: Float32Array; uvs: ArrayLike; + regionUVs: Float32Array; + uvs: ArrayLike; triangles: Array; color = new Color(1, 1, 1, 1); hullLength: number; @@ -17,20 +18,20 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment inheritDeform = false; tempColor = new Color(0, 0, 0, 0); - constructor (name: string) { + constructor(name: string) { super(name); } - applyDeform (sourceAttachment: VertexAttachment): boolean { + applyDeform(sourceAttachment: VertexAttachment): boolean { return this == sourceAttachment || (this.inheritDeform && this.parentMesh == sourceAttachment); } - getParentMesh () { + getParentMesh() { return this.parentMesh; } /** @param parentMesh May be null. */ - setParentMesh (parentMesh: MeshAttachment) { + setParentMesh(parentMesh: MeshAttachment) { this.parentMesh = parentMesh; if (parentMesh != null) { this.bones = parentMesh.bones; @@ -39,9 +40,9 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment this.regionUVs = parentMesh.regionUVs; this.triangles = parentMesh.triangles; this.hullLength = parentMesh.hullLength; - this.worldVerticesLength = parentMesh.worldVerticesLength + this.worldVerticesLength = parentMesh.worldVerticesLength; } } - //computeWorldVerticesWith(slot, 0, this.worldVerticesLength, worldVertices, 0); + // computeWorldVerticesWith(slot, 0, this.worldVerticesLength, worldVertices, 0); } diff --git a/packages/runtime-3.7/src/core/attachments/PathAttachment.ts b/packages/runtime-3.7/src/core/attachments/PathAttachment.ts index ccbf5a6e..be686799 100644 --- a/packages/runtime-3.7/src/core/attachments/PathAttachment.ts +++ b/packages/runtime-3.7/src/core/attachments/PathAttachment.ts @@ -1,5 +1,5 @@ -import {VertexAttachment} from "./Attachment"; -import {AttachmentType, Color} from "@pixi-spine/base"; +import { VertexAttachment } from './Attachment'; +import { AttachmentType, Color } from '@pixi-spine/base'; /** * @public diff --git a/packages/runtime-3.7/src/core/attachments/PointAttachment.ts b/packages/runtime-3.7/src/core/attachments/PointAttachment.ts index dcdcaca5..31a6340b 100644 --- a/packages/runtime-3.7/src/core/attachments/PointAttachment.ts +++ b/packages/runtime-3.7/src/core/attachments/PointAttachment.ts @@ -1,6 +1,6 @@ -import {VertexAttachment} from './Attachment'; -import {AttachmentType, Color, MathUtils, Vector2} from "@pixi-spine/base"; -import type {Bone} from '../Bone'; +import { VertexAttachment } from './Attachment'; +import { AttachmentType, Color, MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Bone } from '../Bone'; /** * @public @@ -18,16 +18,20 @@ export class PointAttachment extends VertexAttachment { computeWorldPosition(bone: Bone, point: Vector2) { const mat = bone.matrix; + point.x = this.x * mat.a + this.y * mat.c + bone.worldX; point.y = this.x * mat.b + this.y * mat.d + bone.worldY; + return point; } computeWorldRotation(bone: Bone) { const mat = bone.matrix; - let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation); - let x = cos * mat.a + sin * mat.c; - let y = cos * mat.b + sin * mat.d; + const cos = MathUtils.cosDeg(this.rotation); + const sin = MathUtils.sinDeg(this.rotation); + const x = cos * mat.a + sin * mat.c; + const y = cos * mat.b + sin * mat.d; + return Math.atan2(y, x) * MathUtils.radDeg; } } diff --git a/packages/runtime-3.7/src/core/attachments/RegionAttachment.ts b/packages/runtime-3.7/src/core/attachments/RegionAttachment.ts index eb836ed8..c36bc0f7 100644 --- a/packages/runtime-3.7/src/core/attachments/RegionAttachment.ts +++ b/packages/runtime-3.7/src/core/attachments/RegionAttachment.ts @@ -1,8 +1,7 @@ +import { Attachment } from './Attachment'; +import { AttachmentType, ArrayLike, Color, TextureRegion, Utils, IRegionAttachment } from '@pixi-spine/base'; -import {Attachment} from './Attachment'; -import {AttachmentType, ArrayLike, Color, TextureRegion, Utils, IRegionAttachment} from "@pixi-spine/base"; - -import type {Bone} from '../Bone'; +import type { Bone } from '../Bone'; import { Slot } from '../Slot'; /** @@ -79,24 +78,25 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { } updateOffset(): void { - let regionScaleX = this.width / this.region.originalWidth * this.scaleX; - let regionScaleY = this.height / this.region.originalHeight * this.scaleY; - let localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX; - let localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY; - let localX2 = localX + this.region.width * regionScaleX; - let localY2 = localY + this.region.height * regionScaleY; - let radians = this.rotation * Math.PI / 180; - let cos = Math.cos(radians); - let sin = Math.sin(radians); - let localXCos = localX * cos + this.x; - let localXSin = localX * sin; - let localYCos = localY * cos + this.y; - let localYSin = localY * sin; - let localX2Cos = localX2 * cos + this.x; - let localX2Sin = localX2 * sin; - let localY2Cos = localY2 * cos + this.y; - let localY2Sin = localY2 * sin; - let offset = this.offset; + const regionScaleX = (this.width / this.region.originalWidth) * this.scaleX; + const regionScaleY = (this.height / this.region.originalHeight) * this.scaleY; + const localX = (-this.width / 2) * this.scaleX + this.region.offsetX * regionScaleX; + const localY = (-this.height / 2) * this.scaleY + this.region.offsetY * regionScaleY; + const localX2 = localX + this.region.width * regionScaleX; + const localY2 = localY + this.region.height * regionScaleY; + const radians = (this.rotation * Math.PI) / 180; + const cos = Math.cos(radians); + const sin = Math.sin(radians); + const localXCos = localX * cos + this.x; + const localXSin = localX * sin; + const localYCos = localY * cos + this.y; + const localYSin = localY * sin; + const localX2Cos = localX2 * cos + this.x; + const localX2Sin = localX2 * sin; + const localY2Cos = localY2 * cos + this.y; + const localY2Sin = localY2 * sin; + const offset = this.offset; + offset[RegionAttachment.OX1] = localXCos - localYSin; offset[RegionAttachment.OY1] = localYCos + localXSin; offset[RegionAttachment.OX2] = localXCos - localY2Sin; @@ -109,7 +109,8 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { setRegion(region: TextureRegion): void { this.region = region; - let uvs = this.uvs; + const uvs = this.uvs; + if (region.rotate) { uvs[2] = region.u; uvs[3] = region.v2; @@ -132,11 +133,16 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { } computeWorldVertices(bone: Bone | Slot, worldVertices: ArrayLike, offset: number, stride: number) { - let vertexOffset = this.offset; - let mat = bone instanceof Slot? bone.bone.matrix : bone.matrix; - let x = mat.tx, y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let offsetX = 0, offsetY = 0; + const vertexOffset = this.offset; + const mat = bone instanceof Slot ? bone.bone.matrix : bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let offsetX = 0; + let offsetY = 0; offsetX = vertexOffset[RegionAttachment.OX1]; offsetY = vertexOffset[RegionAttachment.OY1]; diff --git a/packages/runtime-3.7/src/core/vertexeffects/JitterEffect.ts b/packages/runtime-3.7/src/core/vertexeffects/JitterEffect.ts index 800105e1..a8dccd58 100644 --- a/packages/runtime-3.7/src/core/vertexeffects/JitterEffect.ts +++ b/packages/runtime-3.7/src/core/vertexeffects/JitterEffect.ts @@ -1,6 +1,6 @@ -import {VertexEffect} from "../VertexEffect"; -import type {Skeleton} from "../Skeleton"; -import {Color, MathUtils, Vector2} from "@pixi-spine/base"; +import type { VertexEffect } from '../VertexEffect'; +import type { Skeleton } from '../Skeleton'; +import { Color, MathUtils, Vector2 } from '@pixi-spine/base'; /** * @public @@ -9,19 +9,17 @@ export class JitterEffect implements VertexEffect { jitterX = 0; jitterY = 0; - constructor (jitterX: number, jitterY: number) { + constructor(jitterX: number, jitterY: number) { this.jitterX = jitterX; this.jitterY = jitterY; } - begin(skeleton: Skeleton): void { - } + begin(skeleton: Skeleton): void {} transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { position.x += MathUtils.randomTriangular(-this.jitterX, this.jitterY); position.y += MathUtils.randomTriangular(-this.jitterX, this.jitterY); } - end(): void { - } + end(): void {} } diff --git a/packages/runtime-3.7/src/core/vertexeffects/SwirlEffect.ts b/packages/runtime-3.7/src/core/vertexeffects/SwirlEffect.ts index 416b969c..46d78bce 100644 --- a/packages/runtime-3.7/src/core/vertexeffects/SwirlEffect.ts +++ b/packages/runtime-3.7/src/core/vertexeffects/SwirlEffect.ts @@ -1,6 +1,6 @@ -import {VertexEffect} from "../VertexEffect"; -import type {Skeleton} from "../Skeleton"; -import {Color, MathUtils, PowOut, Vector2} from "@pixi-spine/base"; +import type { VertexEffect } from '../VertexEffect'; +import type { Skeleton } from '../Skeleton'; +import { Color, MathUtils, PowOut, Vector2 } from '@pixi-spine/base'; /** * @public @@ -14,7 +14,7 @@ export class SwirlEffect implements VertexEffect { private worldX = 0; private worldY = 0; - constructor (radius: number) { + constructor(radius: number) { this.radius = radius; } @@ -24,19 +24,20 @@ export class SwirlEffect implements VertexEffect { } transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { - let radAngle = this.angle * MathUtils.degreesToRadians; - let x = position.x - this.worldX; - let y = position.y - this.worldY; - let dist = Math.sqrt(x * x + y * y); + const radAngle = this.angle * MathUtils.degreesToRadians; + const x = position.x - this.worldX; + const y = position.y - this.worldY; + const dist = Math.sqrt(x * x + y * y); + if (dist < this.radius) { - let theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); - let cos = Math.cos(theta); - let sin = Math.sin(theta); + const theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); + const cos = Math.cos(theta); + const sin = Math.sin(theta); + position.x = cos * x - sin * y + this.worldX; position.y = sin * x + cos * y + this.worldY; } } - end(): void { - } + end(): void {} } diff --git a/packages/runtime-3.8/package.json b/packages/runtime-3.8/package.json index 32bf6ff9..1dec5cb4 100644 --- a/packages/runtime-3.8/package.json +++ b/packages/runtime-3.8/package.json @@ -2,21 +2,35 @@ "name": "@pixi-spine/runtime-3.8", "version": "3.1.2", "description": "Pixi runtime for spine 3.8 models", - "main": "lib/runtime-3.8.js", - "module": "lib/runtime-3.8.es.js", - "bundle": "dist/runtime-3.8.js", - "namespace": "PIXI.spine38", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "peerDependencies": { - "@pixi/constants": "^6.1.0", - "@pixi/math": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine38", + "bundle": "dist/runtime-3.8.js", + "bundleModule": "dist/runtime-3.8.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2" + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,13 +53,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/runtime-3.8/rollup.config.js b/packages/runtime-3.8/rollup.config.js deleted file mode 100644 index 8b1fc303..00000000 --- a/packages/runtime-3.8/rollup.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine.base', - }, -}); diff --git a/packages/runtime-3.8/rollup.config.mjs b/packages/runtime-3.8/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/packages/runtime-3.8/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/packages/runtime-3.8/src/Spine.ts b/packages/runtime-3.8/src/Spine.ts index 6faecd39..b3a2b909 100644 --- a/packages/runtime-3.8/src/Spine.ts +++ b/packages/runtime-3.8/src/Spine.ts @@ -1,8 +1,8 @@ -import {SpineBase} from '@pixi-spine/base'; -import {Skeleton} from "./core/Skeleton"; -import {SkeletonData} from "./core/SkeletonData"; -import {AnimationState} from "./core/AnimationState"; -import {AnimationStateData} from "./core/AnimationStateData"; +import { SpineBase } from '@pixi-spine/base'; +import { Skeleton } from './core/Skeleton'; +import type { SkeletonData } from './core/SkeletonData'; +import { AnimationState } from './core/AnimationState'; +import { AnimationStateData } from './core/AnimationStateData'; /** * @public diff --git a/packages/runtime-3.8/src/core/Animation.ts b/packages/runtime-3.8/src/core/Animation.ts index cddda4cc..951e1bc4 100644 --- a/packages/runtime-3.8/src/core/Animation.ts +++ b/packages/runtime-3.8/src/core/Animation.ts @@ -1,11 +1,11 @@ -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; -import {Attachment, VertexAttachment} from "./attachments"; -import {ArrayLike, MathUtils, Utils, MixBlend, MixDirection} from '@pixi-spine/base'; -import {Slot} from "./Slot"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; +import { Attachment, VertexAttachment } from './attachments'; +import { ArrayLike, MathUtils, Utils, MixBlend, MixDirection } from '@pixi-spine/base'; +import type { Slot } from './Slot'; +import type { IkConstraint } from './IkConstraint'; +import type { TransformConstraint } from './TransformConstraint'; +import type { PathConstraint } from './PathConstraint'; /** A simple container for a list of timelines and a name. */ /** * @public @@ -19,18 +19,17 @@ export class Animation { /** The duration of the animation in seconds, which is the highest time of all keys in the timeline. */ duration: number; - constructor (name: string, timelines: Array, duration: number) { - if (name == null) throw new Error("name cannot be null."); - if (timelines == null) throw new Error("timelines cannot be null."); + constructor(name: string, timelines: Array, duration: number) { + if (name == null) throw new Error('name cannot be null.'); + if (timelines == null) throw new Error('timelines cannot be null.'); this.name = name; this.timelines = timelines; this.timelineIds = []; - for (var i = 0; i < timelines.length; i++) - this.timelineIds[timelines[i].getPropertyId()] = true; + for (let i = 0; i < timelines.length; i++) this.timelineIds[timelines[i].getPropertyId()] = true; this.duration = duration; } - hasTimeline (id: number) { + hasTimeline(id: number) { return this.timelineIds[id] == true; } @@ -39,39 +38,39 @@ export class Animation { * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. * @param loop If true, the animation repeats after {@link #getDuration()}. * @param events May be null to ignore fired events. */ - apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - if (skeleton == null) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + if (skeleton == null) throw new Error('skeleton cannot be null.'); if (loop && this.duration != 0) { time %= this.duration; if (lastTime > 0) lastTime %= this.duration; } - let timelines = this.timelines; - for (let i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); + const timelines = this.timelines; + + for (let i = 0, n = timelines.length; i < n; i++) timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); } /** @param target After the first and before the last value. * @returns index of first value greater than the target. */ - static binarySearch (values: ArrayLike, target: number, step: number = 1) { + static binarySearch(values: ArrayLike, target: number, step = 1) { let low = 0; let high = values.length / step - 2; + if (high == 0) return step; let current = high >>> 1; + while (true) { - if (values[(current + 1) * step] <= target) - low = current + 1; - else - high = current; + if (values[(current + 1) * step] <= target) low = current + 1; + else high = current; if (low == high) return (low + 1) * step; current = (low + high) >>> 1; } } - static linearSearch (values: ArrayLike, target: number, step: number) { - for (let i = 0, last = values.length - step; i <= last; i += step) - if (values[i] > target) return i; + static linearSearch(values: ArrayLike, target: number, step: number) { + for (let i = 0, last = values.length - step; i <= last; i += step) if (values[i] > target) return i; + return -1; } } @@ -98,22 +97,31 @@ export interface Timeline { * @param blend Controls how mixing is applied when `alpha` < 1. * @param direction Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions, * such as {@link DrawOrderTimeline} or {@link AttachmentTimeline}. */ - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; /** Uniquely encodes both the type of this timeline and the skeleton property that it affects. */ - getPropertyId (): number; + getPropertyId(): number; } /** * @public */ export enum TimelineType { - rotate, translate, scale, shear, - attachment, color, deform, - event, drawOrder, - ikConstraint, transformConstraint, - pathConstraintPosition, pathConstraintSpacing, pathConstraintMix, - twoColor + rotate, + translate, + scale, + shear, + attachment, + color, + deform, + event, + drawOrder, + ikConstraint, + transformConstraint, + pathConstraintPosition, + pathConstraintSpacing, + pathConstraintMix, + twoColor, } /** The base class for timelines that use interpolation between key frame values. */ @@ -121,58 +129,70 @@ export enum TimelineType { * @public */ export abstract class CurveTimeline implements Timeline { - static LINEAR = 0; static STEPPED = 1; static BEZIER = 2; + static LINEAR = 0; + static STEPPED = 1; + static BEZIER = 2; static BEZIER_SIZE = 10 * 2 - 1; private curves: ArrayLike; // type, x, y, ... abstract getPropertyId(): number; - constructor (frameCount: number) { - if (frameCount <= 0) throw new Error("frameCount must be > 0: " + frameCount); + constructor(frameCount: number) { + if (frameCount <= 0) throw new Error(`frameCount must be > 0: ${frameCount}`); this.curves = Utils.newFloatArray((frameCount - 1) * CurveTimeline.BEZIER_SIZE); } /** The number of key frames for this timeline. */ - getFrameCount () { + getFrameCount() { return this.curves.length / CurveTimeline.BEZIER_SIZE + 1; } /** Sets the specified key frame to linear interpolation. */ - setLinear (frameIndex: number) { + setLinear(frameIndex: number) { this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.LINEAR; } /** Sets the specified key frame to stepped interpolation. */ - setStepped (frameIndex: number) { + setStepped(frameIndex: number) { this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.STEPPED; } /** Returns the interpolation type for the specified key frame. * @returns Linear is 0, stepped is 1, Bezier is 2. */ - getCurveType (frameIndex: number): number { - let index = frameIndex * CurveTimeline.BEZIER_SIZE; + getCurveType(frameIndex: number): number { + const index = frameIndex * CurveTimeline.BEZIER_SIZE; + if (index == this.curves.length) return CurveTimeline.LINEAR; - let type = this.curves[index]; + const type = this.curves[index]; + if (type == CurveTimeline.LINEAR) return CurveTimeline.LINEAR; if (type == CurveTimeline.STEPPED) return CurveTimeline.STEPPED; + return CurveTimeline.BEZIER; } /** Sets the specified key frame to Bezier interpolation. `cx1` and `cx2` are from 0 to 1, * representing the percent of time between the two key frames. `cy1` and `cy2` are the percent of the * difference between the key frame's values. */ - setCurve (frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) { - let tmpx = (-cx1 * 2 + cx2) * 0.03, tmpy = (-cy1 * 2 + cy2) * 0.03; - let dddfx = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006; - let ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; - let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667; + setCurve(frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) { + const tmpx = (-cx1 * 2 + cx2) * 0.03; + const tmpy = (-cy1 * 2 + cy2) * 0.03; + const dddfx = ((cx1 - cx2) * 3 + 1) * 0.006; + const dddfy = ((cy1 - cy2) * 3 + 1) * 0.006; + let ddfx = tmpx * 2 + dddfx; + let ddfy = tmpy * 2 + dddfy; + let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667; + let dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667; let i = frameIndex * CurveTimeline.BEZIER_SIZE; - let curves = this.curves; + const curves = this.curves; + curves[i++] = CurveTimeline.BEZIER; - let x = dfx, y = dfy; + let x = dfx; + let y = dfy; + for (let n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; @@ -186,19 +206,23 @@ export abstract class CurveTimeline implements Timeline { } /** Returns the interpolated percentage for the specified key frame and linear percentage. */ - getCurvePercent (frameIndex: number, percent: number) { + getCurvePercent(frameIndex: number, percent: number) { percent = MathUtils.clamp(percent, 0, 1); - let curves = this.curves; + const curves = this.curves; let i = frameIndex * CurveTimeline.BEZIER_SIZE; - let type = curves[i]; + const type = curves[i]; + if (type == CurveTimeline.LINEAR) return percent; if (type == CurveTimeline.STEPPED) return 0; i++; let x = 0; + for (let start = i, n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) { x = curves[i]; if (x >= percent) { - let prevX: number, prevY: number; + let prevX: number; + let prevY: number; + if (i == start) { prevX = 0; prevY = 0; @@ -206,14 +230,16 @@ export abstract class CurveTimeline implements Timeline { prevX = curves[i - 2]; prevY = curves[i - 1]; } - return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + + return prevY + ((curves[i + 1] - prevY) * (percent - prevX)) / (x - prevX); } } - let y = curves[i - 1]; - return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + const y = curves[i - 1]; + + return y + ((1 - y) * (percent - x)) / (1 - x); // Last point is 1,1. } - abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; + abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; } /** Changes a bone's local {@link Bone#rotation}. */ @@ -222,7 +248,8 @@ export abstract class CurveTimeline implements Timeline { */ export class RotateTimeline extends CurveTimeline { static ENTRIES = 2; - static PREV_TIME = -2; static PREV_ROTATION = -1; + static PREV_TIME = -2; + static PREV_ROTATION = -1; static ROTATION = 1; /** The index of the bone in {@link Skeleton#bones} that will be changed. */ @@ -231,41 +258,47 @@ export class RotateTimeline extends CurveTimeline { /** The time in seconds and rotation in degrees for each key frame. */ frames: ArrayLike; // time, degrees, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount << 1); } - getPropertyId () { + getPropertyId() { return (TimelineType.rotate << 24) + this.boneIndex; } /** Sets the time and angle of the specified keyframe. */ - setFrame (frameIndex: number, time: number, degrees: number) { + setFrame(frameIndex: number, time: number, degrees: number) { frameIndex <<= 1; this.frames[frameIndex] = time; this.frames[frameIndex + RotateTimeline.ROTATION] = degrees; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (!bone.active) return; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation; + return; case MixBlend.first: - let r = bone.data.rotation - bone.rotation; + const r = bone.data.rotation - bone.rotation; + bone.rotation += (r - (16384 - ((16384.499999999996 - r / 360) | 0)) * 360) * alpha; } + return; } - if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { // Time is after last frame. + if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { + // Time is after last frame. let r = frames[frames.length + RotateTimeline.PREV_ROTATION]; + switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation + r * alpha; @@ -277,17 +310,18 @@ export class RotateTimeline extends CurveTimeline { case MixBlend.add: bone.rotation += r * alpha; } + return; } // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); - let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent((frame >> 1) - 1, - 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); + const frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); + const prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; + const frameTime = frames[frame]; + const percent = this.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); let r = frames[frame + RotateTimeline.ROTATION] - prevRotation; + r = prevRotation + (r - (16384 - ((16384.499999999996 - r / 360) | 0)) * 360) * percent; switch (blend) { case MixBlend.setup: @@ -308,8 +342,11 @@ export class RotateTimeline extends CurveTimeline { */ export class TranslateTimeline extends CurveTimeline { static ENTRIES = 3; - static PREV_TIME = -3; static PREV_X = -2; static PREV_Y = -1; - static X = 1; static Y = 2; + static PREV_TIME = -3; + static PREV_X = -2; + static PREV_Y = -1; + static X = 1; + static Y = 2; /** The index of the bone in {@link Skeleton#bones} that will be changed. */ boneIndex: number; @@ -317,53 +354,59 @@ export class TranslateTimeline extends CurveTimeline { /** The time in seconds, x, and y values for each key frame. */ frames: ArrayLike; // time, x, y, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * TranslateTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.translate << 24) + this.boneIndex; } /** Sets the time in seconds, x, and y values for the specified key frame. */ - setFrame (frameIndex: number, time: number, x: number, y: number) { + setFrame(frameIndex: number, time: number, x: number, y: number) { frameIndex *= TranslateTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + TranslateTimeline.X] = x; this.frames[frameIndex + TranslateTimeline.Y] = y; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (!bone.active) return; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.x = bone.data.x; bone.y = bone.data.y; + return; case MixBlend.first: bone.x += (bone.data.x - bone.x) * alpha; bone.y += (bone.data.y - bone.y) * alpha; } + return; } - let x = 0, y = 0; - if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { // Time is after last frame. + let x = 0; + let y = 0; + + if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { + // Time is after last frame. x = frames[frames.length + TranslateTimeline.PREV_X]; y = frames[frames.length + TranslateTimeline.PREV_Y]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES); + x = frames[frame + TranslateTimeline.PREV_X]; y = frames[frame + TranslateTimeline.PREV_Y]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime)); x += (frames[frame + TranslateTimeline.X] - x) * percent; y += (frames[frame + TranslateTimeline.Y] - y) * percent; @@ -390,44 +433,50 @@ export class TranslateTimeline extends CurveTimeline { * @public */ export class ScaleTimeline extends TranslateTimeline { - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.scale << 24) + this.boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (!bone.active) return; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleX = bone.data.scaleX; bone.scaleY = bone.data.scaleY; + return; case MixBlend.first: bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; } + return; } - let x = 0, y = 0; - if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { // Time is after last frame. + let x = 0; + let y = 0; + + if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { + // Time is after last frame. x = frames[frames.length + ScaleTimeline.PREV_X] * bone.data.scaleX; y = frames[frames.length + ScaleTimeline.PREV_Y] * bone.data.scaleY; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES); + x = frames[frame + ScaleTimeline.PREV_X]; y = frames[frame + ScaleTimeline.PREV_Y]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime)); x = (x + (frames[frame + ScaleTimeline.X] - x) * percent) * bone.data.scaleX; y = (y + (frames[frame + ScaleTimeline.Y] - y) * percent) * bone.data.scaleY; @@ -441,7 +490,9 @@ export class ScaleTimeline extends TranslateTimeline { bone.scaleY = y; } } else { - let bx = 0, by = 0; + let bx = 0; + let by = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -494,44 +545,50 @@ export class ScaleTimeline extends TranslateTimeline { * @public */ export class ShearTimeline extends TranslateTimeline { - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.shear << 24) + this.boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const bone = skeleton.bones[this.boneIndex]; - let bone = skeleton.bones[this.boneIndex]; if (!bone.active) return; if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX; bone.shearY = bone.data.shearY; + return; case MixBlend.first: bone.shearX += (bone.data.shearX - bone.shearX) * alpha; bone.shearY += (bone.data.shearY - bone.shearY) * alpha; } + return; } - let x = 0, y = 0; - if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { // Time is after last frame. + let x = 0; + let y = 0; + + if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { + // Time is after last frame. x = frames[frames.length + ShearTimeline.PREV_X]; y = frames[frames.length + ShearTimeline.PREV_Y]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES); + x = frames[frame + ShearTimeline.PREV_X]; y = frames[frame + ShearTimeline.PREV_Y]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime)); x = x + (frames[frame + ShearTimeline.X] - x) * percent; y = y + (frames[frame + ShearTimeline.Y] - y) * percent; @@ -559,8 +616,15 @@ export class ShearTimeline extends TranslateTimeline { */ export class ColorTimeline extends CurveTimeline { static ENTRIES = 5; - static PREV_TIME = -5; static PREV_R = -4; static PREV_G = -3; static PREV_B = -2; static PREV_A = -1; - static R = 1; static G = 2; static B = 3; static A = 4; + static PREV_TIME = -5; + static PREV_R = -4; + static PREV_G = -3; + static PREV_B = -2; + static PREV_A = -1; + static R = 1; + static G = 2; + static B = 3; + static A = 4; /** The index of the slot in {@link Skeleton#slots} that will be changed. */ slotIndex: number; @@ -568,17 +632,17 @@ export class ColorTimeline extends CurveTimeline { /** The time in seconds, red, green, blue, and alpha values for each key frame. */ frames: ArrayLike; // time, r, g, b, a, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * ColorTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.color << 24) + this.slotIndex; } /** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */ - setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number) { + setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number) { frameIndex *= ColorTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + ColorTimeline.R] = r; @@ -587,50 +651,61 @@ export class ColorTimeline extends CurveTimeline { this.frames[frameIndex + ColorTimeline.A] = a; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: slot.color.setFromColor(slot.data.color); + return; case MixBlend.first: - let color = slot.color, setup = slot.data.color; - color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, - (setup.a - color.a) * alpha); + const color = slot.color; + const setup = slot.data.color; + + color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, (setup.a - color.a) * alpha); } + return; } - let r = 0, g = 0, b = 0, a = 0; - if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { // Time is after last frame. - let i = frames.length; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + + if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { + // Time is after last frame. + const i = frames.length; + r = frames[i + ColorTimeline.PREV_R]; g = frames[i + ColorTimeline.PREV_G]; b = frames[i + ColorTimeline.PREV_B]; a = frames[i + ColorTimeline.PREV_A]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES); + r = frames[frame + ColorTimeline.PREV_R]; g = frames[frame + ColorTimeline.PREV_G]; b = frames[frame + ColorTimeline.PREV_B]; a = frames[frame + ColorTimeline.PREV_A]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime)); r += (frames[frame + ColorTimeline.R] - r) * percent; g += (frames[frame + ColorTimeline.G] - g) * percent; b += (frames[frame + ColorTimeline.B] - b) * percent; a += (frames[frame + ColorTimeline.A] - a) * percent; } - if (alpha == 1) - slot.color.set(r, g, b, a); + if (alpha == 1) slot.color.set(r, g, b, a); else { - let color = slot.color; + const color = slot.color; + if (blend == MixBlend.setup) color.setFromColor(slot.data.color); color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); } @@ -643,9 +718,21 @@ export class ColorTimeline extends CurveTimeline { */ export class TwoColorTimeline extends CurveTimeline { static ENTRIES = 8; - static PREV_TIME = -8; static PREV_R = -7; static PREV_G = -6; static PREV_B = -5; static PREV_A = -4; - static PREV_R2 = -3; static PREV_G2 = -2; static PREV_B2 = -1; - static R = 1; static G = 2; static B = 3; static A = 4; static R2 = 5; static G2 = 6; static B2 = 7; + static PREV_TIME = -8; + static PREV_R = -7; + static PREV_G = -6; + static PREV_B = -5; + static PREV_A = -4; + static PREV_R2 = -3; + static PREV_G2 = -2; + static PREV_B2 = -1; + static R = 1; + static G = 2; + static B = 3; + static A = 4; + static R2 = 5; + static G2 = 6; + static B2 = 7; /** The index of the slot in {@link Skeleton#slots()} that will be changed. The {@link Slot#darkColor()} must not be * null. */ @@ -654,17 +741,17 @@ export class TwoColorTimeline extends CurveTimeline { /** The time in seconds, red, green, blue, and alpha values of the color, red, green, blue of the dark color, for each key frame. */ frames: ArrayLike; // time, r, g, b, a, r2, g2, b2, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * TwoColorTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.twoColor << 24) + this.slotIndex; } /** Sets the time in seconds, light, and dark colors for the specified key frame. */ - setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { + setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { frameIndex *= TwoColorTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + TwoColorTimeline.R] = r; @@ -676,28 +763,44 @@ export class TwoColorTimeline extends CurveTimeline { this.frames[frameIndex + TwoColorTimeline.B2] = b2; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: slot.color.setFromColor(slot.data.color); slot.darkColor.setFromColor(slot.data.darkColor); + return; case MixBlend.first: - let light = slot.color, dark = slot.darkColor, setupLight = slot.data.color, setupDark = slot.data.darkColor; - light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, - (setupLight.a - light.a) * alpha); + const light = slot.color; + const dark = slot.darkColor; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + + light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); dark.add((setupDark.r - dark.r) * alpha, (setupDark.g - dark.g) * alpha, (setupDark.b - dark.b) * alpha, 0); } + return; } - let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0; - if (time >= frames[frames.length - TwoColorTimeline.ENTRIES]) { // Time is after last frame. - let i = frames.length; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + let r2 = 0; + let g2 = 0; + let b2 = 0; + + if (time >= frames[frames.length - TwoColorTimeline.ENTRIES]) { + // Time is after last frame. + const i = frames.length; + r = frames[i + TwoColorTimeline.PREV_R]; g = frames[i + TwoColorTimeline.PREV_G]; b = frames[i + TwoColorTimeline.PREV_B]; @@ -707,7 +810,8 @@ export class TwoColorTimeline extends CurveTimeline { b2 = frames[i + TwoColorTimeline.PREV_B2]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, TwoColorTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, TwoColorTimeline.ENTRIES); + r = frames[frame + TwoColorTimeline.PREV_R]; g = frames[frame + TwoColorTimeline.PREV_G]; b = frames[frame + TwoColorTimeline.PREV_B]; @@ -715,9 +819,8 @@ export class TwoColorTimeline extends CurveTimeline { r2 = frames[frame + TwoColorTimeline.PREV_R2]; g2 = frames[frame + TwoColorTimeline.PREV_G2]; b2 = frames[frame + TwoColorTimeline.PREV_B2]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / TwoColorTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + TwoColorTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / TwoColorTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + TwoColorTimeline.PREV_TIME] - frameTime)); r += (frames[frame + TwoColorTimeline.R] - r) * percent; g += (frames[frame + TwoColorTimeline.G] - g) * percent; @@ -731,7 +834,9 @@ export class TwoColorTimeline extends CurveTimeline { slot.color.set(r, g, b, a); slot.darkColor.set(r2, g2, b2, 1); } else { - let light = slot.color, dark = slot.darkColor; + const light = slot.color; + const dark = slot.darkColor; + if (blend == MixBlend.setup) { light.setFromColor(slot.data.color); dark.setFromColor(slot.data.darkColor); @@ -751,55 +856,59 @@ export class AttachmentTimeline implements Timeline { slotIndex: number; /** The time in seconds for each key frame. */ - frames: ArrayLike // time, ... + frames: ArrayLike; // time, ... /** The attachment name for each key frame. May contain null values to clear the attachment. */ attachmentNames: Array; - constructor (frameCount: number) { + constructor(frameCount: number) { this.frames = Utils.newFloatArray(frameCount); this.attachmentNames = new Array(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.attachment << 24) + this.slotIndex; } /** The number of key frames for this timeline. */ - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the attachment name for the specified key frame. */ - setFrame (frameIndex: number, time: number, attachmentName: string) { + setFrame(frameIndex: number, time: number, attachmentName: string) { this.frames[frameIndex] = time; this.attachmentNames[frameIndex] = attachmentName; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; if (direction == MixDirection.mixOut) { - if (blend == MixBlend.setup) - this.setAttachment(skeleton, slot, slot.data.attachmentName); + if (blend == MixBlend.setup) this.setAttachment(skeleton, slot, slot.data.attachmentName); + return; } - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName); + return; } let frameIndex = 0; - if (time >= frames[frames.length - 1]) // Time is after last frame. + + if (time >= frames[frames.length - 1]) + // Time is after last frame. frameIndex = frames.length - 1; - else - frameIndex = Animation.binarySearch(frames, time, 1) - 1; + else frameIndex = Animation.binarySearch(frames, time, 1) - 1; + + const attachmentName = this.attachmentNames[frameIndex]; - let attachmentName = this.attachmentNames[frameIndex]; - skeleton.slots[this.slotIndex] - .setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); + skeleton.slots[this.slotIndex].setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); } setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string) { @@ -807,7 +916,7 @@ export class AttachmentTimeline implements Timeline { } } -let zeros : ArrayLike = null; +let zeros: ArrayLike = null; /** Changes a slot's {@link Slot#deform} to deform a {@link VertexAttachment}. */ /** @@ -826,80 +935,91 @@ export class DeformTimeline extends CurveTimeline { /** The vertices for each key frame. */ frameVertices: Array>; - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount); this.frameVertices = new Array>(frameCount); if (zeros == null) zeros = Utils.newFloatArray(64); } - getPropertyId () { - return (TimelineType.deform << 27) + + this.attachment.id + this.slotIndex; + getPropertyId() { + return (TimelineType.deform << 27) + Number(this.attachment.id) + this.slotIndex; } /** Sets the time in seconds and the vertices for the specified key frame. * @param vertices Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights. */ - setFrame (frameIndex: number, time: number, vertices: ArrayLike) { + setFrame(frameIndex: number, time: number, vertices: ArrayLike) { this.frames[frameIndex] = time; this.frameVertices[frameIndex] = vertices; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot: Slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot: Slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let slotAttachment: Attachment = slot.getAttachment(); + const slotAttachment: Attachment = slot.getAttachment(); + if (!(slotAttachment instanceof VertexAttachment) || !((slotAttachment).deformAttachment == this.attachment)) return; - let deformArray: Array = slot.deform; + const deformArray: Array = slot.deform; + if (deformArray.length == 0) blend = MixBlend.setup; - let frameVertices = this.frameVertices; - let vertexCount = frameVertices[0].length; + const frameVertices = this.frameVertices; + const vertexCount = frameVertices[0].length; + + const frames = this.frames; - let frames = this.frames; if (time < frames[0]) { - let vertexAttachment = slotAttachment; + const vertexAttachment = slotAttachment; + switch (blend) { case MixBlend.setup: deformArray.length = 0; + return; case MixBlend.first: if (alpha == 1) { deformArray.length = 0; break; } - let deform: Array = Utils.setArraySize(deformArray, vertexCount); + const deform: Array = Utils.setArraySize(deformArray, vertexCount); + if (vertexAttachment.bones == null) { // Unweighted vertex positions. - let setupVertices = vertexAttachment.vertices; - for (var i = 0; i < vertexCount; i++) - deform[i] += (setupVertices[i] - deform[i]) * alpha; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) deform[i] += (setupVertices[i] - deform[i]) * alpha; } else { // Weighted deform offsets. alpha = 1 - alpha; - for (var i = 0; i < vertexCount; i++) - deform[i] *= alpha; + for (let i = 0; i < vertexCount; i++) deform[i] *= alpha; } } + return; } - let deform: Array = Utils.setArraySize(deformArray, vertexCount); - if (time >= frames[frames.length - 1]) { // Time is after last frame. - let lastVertices = frameVertices[frames.length - 1]; + const deform: Array = Utils.setArraySize(deformArray, vertexCount); + + if (time >= frames[frames.length - 1]) { + // Time is after last frame. + const lastVertices = frameVertices[frames.length - 1]; + if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { deform[i] += lastVertices[i] - setupVertices[i]; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i]; + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i]; } } else { Utils.arrayCopy(lastVertices, 0, deform, 0, vertexCount); @@ -907,89 +1027,102 @@ export class DeformTimeline extends CurveTimeline { } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let setup = setupVertices[i]; + const setup = setupVertices[i]; + deform[i] = setup + (lastVertices[i] - setup) * alpha; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] = lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] = lastVertices[i] * alpha; } break; } case MixBlend.first: case MixBlend.replace: - for (let i = 0; i < vertexCount; i++) - deform[i] += (lastVertices[i] - deform[i]) * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] += (lastVertices[i] - deform[i]) * alpha; break; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { deform[i] += (lastVertices[i] - setupVertices[i]) * alpha; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i] * alpha; } } } + return; } // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time); - let prevVertices = frameVertices[frame - 1]; - let nextVertices = frameVertices[frame]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); + const frame = Animation.binarySearch(frames, time); + const prevVertices = frameVertices[frame - 1]; + const nextVertices = frameVertices[frame]; + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i]; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += prev + (nextVertices[i] - prev) * percent; } } } else { for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] = prev + (nextVertices[i] - prev) * percent; } } } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i], setup = setupVertices[i]; + const prev = prevVertices[i]; + const setup = setupVertices[i]; + deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -998,23 +1131,28 @@ export class DeformTimeline extends CurveTimeline { case MixBlend.first: case MixBlend.replace: for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha; } break; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -1034,52 +1172,55 @@ export class EventTimeline implements Timeline { /** The event for each key frame. */ events: Array; - constructor (frameCount: number) { + constructor(frameCount: number) { this.frames = Utils.newFloatArray(frameCount); this.events = new Array(frameCount); } - getPropertyId () { + getPropertyId() { return TimelineType.event << 24; } /** The number of key frames for this timeline. */ - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the event for the specified key frame. */ - setFrame (frameIndex: number, event: Event) { + setFrame(frameIndex: number, event: Event) { this.frames[frameIndex] = event.time; this.events[frameIndex] = event; } /** Fires events for frames > `lastTime` and <= `time`. */ - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { if (firedEvents == null) return; - let frames = this.frames; - let frameCount = this.frames.length; + const frames = this.frames; + const frameCount = this.frames.length; - if (lastTime > time) { // Fire events after last time for looped animations. + if (lastTime > time) { + // Fire events after last time for looped animations. this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, blend, direction); lastTime = -1; - } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + } else if (lastTime >= frames[frameCount - 1]) + // Last time is after last frame. return; if (time < frames[0]) return; // Time is before first frame. let frame = 0; - if (lastTime < frames[0]) - frame = 0; + + if (lastTime < frames[0]) frame = 0; else { frame = Animation.binarySearch(frames, lastTime); - let frameTime = frames[frame]; - while (frame > 0) { // Fire multiple events with the same frame. + const frameTime = frames[frame]; + + while (frame > 0) { + // Fire multiple events with the same frame. if (frames[frame - 1] != frameTime) break; frame--; } } - for (; frame < frameCount && time >= frames[frame]; frame++) - firedEvents.push(this.events[frame]); + for (; frame < frameCount && time >= frames[frame]; frame++) firedEvents.push(this.events[frame]); } } @@ -1094,54 +1235,58 @@ export class DrawOrderTimeline implements Timeline { /** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */ drawOrders: Array>; - constructor (frameCount: number) { + constructor(frameCount: number) { this.frames = Utils.newFloatArray(frameCount); this.drawOrders = new Array>(frameCount); } - getPropertyId () { + getPropertyId() { return TimelineType.drawOrder << 24; } /** The number of key frames for this timeline. */ - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the draw order for the specified key frame. * @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose * draw order. */ - setFrame (frameIndex: number, time: number, drawOrder: Array) { + setFrame(frameIndex: number, time: number, drawOrder: Array) { this.frames[frameIndex] = time; this.drawOrders[frameIndex] = drawOrder; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let drawOrder: Array = skeleton.drawOrder; - let slots: Array = skeleton.slots; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const drawOrder: Array = skeleton.drawOrder; + const slots: Array = skeleton.slots; + if (direction == MixDirection.mixOut && blend == MixBlend.setup) { Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } let frame = 0; - if (time >= frames[frames.length - 1]) // Time is after last frame. + + if (time >= frames[frames.length - 1]) + // Time is after last frame. frame = frames.length - 1; - else - frame = Animation.binarySearch(frames, time) - 1; + else frame = Animation.binarySearch(frames, time) - 1; + + const drawOrderToSetupIndex = this.drawOrders[frame]; - let drawOrderToSetupIndex = this.drawOrders[frame]; - if (drawOrderToSetupIndex == null) - Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length); + if (drawOrderToSetupIndex == null) Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length); else { - for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) - drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) drawOrder[i] = slots[drawOrderToSetupIndex[i]]; } } } @@ -1153,8 +1298,17 @@ export class DrawOrderTimeline implements Timeline { */ export class IkConstraintTimeline extends CurveTimeline { static ENTRIES = 6; - static PREV_TIME = -6; static PREV_MIX = -5; static PREV_SOFTNESS = -4; static PREV_BEND_DIRECTION = -3; static PREV_COMPRESS = -2; static PREV_STRETCH = -1; - static MIX = 1; static SOFTNESS = 2; static BEND_DIRECTION = 3; static COMPRESS = 4; static STRETCH = 5; + static PREV_TIME = -6; + static PREV_MIX = -5; + static PREV_SOFTNESS = -4; + static PREV_BEND_DIRECTION = -3; + static PREV_COMPRESS = -2; + static PREV_STRETCH = -1; + static MIX = 1; + static SOFTNESS = 2; + static BEND_DIRECTION = 3; + static COMPRESS = 4; + static STRETCH = 5; /** The index of the IK constraint slot in {@link Skeleton#ikConstraints} that will be changed. */ ikConstraintIndex: number; @@ -1162,17 +1316,17 @@ export class IkConstraintTimeline extends CurveTimeline { /** The time in seconds, mix, softness, bend direction, compress, and stretch for each key frame. */ frames: ArrayLike; // time, mix, softness, bendDirection, compress, stretch, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * IkConstraintTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.ikConstraint << 24) + this.ikConstraintIndex; } /** Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame. */ - setFrame (frameIndex: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) { + setFrame(frameIndex: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) { frameIndex *= IkConstraintTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + IkConstraintTimeline.MIX] = mix; @@ -1182,9 +1336,10 @@ export class IkConstraintTimeline extends CurveTimeline { this.frames[frameIndex + IkConstraintTimeline.STRETCH] = stretch ? 1 : 0; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + if (!constraint.active) return; if (time < frames[0]) { switch (blend) { @@ -1194,6 +1349,7 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.bendDirection = constraint.data.bendDirection; constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; + return; case MixBlend.first: constraint.mix += (constraint.data.mix - constraint.mix) * alpha; @@ -1202,20 +1358,21 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } + return; } - if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { // Time is after last frame. + if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { + // Time is after last frame. if (blend == MixBlend.setup) { constraint.mix = constraint.data.mix + (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.data.mix) * alpha; - constraint.softness = constraint.data.softness - + (frames[frames.length + IkConstraintTimeline.PREV_SOFTNESS] - constraint.data.softness) * alpha; + constraint.softness = constraint.data.softness + (frames[frames.length + IkConstraintTimeline.PREV_SOFTNESS] - constraint.data.softness) * alpha; if (direction == MixDirection.mixOut) { constraint.bendDirection = constraint.data.bendDirection; constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } else { - constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION] + constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION]; constraint.compress = frames[frames.length + IkConstraintTimeline.PREV_COMPRESS] != 0; constraint.stretch = frames[frames.length + IkConstraintTimeline.PREV_STRETCH] != 0; } @@ -1228,21 +1385,20 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.stretch = frames[frames.length + IkConstraintTimeline.PREV_STRETCH] != 0; } } + return; } // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES); - let mix = frames[frame + IkConstraintTimeline.PREV_MIX]; - let softness = frames[frame + IkConstraintTimeline.PREV_SOFTNESS]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime)); + const frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES); + const mix = frames[frame + IkConstraintTimeline.PREV_MIX]; + const softness = frames[frame + IkConstraintTimeline.PREV_SOFTNESS]; + const frameTime = frames[frame]; + const percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime)); if (blend == MixBlend.setup) { constraint.mix = constraint.data.mix + (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.data.mix) * alpha; - constraint.softness = constraint.data.softness - + (softness + (frames[frame + IkConstraintTimeline.SOFTNESS] - softness) * percent - constraint.data.softness) * alpha; + constraint.softness = constraint.data.softness + (softness + (frames[frame + IkConstraintTimeline.SOFTNESS] - softness) * percent - constraint.data.softness) * alpha; if (direction == MixDirection.mixOut) { constraint.bendDirection = constraint.data.bendDirection; constraint.compress = constraint.data.compress; @@ -1271,8 +1427,15 @@ export class IkConstraintTimeline extends CurveTimeline { */ export class TransformConstraintTimeline extends CurveTimeline { static ENTRIES = 5; - static PREV_TIME = -5; static PREV_ROTATE = -4; static PREV_TRANSLATE = -3; static PREV_SCALE = -2; static PREV_SHEAR = -1; - static ROTATE = 1; static TRANSLATE = 2; static SCALE = 3; static SHEAR = 4; + static PREV_TIME = -5; + static PREV_ROTATE = -4; + static PREV_TRANSLATE = -3; + static PREV_SCALE = -2; + static PREV_SHEAR = -1; + static ROTATE = 1; + static TRANSLATE = 2; + static SCALE = 3; + static SHEAR = 4; /** The index of the transform constraint slot in {@link Skeleton#transformConstraints} that will be changed. */ transformConstraintIndex: number; @@ -1280,17 +1443,17 @@ export class TransformConstraintTimeline extends CurveTimeline { /** The time in seconds, rotate mix, translate mix, scale mix, and shear mix for each key frame. */ frames: ArrayLike; // time, rotate mix, translate mix, scale mix, shear mix, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * TransformConstraintTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.transformConstraint << 24) + this.transformConstraintIndex; } /** The time in seconds, rotate mix, translate mix, scale mix, and shear mix for the specified key frame. */ - setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number) { + setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number) { frameIndex *= TransformConstraintTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + TransformConstraintTimeline.ROTATE] = rotateMix; @@ -1299,19 +1462,22 @@ export class TransformConstraintTimeline extends CurveTimeline { this.frames[frameIndex + TransformConstraintTimeline.SHEAR] = shearMix; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + + const constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; - let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; if (!constraint.active) return; if (time < frames[0]) { - let data = constraint.data; + const data = constraint.data; + switch (blend) { case MixBlend.setup: constraint.rotateMix = data.rotateMix; constraint.translateMix = data.translateMix; constraint.scaleMix = data.scaleMix; constraint.shearMix = data.shearMix; + return; case MixBlend.first: constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha; @@ -1319,26 +1485,36 @@ export class TransformConstraintTimeline extends CurveTimeline { constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha; constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha; } + return; } - let rotate = 0, translate = 0, scale = 0, shear = 0; - if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { // Time is after last frame. - let i = frames.length; + let rotate = 0; + let translate = 0; + let scale = 0; + let shear = 0; + + if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { + // Time is after last frame. + const i = frames.length; + rotate = frames[i + TransformConstraintTimeline.PREV_ROTATE]; translate = frames[i + TransformConstraintTimeline.PREV_TRANSLATE]; scale = frames[i + TransformConstraintTimeline.PREV_SCALE]; shear = frames[i + TransformConstraintTimeline.PREV_SHEAR]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES); + rotate = frames[frame + TransformConstraintTimeline.PREV_ROTATE]; translate = frames[frame + TransformConstraintTimeline.PREV_TRANSLATE]; scale = frames[frame + TransformConstraintTimeline.PREV_SCALE]; shear = frames[frame + TransformConstraintTimeline.PREV_SHEAR]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / TransformConstraintTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / TransformConstraintTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime) + ); rotate += (frames[frame + TransformConstraintTimeline.ROTATE] - rotate) * percent; translate += (frames[frame + TransformConstraintTimeline.TRANSLATE] - translate) * percent; @@ -1346,7 +1522,8 @@ export class TransformConstraintTimeline extends CurveTimeline { shear += (frames[frame + TransformConstraintTimeline.SHEAR] - shear) * percent; } if (blend == MixBlend.setup) { - let data = constraint.data; + const data = constraint.data; + constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha; constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha; constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha; @@ -1366,7 +1543,8 @@ export class TransformConstraintTimeline extends CurveTimeline { */ export class PathConstraintPositionTimeline extends CurveTimeline { static ENTRIES = 2; - static PREV_TIME = -2; static PREV_VALUE = -1; + static PREV_TIME = -2; + static PREV_VALUE = -1; static VALUE = 1; /** The index of the path constraint slot in {@link Skeleton#pathConstraints} that will be changed. */ @@ -1375,54 +1553,60 @@ export class PathConstraintPositionTimeline extends CurveTimeline { /** The time in seconds and path constraint position for each key frame. */ frames: ArrayLike; // time, position, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * PathConstraintPositionTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.pathConstraintPosition << 24) + this.pathConstraintIndex; } /** Sets the time in seconds and path constraint position for the specified key frame. */ - setFrame (frameIndex: number, time: number, value: number) { + setFrame(frameIndex: number, time: number, value: number) { frameIndex *= PathConstraintPositionTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + PathConstraintPositionTimeline.VALUE] = value; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.position = constraint.data.position; + return; case MixBlend.first: constraint.position += (constraint.data.position - constraint.position) * alpha; } + return; } let position = 0; - if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) // Time is after last frame. + + if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) + // Time is after last frame. position = frames[frames.length + PathConstraintPositionTimeline.PREV_VALUE]; else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES); + position = frames[frame + PathConstraintPositionTimeline.PREV_VALUE]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / PathConstraintPositionTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / PathConstraintPositionTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime) + ); position += (frames[frame + PathConstraintPositionTimeline.VALUE] - position) * percent; } - if (blend == MixBlend.setup) - constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; - else - constraint.position += (position - constraint.position) * alpha; + if (blend == MixBlend.setup) constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; + else constraint.position += (position - constraint.position) * alpha; } } @@ -1431,47 +1615,53 @@ export class PathConstraintPositionTimeline extends CurveTimeline { * @public */ export class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline { - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); } - getPropertyId () { + getPropertyId() { return (TimelineType.pathConstraintSpacing << 24) + this.pathConstraintIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.spacing = constraint.data.spacing; + return; case MixBlend.first: constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha; } + return; } let spacing = 0; - if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) // Time is after last frame. + + if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) + // Time is after last frame. spacing = frames[frames.length + PathConstraintSpacingTimeline.PREV_VALUE]; else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES); + spacing = frames[frame + PathConstraintSpacingTimeline.PREV_VALUE]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / PathConstraintSpacingTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / PathConstraintSpacingTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime) + ); spacing += (frames[frame + PathConstraintSpacingTimeline.VALUE] - spacing) * percent; } - if (blend == MixBlend.setup) - constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; - else - constraint.spacing += (spacing - constraint.spacing) * alpha; + if (blend == MixBlend.setup) constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; + else constraint.spacing += (spacing - constraint.spacing) * alpha; } } @@ -1482,8 +1672,11 @@ export class PathConstraintSpacingTimeline extends PathConstraintPositionTimelin */ export class PathConstraintMixTimeline extends CurveTimeline { static ENTRIES = 3; - static PREV_TIME = -3; static PREV_ROTATE = -2; static PREV_TRANSLATE = -1; - static ROTATE = 1; static TRANSLATE = 2; + static PREV_TIME = -3; + static PREV_ROTATE = -2; + static PREV_TRANSLATE = -1; + static ROTATE = 1; + static TRANSLATE = 2; /** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */ pathConstraintIndex: number; @@ -1491,52 +1684,61 @@ export class PathConstraintMixTimeline extends CurveTimeline { /** The time in seconds, rotate mix, and translate mix for each key frame. */ frames: ArrayLike; // time, rotate mix, translate mix, ... - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount); this.frames = Utils.newFloatArray(frameCount * PathConstraintMixTimeline.ENTRIES); } - getPropertyId () { + getPropertyId() { return (TimelineType.pathConstraintMix << 24) + this.pathConstraintIndex; } /** The time in seconds, rotate mix, and translate mix for the specified key frame. */ - setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number) { + setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number) { frameIndex *= PathConstraintMixTimeline.ENTRIES; this.frames[frameIndex] = time; this.frames[frameIndex + PathConstraintMixTimeline.ROTATE] = rotateMix; this.frames[frameIndex + PathConstraintMixTimeline.TRANSLATE] = translateMix; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let frames = this.frames; - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const frames = this.frames; + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.rotateMix = constraint.data.rotateMix; constraint.translateMix = constraint.data.translateMix; + return; case MixBlend.first: constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha; constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha; } + return; } - let rotate = 0, translate = 0; - if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { // Time is after last frame. + let rotate = 0; + let translate = 0; + + if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { + // Time is after last frame. rotate = frames[frames.length + PathConstraintMixTimeline.PREV_ROTATE]; translate = frames[frames.length + PathConstraintMixTimeline.PREV_TRANSLATE]; } else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES); + const frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES); + rotate = frames[frame + PathConstraintMixTimeline.PREV_ROTATE]; translate = frames[frame + PathConstraintMixTimeline.PREV_TRANSLATE]; - let frameTime = frames[frame]; - let percent = this.getCurvePercent(frame / PathConstraintMixTimeline.ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime)); + const frameTime = frames[frame]; + const percent = this.getCurvePercent( + frame / PathConstraintMixTimeline.ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime) + ); rotate += (frames[frame + PathConstraintMixTimeline.ROTATE] - rotate) * percent; translate += (frames[frame + PathConstraintMixTimeline.TRANSLATE] - translate) * percent; diff --git a/packages/runtime-3.8/src/core/AnimationState.ts b/packages/runtime-3.8/src/core/AnimationState.ts index af2a518e..b7a7a4f1 100644 --- a/packages/runtime-3.8/src/core/AnimationState.ts +++ b/packages/runtime-3.8/src/core/AnimationState.ts @@ -1,25 +1,9 @@ -import { - IAnimationState, - IAnimationStateListener, - ITrackEntry, - MixBlend, - MixDirection, - MathUtils, - Pool, - IntSet, - Utils -} from "@pixi-spine/base"; -import { - Animation, - AttachmentTimeline, - DrawOrderTimeline, - EventTimeline, - RotateTimeline, Timeline -} from './Animation'; -import {AnimationStateData} from "./AnimationStateData"; -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; -import type {Slot} from "./Slot"; +import { IAnimationState, IAnimationStateListener, ITrackEntry, MixBlend, MixDirection, MathUtils, Pool, IntSet, Utils } from '@pixi-spine/base'; +import { Animation, AttachmentTimeline, DrawOrderTimeline, EventTimeline, RotateTimeline, Timeline } from './Animation'; +import type { AnimationStateData } from './AnimationStateData'; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; +import type { Slot } from './Slot'; /** Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies * multiple animations on top of each other (layering). @@ -29,7 +13,7 @@ import type {Slot} from "./Slot"; * @public */ export class AnimationState implements IAnimationState { - static emptyAnimation = new Animation("", [], 0); + static emptyAnimation = new Animation('', [], 0); /** 1. A previously applied timeline has set this property. * @@ -90,16 +74,18 @@ export class AnimationState implements IAnimationState { trackEntryPool = new Pool(() => new TrackEntry()); - constructor (data: AnimationStateData) { + constructor(data: AnimationStateData) { this.data = data; } /** Increments each track entry {@link TrackEntry#trackTime()}, setting queued animations as current if needed. */ - update (delta: number) { + update(delta: number) { delta *= this.timeScale; - let tracks = this.tracks; + const tracks = this.tracks; + for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (current == null) continue; current.animationLast = current.nextAnimationLast; @@ -115,9 +101,11 @@ export class AnimationState implements IAnimationState { } let next = current.next; + if (next != null) { // When the next entry's delay is passed, change to the next entry, preserving leftover time. - let nextTime = current.trackLast - next.delay; + const nextTime = current.trackLast - next.delay; + if (nextTime >= 0) { next.delay = 0; next.trackTime += current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale; @@ -138,6 +126,7 @@ export class AnimationState implements IAnimationState { if (current.mixingFrom != null && this.updateMixingFrom(current, delta)) { // End mixing from entries once all have completed. let from = current.mixingFrom; + current.mixingFrom = null; if (from != null) from.mixingTo = null; while (from != null) { @@ -153,11 +142,12 @@ export class AnimationState implements IAnimationState { } /** Returns true when all mixing from entries are complete. */ - updateMixingFrom (to: TrackEntry, delta: number): boolean { - let from = to.mixingFrom; + updateMixingFrom(to: TrackEntry, delta: number): boolean { + const from = to.mixingFrom; + if (from == null) return true; - let finished = this.updateMixingFrom(from, delta); + const finished = this.updateMixingFrom(from, delta); from.animationLast = from.nextAnimationLast; from.trackLast = from.nextTrackLast; @@ -171,64 +161,69 @@ export class AnimationState implements IAnimationState { to.interruptAlpha = from.interruptAlpha; this.queue.end(from); } + return finished; } from.trackTime += delta * from.timeScale; to.mixTime += delta; + return false; } /** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the * animation state can be applied to multiple skeletons to pose them identically. * @returns True if any animations were applied. */ - apply (skeleton: Skeleton) : boolean { - if (skeleton == null) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton): boolean { + if (skeleton == null) throw new Error('skeleton cannot be null.'); if (this.animationsChanged) this._animationsChanged(); - let events = this.events; - let tracks = this.tracks; + const events = this.events; + const tracks = this.tracks; let applied = false; for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (current == null || current.delay > 0) continue; applied = true; - let blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; + const blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; // Apply mixing from entries first. let mix = current.alpha; - if (current.mixingFrom != null) - mix *= this.applyMixingFrom(current, skeleton, blend); - else if (current.trackTime >= current.trackEnd && current.next == null) - mix = 0; + + if (current.mixingFrom != null) mix *= this.applyMixingFrom(current, skeleton, blend); + else if (current.trackTime >= current.trackEnd && current.next == null) mix = 0; // Apply current entry. - let animationLast = current.animationLast, animationTime = current.getAnimationTime(); - let timelineCount = current.animation.timelines.length; - let timelines = current.animation.timelines; + const animationLast = current.animationLast; + const animationTime = current.getAnimationTime(); + const timelineCount = current.animation.timelines.length; + const timelines = current.animation.timelines; + if ((i == 0 && mix == 1) || blend == MixBlend.add) { for (let ii = 0; ii < timelineCount; ii++) { // Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets // to sometimes stop rendering when using color correction, as their RGBA values become NaN. // (https://github.com/pixijs/pixi-spine/issues/302) Utils.webkit602BugfixHelper(mix, blend); - var timeline = timelines[ii]; - if (timeline instanceof AttachmentTimeline) - this.applyAttachmentTimeline(timeline, skeleton, animationTime, blend, true); - else - timeline.apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.mixIn); + const timeline = timelines[ii]; + + if (timeline instanceof AttachmentTimeline) this.applyAttachmentTimeline(timeline, skeleton, animationTime, blend, true); + else timeline.apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.mixIn); } } else { - let timelineMode = current.timelineMode; + const timelineMode = current.timelineMode; + + const firstFrame = current.timelinesRotation.length == 0; - let firstFrame = current.timelinesRotation.length == 0; if (firstFrame) Utils.setArraySize(current.timelinesRotation, timelineCount << 1, null); - let timelinesRotation = current.timelinesRotation; + const timelinesRotation = current.timelinesRotation; for (let ii = 0; ii < timelineCount; ii++) { - let timeline = timelines[ii]; - let timelineBlend = timelineMode[ii] == AnimationState.SUBSEQUENT ? blend : MixBlend.setup; + const timeline = timelines[ii]; + const timelineBlend = timelineMode[ii] == AnimationState.SUBSEQUENT ? blend : MixBlend.setup; + if (timeline instanceof RotateTimeline) { this.applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); } else if (timeline instanceof AttachmentTimeline) { @@ -249,27 +244,34 @@ export class AnimationState implements IAnimationState { // Set slots attachments to the setup pose, if needed. This occurs if an animation that is mixing out sets attachments so // subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or // the time is before the first key). - var setupState = this.unkeyedState + AnimationState.SETUP; - var slots = skeleton.slots; - for (var i = 0, n = skeleton.slots.length; i < n; i++) { - var slot = slots[i]; + const setupState = this.unkeyedState + AnimationState.SETUP; + const slots = skeleton.slots; + + for (let i = 0, n = skeleton.slots.length; i < n; i++) { + const slot = slots[i]; + if (slot.attachmentState == setupState) { - var attachmentName = slot.data.attachmentName; + const attachmentName = slot.data.attachmentName; + slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(slot.data.index, attachmentName)); } } this.unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot. this.queue.drain(); + return applied; } - applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { - let from = to.mixingFrom; + applyMixingFrom(to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { + const from = to.mixingFrom; + if (from.mixingFrom != null) this.applyMixingFrom(from, skeleton, blend); let mix = 0; - if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes. + + if (to.mixDuration == 0) { + // Single frame mix to undo mixingFrom changes. mix = 1; if (blend == MixBlend.first) blend = MixBlend.setup; } else { @@ -278,29 +280,34 @@ export class AnimationState implements IAnimationState { if (blend != MixBlend.first) blend = from.mixBlend; } - let events = mix < from.eventThreshold ? this.events : null; - let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; - let animationLast = from.animationLast, animationTime = from.getAnimationTime(); - let timelineCount = from.animation.timelines.length; - let timelines = from.animation.timelines; - let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix); + const events = mix < from.eventThreshold ? this.events : null; + const attachments = mix < from.attachmentThreshold; + const drawOrder = mix < from.drawOrderThreshold; + const animationLast = from.animationLast; + const animationTime = from.getAnimationTime(); + const timelineCount = from.animation.timelines.length; + const timelines = from.animation.timelines; + const alphaHold = from.alpha * to.interruptAlpha; + const alphaMix = alphaHold * (1 - mix); + if (blend == MixBlend.add) { - for (let i = 0; i < timelineCount; i++) - timelines[i].apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.mixOut); + for (let i = 0; i < timelineCount; i++) timelines[i].apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.mixOut); } else { - let timelineMode = from.timelineMode; - let timelineHoldMix = from.timelineHoldMix; + const timelineMode = from.timelineMode; + const timelineHoldMix = from.timelineHoldMix; + + const firstFrame = from.timelinesRotation.length == 0; - let firstFrame = from.timelinesRotation.length == 0; if (firstFrame) Utils.setArraySize(from.timelinesRotation, timelineCount << 1, null); - let timelinesRotation = from.timelinesRotation; + const timelinesRotation = from.timelinesRotation; from.totalAlpha = 0; for (let i = 0; i < timelineCount; i++) { - let timeline = timelines[i]; + const timeline = timelines[i]; let direction = MixDirection.mixOut; let timelineBlend: MixBlend; let alpha = 0; + switch (timelineMode[i]) { case AnimationState.SUBSEQUENT: if (!drawOrder && timeline instanceof DrawOrderTimeline) continue; @@ -321,21 +328,19 @@ export class AnimationState implements IAnimationState { break; default: timelineBlend = MixBlend.setup; - let holdMix = timelineHoldMix[i]; + const holdMix = timelineHoldMix[i]; + alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration); break; } from.totalAlpha += alpha; - if (timeline instanceof RotateTimeline) - this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame); - else if (timeline instanceof AttachmentTimeline) - this.applyAttachmentTimeline(timeline, skeleton, animationTime, timelineBlend, attachments); + if (timeline instanceof RotateTimeline) this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame); + else if (timeline instanceof AttachmentTimeline) this.applyAttachmentTimeline(timeline, skeleton, animationTime, timelineBlend, attachments); else { // This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109 Utils.webkit602BugfixHelper(alpha, blend); - if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup) - direction = MixDirection.mixIn; + if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup) direction = MixDirection.mixIn; timeline.apply(skeleton, animationLast, animationTime, events, alpha, timelineBlend, direction); } } @@ -349,22 +354,23 @@ export class AnimationState implements IAnimationState { return mix; } - applyAttachmentTimeline (timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) { + applyAttachmentTimeline(timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) { + const slot = skeleton.slots[timeline.slotIndex]; - var slot = skeleton.slots[timeline.slotIndex]; if (!slot.bone.active) return; - var frames = timeline.frames; - if (time < frames[0]) { // Time is before first frame. - if (blend == MixBlend.setup || blend == MixBlend.first) - this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments); - } - else { - var frameIndex; - if (time >= frames[frames.length - 1]) // Time is after last frame. + const frames = timeline.frames; + + if (time < frames[0]) { + // Time is before first frame. + if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments); + } else { + let frameIndex; + + if (time >= frames[frames.length - 1]) + // Time is after last frame. frameIndex = frames.length - 1; - else - frameIndex = Animation.binarySearch(frames, time) - 1; + else frameIndex = Animation.binarySearch(frames, time) - 1; this.setAttachment(skeleton, slot, timeline.attachmentNames[frameIndex], attachments); } @@ -372,27 +378,28 @@ export class AnimationState implements IAnimationState { if (slot.attachmentState <= this.unkeyedState) slot.attachmentState = this.unkeyedState + AnimationState.SETUP; } - setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string, attachments: boolean) { + setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string, attachments: boolean) { slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(slot.data.index, attachmentName)); if (attachments) slot.attachmentState = this.unkeyedState + AnimationState.CURRENT; } - - applyRotateTimeline (timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend, - timelinesRotation: Array, i: number, firstFrame: boolean) { - + applyRotateTimeline(timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend, timelinesRotation: Array, i: number, firstFrame: boolean) { if (firstFrame) timelinesRotation[i] = 0; if (alpha == 1) { timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn); + return; } - let rotateTimeline = timeline as RotateTimeline; - let frames = rotateTimeline.frames; - let bone = skeleton.bones[rotateTimeline.boneIndex]; + const rotateTimeline = timeline as RotateTimeline; + const frames = rotateTimeline.frames; + const bone = skeleton.bones[rotateTimeline.boneIndex]; + if (!bone.active) return; - let r1 = 0, r2 = 0; + let r1 = 0; + let r2 = 0; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: @@ -405,15 +412,15 @@ export class AnimationState implements IAnimationState { } } else { r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation; - if (time >= frames[frames.length - RotateTimeline.ENTRIES]) // Time is after last frame. + if (time >= frames[frames.length - RotateTimeline.ENTRIES]) + // Time is after last frame. r2 = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION]; else { // Interpolate between the previous frame and the current frame. - let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); - let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; - let frameTime = frames[frame]; - let percent = rotateTimeline.getCurvePercent((frame >> 1) - 1, - 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); + const frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); + const prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; + const frameTime = frames[frame]; + const percent = rotateTimeline.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation; r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360; @@ -423,12 +430,16 @@ export class AnimationState implements IAnimationState { } // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. - let total = 0, diff = r2 - r1; + let total = 0; + let diff = r2 - r1; + diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360; if (diff == 0) { total = timelinesRotation[i]; } else { - let lastTotal = 0, lastDiff = 0; + let lastTotal = 0; + let lastDiff = 0; + if (firstFrame) { lastTotal = 0; lastDiff = diff; @@ -436,14 +447,16 @@ export class AnimationState implements IAnimationState { lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. lastDiff = timelinesRotation[i + 1]; // Difference between bones. } - let current = diff > 0, dir = lastTotal >= 0; + const current = diff > 0; + let dir = lastTotal >= 0; // Detect cross at 0 (not 180). + if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) { // A cross after a 360 rotation is a loop. if (Math.abs(lastTotal) > 180) lastTotal += 360 * MathUtils.signum(lastTotal); dir = current; } - total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. + total = diff + lastTotal - (lastTotal % 360); // Store loops as part of lastTotal. if (dir != current) total += 360 * MathUtils.signum(lastTotal); timelinesRotation[i] = total; } @@ -452,16 +465,20 @@ export class AnimationState implements IAnimationState { bone.rotation = r1 - (16384 - ((16384.499999999996 - r1 / 360) | 0)) * 360; } - queueEvents (entry: TrackEntry, animationTime: number) { - let animationStart = entry.animationStart, animationEnd = entry.animationEnd; - let duration = animationEnd - animationStart; - let trackLastWrapped = entry.trackLast % duration; + queueEvents(entry: TrackEntry, animationTime: number) { + const animationStart = entry.animationStart; + const animationEnd = entry.animationEnd; + const duration = animationEnd - animationStart; + const trackLastWrapped = entry.trackLast % duration; // Queue events before complete. - let events = this.events; - let i = 0, n = events.length; + const events = this.events; + let i = 0; + const n = events.length; + for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < trackLastWrapped) break; if (event.time > animationEnd) continue; // Discard events outside animation start/end. this.queue.event(entry, event); @@ -469,15 +486,15 @@ export class AnimationState implements IAnimationState { // Queue complete if completed a loop iteration or the animation. let complete = false; - if (entry.loop) - complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; - else - complete = animationTime >= animationEnd && entry.animationLast < animationEnd; + + if (entry.loop) complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; + else complete = animationTime >= animationEnd && entry.animationLast < animationEnd; if (complete) this.queue.complete(entry); // Queue events after complete. for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < animationStart) continue; // Discard events outside animation start/end. this.queue.event(entry, events[i]); } @@ -487,11 +504,11 @@ export class AnimationState implements IAnimationState { * * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose, * rather than leaving them in their current pose. */ - clearTracks () { - let oldDrainDisabled = this.queue.drainDisabled; + clearTracks() { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; - for (let i = 0, n = this.tracks.length; i < n; i++) - this.clearTrack(i); + for (let i = 0, n = this.tracks.length; i < n; i++) this.clearTrack(i); this.tracks.length = 0; this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); @@ -501,9 +518,10 @@ export class AnimationState implements IAnimationState { * * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose, * rather than leaving them in their current pose. */ - clearTrack (trackIndex: number) { + clearTrack(trackIndex: number) { if (trackIndex >= this.tracks.length) return; - let current = this.tracks[trackIndex]; + const current = this.tracks[trackIndex]; + if (current == null) return; this.queue.end(current); @@ -511,8 +529,10 @@ export class AnimationState implements IAnimationState { this.disposeNext(current); let entry = current; + while (true) { - let from = entry.mixingFrom; + const from = entry.mixingFrom; + if (from == null) break; this.queue.end(from); entry.mixingFrom = null; @@ -525,8 +545,9 @@ export class AnimationState implements IAnimationState { this.queue.drain(); } - setCurrent (index: number, current: TrackEntry, interrupt: boolean) { - let from = this.expandToIndex(index); + setCurrent(index: number, current: TrackEntry, interrupt: boolean) { + const from = this.expandToIndex(index); + this.tracks[index] = current; if (from != null) { @@ -536,8 +557,7 @@ export class AnimationState implements IAnimationState { current.mixTime = 0; // Store the interrupted mix percentage. - if (from.mixingFrom != null && from.mixDuration > 0) - current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); + if (from.mixingFrom != null && from.mixDuration > 0) current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in. } @@ -548,9 +568,11 @@ export class AnimationState implements IAnimationState { /** Sets an animation by name. * * {@link #setAnimationWith(}. */ - setAnimation (trackIndex: number, animationName: string, loop: boolean) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (animation == null) throw new Error("Animation not found: " + animationName); + setAnimation(trackIndex: number, animationName: string, loop: boolean) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (animation == null) throw new Error(`Animation not found: ${animationName}`); + return this.setAnimationWith(trackIndex, animation, loop); } @@ -560,10 +582,11 @@ export class AnimationState implements IAnimationState { * duration. In either case {@link TrackEntry#trackEnd} determines when the track is cleared. * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - setAnimationWith (trackIndex: number, animation: Animation, loop: boolean) { - if (animation == null) throw new Error("animation cannot be null."); + setAnimationWith(trackIndex: number, animation: Animation, loop: boolean) { + if (animation == null) throw new Error('animation cannot be null.'); let interrupt = true; let current = this.expandToIndex(trackIndex); + if (current != null) { if (current.nextTrackLast == -1) { // Don't mix from an entry that was never applied. @@ -573,21 +596,24 @@ export class AnimationState implements IAnimationState { this.disposeNext(current); current = current.mixingFrom; interrupt = false; - } else - this.disposeNext(current); + } else this.disposeNext(current); } - let entry = this.trackEntry(trackIndex, animation, loop, current); + const entry = this.trackEntry(trackIndex, animation, loop, current); + this.setCurrent(trackIndex, entry, interrupt); this.queue.drain(); + return entry; } /** Queues an animation by name. * * See {@link #addAnimationWith()}. */ - addAnimation (trackIndex: number, animationName: string, loop: boolean, delay: number) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (animation == null) throw new Error("Animation not found: " + animationName); + addAnimation(trackIndex: number, animationName: string, loop: boolean, delay: number) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (animation == null) throw new Error(`Animation not found: ${animationName}`); + return this.addAnimationWith(trackIndex, animation, loop, delay); } @@ -599,16 +625,16 @@ export class AnimationState implements IAnimationState { * previous entry is looping, its next loop completion is used instead of its duration. * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - addAnimationWith (trackIndex: number, animation: Animation, loop: boolean, delay: number) { - if (animation == null) throw new Error("animation cannot be null."); + addAnimationWith(trackIndex: number, animation: Animation, loop: boolean, delay: number) { + if (animation == null) throw new Error('animation cannot be null.'); let last = this.expandToIndex(trackIndex); + if (last != null) { - while (last.next != null) - last = last.next; + while (last.next != null) last = last.next; } - let entry = this.trackEntry(trackIndex, animation, loop, last); + const entry = this.trackEntry(trackIndex, animation, loop, last); if (last == null) { this.setCurrent(trackIndex, entry, true); @@ -616,19 +642,18 @@ export class AnimationState implements IAnimationState { } else { last.next = entry; if (delay <= 0) { - let duration = last.animationEnd - last.animationStart; + const duration = last.animationEnd - last.animationStart; + if (duration != 0) { - if (last.loop) - delay += duration * (1 + ((last.trackTime / duration) | 0)); - else - delay += Math.max(duration, last.trackTime); + if (last.loop) delay += duration * (1 + ((last.trackTime / duration) | 0)); + else delay += Math.max(duration, last.trackTime); delay -= this.data.getMix(last.animation, animation); - } else - delay = last.trackTime; + } else delay = last.trackTime; } } entry.delay = delay; + return entry; } @@ -646,10 +671,12 @@ export class AnimationState implements IAnimationState { * {@link TrackEntry#setMixDuration()}. Mixing from an empty animation causes the new animation to be applied more and * more over the mix duration. Properties keyed in the new animation transition from the value from lower tracks or from the * setup pose value if no lower tracks key the property to the value keyed in the new animation. */ - setEmptyAnimation (trackIndex: number, mixDuration: number) { - let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false); + setEmptyAnimation(trackIndex: number, mixDuration: number) { + const entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false); + entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } @@ -664,37 +691,43 @@ export class AnimationState implements IAnimationState { * loop completion is used instead of its duration. * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - addEmptyAnimation (trackIndex: number, mixDuration: number, delay: number) { + addEmptyAnimation(trackIndex: number, mixDuration: number, delay: number) { if (delay <= 0) delay -= mixDuration; - let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay); + const entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay); + entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } /** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix * duration. */ - setEmptyAnimations (mixDuration: number) { - let oldDrainDisabled = this.queue.drainDisabled; + setEmptyAnimations(mixDuration: number) { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; for (let i = 0, n = this.tracks.length; i < n; i++) { - let current = this.tracks[i]; + const current = this.tracks[i]; + if (current != null) this.setEmptyAnimation(current.trackIndex, mixDuration); } this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); } - expandToIndex (index: number) { + expandToIndex(index: number) { if (index < this.tracks.length) return this.tracks[index]; Utils.ensureArrayCapacity(this.tracks, index + 1, null); this.tracks.length = index + 1; + return null; } /** @param last May be null. */ - trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) { - let entry = this.trackEntryPool.obtain(); + trackEntry(trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) { + const entry = this.trackEntryPool.obtain(); + entry.trackIndex = trackIndex; entry.animation = animation; entry.loop = loop; @@ -721,11 +754,13 @@ export class AnimationState implements IAnimationState { entry.mixTime = 0; entry.mixDuration = last == null ? 0 : this.data.getMix(last.animation, animation); entry.mixBlend = MixBlend.replace; + return entry; } - disposeNext (entry: TrackEntry) { + disposeNext(entry: TrackEntry) { let next = entry.next; + while (next != null) { this.queue.dispose(next); next = next.next; @@ -733,132 +768,144 @@ export class AnimationState implements IAnimationState { entry.next = null; } - _animationsChanged () { + _animationsChanged() { this.animationsChanged = false; this.propertyIDs.clear(); for (let i = 0, n = this.tracks.length; i < n; i++) { let entry = this.tracks[i]; + if (entry == null) continue; - while (entry.mixingFrom != null) - entry = entry.mixingFrom; + while (entry.mixingFrom != null) entry = entry.mixingFrom; do { if (entry.mixingFrom == null || entry.mixBlend != MixBlend.add) this.computeHold(entry); entry = entry.mixingTo; - } while (entry != null) + } while (entry != null); } } - computeHold (entry: TrackEntry) { - let to = entry.mixingTo; - let timelines = entry.animation.timelines; - let timelinesCount = entry.animation.timelines.length; - let timelineMode = Utils.setArraySize(entry.timelineMode, timelinesCount); + computeHold(entry: TrackEntry) { + const to = entry.mixingTo; + const timelines = entry.animation.timelines; + const timelinesCount = entry.animation.timelines.length; + const timelineMode = Utils.setArraySize(entry.timelineMode, timelinesCount); + entry.timelineHoldMix.length = 0; - let timelineDipMix = Utils.setArraySize(entry.timelineHoldMix, timelinesCount); - let propertyIDs = this.propertyIDs; + const timelineDipMix = Utils.setArraySize(entry.timelineHoldMix, timelinesCount); + const propertyIDs = this.propertyIDs; if (to != null && to.holdPrevious) { for (let i = 0; i < timelinesCount; i++) { timelineMode[i] = propertyIDs.add(timelines[i].getPropertyId()) ? AnimationState.HOLD_FIRST : AnimationState.HOLD_SUBSEQUENT; } + return; } - outer: - for (let i = 0; i < timelinesCount; i++) { - let timeline = timelines[i]; - let id = timeline.getPropertyId(); - if (!propertyIDs.add(id)) - timelineMode[i] = AnimationState.SUBSEQUENT; - else if (to == null || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline - || timeline instanceof EventTimeline || !to.animation.hasTimeline(id)) { - timelineMode[i] = AnimationState.FIRST; - } else { - for (let next = to.mixingTo; next != null; next = next.mixingTo) { - if (next.animation.hasTimeline(id)) continue; - if (entry.mixDuration > 0) { - timelineMode[i] = AnimationState.HOLD_MIX; - timelineDipMix[i] = next; - continue outer; - } - break; + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < timelinesCount; i++) { + const timeline = timelines[i]; + const id = timeline.getPropertyId(); + + if (!propertyIDs.add(id)) timelineMode[i] = AnimationState.SUBSEQUENT; + else if ( + to == null || + timeline instanceof AttachmentTimeline || + timeline instanceof DrawOrderTimeline || + timeline instanceof EventTimeline || + !to.animation.hasTimeline(id) + ) { + timelineMode[i] = AnimationState.FIRST; + } else { + for (let next = to.mixingTo; next != null; next = next.mixingTo) { + if (next.animation.hasTimeline(id)) continue; + if (entry.mixDuration > 0) { + timelineMode[i] = AnimationState.HOLD_MIX; + timelineDipMix[i] = next; + // eslint-disable-next-line no-labels + continue outer; } - timelineMode[i] = AnimationState.HOLD_FIRST; + break; } + timelineMode[i] = AnimationState.HOLD_FIRST; } + } } /** Returns the track entry for the animation currently playing on the track, or null if no animation is currently playing. */ - getCurrent (trackIndex: number) { + getCurrent(trackIndex: number) { if (trackIndex >= this.tracks.length) return null; + return this.tracks[trackIndex]; } /** Adds a listener to receive events for all track entries. */ - addListener (listener: AnimationStateListener) { - if (listener == null) throw new Error("listener cannot be null."); + addListener(listener: AnimationStateListener) { + if (listener == null) throw new Error('listener cannot be null.'); this.listeners.push(listener); } /** Removes the listener added with {@link #addListener()}. */ - removeListener (listener: AnimationStateListener) { - let index = this.listeners.indexOf(listener); + removeListener(listener: AnimationStateListener) { + const index = this.listeners.indexOf(listener); + if (index >= 0) this.listeners.splice(index, 1); } /** Removes all listeners added with {@link #addListener()}. */ - clearListeners () { + clearListeners() { this.listeners.length = 0; } /** Discards all listener notifications that have not yet been delivered. This can be useful to call from an * {@link AnimationStateListener} when it is known that further notifications that may have been already queued for delivery * are not wanted because new animations are being set. */ - clearListenerNotifications () { + clearListenerNotifications() { this.queue.clear(); } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; onEnd: (trackIndex: number) => any; - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; setAnimationByName(trackIndex: number, animationName: string, loop: boolean) { if (!AnimationState.deprecatedWarning1) { AnimationState.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on.'); } this.setAnimation(trackIndex, animationName, loop); } - private static deprecatedWarning2: boolean = false; + private static deprecatedWarning2 = false; addAnimationByName(trackIndex: number, animationName: string, loop: boolean, delay: number) { if (!AnimationState.deprecatedWarning2) { AnimationState.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on.'); } this.addAnimation(trackIndex, animationName, loop, delay); } - private static deprecatedWarning3: boolean = false; + private static deprecatedWarning3 = false; hasAnimation(animationName: string): boolean { - let animation = this.data.skeletonData.findAnimation(animationName); + const animation = this.data.skeletonData.findAnimation(animationName); + return animation !== null; } hasAnimationByName(animationName: string): boolean { if (!AnimationState.deprecatedWarning3) { AnimationState.deprecatedWarning3 = true; - console.warn("Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on.'); } + return this.hasAnimation(animationName); } } @@ -937,7 +984,6 @@ export class TrackEntry implements ITrackEntry { * loop back to {@link #animationStart} at this time. Defaults to the animation {@link Animation#duration}. */ animationEnd: number; - /** The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this * animation is applied, event timelines will fire all events between the `animationLast` time (exclusive) and * `animationTime` (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation @@ -959,7 +1005,8 @@ export class TrackEntry implements ITrackEntry { * looping. */ trackTime: number; - trackLast: number; nextTrackLast: number; + trackLast: number; + nextTrackLast: number; /** The track time in seconds when this animation will be removed from the track. Defaults to the highest possible float * value, meaning the animation will be applied until a new animation is set or the track is cleared. If the track end time @@ -1007,7 +1054,9 @@ export class TrackEntry implements ITrackEntry { * When using {@link AnimationState#addAnimation()} with a `delay` <= 0, note the * {@link #delay} is set using the mix duration from the {@link AnimationStateData}, not a mix duration set * afterward. */ - mixDuration: number; interruptAlpha: number; totalAlpha: number; + mixDuration: number; + interruptAlpha: number; + totalAlpha: number; /** Controls how properties keyed in the animation are mixed with lower tracks. Defaults to {@link MixBlend#replace}, which * replaces the values from the lower tracks with the animation values. {@link MixBlend#add} adds the animation values to @@ -1020,7 +1069,7 @@ export class TrackEntry implements ITrackEntry { timelineHoldMix = new Array(); timelinesRotation = new Array(); - reset () { + reset() { this.next = null; this.mixingFrom = null; this.mixingTo = null; @@ -1034,12 +1083,15 @@ export class TrackEntry implements ITrackEntry { /** Uses {@link #trackTime} to compute the `animationTime`, which is between {@link #animationStart} * and {@link #animationEnd}. When the `trackTime` is 0, the `animationTime` is equal to the * `animationStart` time. */ - getAnimationTime () { + getAnimationTime() { if (this.loop) { - let duration = this.animationEnd - this.animationStart; + const duration = this.animationEnd - this.animationStart; + if (duration == 0) return this.animationStart; + return (this.trackTime % duration) + this.animationStart; } + return Math.min(this.trackTime + this.animationStart, this.animationEnd); } @@ -1051,7 +1103,7 @@ export class TrackEntry implements ITrackEntry { /** Returns true if at least one loop has been completed. * * See {@link AnimationStateListener#complete()}. */ - isComplete () { + isComplete() { return this.trackTime >= this.animationEnd - this.animationStart; } @@ -1062,11 +1114,11 @@ export class TrackEntry implements ITrackEntry { * the short way or the long way around. The two rotations likely change over time, so which direction is the short or long * way also changes. If the short way was always chosen, bones would flip to the other side when that direction became the * long way. TrackEntry chooses the short way the first time it is applied and remembers that direction. */ - resetRotationDirections () { + resetRotationDirections() { this.timelinesRotation.length = 0; } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; @@ -1078,15 +1130,16 @@ export class TrackEntry implements ITrackEntry { get time() { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } + return this.trackTime; } set time(value: number) { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } this.trackTime = value; } @@ -1094,15 +1147,16 @@ export class TrackEntry implements ITrackEntry { get endTime() { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } + return this.trackTime; } set endTime(value: number) { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } this.trackTime = value; } @@ -1124,34 +1178,34 @@ export class EventQueue { this.animState = animState; } - start (entry: TrackEntry) { + start(entry: TrackEntry) { this.objects.push(EventType.start); this.objects.push(entry); this.animState.animationsChanged = true; } - interrupt (entry: TrackEntry) { + interrupt(entry: TrackEntry) { this.objects.push(EventType.interrupt); this.objects.push(entry); } - end (entry: TrackEntry) { + end(entry: TrackEntry) { this.objects.push(EventType.end); this.objects.push(entry); this.animState.animationsChanged = true; } - dispose (entry: TrackEntry) { + dispose(entry: TrackEntry) { this.objects.push(EventType.dispose); this.objects.push(entry); } - complete (entry: TrackEntry) { + complete(entry: TrackEntry) { this.objects.push(EventType.complete); this.objects.push(entry); } - event (entry: TrackEntry, event: Event) { + event(entry: TrackEntry, event: Event) { this.objects.push(EventType.event); this.objects.push(entry); this.objects.push(event); @@ -1162,65 +1216,65 @@ export class EventQueue { deprecateStuff() { if (!EventQueue.deprecatedWarning1) { EventQueue.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: onComplete, onStart, onEnd, onEvent art deprecated, please use listeners from now on. 'state.addListener({ complete: function(track, event) { } })'"); + console.warn( + "Spine Deprecation Warning: onComplete, onStart, onEnd, onEvent art deprecated, please use listeners from now on. 'state.addListener({ complete: function(track, event) { } })'" + ); } + return true; } - drain () { + drain() { if (this.drainDisabled) return; this.drainDisabled = true; - let objects = this.objects; - let listeners = this.animState.listeners; + const objects = this.objects; + const listeners = this.animState.listeners; for (let i = 0; i < objects.length; i += 2) { - let type = objects[i] as EventType; - let entry = objects[i + 1] as TrackEntry; + const type = objects[i] as EventType; + const entry = objects[i + 1] as TrackEntry; + switch (type) { case EventType.start: if (entry.listener != null && entry.listener.start) entry.listener.start(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].start) listeners[ii].start(entry); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].start) listeners[ii].start(entry); + // deprecation entry.onStart && this.deprecateStuff() && entry.onStart(entry.trackIndex); this.animState.onStart && this.deprecateStuff() && this.deprecateStuff && this.animState.onStart(entry.trackIndex); break; case EventType.interrupt: if (entry.listener != null && entry.listener.interrupt) entry.listener.interrupt(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].interrupt) listeners[ii].interrupt(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].interrupt) listeners[ii].interrupt(entry); break; case EventType.end: if (entry.listener != null && entry.listener.end) entry.listener.end(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].end) listeners[ii].end(entry); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].end) listeners[ii].end(entry); + // deprecation entry.onEnd && this.deprecateStuff() && entry.onEnd(entry.trackIndex); this.animState.onEnd && this.deprecateStuff() && this.animState.onEnd(entry.trackIndex); // Fall through. case EventType.dispose: if (entry.listener != null && entry.listener.dispose) entry.listener.dispose(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].dispose) listeners[ii].dispose(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].dispose) listeners[ii].dispose(entry); this.animState.trackEntryPool.free(entry); break; case EventType.complete: if (entry.listener != null && entry.listener.complete) entry.listener.complete(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].complete) listeners[ii].complete(entry); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].complete) listeners[ii].complete(entry); + // deprecation + + const count = MathUtils.toInt(entry.loopsCount()); - let count = MathUtils.toInt(entry.loopsCount()) ; entry.onComplete && this.deprecateStuff() && entry.onComplete(entry.trackIndex, count); this.animState.onComplete && this.deprecateStuff() && this.animState.onComplete(entry.trackIndex, count); break; case EventType.event: - let event = objects[i++ + 2] as Event; + const event = objects[i++ + 2] as Event; + if (entry.listener != null && entry.listener.event) entry.listener.event(entry, event); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].event) listeners[ii].event(entry, event); - //deprecation + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].event) listeners[ii].event(entry, event); + // deprecation entry.onEvent && this.deprecateStuff() && entry.onEvent(entry.trackIndex, event); this.animState.onEvent && this.deprecateStuff() && this.animState.onEvent(entry.trackIndex, event); break; @@ -1231,7 +1285,7 @@ export class EventQueue { this.drainDisabled = false; } - clear () { + clear() { this.objects.length = 0; } } @@ -1240,7 +1294,12 @@ export class EventQueue { * @public */ export enum EventType { - start, interrupt, end, dispose, complete, event + start, + interrupt, + end, + dispose, + complete, + event, } /** @@ -1248,45 +1307,39 @@ export enum EventType { */ export interface AnimationStateListener extends IAnimationStateListener { /** Invoked when this entry has been set as the current entry. */ - start? (entry: TrackEntry): void; + start?(entry: TrackEntry): void; /** Invoked when another entry has replaced this entry as the current entry. This entry may continue being applied for * mixing. */ - interrupt? (entry: TrackEntry): void; + interrupt?(entry: TrackEntry): void; /** Invoked when this entry is no longer the current entry and will never be applied again. */ - end? (entry: TrackEntry): void; + end?(entry: TrackEntry): void; /** Invoked when this entry will be disposed. This may occur without the entry ever being set as the current entry. * References to the entry should not be kept after dispose is called, as it may be destroyed or reused. */ - dispose? (entry: TrackEntry): void; + dispose?(entry: TrackEntry): void; /** Invoked every time this entry's animation completes a loop. */ - complete? (entry: TrackEntry): void; + complete?(entry: TrackEntry): void; /** Invoked when this entry's animation triggers an event. */ - event? (entry: TrackEntry, event: Event): void; + event?(entry: TrackEntry, event: Event): void; } /** * @public */ export abstract class AnimationStateAdapter implements AnimationStateListener { - start (entry: TrackEntry) { - } + start(entry: TrackEntry) {} - interrupt (entry: TrackEntry) { - } + interrupt(entry: TrackEntry) {} - end (entry: TrackEntry) { - } + end(entry: TrackEntry) {} - dispose (entry: TrackEntry) { - } + dispose(entry: TrackEntry) {} - complete (entry: TrackEntry) { - } + complete(entry: TrackEntry) {} - event (entry: TrackEntry, event: Event) { - } + event(entry: TrackEntry, event: Event) {} } diff --git a/packages/runtime-3.8/src/core/AnimationStateData.ts b/packages/runtime-3.8/src/core/AnimationStateData.ts index 52ca5ab0..8dbe58fc 100644 --- a/packages/runtime-3.8/src/core/AnimationStateData.ts +++ b/packages/runtime-3.8/src/core/AnimationStateData.ts @@ -1,6 +1,6 @@ -import {SkeletonData} from "./SkeletonData"; -import {IAnimation, IAnimationStateData, Map} from '@pixi-spine/base'; -import type { Animation } from "./Animation"; +import type { SkeletonData } from './SkeletonData'; +import type { IAnimation, IAnimationStateData, Map } from '@pixi-spine/base'; +import type { Animation } from './Animation'; /** * @public @@ -11,38 +11,42 @@ export class AnimationStateData implements IAnimationStateData 0.0001) { s = Math.abs(pa * pd - pb * pc) / s; pa /= this.skeleton.scaleX; @@ -134,12 +145,13 @@ export class Bone implements Updatable, IBone { pc = 0; prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg; } - let rx = rotation + shearX - prx; - let ry = rotation + shearY - prx + 90; - let la = MathUtils.cosDeg(rx) * scaleX; - let lb = MathUtils.cosDeg(ry) * scaleY; - let lc = MathUtils.sinDeg(rx) * scaleX; - let ld = MathUtils.sinDeg(ry) * scaleY; + const rx = rotation + shearX - prx; + const ry = rotation + shearY - prx + 90; + const la = MathUtils.cosDeg(rx) * scaleX; + const lb = MathUtils.cosDeg(ry) * scaleY; + const lc = MathUtils.sinDeg(rx) * scaleX; + const ld = MathUtils.sinDeg(ry) * scaleY; + m.a = pa * la - pb * lc; m.c = pa * lb - pb * ld; m.b = pc * la + pd * lc; @@ -148,28 +160,29 @@ export class Bone implements Updatable, IBone { } case TransformMode.NoScale: case TransformMode.NoScaleOrReflection: { - let cos = MathUtils.cosDeg(rotation); - let sin = MathUtils.sinDeg(rotation); + const cos = MathUtils.cosDeg(rotation); + const sin = MathUtils.sinDeg(rotation); let za = (pa * cos + pb * sin) / sx; let zc = (pc * cos + pd * sin) / sy; let s = Math.sqrt(za * za + zc * zc); + if (s > 0.00001) s = 1 / s; za *= s; zc *= s; s = Math.sqrt(za * za + zc * zc); if ( - this.data.transformMode == TransformMode.NoScale - && (pa * pd - pb * pc < 0) != (settings.yDown? - (this.skeleton.scaleX < 0 != this.skeleton.scaleY > 0) : - (this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0)) - ) s = -s; - let r = Math.PI / 2 + Math.atan2(zc, za); - let zb = Math.cos(r) * s; - let zd = Math.sin(r) * s; - let la = MathUtils.cosDeg(shearX) * scaleX; - let lb = MathUtils.cosDeg(90 + shearY) * scaleY; - let lc = MathUtils.sinDeg(shearX) * scaleX; - let ld = MathUtils.sinDeg(90 + shearY) * scaleY; + this.data.transformMode == TransformMode.NoScale && + pa * pd - pb * pc < 0 != (settings.yDown ? this.skeleton.scaleX < 0 != this.skeleton.scaleY > 0 : this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0) + ) + s = -s; + const r = Math.PI / 2 + Math.atan2(zc, za); + const zb = Math.cos(r) * s; + const zd = Math.sin(r) * s; + const la = MathUtils.cosDeg(shearX) * scaleX; + const lb = MathUtils.cosDeg(90 + shearY) * scaleY; + const lc = MathUtils.sinDeg(shearX) * scaleX; + const ld = MathUtils.sinDeg(90 + shearY) * scaleY; + m.a = za * la + zb * lc; m.c = za * lb + zb * ld; m.b = zc * la + zd * lc; @@ -184,7 +197,8 @@ export class Bone implements Updatable, IBone { } setToSetupPose() { - let data = this.data; + const data = this.data; + this.x = data.x; this.y = data.y; this.rotation = data.rotation; @@ -203,12 +217,14 @@ export class Bone implements Updatable, IBone { } getWorldScaleX() { - let m = this.matrix; + const m = this.matrix; + return Math.sqrt(m.a * m.a + m.c * m.c); } getWorldScaleY() { - let m = this.matrix; + const m = this.matrix; + return Math.sqrt(m.b * m.b + m.d * m.d); } @@ -218,8 +234,9 @@ export class Bone implements Updatable, IBone { * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. */ updateAppliedTransform() { this.appliedValid = true; - let parent = this.parent; - let m = this.matrix; + const parent = this.parent; + const m = this.matrix; + if (parent == null) { this.ax = m.tx; this.ay = m.ty; @@ -228,25 +245,30 @@ export class Bone implements Updatable, IBone { this.ascaleY = Math.sqrt(m.c * m.c + m.d * m.d); this.ashearX = 0; this.ashearY = Math.atan2(m.a * m.c + m.b * m.d, m.a * m.d - m.b * m.c) * MathUtils.radDeg; + return; } - let pm = parent.matrix; - let pid = 1 / (pm.a * pm.d - pm.b * pm.c); - let dx = m.tx - pm.tx, dy = m.ty - pm.ty; - this.ax = (dx * pm.d * pid - dy * pm.c * pid); - this.ay = (dy * pm.a * pid - dx * pm.b * pid); - let ia = pid * pm.d; - let id = pid * pm.a; - let ib = pid * pm.c; - let ic = pid * pm.b; - let ra = ia * m.a - ib * m.b; - let rb = ia * m.c - ib * m.d; - let rc = id * m.b - ic * m.a; - let rd = id * m.d - ic * m.c; + const pm = parent.matrix; + const pid = 1 / (pm.a * pm.d - pm.b * pm.c); + const dx = m.tx - pm.tx; + const dy = m.ty - pm.ty; + + this.ax = dx * pm.d * pid - dy * pm.c * pid; + this.ay = dy * pm.a * pid - dx * pm.b * pid; + const ia = pid * pm.d; + const id = pid * pm.a; + const ib = pid * pm.c; + const ic = pid * pm.b; + const ra = ia * m.a - ib * m.b; + const rb = ia * m.c - ib * m.d; + const rc = id * m.b - ic * m.a; + const rd = id * m.d - ic * m.c; + this.ashearX = 0; this.ascaleX = Math.sqrt(ra * ra + rc * rc); if (this.ascaleX > 0.0001) { - let det = ra * rd - rb * rc; + const det = ra * rd - rb * rc; + this.ascaleY = det / this.ascaleX; this.ashearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg; this.arotation = Math.atan2(rc, ra) * MathUtils.radDeg; @@ -259,39 +281,57 @@ export class Bone implements Updatable, IBone { } worldToLocal(world: Vector2) { - let m = this.matrix; - let a = m.a, b = m.c, c = m.b, d = m.d; - let invDet = 1 / (a * d - b * c); - let x = world.x - m.tx, y = world.y - m.ty; - world.x = (x * d * invDet - y * b * invDet); - world.y = (y * a * invDet - x * c * invDet); + const m = this.matrix; + const a = m.a; + const b = m.c; + const c = m.b; + const d = m.d; + const invDet = 1 / (a * d - b * c); + const x = world.x - m.tx; + const y = world.y - m.ty; + + world.x = x * d * invDet - y * b * invDet; + world.y = y * a * invDet - x * c * invDet; + return world; } localToWorld(local: Vector2) { - let m = this.matrix; - let x = local.x, y = local.y; + const m = this.matrix; + const x = local.x; + const y = local.y; + local.x = x * m.a + y * m.c + m.tx; local.y = x * m.b + y * m.d + m.ty; + return local; } - worldToLocalRotation (worldRotation: number) { - let sin = MathUtils.sinDeg(worldRotation), cos = MathUtils.cosDeg(worldRotation); - let mat = this.matrix; + worldToLocalRotation(worldRotation: number) { + const sin = MathUtils.sinDeg(worldRotation); + const cos = MathUtils.cosDeg(worldRotation); + const mat = this.matrix; + return Math.atan2(mat.a * sin - mat.b * cos, mat.d * cos - mat.c * sin) * MathUtils.radDeg; } - localToWorldRotation (localRotation: number) { - let sin = MathUtils.sinDeg(localRotation), cos = MathUtils.cosDeg(localRotation); - let mat = this.matrix; + localToWorldRotation(localRotation: number) { + const sin = MathUtils.sinDeg(localRotation); + const cos = MathUtils.cosDeg(localRotation); + const mat = this.matrix; + return Math.atan2(cos * mat.b + sin * mat.d, cos * mat.a + sin * mat.c) * MathUtils.radDeg; } - rotateWorld (degrees: number) { - let mat = this.matrix; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees); + rotateWorld(degrees: number) { + const mat = this.matrix; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + const cos = MathUtils.cosDeg(degrees); + const sin = MathUtils.sinDeg(degrees); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; diff --git a/packages/runtime-3.8/src/core/BoneData.ts b/packages/runtime-3.8/src/core/BoneData.ts index abdc33a0..3f527569 100644 --- a/packages/runtime-3.8/src/core/BoneData.ts +++ b/packages/runtime-3.8/src/core/BoneData.ts @@ -1,4 +1,4 @@ -import {Color, TransformMode} from '@pixi-spine/base'; +import { Color, TransformMode } from '@pixi-spine/base'; /** * @public @@ -21,8 +21,8 @@ export class BoneData { color = new Color(); constructor(index: number, name: string, parent: BoneData) { - if (index < 0) throw new Error("index must be >= 0."); - if (name == null) throw new Error("name cannot be null."); + if (index < 0) throw new Error('index must be >= 0.'); + if (name == null) throw new Error('name cannot be null.'); this.index = index; this.name = name; this.parent = parent; diff --git a/packages/runtime-3.8/src/core/Constraint.ts b/packages/runtime-3.8/src/core/Constraint.ts index c4ce6625..ab246042 100644 --- a/packages/runtime-3.8/src/core/Constraint.ts +++ b/packages/runtime-3.8/src/core/Constraint.ts @@ -2,5 +2,5 @@ * @public */ export abstract class ConstraintData { - constructor(public name: string, public order: number, public skinRequired: boolean) { } + constructor(public name: string, public order: number, public skinRequired: boolean) {} } diff --git a/packages/runtime-3.8/src/core/Event.ts b/packages/runtime-3.8/src/core/Event.ts index ac8acd9e..55010bac 100644 --- a/packages/runtime-3.8/src/core/Event.ts +++ b/packages/runtime-3.8/src/core/Event.ts @@ -1,5 +1,5 @@ -import {EventData} from "./EventData"; -import {IEvent} from "@pixi-spine/base"; +import type { EventData } from './EventData'; +import type { IEvent } from '@pixi-spine/base'; /** * @public @@ -13,9 +13,8 @@ export class Event implements IEvent { volume: number; balance: number; - constructor(time: number, data: EventData) { - if (data == null) throw new Error("data cannot be null."); + if (data == null) throw new Error('data cannot be null.'); this.time = time; this.data = data; } diff --git a/packages/runtime-3.8/src/core/EventData.ts b/packages/runtime-3.8/src/core/EventData.ts index 6951be45..47a270a2 100644 --- a/packages/runtime-3.8/src/core/EventData.ts +++ b/packages/runtime-3.8/src/core/EventData.ts @@ -1,4 +1,4 @@ -import {IEventData} from "@pixi-spine/base"; +import type { IEventData } from '@pixi-spine/base'; /** * @public @@ -12,7 +12,7 @@ export class EventData implements IEventData { volume: number; balance: number; - constructor (name: string) { + constructor(name: string) { this.name = name; } } diff --git a/packages/runtime-3.8/src/core/IkConstraint.ts b/packages/runtime-3.8/src/core/IkConstraint.ts index 80c0eb20..b0408912 100644 --- a/packages/runtime-3.8/src/core/IkConstraint.ts +++ b/packages/runtime-3.8/src/core/IkConstraint.ts @@ -1,8 +1,8 @@ -import {Updatable} from "./Updatable"; -import {IkConstraintData} from "./IkConstraintData"; -import {Bone} from "./Bone"; -import {Skeleton} from "./Skeleton"; -import {IIkConstraint, MathUtils, TransformMode} from "@pixi-spine/base"; +import type { Updatable } from './Updatable'; +import type { IkConstraintData } from './IkConstraintData'; +import type { Bone } from './Bone'; +import type { Skeleton } from './Skeleton'; +import { IIkConstraint, MathUtils, TransformMode } from '@pixi-spine/base'; /** * @public @@ -18,9 +18,9 @@ export class IkConstraint implements IIkConstraint, Updatable { softness = 0; active = false; - constructor (data: IkConstraintData, skeleton: Skeleton) { - if (data == null) throw new Error("data cannot be null."); - if (skeleton == null) throw new Error("skeleton cannot be null."); + constructor(data: IkConstraintData, skeleton: Skeleton) { + if (data == null) throw new Error('data cannot be null.'); + if (skeleton == null) throw new Error('skeleton cannot be null.'); this.data = data; this.mix = data.mix; this.softness = data.softness; @@ -29,22 +29,22 @@ export class IkConstraint implements IIkConstraint, Updatable { this.stretch = data.stretch; this.bones = new Array(); - for (let i = 0; i < data.bones.length; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0; i < data.bones.length; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findBone(data.target.name); } - isActive () { + isActive() { return this.active; } - apply () { + apply() { this.update(); } - update () { - let target = this.target; - let bones = this.bones; + update() { + const target = this.target; + const bones = this.bones; + switch (bones.length) { case 1: this.apply1(bones[0], target.worldX, target.worldY, this.compress, this.stretch, this.data.uniform, this.mix); @@ -57,40 +57,48 @@ export class IkConstraint implements IIkConstraint, Updatable { /** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world * coordinate system. */ - apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { + apply1(bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { if (!bone.appliedValid) bone.updateAppliedTransform(); - let p = bone.parent.matrix; - + const p = bone.parent.matrix; - let pa = p.a, pb = p.c, pc = p.b, pd = p.d; - let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0; + const pa = p.a; + let pb = p.c; + const pc = p.b; + let pd = p.d; + let rotationIK = -bone.ashearX - bone.arotation; + let tx = 0; + let ty = 0; - switch(bone.data.transformMode) { + switch (bone.data.transformMode) { case TransformMode.OnlyTranslation: tx = targetX - bone.worldX; ty = targetY - bone.worldY; break; case TransformMode.NoRotationOrReflection: - let s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); - let sa = pa / bone.skeleton.scaleX; - let sc = pc / bone.skeleton.scaleY; + const s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); + const sa = pa / bone.skeleton.scaleX; + const sc = pc / bone.skeleton.scaleY; + pb = -sc * s * bone.skeleton.scaleX; pd = sa * s * bone.skeleton.scaleY; rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg; // Fall through default: - let x = targetX - p.tx, y = targetY - p.ty; - let d = pa * pd - pb * pc; + const x = targetX - p.tx; + const y = targetY - p.ty; + const d = pa * pd - pb * pc; + tx = (x * pd - y * pb) / d - bone.ax; ty = (y * pa - x * pc) / d - bone.ay; } rotationIK += Math.atan2(ty, tx) * MathUtils.radDeg; if (bone.ascaleX < 0) rotationIK += 180; - if (rotationIK > 180) - rotationIK -= 360; + if (rotationIK > 180) rotationIK -= 360; else if (rotationIK < -180) rotationIK += 360; - let sx = bone.ascaleX, sy = bone.ascaleY; + let sx = bone.ascaleX; + let sy = bone.ascaleY; + if (compress || stretch) { switch (bone.data.transformMode) { case TransformMode.NoScale: @@ -98,30 +106,41 @@ export class IkConstraint implements IIkConstraint, Updatable { tx = targetX - bone.worldX; ty = targetY - bone.worldY; } - let b = bone.data.length * sx, dd = Math.sqrt(tx * tx + ty * ty); - if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) { - let s = (dd / b - 1) * alpha + 1; + const b = bone.data.length * sx; + const dd = Math.sqrt(tx * tx + ty * ty); + + if ((compress && dd < b) || (stretch && dd > b && b > 0.0001)) { + const s = (dd / b - 1) * alpha + 1; + sx *= s; if (uniform) sy *= s; } } - bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, - bone.ashearY); + bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY); } /** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The * target is specified in the world coordinate system. * @param child A direct descendant of the parent bone. */ - apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, softness: number, alpha: number) { + apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, softness: number, alpha: number) { if (alpha == 0) { child.updateWorldTransform(); + return; } if (!parent.appliedValid) parent.updateAppliedTransform(); if (!child.appliedValid) child.updateAppliedTransform(); - let px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX; - let pmat = parent.matrix; - let os1 = 0, os2 = 0, s2 = 0; + const px = parent.ax; + const py = parent.ay; + let psx = parent.ascaleX; + let sx = psx; + let psy = parent.ascaleY; + let csx = child.ascaleX; + const pmat = parent.matrix; + let os1 = 0; + let os2 = 0; + let s2 = 0; + if (psx < 0) { psx = -psx; os1 = 180; @@ -137,10 +156,17 @@ export class IkConstraint implements IIkConstraint, Updatable { if (csx < 0) { csx = -csx; os2 = 180; - } else - os2 = 0; - let cx = child.ax, cy = 0, cwx = 0, cwy = 0, a = pmat.a, b = pmat.c, c = pmat.b, d = pmat.d; - let u = Math.abs(psx - psy) <= 0.0001; + } else os2 = 0; + const cx = child.ax; + let cy = 0; + let cwx = 0; + let cwy = 0; + let a = pmat.a; + let b = pmat.c; + let c = pmat.b; + let d = pmat.d; + const u = Math.abs(psx - psy) <= 0.0001; + if (!u) { cy = 0; cwx = a * cx + pmat.tx; @@ -150,108 +176,137 @@ export class IkConstraint implements IIkConstraint, Updatable { cwx = a * cx + b * cy + pmat.tx; cwy = c * cx + d * cy + pmat.ty; } - let pp = parent.parent.matrix; + const pp = parent.parent.matrix; + a = pp.a; b = pp.c; c = pp.b; d = pp.d; - let id = 1 / (a * d - b * c), x = cwx - pp.tx, y = cwy - pp.ty; - let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; - let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2; + const id = 1 / (a * d - b * c); + let x = cwx - pp.tx; + let y = cwy - pp.ty; + const dx = (x * d - y * b) * id - px; + const dy = (y * a - x * c) * id - py; + const l1 = Math.sqrt(dx * dx + dy * dy); + let l2 = child.data.length * csx; + let a1; + let a2; + if (l1 < 0.0001) { this.apply1(parent, targetX, targetY, false, stretch, false, alpha); child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); + return; } x = targetX - pp.tx; y = targetY - pp.ty; - let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py; + let tx = (x * d - y * b) * id - px; + let ty = (y * a - x * c) * id - py; let dd = tx * tx + ty * ty; + if (softness != 0) { - softness *= psx * (csx + 1) / 2; - let td = Math.sqrt(dd), sd = td - l1 - l2 * psx + softness; + softness *= (psx * (csx + 1)) / 2; + const td = Math.sqrt(dd); + const sd = td - l1 - l2 * psx + softness; + if (sd > 0) { let p = Math.min(1, sd / (softness * 2)) - 1; + p = (sd - softness * (1 - p * p)) / td; tx -= p * tx; ty -= p * ty; dd = tx * tx + ty * ty; } } - outer: - if (u) { - l2 *= psx; - let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); - if (cos < -1) - cos = -1; - else if (cos > 1) { - cos = 1; - if (stretch) sx *= (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; - } - a2 = Math.acos(cos) * bendDir; - a = l1 + l2 * cos; - b = l2 * Math.sin(a2); - a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); - } else { - a = psx * l2; - b = psy * l2; - let aa = a * a, bb = b * b, ta = Math.atan2(ty, tx); - c = bb * l1 * l1 + aa * dd - aa * bb; - let c1 = -2 * bb * l1, c2 = bb - aa; - d = c1 * c1 - 4 * c2 * c; - if (d >= 0) { - let q = Math.sqrt(d); - if (c1 < 0) q = -q; - q = -(c1 + q) / 2; - let r0 = q / c2, r1 = c / q; - let r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; - if (r * r <= dd) { - y = Math.sqrt(dd - r * r) * bendDir; - a1 = ta - Math.atan2(y, r); - a2 = Math.atan2(y / psy, (r - l1) / psx); - break outer; - } + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: if (u) { + l2 *= psx; + let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); + + if (cos < -1) cos = -1; + else if (cos > 1) { + cos = 1; + if (stretch) sx *= (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; + } + a2 = Math.acos(cos) * bendDir; + a = l1 + l2 * cos; + b = l2 * Math.sin(a2); + a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); + } else { + a = psx * l2; + b = psy * l2; + const aa = a * a; + const bb = b * b; + const ta = Math.atan2(ty, tx); + + c = bb * l1 * l1 + aa * dd - aa * bb; + const c1 = -2 * bb * l1; + const c2 = bb - aa; + + d = c1 * c1 - 4 * c2 * c; + if (d >= 0) { + let q = Math.sqrt(d); + + if (c1 < 0) q = -q; + q = -(c1 + q) / 2; + const r0 = q / c2; + const r1 = c / q; + const r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; + + if (r * r <= dd) { + y = Math.sqrt(dd - r * r) * bendDir; + a1 = ta - Math.atan2(y, r); + a2 = Math.atan2(y / psy, (r - l1) / psx); + // eslint-disable-next-line no-labels + break outer; } - let minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0; - let maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; - c = -a * l1 / (aa - bb); - if (c >= -1 && c <= 1) { - c = Math.acos(c); - x = a * Math.cos(c) + l1; - y = b * Math.sin(c); - d = x * x + y * y; - if (d < minDist) { - minAngle = c; - minDist = d; - minX = x; - minY = y; - } - if (d > maxDist) { - maxAngle = c; - maxDist = d; - maxX = x; - maxY = y; - } + } + let minAngle = MathUtils.PI; + let minX = l1 - a; + let minDist = minX * minX; + let minY = 0; + let maxAngle = 0; + let maxX = l1 + a; + let maxDist = maxX * maxX; + let maxY = 0; + + c = (-a * l1) / (aa - bb); + if (c >= -1 && c <= 1) { + c = Math.acos(c); + x = a * Math.cos(c) + l1; + y = b * Math.sin(c); + d = x * x + y * y; + if (d < minDist) { + minAngle = c; + minDist = d; + minX = x; + minY = y; } - if (dd <= (minDist + maxDist) / 2) { - a1 = ta - Math.atan2(minY * bendDir, minX); - a2 = minAngle * bendDir; - } else { - a1 = ta - Math.atan2(maxY * bendDir, maxX); - a2 = maxAngle * bendDir; + if (d > maxDist) { + maxAngle = c; + maxDist = d; + maxX = x; + maxY = y; } } - let os = Math.atan2(cy, cx) * s2; + if (dd <= (minDist + maxDist) / 2) { + a1 = ta - Math.atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } else { + a1 = ta - Math.atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + const os = Math.atan2(cy, cx) * s2; let rotation = parent.arotation; + a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation; - if (a1 > 180) - a1 -= 360; + if (a1 > 180) a1 -= 360; else if (a1 < -180) a1 += 360; parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0); rotation = child.arotation; a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation; - if (a2 > 180) - a2 -= 360; + if (a2 > 180) a2 -= 360; else if (a2 < -180) a2 += 360; child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); } diff --git a/packages/runtime-3.8/src/core/IkConstraintData.ts b/packages/runtime-3.8/src/core/IkConstraintData.ts index 258917f8..ad97b75f 100644 --- a/packages/runtime-3.8/src/core/IkConstraintData.ts +++ b/packages/runtime-3.8/src/core/IkConstraintData.ts @@ -1,6 +1,6 @@ -import {ConstraintData} from "./Constraint"; -import {BoneData} from "./BoneData"; -import {IIkConstraintData} from "@pixi-spine/base"; +import { ConstraintData } from './Constraint'; +import type { BoneData } from './BoneData'; +import type { IIkConstraintData } from '@pixi-spine/base'; /** * @public @@ -15,7 +15,7 @@ export class IkConstraintData extends ConstraintData implements IIkConstraintDat mix = 1; softness = 0; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } diff --git a/packages/runtime-3.8/src/core/PathConstraint.ts b/packages/runtime-3.8/src/core/PathConstraint.ts index 945227e4..af032006 100644 --- a/packages/runtime-3.8/src/core/PathConstraint.ts +++ b/packages/runtime-3.8/src/core/PathConstraint.ts @@ -1,35 +1,42 @@ -import {PathAttachment} from "./attachments"; -import {Updatable} from "./Updatable"; -import {PathConstraintData, SpacingMode} from "./PathConstraintData"; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Skeleton} from "./Skeleton"; -import {MathUtils, PositionMode, RotateMode, Utils} from "@pixi-spine/base"; +import { PathAttachment } from './attachments'; +import type { Updatable } from './Updatable'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import type { Bone } from './Bone'; +import type { Slot } from './Slot'; +import type { Skeleton } from './Skeleton'; +import { MathUtils, PositionMode, RotateMode, Utils } from '@pixi-spine/base'; /** * @public */ export class PathConstraint implements Updatable { - static NONE = -1; static BEFORE = -2; static AFTER = -3; + static NONE = -1; + static BEFORE = -2; + static AFTER = -3; static epsilon = 0.00001; data: PathConstraintData; bones: Array; target: Slot; - position = 0; spacing = 0; rotateMix = 0; translateMix = 0; + position = 0; + spacing = 0; + rotateMix = 0; + translateMix = 0; - spaces = new Array(); positions = new Array(); - world = new Array(); curves = new Array(); lengths = new Array(); + spaces = new Array(); + positions = new Array(); + world = new Array(); + curves = new Array(); + lengths = new Array(); segments = new Array(); active = false; - constructor (data: PathConstraintData, skeleton: Skeleton) { - if (data == null) throw new Error("data cannot be null."); - if (skeleton == null) throw new Error("skeleton cannot be null."); + constructor(data: PathConstraintData, skeleton: Skeleton) { + if (data == null) throw new Error('data cannot be null.'); + if (skeleton == null) throw new Error('skeleton cannot be null.'); this.data = data; this.bones = new Array(); - for (let i = 0, n = data.bones.length; i < n; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0, n = data.bones.length; i < n; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findSlot(data.target.name); this.position = data.position; this.spacing = data.spacing; @@ -37,72 +44,97 @@ export class PathConstraint implements Updatable { this.translateMix = data.translateMix; } - isActive () { + isActive() { return this.active; } - apply () { + apply() { this.update(); } - update () { - let attachment = this.target.getAttachment(); + update() { + const attachment = this.target.getAttachment(); + if (!(attachment instanceof PathAttachment)) return; - let rotateMix = this.rotateMix, translateMix = this.translateMix; - let translate = translateMix > 0, rotate = rotateMix > 0; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const translate = translateMix > 0; + const rotate = rotateMix > 0; + if (!translate && !rotate) return; - let data = this.data; - let spacingMode = data.spacingMode; - let lengthSpacing = spacingMode == SpacingMode.Length; - let rotateMode = data.rotateMode; - let tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale; - let boneCount = this.bones.length, spacesCount = tangents ? boneCount : boneCount + 1; - let bones = this.bones; - let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array = null; - let spacing = this.spacing; + const data = this.data; + const spacingMode = data.spacingMode; + const lengthSpacing = spacingMode == SpacingMode.Length; + const rotateMode = data.rotateMode; + const tangents = rotateMode == RotateMode.Tangent; + const scale = rotateMode == RotateMode.ChainScale; + const boneCount = this.bones.length; + const spacesCount = tangents ? boneCount : boneCount + 1; + const bones = this.bones; + const spaces = Utils.setArraySize(this.spaces, spacesCount); + let lengths: Array = null; + const spacing = this.spacing; + if (scale || lengthSpacing) { if (scale) lengths = Utils.setArraySize(this.lengths, boneCount); - for (let i = 0, n = spacesCount - 1; i < n;) { - let bone = bones[i]; - let setupLength = bone.data.length; + for (let i = 0, n = spacesCount - 1; i < n; ) { + const bone = bones[i]; + const setupLength = bone.data.length; + if (setupLength < PathConstraint.epsilon) { if (scale) lengths[i] = 0; spaces[++i] = 0; } else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; - let length = Math.sqrt(x * x + y * y); + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + const length = Math.sqrt(x * x + y * y); + if (scale) lengths[i] = length; - spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + spaces[++i] = ((lengthSpacing ? setupLength + spacing : spacing) * length) / setupLength; } } } else { - for (let i = 1; i < spacesCount; i++) - spaces[i] = spacing; + for (let i = 1; i < spacesCount; i++) spaces[i] = spacing; } - let positions = this.computeWorldPositions(attachment, spacesCount, tangents, - data.positionMode == PositionMode.Percent, spacingMode == SpacingMode.Percent); - let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; + const positions = this.computeWorldPositions( + attachment, + spacesCount, + tangents, + data.positionMode == PositionMode.Percent, + spacingMode == SpacingMode.Percent + ); + let boneX = positions[0]; + let boneY = positions[1]; + let offsetRotation = data.offsetRotation; let tip = false; - if (offsetRotation == 0) - tip = rotateMode == RotateMode.Chain; + + if (offsetRotation == 0) tip = rotateMode == RotateMode.Chain; else { tip = false; - let p = this.target.bone.matrix; + const p = this.target.bone.matrix; + offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.degRad : -MathUtils.degRad; } for (let i = 0, p = 3; i < boneCount; i++, p += 3) { - let bone = bones[i]; - let mat = bone.matrix; + const bone = bones[i]; + const mat = bone.matrix; + mat.tx += (boneX - mat.tx) * translateMix; mat.ty += (boneY - mat.ty) * translateMix; - let x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; + const x = positions[p]; + const y = positions[p + 1]; + const dx = x - boneX; + const dy = y - boneY; + if (scale) { - let length = lengths[i]; + const length = lengths[i]; + if (length != 0) { - let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; + const s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; + mat.a *= s; mat.b *= s; } @@ -110,26 +142,32 @@ export class PathConstraint implements Updatable { boneX = x; boneY = y; if (rotate) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d, r = 0, cos = 0, sin = 0; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let r = 0; + let cos = 0; + let sin = 0; + if (tangents) - r = positions[p - 1]; - else if (spaces[i + 1] == 0) - r = positions[p + 2]; - else - r = Math.atan2(dy, dx); + if (tangents) r = positions[p - 1]; + else if (spaces[i + 1] == 0) r = positions[p + 2]; + else r = Math.atan2(dy, dx); r -= Math.atan2(c, a); if (tip) { cos = Math.cos(r); sin = Math.sin(r); - let length = bone.data.length; + const length = bone.data.length; + boneX += (length * (cos * a - sin * c) - dx) * rotateMix; boneY += (length * (sin * a + cos * c) - dy) * rotateMix; } else { r += offsetRotation; } - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= rotateMix; cos = Math.cos(r); @@ -143,26 +181,31 @@ export class PathConstraint implements Updatable { } } - computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean, - percentSpacing: boolean) { - let target = this.target; + computeWorldPositions(path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean, percentSpacing: boolean) { + const target = this.target; let position = this.position; - let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array = null; - let closed = path.closed; - let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE; + const spaces = this.spaces; + const out = Utils.setArraySize(this.positions, spacesCount * 3 + 2); + let world: Array = null; + const closed = path.closed; + let verticesLength = path.worldVerticesLength; + let curveCount = verticesLength / 6; + let prevCurve = PathConstraint.NONE; if (!path.constantSpeed) { - let lengths = path.lengths; + const lengths = path.lengths; + curveCount -= closed ? 1 : 2; - let pathLength = lengths[curveCount]; + const pathLength = lengths[curveCount]; + if (percentPosition) position *= pathLength; if (percentSpacing) { - for (let i = 0; i < spacesCount; i++) - spaces[i] *= pathLength; + for (let i = 0; i < spacesCount; i++) spaces[i] *= pathLength; } world = Utils.setArraySize(this.world, 8); for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i]; + const space = spaces[i]; + position += space; let p = position; @@ -187,13 +230,14 @@ export class PathConstraint implements Updatable { } // Determine curve containing position. - for (;; curve++) { - let length = lengths[curve]; + for (; ; curve++) { + const length = lengths[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = lengths[curve - 1]; + const prev = lengths[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -203,12 +247,11 @@ export class PathConstraint implements Updatable { if (closed && curve == curveCount) { path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); path.computeWorldVertices(target, 0, 4, world, 4, 2); - } else - path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); + } else path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); } - this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, - tangents || (i > 0 && space == 0)); + this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0)); } + return out; } @@ -228,10 +271,25 @@ export class PathConstraint implements Updatable { } // Curve lengths. - let curves = Utils.setArraySize(this.curves, curveCount); + const curves = Utils.setArraySize(this.curves, curveCount); let pathLength = 0; - let x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; - let tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0; + let x1 = world[0]; + let y1 = world[1]; + let cx1 = 0; + let cy1 = 0; + let cx2 = 0; + let cy2 = 0; + let x2 = 0; + let y2 = 0; + let tmpx = 0; + let tmpy = 0; + let dddfx = 0; + let dddfy = 0; + let ddfx = 0; + let ddfy = 0; + let dfx = 0; + let dfy = 0; + for (let i = 0, w = 2; i < curveCount; i++, w += 6) { cx1 = world[w]; cy1 = world[w + 1]; @@ -265,14 +323,15 @@ export class PathConstraint implements Updatable { } if (percentPosition) position *= pathLength; if (percentSpacing) { - for (let i = 0; i < spacesCount; i++) - spaces[i] *= pathLength; + for (let i = 0; i < spacesCount; i++) spaces[i] *= pathLength; } - let segments = this.segments; + const segments = this.segments; let curveLength = 0; + for (let i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i]; + const space = spaces[i]; + position += space; let p = position; @@ -289,13 +348,14 @@ export class PathConstraint implements Updatable { } // Determine curve containing position. - for (;; curve++) { - let length = curves[curve]; + for (; ; curve++) { + const length = curves[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = curves[curve - 1]; + const prev = curves[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -305,6 +365,7 @@ export class PathConstraint implements Updatable { if (curve != prevCurve) { prevCurve = curve; let ii = curve * 6; + x1 = world[ii]; y1 = world[ii + 1]; cx1 = world[ii + 2]; @@ -344,42 +405,75 @@ export class PathConstraint implements Updatable { // Weight by segment length. p *= curveLength; - for (;; segment++) { - let length = segments[segment]; + for (; ; segment++) { + const length = segments[segment]; + if (p > length) continue; - if (segment == 0) - p /= length; + if (segment == 0) p /= length; else { - let prev = segments[segment - 1]; + const prev = segments[segment - 1]; + p = segment + (p - prev) / (length - prev); } break; } this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0)); } + return out; } - addBeforePosition (p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx); + addBeforePosition(p: number, temp: Array, i: number, out: Array, o: number) { + const x1 = temp[i]; + const y1 = temp[i + 1]; + const dx = temp[i + 2] - x1; + const dy = temp[i + 3] - y1; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } - addAfterPosition (p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx); + addAfterPosition(p: number, temp: Array, i: number, out: Array, o: number) { + const x1 = temp[i + 2]; + const y1 = temp[i + 3]; + const dx = x1 - temp[i]; + const dy = y1 - temp[i + 1]; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } - addCurvePosition (p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, - out: Array, o: number, tangents: boolean) { + addCurvePosition( + p: number, + x1: number, + y1: number, + cx1: number, + cy1: number, + cx2: number, + cy2: number, + x2: number, + y2: number, + out: Array, + o: number, + tangents: boolean + ) { if (p == 0 || isNaN(p)) p = 0.0001; - let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; - let ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; - let x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + const tt = p * p; + const ttt = tt * p; + const u = 1 - p; + const uu = u * u; + const uuu = uu * u; + const ut = u * p; + const ut3 = ut * 3; + const uut3 = u * ut3; + const utt3 = ut3 * p; + const x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt; + const y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + out[o] = x; out[o + 1] = y; if (tangents) out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); diff --git a/packages/runtime-3.8/src/core/PathConstraintData.ts b/packages/runtime-3.8/src/core/PathConstraintData.ts index 6de037df..1784eb42 100644 --- a/packages/runtime-3.8/src/core/PathConstraintData.ts +++ b/packages/runtime-3.8/src/core/PathConstraintData.ts @@ -1,7 +1,7 @@ -import {ConstraintData} from "./Constraint"; -import type {SlotData} from "./SlotData"; -import type {BoneData} from "./BoneData"; -import { RotateMode, PositionMode } from "@pixi-spine/base"; +import { ConstraintData } from './Constraint'; +import type { SlotData } from './SlotData'; +import type { BoneData } from './BoneData'; +import type { RotateMode, PositionMode } from '@pixi-spine/base'; /** * @public @@ -13,9 +13,12 @@ export class PathConstraintData extends ConstraintData { spacingMode: SpacingMode; rotateMode: RotateMode; offsetRotation: number; - position: number; spacing: number; rotateMix: number; translateMix: number; + position: number; + spacing: number; + rotateMix: number; + translateMix: number; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } @@ -23,5 +26,7 @@ export class PathConstraintData extends ConstraintData { * @public */ export enum SpacingMode { - Length, Fixed, Percent + Length, + Fixed, + Percent, } diff --git a/packages/runtime-3.8/src/core/Skeleton.ts b/packages/runtime-3.8/src/core/Skeleton.ts index 2bfe0806..62d4d09b 100644 --- a/packages/runtime-3.8/src/core/Skeleton.ts +++ b/packages/runtime-3.8/src/core/Skeleton.ts @@ -1,13 +1,13 @@ -import {Attachment, RegionAttachment, MeshAttachment, PathAttachment} from './attachments'; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Updatable} from "./Updatable"; -import {SkeletonData} from "./SkeletonData"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; -import {Skin} from "./Skin"; -import {Color, Utils, Vector2, ISkeleton} from "@pixi-spine/base"; +import { Attachment, RegionAttachment, MeshAttachment, PathAttachment } from './attachments'; +import { Bone } from './Bone'; +import { Slot } from './Slot'; +import type { Updatable } from './Updatable'; +import type { SkeletonData } from './SkeletonData'; +import { IkConstraint } from './IkConstraint'; +import { TransformConstraint } from './TransformConstraint'; +import { PathConstraint } from './PathConstraint'; +import type { Skin } from './Skin'; +import { Color, Utils, Vector2, ISkeleton } from '@pixi-spine/base'; /** * @public @@ -25,21 +25,24 @@ export class Skeleton implements ISkeleton { skin: Skin; color: Color; time = 0; - scaleX = 1; scaleY = 1; - x = 0; y = 0; + scaleX = 1; + scaleY = 1; + x = 0; + y = 0; - constructor (data: SkeletonData) { - if (data == null) throw new Error("data cannot be null."); + constructor(data: SkeletonData) { + if (data == null) throw new Error('data cannot be null.'); this.data = data; this.bones = new Array(); for (let i = 0; i < data.bones.length; i++) { - let boneData = data.bones[i]; + const boneData = data.bones[i]; let bone: Bone; - if (boneData.parent == null) - bone = new Bone(boneData, this, null); + + if (boneData.parent == null) bone = new Bone(boneData, this, null); else { - let parent = this.bones[boneData.parent.index]; + const parent = this.bones[boneData.parent.index]; + bone = new Bone(boneData, this, parent); parent.children.push(bone); } @@ -49,28 +52,32 @@ export class Skeleton implements ISkeleton { this.slots = new Array(); this.drawOrder = new Array(); for (let i = 0; i < data.slots.length; i++) { - let slotData = data.slots[i]; - let bone = this.bones[slotData.boneData.index]; - let slot = new Slot(slotData, bone); + const slotData = data.slots[i]; + const bone = this.bones[slotData.boneData.index]; + const slot = new Slot(slotData, bone); + this.slots.push(slot); this.drawOrder.push(slot); } this.ikConstraints = new Array(); for (let i = 0; i < data.ikConstraints.length; i++) { - let ikConstraintData = data.ikConstraints[i]; + const ikConstraintData = data.ikConstraints[i]; + this.ikConstraints.push(new IkConstraint(ikConstraintData, this)); } this.transformConstraints = new Array(); for (let i = 0; i < data.transformConstraints.length; i++) { - let transformConstraintData = data.transformConstraints[i]; + const transformConstraintData = data.transformConstraints[i]; + this.transformConstraints.push(new TransformConstraint(transformConstraintData, this)); } this.pathConstraints = new Array(); for (let i = 0; i < data.pathConstraints.length; i++) { - let pathConstraintData = data.pathConstraints[i]; + const pathConstraintData = data.pathConstraints[i]; + this.pathConstraints.push(new PathConstraint(pathConstraintData, this)); } @@ -78,22 +85,27 @@ export class Skeleton implements ISkeleton { this.updateCache(); } - updateCache () { - let updateCache = this._updateCache; + updateCache() { + const updateCache = this._updateCache; + updateCache.length = 0; this.updateCacheReset.length = 0; - let bones = this.bones; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + bone.sorted = bone.data.skinRequired; bone.active = !bone.sorted; } if (this.skin != null) { - let skinBones = this.skin.bones; + const skinBones = this.skin.bones; + for (let i = 0, n = this.skin.bones.length; i < n; i++) { let bone = this.bones[skinBones[i].index]; + do { bone.sorted = false; bone.active = true; @@ -103,54 +115,64 @@ export class Skeleton implements ISkeleton { } // IK first, lowest hierarchy depth first. - let ikConstraints = this.ikConstraints; - let transformConstraints = this.transformConstraints; - let pathConstraints = this.pathConstraints; - let ikCount = ikConstraints.length, transformCount = transformConstraints.length, pathCount = pathConstraints.length; - let constraintCount = ikCount + transformCount + pathCount; - - outer: - for (let i = 0; i < constraintCount; i++) { - for (let ii = 0; ii < ikCount; ii++) { - let constraint = ikConstraints[ii]; - if (constraint.data.order == i) { - this.sortIkConstraint(constraint); - continue outer; - } + const ikConstraints = this.ikConstraints; + const transformConstraints = this.transformConstraints; + const pathConstraints = this.pathConstraints; + const ikCount = ikConstraints.length; + const transformCount = transformConstraints.length; + const pathCount = pathConstraints.length; + const constraintCount = ikCount + transformCount + pathCount; + + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < constraintCount; i++) { + for (let ii = 0; ii < ikCount; ii++) { + const constraint = ikConstraints[ii]; + + if (constraint.data.order == i) { + this.sortIkConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < transformCount; ii++) { - let constraint = transformConstraints[ii]; - if (constraint.data.order == i) { - this.sortTransformConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < transformCount; ii++) { + const constraint = transformConstraints[ii]; + + if (constraint.data.order == i) { + this.sortTransformConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < pathCount; ii++) { - let constraint = pathConstraints[ii]; - if (constraint.data.order == i) { - this.sortPathConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < pathCount; ii++) { + const constraint = pathConstraints[ii]; + + if (constraint.data.order == i) { + this.sortPathConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } } + } - for (let i = 0, n = bones.length; i < n; i++) - this.sortBone(bones[i]); + for (let i = 0, n = bones.length; i < n; i++) this.sortBone(bones[i]); } - sortIkConstraint (constraint: IkConstraint) { + sortIkConstraint(constraint: IkConstraint) { constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin != null && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; - let target = constraint.target; + const target = constraint.target; + this.sortBone(target); - let constrained = constraint.bones; - let parent = constrained[0]; + const constrained = constraint.bones; + const parent = constrained[0]; + this.sortBone(parent); if (constrained.length > 1) { - let child = constrained[constrained.length - 1]; + const child = constrained[constrained.length - 1]; + if (!(this._updateCache.indexOf(child) > -1)) this.updateCacheReset.push(child); } @@ -160,46 +182,47 @@ export class Skeleton implements ISkeleton { constrained[constrained.length - 1].sorted = true; } - sortPathConstraint (constraint: PathConstraint) { - constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin != null && Utils.contains(this.skin.constraints, constraint.data, true))); + sortPathConstraint(constraint: PathConstraint) { + constraint.active = + constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin != null && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; - let slot = constraint.target; - let slotIndex = slot.data.index; - let slotBone = slot.bone; + const slot = constraint.target; + const slotIndex = slot.data.index; + const slotBone = slot.bone; + if (this.skin != null) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone); - if (this.data.defaultSkin != null && this.data.defaultSkin != this.skin) - this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); - for (let i = 0, n = this.data.skins.length; i < n; i++) - this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + if (this.data.defaultSkin != null && this.data.defaultSkin != this.skin) this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); + for (let i = 0, n = this.data.skins.length; i < n; i++) this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + + const attachment = slot.getAttachment(); - let attachment = slot.getAttachment(); if (attachment instanceof PathAttachment) this.sortPathConstraintAttachmentWith(attachment, slotBone); - let constrained = constraint.bones; - let boneCount = constrained.length; - for (let i = 0; i < boneCount; i++) - this.sortBone(constrained[i]); + const constrained = constraint.bones; + const boneCount = constrained.length; + + for (let i = 0; i < boneCount; i++) this.sortBone(constrained[i]); this._updateCache.push(constraint); - for (let i = 0; i < boneCount; i++) - this.sortReset(constrained[i].children); - for (let i = 0; i < boneCount; i++) - constrained[i].sorted = true; + for (let i = 0; i < boneCount; i++) this.sortReset(constrained[i].children); + for (let i = 0; i < boneCount; i++) constrained[i].sorted = true; } - sortTransformConstraint (constraint: TransformConstraint) { + sortTransformConstraint(constraint: TransformConstraint) { constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin != null && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; this.sortBone(constraint.target); - let constrained = constraint.bones; - let boneCount = constrained.length; + const constrained = constraint.bones; + const boneCount = constrained.length; + if (constraint.data.local) { for (let i = 0; i < boneCount; i++) { - let child = constrained[i]; + const child = constrained[i]; + this.sortBone(child.parent); if (!(this._updateCache.indexOf(child) > -1)) this.updateCacheReset.push(child); } @@ -211,49 +234,53 @@ export class Skeleton implements ISkeleton { this._updateCache.push(constraint); - for (let ii = 0; ii < boneCount; ii++) - this.sortReset(constrained[ii].children); - for (let ii = 0; ii < boneCount; ii++) - constrained[ii].sorted = true; + for (let ii = 0; ii < boneCount; ii++) this.sortReset(constrained[ii].children); + for (let ii = 0; ii < boneCount; ii++) constrained[ii].sorted = true; } - sortPathConstraintAttachment (skin: Skin, slotIndex: number, slotBone: Bone) { - let attachments = skin.attachments[slotIndex]; + sortPathConstraintAttachment(skin: Skin, slotIndex: number, slotBone: Bone) { + const attachments = skin.attachments[slotIndex]; + if (!attachments) return; - for (let key in attachments) { + for (const key in attachments) { this.sortPathConstraintAttachmentWith(attachments[key], slotBone); } } - sortPathConstraintAttachmentWith (attachment: Attachment, slotBone: Bone) { + sortPathConstraintAttachmentWith(attachment: Attachment, slotBone: Bone) { if (!(attachment instanceof PathAttachment)) return; - let pathBones = (attachment).bones; - if (pathBones == null) - this.sortBone(slotBone); + const pathBones = (attachment).bones; + + if (pathBones == null) this.sortBone(slotBone); else { - let bones = this.bones; + const bones = this.bones; let i = 0; + while (i < pathBones.length) { - let boneCount = pathBones[i++]; + const boneCount = pathBones[i++]; + for (let n = i + boneCount; i < n; i++) { - let boneIndex = pathBones[i]; + const boneIndex = pathBones[i]; + this.sortBone(bones[boneIndex]); } } } } - sortBone (bone: Bone) { + sortBone(bone: Bone) { if (bone.sorted) return; - let parent = bone.parent; + const parent = bone.parent; + if (parent != null) this.sortBone(parent); bone.sorted = true; this._updateCache.push(bone); } - sortReset (bones: Array) { + sortReset(bones: Array) { for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (!bone.active) continue; if (bone.sorted) this.sortReset(bone.children); bone.sorted = false; @@ -261,10 +288,12 @@ export class Skeleton implements ISkeleton { } /** Updates the world transform for each bone and applies constraints. */ - updateWorldTransform () { - let updateCacheReset = this.updateCacheReset; + updateWorldTransform() { + const updateCacheReset = this.updateCacheReset; + for (let i = 0, n = updateCacheReset.length; i < n; i++) { - let bone = updateCacheReset[i] as Bone; + const bone = updateCacheReset[i] as Bone; + bone.ax = bone.x; bone.ay = bone.y; bone.arotation = bone.rotation; @@ -274,26 +303,28 @@ export class Skeleton implements ISkeleton { bone.ashearY = bone.shearY; bone.appliedValid = true; } - let updateCache = this._updateCache; - for (let i = 0, n = updateCache.length; i < n; i++) - updateCache[i].update(); + const updateCache = this._updateCache; + + for (let i = 0, n = updateCache.length; i < n; i++) updateCache[i].update(); } /** Sets the bones, constraints, and slots to their setup pose values. */ - setToSetupPose () { + setToSetupPose() { this.setBonesToSetupPose(); this.setSlotsToSetupPose(); } /** Sets the bones and constraints to their setup pose values. */ - setBonesToSetupPose () { - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - bones[i].setToSetupPose(); + setBonesToSetupPose() { + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) bones[i].setToSetupPose(); + + const ikConstraints = this.ikConstraints; - let ikConstraints = this.ikConstraints; for (let i = 0, n = ikConstraints.length; i < n; i++) { - let constraint = ikConstraints[i]; + const constraint = ikConstraints[i]; + constraint.mix = constraint.data.mix; constraint.softness = constraint.data.softness; constraint.bendDirection = constraint.data.bendDirection; @@ -301,20 +332,24 @@ export class Skeleton implements ISkeleton { constraint.stretch = constraint.data.stretch; } - let transformConstraints = this.transformConstraints; + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; - let data = constraint.data; + const constraint = transformConstraints[i]; + const data = constraint.data; + constraint.rotateMix = data.rotateMix; constraint.translateMix = data.translateMix; constraint.scaleMix = data.scaleMix; constraint.shearMix = data.shearMix; } - let pathConstraints = this.pathConstraints; + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; - let data = constraint.data; + const constraint = pathConstraints[i]; + const data = constraint.data; + constraint.position = data.position; constraint.spacing = data.spacing; constraint.rotateMix = data.rotateMix; @@ -322,64 +357,74 @@ export class Skeleton implements ISkeleton { } } - setSlotsToSetupPose () { - let slots = this.slots; + setSlotsToSetupPose() { + const slots = this.slots; + Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length); - for (let i = 0, n = slots.length; i < n; i++) - slots[i].setToSetupPose(); + for (let i = 0, n = slots.length; i < n; i++) slots[i].setToSetupPose(); } /** @return May return null. */ - getRootBone () { + getRootBone() { if (this.bones.length == 0) return null; + return this.bones[0]; } /** @return May be null. */ - findBone (boneName: string) { - if (boneName == null) throw new Error("boneName cannot be null."); - let bones = this.bones; + findBone(boneName: string) { + if (boneName == null) throw new Error('boneName cannot be null.'); + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (bone.data.name == boneName) return bone; } + return null; } /** @return -1 if the bone was not found. */ - findBoneIndex (boneName: string) { - if (boneName == null) throw new Error("boneName cannot be null."); - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - if (bones[i].data.name == boneName) return i; + findBoneIndex(boneName: string) { + if (boneName == null) throw new Error('boneName cannot be null.'); + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) if (bones[i].data.name == boneName) return i; + return -1; } /** @return May be null. */ - findSlot (slotName: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; + findSlot(slotName: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) return slot; } + return null; } /** @return -1 if the bone was not found. */ - findSlotIndex (slotName: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; - for (let i = 0, n = slots.length; i < n; i++) - if (slots[i].data.name == slotName) return i; + findSlotIndex(slotName: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + + for (let i = 0, n = slots.length; i < n; i++) if (slots[i].data.name == slotName) return i; + return -1; } /** Sets a skin by name. * @see #setSkin(Skin) */ - setSkinByName (skinName: string) { - let skin = this.data.findSkin(skinName); - if (skin == null) throw new Error("Skin not found: " + skinName); + setSkinByName(skinName: string) { + const skin = this.data.findSkin(skinName); + + if (skin == null) throw new Error(`Skin not found: ${skinName}`); this.setSkin(skin); } @@ -387,18 +432,20 @@ export class Skeleton implements ISkeleton { * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was no * old skin, each slot's setup mode attachment is attached from the new skin. * @param newSkin May be null. */ - setSkin (newSkin: Skin) { + setSkin(newSkin: Skin) { if (newSkin == this.skin) return; if (newSkin != null) { - if (this.skin != null) - newSkin.attachAll(this, this.skin); + if (this.skin != null) newSkin.attachAll(this, this.skin); else { - let slots = this.slots; + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; - let name = slot.data.attachmentName; + const slot = slots[i]; + const name = slot.data.attachmentName; + if (name != null) { - let attachment: Attachment = newSkin.getAttachment(i, name); + const attachment: Attachment = newSkin.getAttachment(i, name); + if (attachment != null) slot.setAttachment(attachment); } } @@ -409,71 +456,85 @@ export class Skeleton implements ISkeleton { } /** @return May be null. */ - getAttachmentByName (slotName: string, attachmentName: string): Attachment { + getAttachmentByName(slotName: string, attachmentName: string): Attachment { return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName); } /** @return May be null. */ - getAttachment (slotIndex: number, attachmentName: string): Attachment { - if (attachmentName == null) throw new Error("attachmentName cannot be null."); + getAttachment(slotIndex: number, attachmentName: string): Attachment { + if (attachmentName == null) throw new Error('attachmentName cannot be null.'); if (this.skin != null) { - let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName); + const attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment != null) return attachment; } if (this.data.defaultSkin != null) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; } /** @param attachmentName May be null. */ - setAttachment (slotName: string, attachmentName?: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; + setAttachment(slotName: string, attachmentName?: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) { let attachment: Attachment = null; + if (attachmentName != null) { attachment = this.getAttachment(i, attachmentName); - if (attachment == null) - throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName); + if (attachment == null) throw new Error(`Attachment not found: ${attachmentName}, for slot: ${slotName}`); } slot.setAttachment(attachment); + return; } } - throw new Error("Slot not found: " + slotName); + throw new Error(`Slot not found: ${slotName}`); } /** @return May be null. */ - findIkConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let ikConstraints = this.ikConstraints; + findIkConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const ikConstraints = this.ikConstraints; + for (let i = 0, n = ikConstraints.length; i < n; i++) { - let ikConstraint = ikConstraints[i]; + const ikConstraint = ikConstraints[i]; + if (ikConstraint.data.name == constraintName) return ikConstraint; } + return null; } /** @return May be null. */ - findTransformConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let transformConstraints = this.transformConstraints; + findTransformConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; + const constraint = transformConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } /** @return May be null. */ - findPathConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let pathConstraints = this.pathConstraints; + findPathConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; + const constraint = pathConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } @@ -481,30 +542,39 @@ export class Skeleton implements ISkeleton { * @param offset The distance from the skeleton origin to the bottom left corner of the AABB. * @param size The width and height of the AABB. * @param temp Working memory */ - getBounds (offset: Vector2, size: Vector2, temp: Array = new Array(2)) { - if (offset == null) throw new Error("offset cannot be null."); - if (size == null) throw new Error("size cannot be null."); - let drawOrder = this.drawOrder; - let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; + getBounds(offset: Vector2, size: Vector2, temp: Array = new Array(2)) { + if (offset == null) throw new Error('offset cannot be null.'); + if (size == null) throw new Error('size cannot be null.'); + const drawOrder = this.drawOrder; + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + let maxX = Number.NEGATIVE_INFINITY; + let maxY = Number.NEGATIVE_INFINITY; + for (let i = 0, n = drawOrder.length; i < n; i++) { - let slot = drawOrder[i]; + const slot = drawOrder[i]; + if (!slot.bone.active) continue; let verticesLength = 0; let vertices: ArrayLike = null; - let attachment = slot.getAttachment(); + const attachment = slot.getAttachment(); + if (attachment instanceof RegionAttachment) { verticesLength = 8; vertices = Utils.setArraySize(temp, verticesLength, 0); (attachment).computeWorldVertices(slot.bone, vertices, 0, 2); } else if (attachment instanceof MeshAttachment) { - let mesh = (attachment); + const mesh = attachment; + verticesLength = mesh.worldVerticesLength; vertices = Utils.setArraySize(temp, verticesLength, 0); mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); } if (vertices != null) { for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) { - let x = vertices[ii], y = vertices[ii + 1]; + const x = vertices[ii]; + const y = vertices[ii + 1]; + minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); @@ -516,7 +586,7 @@ export class Skeleton implements ISkeleton { size.set(maxX - minX, maxY - minY); } - update (delta: number) { + update(delta: number) { this.time += delta; } @@ -527,7 +597,7 @@ export class Skeleton implements ISkeleton { set flipX(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleX = value ? 1.0 : -1.0; } @@ -539,10 +609,10 @@ export class Skeleton implements ISkeleton { set flipY(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleY = value ? 1.0 : -1.0; } - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; } diff --git a/packages/runtime-3.8/src/core/SkeletonBinary.ts b/packages/runtime-3.8/src/core/SkeletonBinary.ts index ff36923d..dfd68b35 100644 --- a/packages/runtime-3.8/src/core/SkeletonBinary.ts +++ b/packages/runtime-3.8/src/core/SkeletonBinary.ts @@ -1,17 +1,21 @@ -import type {Attachment, AttachmentLoader, MeshAttachment, VertexAttachment} from './attachments'; -import {Animation} from './Animation'; -import {Event} from './Event'; -import {SkeletonData} from './SkeletonData'; -import {SlotData} from './SlotData'; -import {BoneData} from './BoneData'; -import {IkConstraintData} from './IkConstraintData'; -import {TransformConstraintData} from './TransformConstraintData'; -import {PathConstraintData, SpacingMode} from './PathConstraintData'; -import {Skin} from './Skin'; -import {EventData} from './EventData'; +import type { Attachment, AttachmentLoader, MeshAttachment, VertexAttachment } from './attachments'; +import { Event } from './Event'; +import { SkeletonData } from './SkeletonData'; +import { SlotData } from './SlotData'; +import { BoneData } from './BoneData'; +import { IkConstraintData } from './IkConstraintData'; +import { TransformConstraintData } from './TransformConstraintData'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import { Skin } from './Skin'; +import { EventData } from './EventData'; import { + Animation, AttachmentTimeline, - ColorTimeline, CurveTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, + ColorTimeline, + CurveTimeline, + DeformTimeline, + DrawOrderTimeline, + EventTimeline, IkConstraintTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, @@ -22,21 +26,30 @@ import { Timeline, TransformConstraintTimeline, TranslateTimeline, - TwoColorTimeline + TwoColorTimeline, } from './Animation'; -import {AttachmentType, BinaryInput, Color, PositionMode, RotateMode, TransformMode, Utils} from '@pixi-spine/base'; -import {BLEND_MODES} from '@pixi/constants'; +import { AttachmentType, BinaryInput, Color, PositionMode, RotateMode, TransformMode, Utils } from '@pixi-spine/base'; +import { BLEND_MODES } from '@pixi/constants'; /** * @public */ export class SkeletonBinary { - static AttachmentTypeValues = [ 0 /*AttachmentType.Region*/, 1/*AttachmentType.BoundingBox*/, 2/*AttachmentType.Mesh*/, 3/*AttachmentType.LinkedMesh*/, 4/*AttachmentType.Path*/, 5/*AttachmentType.Point*/, 6/*AttachmentType.Clipping*/ ]; - static TransformModeValues = [TransformMode.Normal, TransformMode.OnlyTranslation, TransformMode.NoRotationOrReflection, TransformMode.NoScale, TransformMode.NoScaleOrReflection]; - static PositionModeValues = [ PositionMode.Fixed, PositionMode.Percent ]; - static SpacingModeValues = [ SpacingMode.Length, SpacingMode.Fixed, SpacingMode.Percent]; - static RotateModeValues = [ RotateMode.Tangent, RotateMode.Chain, RotateMode.ChainScale ]; - static BlendModeValues = [ BLEND_MODES.NORMAL, BLEND_MODES.ADD, BLEND_MODES.MULTIPLY, BLEND_MODES.SCREEN]; + static AttachmentTypeValues = [ + 0 /* AttachmentType.Region*/, 1 /* AttachmentType.BoundingBox*/, 2 /* AttachmentType.Mesh*/, 3 /* AttachmentType.LinkedMesh*/, 4 /* AttachmentType.Path*/, + 5 /* AttachmentType.Point*/, 6 /* AttachmentType.Clipping*/, + ]; + static TransformModeValues = [ + TransformMode.Normal, + TransformMode.OnlyTranslation, + TransformMode.NoRotationOrReflection, + TransformMode.NoScale, + TransformMode.NoScaleOrReflection, + ]; + static PositionModeValues = [PositionMode.Fixed, PositionMode.Percent]; + static SpacingModeValues = [SpacingMode.Length, SpacingMode.Fixed, SpacingMode.Percent]; + static RotateModeValues = [RotateMode.Tangent, RotateMode.Chain, RotateMode.ChainScale]; + static BlendModeValues = [BLEND_MODES.NORMAL, BLEND_MODES.ADD, BLEND_MODES.MULTIPLY, BLEND_MODES.SCREEN]; static BONE_ROTATE = 0; static BONE_TRANSLATE = 1; @@ -59,23 +72,24 @@ export class SkeletonBinary { scale = 1; private linkedMeshes = new Array(); - constructor (attachmentLoader: AttachmentLoader) { + constructor(attachmentLoader: AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - readSkeletonData (binary: Uint8Array): SkeletonData { - let scale = this.scale; + readSkeletonData(binary: Uint8Array): SkeletonData { + const scale = this.scale; - let skeletonData = new SkeletonData(); - skeletonData.name = ""; // BOZO + const skeletonData = new SkeletonData(); - let input = new BinaryInput(binary); + skeletonData.name = ''; // BOZO + + const input = new BinaryInput(binary); skeletonData.hash = input.readString(); skeletonData.version = input.readString(); - if (skeletonData.version === '3.8.75') - { - let error = `Unsupported skeleton data, 3.8.75 is deprecated, please export with a newer version of Spine.`; + if (skeletonData.version === '3.8.75') { + const error = `Unsupported skeleton data, 3.8.75 is deprecated, please export with a newer version of Spine.`; + console.error(error); } skeletonData.x = input.readFloat(); @@ -83,7 +97,8 @@ export class SkeletonBinary { skeletonData.width = input.readFloat(); skeletonData.height = input.readFloat(); - let nonessential = input.readBoolean(); + const nonessential = input.readBoolean(); + if (nonessential) { skeletonData.fps = input.readFloat(); @@ -93,16 +108,17 @@ export class SkeletonBinary { let n = 0; // Strings. - n = input.readInt(true) - for (let i = 0; i < n; i++) - input.strings.push(input.readString()); + + n = input.readInt(true); + for (let i = 0; i < n; i++) input.strings.push(input.readString()); // Bones. - n = input.readInt(true) + n = input.readInt(true); for (let i = 0; i < n; i++) { - let name = input.readString(); - let parent = i == 0 ? null : skeletonData.bones[input.readInt(true)]; - let data = new BoneData(i, name, parent); + const name = input.readString(); + const parent = i == 0 ? null : skeletonData.bones[input.readInt(true)]; + const data = new BoneData(i, name, parent); + data.rotation = input.readFloat(); data.x = input.readFloat() * scale; data.y = input.readFloat() * scale; @@ -120,13 +136,15 @@ export class SkeletonBinary { // Slots. n = input.readInt(true); for (let i = 0; i < n; i++) { - let slotName = input.readString(); - let boneData = skeletonData.bones[input.readInt(true)]; - let data = new SlotData(i, slotName, boneData); + const slotName = input.readString(); + const boneData = skeletonData.bones[input.readInt(true)]; + const data = new SlotData(i, slotName, boneData); + Color.rgba8888ToColor(data.color, input.readInt32()); - let darkColor = input.readInt32(); - if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor); + const darkColor = input.readInt32(); + + if (darkColor != -1) Color.rgb888ToColor((data.darkColor = new Color()), darkColor); data.attachmentName = input.readStringRef(); data.blendMode = SkeletonBinary.BlendModeValues[input.readInt(true)]; @@ -136,12 +154,12 @@ export class SkeletonBinary { // IK constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let data = new IkConstraintData(input.readString()); + const data = new IkConstraintData(input.readString()); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.bones[input.readInt(true)]; data.mix = input.readFloat(); data.softness = input.readFloat() * scale; @@ -155,12 +173,12 @@ export class SkeletonBinary { // Transform constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let data = new TransformConstraintData(input.readString()); + const data = new TransformConstraintData(input.readString()); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.bones[input.readInt(true)]; data.local = input.readBoolean(); data.relative = input.readBoolean(); @@ -180,12 +198,12 @@ export class SkeletonBinary { // Path constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let data = new PathConstraintData(input.readString()); + const data = new PathConstraintData(input.readString()); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.slots[input.readInt(true)]; data.positionMode = SkeletonBinary.PositionModeValues[input.readInt(true)]; data.spacingMode = SkeletonBinary.SpacingModeValues[input.readInt(true)]; @@ -201,7 +219,8 @@ export class SkeletonBinary { } // Default skin. - let defaultSkin = this.readSkin(input, skeletonData, true, nonessential); + const defaultSkin = this.readSkin(input, skeletonData, true, nonessential); + if (defaultSkin != null) { skeletonData.defaultSkin = defaultSkin; skeletonData.skins.push(defaultSkin); @@ -210,20 +229,22 @@ export class SkeletonBinary { // Skins. { let i = skeletonData.skins.length; - Utils.setArraySize(skeletonData.skins, n = i + input.readInt(true)); - for (; i < n; i++) - skeletonData.skins[i] = this.readSkin(input, skeletonData, false, nonessential); + + Utils.setArraySize(skeletonData.skins, (n = i + input.readInt(true))); + for (; i < n; i++) skeletonData.skins[i] = this.readSkin(input, skeletonData, false, nonessential); } // Linked meshes. n = this.linkedMeshes.length; for (let i = 0; i < n; i++) { - let linkedMesh = this.linkedMeshes[i]; - let skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); - if (skin == null) throw new Error("Skin not found: " + linkedMesh.skin); - let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); - if (parent == null) throw new Error("Parent mesh not found: " + linkedMesh.parent); - linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? parent as VertexAttachment : linkedMesh.mesh; + const linkedMesh = this.linkedMeshes[i]; + const skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + + if (skin == null) throw new Error(`Skin not found: ${linkedMesh.skin}`); + const parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + + if (parent == null) throw new Error(`Parent mesh not found: ${linkedMesh.parent}`); + linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? (parent as VertexAttachment) : linkedMesh.mesh; linkedMesh.mesh.setParentMesh(parent as MeshAttachment); // linkedMesh.mesh.updateUVs(); } @@ -232,7 +253,8 @@ export class SkeletonBinary { // Events. n = input.readInt(true); for (let i = 0; i < n; i++) { - let data = new EventData(input.readStringRef()); + const data = new EventData(input.readStringRef()); + data.intValue = input.readInt(false); data.floatValue = input.readFloat(); data.stringValue = input.readString(); @@ -246,219 +268,243 @@ export class SkeletonBinary { // Animations. n = input.readInt(true); - for (let i = 0; i < n; i++) - skeletonData.animations.push(this.readAnimation(input, input.readString(), skeletonData)); + for (let i = 0; i < n; i++) skeletonData.animations.push(this.readAnimation(input, input.readString(), skeletonData)); + return skeletonData; } - private readSkin (input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin { + private readSkin(input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin { let skin = null; let slotCount = 0; if (defaultSkin) { - slotCount = input.readInt(true) + slotCount = input.readInt(true); if (slotCount == 0) return null; - skin = new Skin("default"); + skin = new Skin('default'); } else { skin = new Skin(input.readStringRef()); skin.bones.length = input.readInt(true); - for (let i = 0, n = skin.bones.length; i < n; i++) - skin.bones[i] = skeletonData.bones[input.readInt(true)]; + for (let i = 0, n = skin.bones.length; i < n; i++) skin.bones[i] = skeletonData.bones[input.readInt(true)]; - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]); - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]); - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]); slotCount = input.readInt(true); } for (let i = 0; i < slotCount; i++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let name = input.readStringRef(); - let attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + const name = input.readStringRef(); + const attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + if (attachment != null) skin.setAttachment(slotIndex, name, attachment); } } + return skin; } private readAttachment(input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment { - let scale = this.scale; + const scale = this.scale; let name = input.readStringRef(); + if (name == null) name = attachmentName; - let typeIndex = input.readByte(); - let type = SkeletonBinary.AttachmentTypeValues[typeIndex]; + const typeIndex = input.readByte(); + const type = SkeletonBinary.AttachmentTypeValues[typeIndex]; + switch (type) { - case AttachmentType.Region: { - let path = input.readStringRef(); - let rotation = input.readFloat(); - let x = input.readFloat(); - let y = input.readFloat(); - let scaleX = input.readFloat(); - let scaleY = input.readFloat(); - let width = input.readFloat(); - let height = input.readFloat(); - let color = input.readInt32(); - - if (path == null) path = name; - let region = this.attachmentLoader.newRegionAttachment(skin, name, path); - if (region == null) return null; - region.path = path; - region.x = x * scale; - region.y = y * scale; - region.scaleX = scaleX; - region.scaleY = scaleY; - region.rotation = rotation; - region.width = width * scale; - region.height = height * scale; - Color.rgba8888ToColor(region.color, color); - // region.updateOffset(); - return region; - } - case AttachmentType.BoundingBox: { - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let color = nonessential ? input.readInt32() : 0; - - let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); - if (box == null) return null; - box.worldVerticesLength = vertexCount << 1; - box.vertices = vertices.vertices; - box.bones = vertices.bones; - if (nonessential) Color.rgba8888ToColor(box.color, color); - return box; - } - case AttachmentType.Mesh: { - let path = input.readStringRef(); - let color = input.readInt32(); - let vertexCount = input.readInt(true); - let uvs = this.readFloatArray(input, vertexCount << 1, 1); - let triangles = this.readShortArray(input); - let vertices = this.readVertices(input, vertexCount); - let hullLength = input.readInt(true); - let edges = null; - let width = 0, height = 0; - if (nonessential) { - edges = this.readShortArray(input); - width = input.readFloat(); - height = input.readFloat(); + case AttachmentType.Region: { + let path = input.readStringRef(); + const rotation = input.readFloat(); + const x = input.readFloat(); + const y = input.readFloat(); + const scaleX = input.readFloat(); + const scaleY = input.readFloat(); + const width = input.readFloat(); + const height = input.readFloat(); + const color = input.readInt32(); + + if (path == null) path = name; + const region = this.attachmentLoader.newRegionAttachment(skin, name, path); + + if (region == null) return null; + region.path = path; + region.x = x * scale; + region.y = y * scale; + region.scaleX = scaleX; + region.scaleY = scaleY; + region.rotation = rotation; + region.width = width * scale; + region.height = height * scale; + Color.rgba8888ToColor(region.color, color); + // region.updateOffset(); + + return region; } + case AttachmentType.BoundingBox: { + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const color = nonessential ? input.readInt32() : 0; + + const box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); - if (path == null) path = name; - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); - if (mesh == null) return null; - mesh.path = path; - Color.rgba8888ToColor(mesh.color, color); - mesh.bones = vertices.bones; - mesh.vertices = vertices.vertices; - mesh.worldVerticesLength = vertexCount << 1; - mesh.triangles = triangles; - mesh.regionUVs = new Float32Array(uvs); - // mesh.updateUVs(); - mesh.hullLength = hullLength << 1; - if (nonessential) { - mesh.edges = edges; - mesh.width = width * scale; - mesh.height = height * scale; + if (box == null) return null; + box.worldVerticesLength = vertexCount << 1; + box.vertices = vertices.vertices; + box.bones = vertices.bones; + if (nonessential) Color.rgba8888ToColor(box.color, color); + + return box; } - return mesh; - } - case AttachmentType.LinkedMesh: { - let path = input.readStringRef(); - let color = input.readInt32(); - let skinName = input.readStringRef(); - let parent = input.readStringRef(); - let inheritDeform = input.readBoolean(); - let width = 0, height = 0; - if (nonessential) { - width = input.readFloat(); - height = input.readFloat(); + case AttachmentType.Mesh: { + let path = input.readStringRef(); + const color = input.readInt32(); + const vertexCount = input.readInt(true); + const uvs = this.readFloatArray(input, vertexCount << 1, 1); + const triangles = this.readShortArray(input); + const vertices = this.readVertices(input, vertexCount); + const hullLength = input.readInt(true); + let edges = null; + let width = 0; + let height = 0; + + if (nonessential) { + edges = this.readShortArray(input); + width = input.readFloat(); + height = input.readFloat(); + } + + if (path == null) path = name; + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + + if (mesh == null) return null; + mesh.path = path; + Color.rgba8888ToColor(mesh.color, color); + mesh.bones = vertices.bones; + mesh.vertices = vertices.vertices; + mesh.worldVerticesLength = vertexCount << 1; + mesh.triangles = triangles; + mesh.regionUVs = new Float32Array(uvs); + // mesh.updateUVs(); + mesh.hullLength = hullLength << 1; + if (nonessential) { + mesh.edges = edges; + mesh.width = width * scale; + mesh.height = height * scale; + } + + return mesh; } + case AttachmentType.LinkedMesh: { + let path = input.readStringRef(); + const color = input.readInt32(); + const skinName = input.readStringRef(); + const parent = input.readStringRef(); + const inheritDeform = input.readBoolean(); + let width = 0; + let height = 0; + + if (nonessential) { + width = input.readFloat(); + height = input.readFloat(); + } + + if (path == null) path = name; + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); - if (path == null) path = name; - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); - if (mesh == null) return null; - mesh.path = path; - Color.rgba8888ToColor(mesh.color, color); - if (nonessential) { - mesh.width = width * scale; - mesh.height = height * scale; + if (mesh == null) return null; + mesh.path = path; + Color.rgba8888ToColor(mesh.color, color); + if (nonessential) { + mesh.width = width * scale; + mesh.height = height * scale; + } + this.linkedMeshes.push(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform)); + + return mesh; + } + case AttachmentType.Path: { + const closed = input.readBoolean(); + const constantSpeed = input.readBoolean(); + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const lengths = Utils.newArray(vertexCount / 3, 0); + + for (let i = 0, n = lengths.length; i < n; i++) lengths[i] = input.readFloat() * scale; + const color = nonessential ? input.readInt32() : 0; + + const path = this.attachmentLoader.newPathAttachment(skin, name); + + if (path == null) return null; + path.closed = closed; + path.constantSpeed = constantSpeed; + path.worldVerticesLength = vertexCount << 1; + path.vertices = vertices.vertices; + path.bones = vertices.bones; + path.lengths = lengths; + if (nonessential) Color.rgba8888ToColor(path.color, color); + + return path; + } + case AttachmentType.Point: { + const rotation = input.readFloat(); + const x = input.readFloat(); + const y = input.readFloat(); + const color = nonessential ? input.readInt32() : 0; + + const point = this.attachmentLoader.newPointAttachment(skin, name); + + if (point == null) return null; + point.x = x * scale; + point.y = y * scale; + point.rotation = rotation; + if (nonessential) Color.rgba8888ToColor(point.color, color); + + return point; + } + case AttachmentType.Clipping: { + const endSlotIndex = input.readInt(true); + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const color = nonessential ? input.readInt32() : 0; + + const clip = this.attachmentLoader.newClippingAttachment(skin, name); + + if (clip == null) return null; + clip.endSlot = skeletonData.slots[endSlotIndex]; + clip.worldVerticesLength = vertexCount << 1; + clip.vertices = vertices.vertices; + clip.bones = vertices.bones; + if (nonessential) Color.rgba8888ToColor(clip.color, color); + + return clip; } - this.linkedMeshes.push(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform)); - return mesh; - } - case AttachmentType.Path: { - let closed = input.readBoolean(); - let constantSpeed = input.readBoolean(); - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let lengths = Utils.newArray(vertexCount / 3, 0); - for (let i = 0, n = lengths.length; i < n; i++) - lengths[i] = input.readFloat() * scale; - let color = nonessential ? input.readInt32() : 0; - - let path = this.attachmentLoader.newPathAttachment(skin, name); - if (path == null) return null; - path.closed = closed; - path.constantSpeed = constantSpeed; - path.worldVerticesLength = vertexCount << 1; - path.vertices = vertices.vertices; - path.bones = vertices.bones; - path.lengths = lengths; - if (nonessential) Color.rgba8888ToColor(path.color, color); - return path; - } - case AttachmentType.Point: { - let rotation = input.readFloat(); - let x = input.readFloat(); - let y = input.readFloat(); - let color = nonessential ? input.readInt32() : 0; - - let point = this.attachmentLoader.newPointAttachment(skin, name); - if (point == null) return null; - point.x = x * scale; - point.y = y * scale; - point.rotation = rotation; - if (nonessential) Color.rgba8888ToColor(point.color, color); - return point; - } - case AttachmentType.Clipping: { - let endSlotIndex = input.readInt(true); - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let color = nonessential ? input.readInt32() : 0; - - let clip = this.attachmentLoader.newClippingAttachment(skin, name); - if (clip == null) return null; - clip.endSlot = skeletonData.slots[endSlotIndex]; - clip.worldVerticesLength = vertexCount << 1; - clip.vertices = vertices.vertices; - clip.bones = vertices.bones; - if (nonessential) Color.rgba8888ToColor(clip.color, color); - return clip; - } } + return null; } - private readVertices (input: BinaryInput, vertexCount: number): Vertices { - let verticesLength = vertexCount << 1; - let vertices = new Vertices(); - let scale = this.scale; + private readVertices(input: BinaryInput, vertexCount: number): Vertices { + const verticesLength = vertexCount << 1; + const vertices = new Vertices(); + const scale = this.scale; + if (!input.readBoolean()) { vertices.vertices = this.readFloatArray(input, verticesLength, scale); + return vertices; } - let weights = new Array(); - let bonesArray = new Array(); + const weights = new Array(); + const bonesArray = new Array(); + for (let i = 0; i < vertexCount; i++) { - let boneCount = input.readInt(true); + const boneCount = input.readInt(true); + bonesArray.push(boneCount); for (let ii = 0; ii < boneCount; ii++) { bonesArray.push(input.readInt(true)); @@ -469,138 +515,146 @@ export class SkeletonBinary { } vertices.vertices = Utils.toFloatArray(weights); vertices.bones = bonesArray; + return vertices; } - private readFloatArray (input: BinaryInput, n: number, scale: number): number[] { - let array = new Array(n); + private readFloatArray(input: BinaryInput, n: number, scale: number): number[] { + const array = new Array(n); + if (scale == 1) { - for (let i = 0; i < n; i++) - array[i] = input.readFloat(); + for (let i = 0; i < n; i++) array[i] = input.readFloat(); } else { - for (let i = 0; i < n; i++) - array[i] = input.readFloat() * scale; + for (let i = 0; i < n; i++) array[i] = input.readFloat() * scale; } + return array; } - private readShortArray (input: BinaryInput): number[] { - let n = input.readInt(true); - let array = new Array(n); - for (let i = 0; i < n; i++) - array[i] = input.readShort(); + private readShortArray(input: BinaryInput): number[] { + const n = input.readInt(true); + const array = new Array(n); + + for (let i = 0; i < n; i++) array[i] = input.readShort(); + return array; } - private readAnimation (input: BinaryInput, name: string, skeletonData: SkeletonData): Animation { - let timelines = new Array(); - let scale = this.scale; + private readAnimation(input: BinaryInput, name: string, skeletonData: SkeletonData): Animation { + const timelines = new Array(); + const scale = this.scale; let duration = 0; - let tempColor1 = new Color(); - let tempColor2 = new Color(); + const tempColor1 = new Color(); + const tempColor2 = new Color(); // Slot timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let timelineType = input.readByte(); - let frameCount = input.readInt(true); + const timelineType = input.readByte(); + const frameCount = input.readInt(true); + switch (timelineType) { - case SkeletonBinary.SLOT_ATTACHMENT: { - let timeline = new AttachmentTimeline(frameCount); - timeline.slotIndex = slotIndex; - for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) - timeline.setFrame(frameIndex, input.readFloat(), input.readStringRef()); - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[frameCount - 1]); - break; - } - case SkeletonBinary.SLOT_COLOR: { - let timeline = new ColorTimeline(frameCount); - timeline.slotIndex = slotIndex; - for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - let time = input.readFloat(); - Color.rgba8888ToColor(tempColor1, input.readInt32()); - timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a); - if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + case SkeletonBinary.SLOT_ATTACHMENT: { + const timeline = new AttachmentTimeline(frameCount); + + timeline.slotIndex = slotIndex; + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) timeline.setFrame(frameIndex, input.readFloat(), input.readStringRef()); + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[frameCount - 1]); + break; } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[(frameCount - 1) * ColorTimeline.ENTRIES]); - break; - } - case SkeletonBinary.SLOT_TWO_COLOR: { - let timeline = new TwoColorTimeline(frameCount); - timeline.slotIndex = slotIndex; - for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - let time = input.readFloat(); - Color.rgba8888ToColor(tempColor1, input.readInt32()); - Color.rgb888ToColor(tempColor2, input.readInt32()); - timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a, tempColor2.r, - tempColor2.g, tempColor2.b); - if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + case SkeletonBinary.SLOT_COLOR: { + const timeline = new ColorTimeline(frameCount); + + timeline.slotIndex = slotIndex; + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { + const time = input.readFloat(); + + Color.rgba8888ToColor(tempColor1, input.readInt32()); + timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a); + if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(frameCount - 1) * ColorTimeline.ENTRIES]); + break; + } + case SkeletonBinary.SLOT_TWO_COLOR: { + const timeline = new TwoColorTimeline(frameCount); + + timeline.slotIndex = slotIndex; + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { + const time = input.readFloat(); + + Color.rgba8888ToColor(tempColor1, input.readInt32()); + Color.rgb888ToColor(tempColor2, input.readInt32()); + timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a, tempColor2.r, tempColor2.g, tempColor2.b); + if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(frameCount - 1) * TwoColorTimeline.ENTRIES]); + break; } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[(frameCount - 1) * TwoColorTimeline.ENTRIES]); - break; - } } } } // Bone timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let boneIndex = input.readInt(true); + const boneIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let timelineType = input.readByte(); - let frameCount = input.readInt(true); + const timelineType = input.readByte(); + const frameCount = input.readInt(true); + switch (timelineType) { - case SkeletonBinary.BONE_ROTATE: { - let timeline = new RotateTimeline(frameCount); - timeline.boneIndex = boneIndex; - for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat()); - if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]); - break; - } - case SkeletonBinary.BONE_TRANSLATE: - case SkeletonBinary.BONE_SCALE: - case SkeletonBinary.BONE_SHEAR: { - let timeline; - let timelineScale = 1; - if (timelineType == SkeletonBinary.BONE_SCALE) - timeline = new ScaleTimeline(frameCount); - else if (timelineType == SkeletonBinary.BONE_SHEAR) - timeline = new ShearTimeline(frameCount); - else { - timeline = new TranslateTimeline(frameCount); - timelineScale = scale; + case SkeletonBinary.BONE_ROTATE: { + const timeline = new RotateTimeline(frameCount); + + timeline.boneIndex = boneIndex; + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat()); + if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]); + break; } - timeline.boneIndex = boneIndex; - for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale, - input.readFloat() * timelineScale); - if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + case SkeletonBinary.BONE_TRANSLATE: + case SkeletonBinary.BONE_SCALE: + case SkeletonBinary.BONE_SHEAR: { + let timeline; + let timelineScale = 1; + + if (timelineType == SkeletonBinary.BONE_SCALE) timeline = new ScaleTimeline(frameCount); + else if (timelineType == SkeletonBinary.BONE_SHEAR) timeline = new ShearTimeline(frameCount); + else { + timeline = new TranslateTimeline(frameCount); + timelineScale = scale; + } + timeline.boneIndex = boneIndex; + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale, input.readFloat() * timelineScale); + if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]); + break; } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]); - break; - } } } } // IK constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true); - let frameCount = input.readInt(true); - let timeline = new IkConstraintTimeline(frameCount); + const index = input.readInt(true); + const frameCount = input.readInt(true); + const timeline = new IkConstraintTimeline(frameCount); + timeline.ikConstraintIndex = index; for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat() * scale, input.readByte(), input.readBoolean(), - input.readBoolean()); + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat() * scale, input.readByte(), input.readBoolean(), input.readBoolean()); if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); } timelines.push(timeline); @@ -609,13 +663,13 @@ export class SkeletonBinary { // Transform constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true); - let frameCount = input.readInt(true); - let timeline = new TransformConstraintTimeline(frameCount); + const index = input.readInt(true); + const frameCount = input.readInt(true); + const timeline = new TransformConstraintTimeline(frameCount); + timeline.transformConstraintIndex = index; for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), - input.readFloat()); + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat()); if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); } timelines.push(timeline); @@ -624,83 +678,88 @@ export class SkeletonBinary { // Path constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true); - let data = skeletonData.pathConstraints[index]; + const index = input.readInt(true); + const data = skeletonData.pathConstraints[index]; + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let timelineType = input.readByte(); - let frameCount = input.readInt(true); + const timelineType = input.readByte(); + const frameCount = input.readInt(true); + switch (timelineType) { - case SkeletonBinary.PATH_POSITION: - case SkeletonBinary.PATH_SPACING: { - let timeline; - let timelineScale = 1; - if (timelineType == SkeletonBinary.PATH_SPACING) { - timeline = new PathConstraintSpacingTimeline(frameCount); - if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale; - } else { - timeline = new PathConstraintPositionTimeline(frameCount); - if (data.positionMode == PositionMode.Fixed) timelineScale = scale; + case SkeletonBinary.PATH_POSITION: + case SkeletonBinary.PATH_SPACING: { + let timeline; + let timelineScale = 1; + + if (timelineType == SkeletonBinary.PATH_SPACING) { + timeline = new PathConstraintSpacingTimeline(frameCount); + if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale; + } else { + timeline = new PathConstraintPositionTimeline(frameCount); + if (data.positionMode == PositionMode.Fixed) timelineScale = scale; + } + timeline.pathConstraintIndex = index; + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale); + if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]); + break; } - timeline.pathConstraintIndex = index; - for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale); - if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + case SkeletonBinary.PATH_MIX: { + const timeline = new PathConstraintMixTimeline(frameCount); + + timeline.pathConstraintIndex = index; + for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat()); + if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]); + break; } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]); - break; - } - case SkeletonBinary.PATH_MIX: { - let timeline = new PathConstraintMixTimeline(frameCount); - timeline.pathConstraintIndex = index; - for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat()); - if (frameIndex < frameCount - 1) this.readCurve(input, frameIndex, timeline); - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]); - break; - } } } } // Deform timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let skin = skeletonData.skins[input.readInt(true)]; + const skin = skeletonData.skins[input.readInt(true)]; + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let iii = 0, nnn = input.readInt(true); iii < nnn; iii++) { - let attachment = skin.getAttachment(slotIndex, input.readStringRef()) as VertexAttachment; - let weighted = attachment.bones != null; - let vertices = attachment.vertices; - let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + const attachment = skin.getAttachment(slotIndex, input.readStringRef()) as VertexAttachment; + const weighted = attachment.bones != null; + const vertices = attachment.vertices; + const deformLength = weighted ? (vertices.length / 3) * 2 : vertices.length; + + const frameCount = input.readInt(true); + const timeline = new DeformTimeline(frameCount); - let frameCount = input.readInt(true); - let timeline = new DeformTimeline(frameCount); timeline.slotIndex = slotIndex; timeline.attachment = attachment; for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) { - let time = input.readFloat(); + const time = input.readFloat(); let deform; let end = input.readInt(true); - if (end == 0) - deform = weighted ? Utils.newFloatArray(deformLength) : vertices; + + if (end == 0) deform = weighted ? Utils.newFloatArray(deformLength) : vertices; else { deform = Utils.newFloatArray(deformLength); - let start = input.readInt(true); + const start = input.readInt(true); + end += start; if (scale == 1) { - for (let v = start; v < end; v++) - deform[v] = input.readFloat(); + for (let v = start; v < end; v++) deform[v] = input.readFloat(); } else { - for (let v = start; v < end; v++) - deform[v] = input.readFloat() * scale; + for (let v = start; v < end; v++) deform[v] = input.readFloat() * scale; } if (!weighted) { - for (let v = 0, vn = deform.length; v < vn; v++) - deform[v] += vertices[v]; + for (let v = 0, vn = deform.length; v < vn; v++) deform[v] += vertices[v]; } } @@ -714,32 +773,34 @@ export class SkeletonBinary { } // Draw order timeline. - let drawOrderCount = input.readInt(true); + const drawOrderCount = input.readInt(true); + if (drawOrderCount > 0) { - let timeline = new DrawOrderTimeline(drawOrderCount); - let slotCount = skeletonData.slots.length; + const timeline = new DrawOrderTimeline(drawOrderCount); + const slotCount = skeletonData.slots.length; + for (let i = 0; i < drawOrderCount; i++) { - let time = input.readFloat(); - let offsetCount = input.readInt(true); - let drawOrder = Utils.newArray(slotCount, 0); - for (let ii = slotCount - 1; ii >= 0; ii--) - drawOrder[ii] = -1; - let unchanged = Utils.newArray(slotCount - offsetCount, 0); - let originalIndex = 0, unchangedIndex = 0; + const time = input.readFloat(); + const offsetCount = input.readInt(true); + const drawOrder = Utils.newArray(slotCount, 0); + + for (let ii = slotCount - 1; ii >= 0; ii--) drawOrder[ii] = -1; + const unchanged = Utils.newArray(slotCount - offsetCount, 0); + let originalIndex = 0; + let unchangedIndex = 0; + for (let ii = 0; ii < offsetCount; ii++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; + + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + input.readInt(true)] = originalIndex++; } // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. - for (let ii = slotCount - 1; ii >= 0; ii--) - if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + for (let ii = slotCount - 1; ii >= 0; ii--) if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; timeline.setFrame(i, time, drawOrder); } timelines.push(timeline); @@ -747,13 +808,16 @@ export class SkeletonBinary { } // Event timeline. - let eventCount = input.readInt(true); + const eventCount = input.readInt(true); + if (eventCount > 0) { - let timeline = new EventTimeline(eventCount); + const timeline = new EventTimeline(eventCount); + for (let i = 0; i < eventCount; i++) { - let time = input.readFloat(); - let eventData = skeletonData.events[input.readInt(true)]; - let event = new Event(time, eventData); + const time = input.readFloat(); + const eventData = skeletonData.events[input.readInt(true)]; + const event = new Event(time, eventData); + event.intValue = input.readInt(false); event.floatValue = input.readFloat(); event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue; @@ -770,29 +834,30 @@ export class SkeletonBinary { return new Animation(name, timelines, duration); } - private readCurve (input: BinaryInput, frameIndex: number, timeline: CurveTimeline) { + private readCurve(input: BinaryInput, frameIndex: number, timeline: CurveTimeline) { switch (input.readByte()) { - case SkeletonBinary.CURVE_STEPPED: - timeline.setStepped(frameIndex); - break; - case SkeletonBinary.CURVE_BEZIER: - this.setCurve(timeline, frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat()); - break; + case SkeletonBinary.CURVE_STEPPED: + timeline.setStepped(frameIndex); + break; + case SkeletonBinary.CURVE_BEZIER: + this.setCurve(timeline, frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat()); + break; } } - setCurve (timeline: CurveTimeline, frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) { + setCurve(timeline: CurveTimeline, frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) { timeline.setCurve(frameIndex, cx1, cy1, cx2, cy2); } } class LinkedMesh { - parent: string; skin: string; + parent: string; + skin: string; slotIndex: number; mesh: MeshAttachment; inheritDeform: boolean; - constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { + constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; @@ -802,5 +867,5 @@ class LinkedMesh { } class Vertices { - constructor(public bones: Array = null, public vertices: Array | Float32Array = null) { } + constructor(public bones: Array = null, public vertices: Array | Float32Array = null) {} } diff --git a/packages/runtime-3.8/src/core/SkeletonBounds.ts b/packages/runtime-3.8/src/core/SkeletonBounds.ts index cce4ffe0..126d81aa 100644 --- a/packages/runtime-3.8/src/core/SkeletonBounds.ts +++ b/packages/runtime-3.8/src/core/SkeletonBounds.ts @@ -1,8 +1,8 @@ -import {BoundingBoxAttachment} from "./attachments"; -import {SkeletonBoundsBase} from "@pixi-spine/base"; +import type { BoundingBoxAttachment } from './attachments'; +import { SkeletonBoundsBase } from '@pixi-spine/base'; /** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are * provided along with convenience methods for doing hit detection. * @public * */ - export class SkeletonBounds extends SkeletonBoundsBase{}; \ No newline at end of file +export class SkeletonBounds extends SkeletonBoundsBase {} diff --git a/packages/runtime-3.8/src/core/SkeletonData.ts b/packages/runtime-3.8/src/core/SkeletonData.ts index 308eba74..cea4850e 100644 --- a/packages/runtime-3.8/src/core/SkeletonData.ts +++ b/packages/runtime-3.8/src/core/SkeletonData.ts @@ -1,12 +1,12 @@ -import type {ISkeletonData} from '@pixi-spine/base'; -import type {Animation} from "./Animation"; -import {BoneData} from "./BoneData"; -import {SlotData} from "./SlotData"; -import {Skin} from "./Skin"; -import {EventData} from "./EventData"; -import {IkConstraintData} from "./IkConstraintData"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {PathConstraintData} from "./PathConstraintData"; +import type { ISkeletonData } from '@pixi-spine/base'; +import type { Animation } from './Animation'; +import type { BoneData } from './BoneData'; +import type { SlotData } from './SlotData'; +import type { Skin } from './Skin'; +import type { EventData } from './EventData'; +import type { IkConstraintData } from './IkConstraintData'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { PathConstraintData } from './PathConstraintData'; /** * @public @@ -22,115 +22,146 @@ export class SkeletonData implements ISkeletonData(); transformConstraints = new Array(); pathConstraints = new Array(); - x: number; y: number; width: number; height: number; - version: string; hash: string; + x: number; + y: number; + width: number; + height: number; + version: string; + hash: string; // Nonessential fps = 0; imagesPath: string; audioPath: string; - findBone (boneName: string) { - if (boneName == null) throw new Error("boneName cannot be null."); - let bones = this.bones; + findBone(boneName: string) { + if (boneName == null) throw new Error('boneName cannot be null.'); + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (bone.name == boneName) return bone; } + return null; } - findBoneIndex (boneName: string) { - if (boneName == null) throw new Error("boneName cannot be null."); - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - if (bones[i].name == boneName) return i; + findBoneIndex(boneName: string) { + if (boneName == null) throw new Error('boneName cannot be null.'); + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) if (bones[i].name == boneName) return i; + return -1; } - findSlot (slotName: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; + findSlot(slotName: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.name == slotName) return slot; } + return null; } - findSlotIndex (slotName: string) { - if (slotName == null) throw new Error("slotName cannot be null."); - let slots = this.slots; - for (let i = 0, n = slots.length; i < n; i++) - if (slots[i].name == slotName) return i; + findSlotIndex(slotName: string) { + if (slotName == null) throw new Error('slotName cannot be null.'); + const slots = this.slots; + + for (let i = 0, n = slots.length; i < n; i++) if (slots[i].name == slotName) return i; + return -1; } - findSkin (skinName: string) { - if (skinName == null) throw new Error("skinName cannot be null."); - let skins = this.skins; + findSkin(skinName: string) { + if (skinName == null) throw new Error('skinName cannot be null.'); + const skins = this.skins; + for (let i = 0, n = skins.length; i < n; i++) { - let skin = skins[i]; + const skin = skins[i]; + if (skin.name == skinName) return skin; } + return null; } - findEvent (eventDataName: string) { - if (eventDataName == null) throw new Error("eventDataName cannot be null."); - let events = this.events; + findEvent(eventDataName: string) { + if (eventDataName == null) throw new Error('eventDataName cannot be null.'); + const events = this.events; + for (let i = 0, n = events.length; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.name == eventDataName) return event; } + return null; } - findAnimation (animationName: string) { - if (animationName == null) throw new Error("animationName cannot be null."); - let animations = this.animations; + findAnimation(animationName: string) { + if (animationName == null) throw new Error('animationName cannot be null.'); + const animations = this.animations; + for (let i = 0, n = animations.length; i < n; i++) { - let animation = animations[i]; + const animation = animations[i]; + if (animation.name == animationName) return animation; } + return null; } - findIkConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let ikConstraints = this.ikConstraints; + findIkConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const ikConstraints = this.ikConstraints; + for (let i = 0, n = ikConstraints.length; i < n; i++) { - let constraint = ikConstraints[i]; + const constraint = ikConstraints[i]; + if (constraint.name == constraintName) return constraint; } + return null; } - findTransformConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let transformConstraints = this.transformConstraints; + findTransformConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; + const constraint = transformConstraints[i]; + if (constraint.name == constraintName) return constraint; } + return null; } - findPathConstraint (constraintName: string) { - if (constraintName == null) throw new Error("constraintName cannot be null."); - let pathConstraints = this.pathConstraints; + findPathConstraint(constraintName: string) { + if (constraintName == null) throw new Error('constraintName cannot be null.'); + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; + const constraint = pathConstraints[i]; + if (constraint.name == constraintName) return constraint; } + return null; } - findPathConstraintIndex (pathConstraintName: string) { - if (pathConstraintName == null) throw new Error("pathConstraintName cannot be null."); - let pathConstraints = this.pathConstraints; - for (let i = 0, n = pathConstraints.length; i < n; i++) - if (pathConstraints[i].name == pathConstraintName) return i; + findPathConstraintIndex(pathConstraintName: string) { + if (pathConstraintName == null) throw new Error('pathConstraintName cannot be null.'); + const pathConstraints = this.pathConstraints; + + for (let i = 0, n = pathConstraints.length; i < n; i++) if (pathConstraints[i].name == pathConstraintName) return i; + return -1; } } diff --git a/packages/runtime-3.8/src/core/SkeletonJson.ts b/packages/runtime-3.8/src/core/SkeletonJson.ts index 1145d88c..611ffeb8 100644 --- a/packages/runtime-3.8/src/core/SkeletonJson.ts +++ b/packages/runtime-3.8/src/core/SkeletonJson.ts @@ -1,17 +1,21 @@ -import type {Attachment, AttachmentLoader, MeshAttachment, VertexAttachment} from './attachments'; -import {Animation} from './Animation'; -import {Event} from './Event'; -import {SkeletonData} from './SkeletonData'; -import {SlotData} from './SlotData'; -import {BoneData} from './BoneData'; -import {IkConstraintData} from './IkConstraintData'; -import {TransformConstraintData} from './TransformConstraintData'; -import {PathConstraintData, SpacingMode} from './PathConstraintData'; -import {Skin} from './Skin'; -import {EventData} from './EventData'; +import type { Attachment, AttachmentLoader, MeshAttachment, VertexAttachment } from './attachments'; +import { Event } from './Event'; +import { SkeletonData } from './SkeletonData'; +import { SlotData } from './SlotData'; +import { BoneData } from './BoneData'; +import { IkConstraintData } from './IkConstraintData'; +import { TransformConstraintData } from './TransformConstraintData'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import { Skin } from './Skin'; +import { EventData } from './EventData'; import { + Animation, AttachmentTimeline, - ColorTimeline, CurveTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, + ColorTimeline, + CurveTimeline, + DeformTimeline, + DrawOrderTimeline, + EventTimeline, IkConstraintTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, @@ -22,10 +26,10 @@ import { Timeline, TransformConstraintTimeline, TranslateTimeline, - TwoColorTimeline + TwoColorTimeline, } from './Animation'; -import {ArrayLike, Color, PositionMode, RotateMode, TransformMode, Utils, settings} from '@pixi-spine/base'; -import {BLEND_MODES} from '@pixi/constants'; +import { ArrayLike, Color, PositionMode, RotateMode, TransformMode, Utils, settings } from '@pixi-spine/base'; +import { BLEND_MODES } from '@pixi/constants'; /** * @public @@ -35,27 +39,29 @@ export class SkeletonJson { scale = 1; private linkedMeshes = new Array(); - constructor (attachmentLoader: AttachmentLoader) { + constructor(attachmentLoader: AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - readSkeletonData (json: string | any): SkeletonData { - let scale = this.scale; - let skeletonData = new SkeletonData(); - let root = typeof(json) === "string" ? JSON.parse(json) : json; + readSkeletonData(json: string | any): SkeletonData { + const scale = this.scale; + const skeletonData = new SkeletonData(); + const root = typeof json === 'string' ? JSON.parse(json) : json; // Skeleton - let skeletonMap = root.skeleton; + const skeletonMap = root.skeleton; + if (skeletonMap != null) { skeletonData.hash = skeletonMap.hash; skeletonData.version = skeletonMap.spine; if (skeletonData.version.substr(0, 3) !== '3.8') { - let error = `Spine 3.8 loader cant load version ${skeletonMap.spine}. Please configure your pixi-spine bundle`; + const error = `Spine 3.8 loader cant load version ${skeletonMap.spine}. Please configure your pixi-spine bundle`; + console.error(error); } - if (skeletonData.version === '3.8.75') - { - let error = `Unsupported skeleton data, 3.8.75 is deprecated, please export with a newer version of Spine.`; + if (skeletonData.version === '3.8.75') { + const error = `Unsupported skeleton data, 3.8.75 is deprecated, please export with a newer version of Spine.`; + console.error(error); } skeletonData.x = skeletonMap.x; @@ -69,25 +75,27 @@ export class SkeletonJson { // Bones if (root.bones) { for (let i = 0; i < root.bones.length; i++) { - let boneMap = root.bones[i]; + const boneMap = root.bones[i]; let parent: BoneData = null; - let parentName: string = this.getValue(boneMap, "parent", null); + const parentName: string = this.getValue(boneMap, 'parent', null); + if (parentName != null) { parent = skeletonData.findBone(parentName); - if (parent == null) throw new Error("Parent bone not found: " + parentName); + if (parent == null) throw new Error(`Parent bone not found: ${parentName}`); } - let data = new BoneData(skeletonData.bones.length, boneMap.name, parent); - data.length = this.getValue(boneMap, "length", 0) * scale; - data.x = this.getValue(boneMap, "x", 0) * scale; - data.y = this.getValue(boneMap, "y", 0) * scale; - data.rotation = this.getValue(boneMap, "rotation", 0); - data.scaleX = this.getValue(boneMap, "scaleX", 1); - data.scaleY = this.getValue(boneMap, "scaleY", 1); - data.shearX = this.getValue(boneMap, "shearX", 0); - data.shearY = this.getValue(boneMap, "shearY", 0); - data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, "transform", "normal")); - data.skinRequired = this.getValue(boneMap, "skin", false); + const data = new BoneData(skeletonData.bones.length, boneMap.name, parent); + + data.length = this.getValue(boneMap, 'length', 0) * scale; + data.x = this.getValue(boneMap, 'x', 0) * scale; + data.y = this.getValue(boneMap, 'y', 0) * scale; + data.rotation = this.getValue(boneMap, 'rotation', 0); + data.scaleX = this.getValue(boneMap, 'scaleX', 1); + data.scaleY = this.getValue(boneMap, 'scaleY', 1); + data.shearX = this.getValue(boneMap, 'shearX', 0); + data.shearY = this.getValue(boneMap, 'shearY', 0); + data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, 'transform', 'normal')); + data.skinRequired = this.getValue(boneMap, 'skin', false); skeletonData.bones.push(data); } @@ -96,24 +104,27 @@ export class SkeletonJson { // Slots. if (root.slots) { for (let i = 0; i < root.slots.length; i++) { - let slotMap = root.slots[i]; - let slotName: string = slotMap.name; - let boneName: string = slotMap.bone; - let boneData = skeletonData.findBone(boneName); - if (boneData == null) throw new Error("Slot bone not found: " + boneName); - let data = new SlotData(skeletonData.slots.length, slotName, boneData); - - let color: string = this.getValue(slotMap, "color", null); + const slotMap = root.slots[i]; + const slotName: string = slotMap.name; + const boneName: string = slotMap.bone; + const boneData = skeletonData.findBone(boneName); + + if (boneData == null) throw new Error(`Slot bone not found: ${boneName}`); + const data = new SlotData(skeletonData.slots.length, slotName, boneData); + + const color: string = this.getValue(slotMap, 'color', null); + if (color != null) data.color.setFromString(color); - let dark: string = this.getValue(slotMap, "dark", null); + const dark: string = this.getValue(slotMap, 'dark', null); + if (dark != null) { data.darkColor = new Color(1, 1, 1, 1); data.darkColor.setFromString(dark); } - data.attachmentName = this.getValue(slotMap, "attachment", null); - data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, "blend", "normal")); + data.attachmentName = this.getValue(slotMap, 'attachment', null); + data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, 'blend', 'normal')); skeletonData.slots.push(data); } } @@ -121,28 +132,31 @@ export class SkeletonJson { // IK constraints if (root.ik) { for (let i = 0; i < root.ik.length; i++) { - let constraintMap = root.ik[i]; - let data = new IkConstraintData(constraintMap.name); - data.order = this.getValue(constraintMap, "order", 0); - data.skinRequired = this.getValue(constraintMap, "skin", false); + const constraintMap = root.ik[i]; + const data = new IkConstraintData(constraintMap.name); + + data.order = this.getValue(constraintMap, 'order', 0); + data.skinRequired = this.getValue(constraintMap, 'skin', false); for (let j = 0; j < constraintMap.bones.length; j++) { - let boneName = constraintMap.bones[j]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("IK bone not found: " + boneName); + const boneName = constraintMap.bones[j]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`IK bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findBone(targetName); - if (data.target == null) throw new Error("IK target bone not found: " + targetName); + if (data.target == null) throw new Error(`IK target bone not found: ${targetName}`); - data.mix = this.getValue(constraintMap, "mix", 1); - data.softness = this.getValue(constraintMap, "softness", 0) * scale; - data.bendDirection = this.getValue(constraintMap, "bendPositive", true) ? 1 : -1; - data.compress = this.getValue(constraintMap, "compress", false); - data.stretch = this.getValue(constraintMap, "stretch", false); - data.uniform = this.getValue(constraintMap, "uniform", false); + data.mix = this.getValue(constraintMap, 'mix', 1); + data.softness = this.getValue(constraintMap, 'softness', 0) * scale; + data.bendDirection = this.getValue(constraintMap, 'bendPositive', true) ? 1 : -1; + data.compress = this.getValue(constraintMap, 'compress', false); + data.stretch = this.getValue(constraintMap, 'stretch', false); + data.uniform = this.getValue(constraintMap, 'uniform', false); skeletonData.ikConstraints.push(data); } @@ -151,35 +165,38 @@ export class SkeletonJson { // Transform constraints. if (root.transform) { for (let i = 0; i < root.transform.length; i++) { - let constraintMap = root.transform[i]; - let data = new TransformConstraintData(constraintMap.name); - data.order = this.getValue(constraintMap, "order", 0); - data.skinRequired = this.getValue(constraintMap, "skin", false); + const constraintMap = root.transform[i]; + const data = new TransformConstraintData(constraintMap.name); + + data.order = this.getValue(constraintMap, 'order', 0); + data.skinRequired = this.getValue(constraintMap, 'skin', false); for (let j = 0; j < constraintMap.bones.length; j++) { - let boneName = constraintMap.bones[j]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); + const boneName = constraintMap.bones[j]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`Transform constraint bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findBone(targetName); - if (data.target == null) throw new Error("Transform constraint target bone not found: " + targetName); - - data.local = this.getValue(constraintMap, "local", false); - data.relative = this.getValue(constraintMap, "relative", false); - data.offsetRotation = this.getValue(constraintMap, "rotation", 0); - data.offsetX = this.getValue(constraintMap, "x", 0) * scale; - data.offsetY = this.getValue(constraintMap, "y", 0) * scale; - data.offsetScaleX = this.getValue(constraintMap, "scaleX", 0); - data.offsetScaleY = this.getValue(constraintMap, "scaleY", 0); - data.offsetShearY = this.getValue(constraintMap, "shearY", 0); - - data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); - data.translateMix = this.getValue(constraintMap, "translateMix", 1); - data.scaleMix = this.getValue(constraintMap, "scaleMix", 1); - data.shearMix = this.getValue(constraintMap, "shearMix", 1); + if (data.target == null) throw new Error(`Transform constraint target bone not found: ${targetName}`); + + data.local = this.getValue(constraintMap, 'local', false); + data.relative = this.getValue(constraintMap, 'relative', false); + data.offsetRotation = this.getValue(constraintMap, 'rotation', 0); + data.offsetX = this.getValue(constraintMap, 'x', 0) * scale; + data.offsetY = this.getValue(constraintMap, 'y', 0) * scale; + data.offsetScaleX = this.getValue(constraintMap, 'scaleX', 0); + data.offsetScaleY = this.getValue(constraintMap, 'scaleY', 0); + data.offsetShearY = this.getValue(constraintMap, 'shearY', 0); + + data.rotateMix = this.getValue(constraintMap, 'rotateMix', 1); + data.translateMix = this.getValue(constraintMap, 'translateMix', 1); + data.scaleMix = this.getValue(constraintMap, 'scaleMix', 1); + data.shearMix = this.getValue(constraintMap, 'shearMix', 1); skeletonData.transformConstraints.push(data); } @@ -188,32 +205,35 @@ export class SkeletonJson { // Path constraints. if (root.path) { for (let i = 0; i < root.path.length; i++) { - let constraintMap = root.path[i]; - let data = new PathConstraintData(constraintMap.name); - data.order = this.getValue(constraintMap, "order", 0); - data.skinRequired = this.getValue(constraintMap, "skin", false); + const constraintMap = root.path[i]; + const data = new PathConstraintData(constraintMap.name); + + data.order = this.getValue(constraintMap, 'order', 0); + data.skinRequired = this.getValue(constraintMap, 'skin', false); for (let j = 0; j < constraintMap.bones.length; j++) { - let boneName = constraintMap.bones[j]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); + const boneName = constraintMap.bones[j]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`Transform constraint bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findSlot(targetName); - if (data.target == null) throw new Error("Path target slot not found: " + targetName); + if (data.target == null) throw new Error(`Path target slot not found: ${targetName}`); - data.positionMode = SkeletonJson.positionModeFromString(this.getValue(constraintMap, "positionMode", "percent")); - data.spacingMode = SkeletonJson.spacingModeFromString(this.getValue(constraintMap, "spacingMode", "length")); - data.rotateMode = SkeletonJson.rotateModeFromString(this.getValue(constraintMap, "rotateMode", "tangent")); - data.offsetRotation = this.getValue(constraintMap, "rotation", 0); - data.position = this.getValue(constraintMap, "position", 0); + data.positionMode = SkeletonJson.positionModeFromString(this.getValue(constraintMap, 'positionMode', 'percent')); + data.spacingMode = SkeletonJson.spacingModeFromString(this.getValue(constraintMap, 'spacingMode', 'length')); + data.rotateMode = SkeletonJson.rotateModeFromString(this.getValue(constraintMap, 'rotateMode', 'tangent')); + data.offsetRotation = this.getValue(constraintMap, 'rotation', 0); + data.position = this.getValue(constraintMap, 'position', 0); if (data.positionMode == PositionMode.Fixed) data.position *= scale; - data.spacing = this.getValue(constraintMap, "spacing", 0); + data.spacing = this.getValue(constraintMap, 'spacing', 0); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale; - data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); - data.translateMix = this.getValue(constraintMap, "translateMix", 1); + data.rotateMix = this.getValue(constraintMap, 'rotateMix', 1); + data.translateMix = this.getValue(constraintMap, 'translateMix', 1); skeletonData.pathConstraints.push(data); } @@ -222,80 +242,90 @@ export class SkeletonJson { // Skins. if (root.skins) { for (let i = 0; i < root.skins.length; i++) { - let skinMap = root.skins[i] - let skin = new Skin(skinMap.name); + const skinMap = root.skins[i]; + const skin = new Skin(skinMap.name); if (skinMap.bones) { for (let ii = 0; ii < skinMap.bones.length; ii++) { - let bone = skeletonData.findBone(skinMap.bones[ii]); - if (bone == null) throw new Error("Skin bone not found: " + skinMap.bones[i]); + const bone = skeletonData.findBone(skinMap.bones[ii]); + + if (bone == null) throw new Error(`Skin bone not found: ${skinMap.bones[i]}`); skin.bones.push(bone); } } if (skinMap.ik) { for (let ii = 0; ii < skinMap.ik.length; ii++) { - let constraint = skeletonData.findIkConstraint(skinMap.ik[ii]); - if (constraint == null) throw new Error("Skin IK constraint not found: " + skinMap.ik[i]); + const constraint = skeletonData.findIkConstraint(skinMap.ik[ii]); + + if (constraint == null) throw new Error(`Skin IK constraint not found: ${skinMap.ik[i]}`); skin.constraints.push(constraint); } } if (skinMap.transform) { for (let ii = 0; ii < skinMap.transform.length; ii++) { - let constraint = skeletonData.findTransformConstraint(skinMap.transform[ii]); - if (constraint == null) throw new Error("Skin transform constraint not found: " + skinMap.transform[i]); + const constraint = skeletonData.findTransformConstraint(skinMap.transform[ii]); + + if (constraint == null) throw new Error(`Skin transform constraint not found: ${skinMap.transform[i]}`); skin.constraints.push(constraint); } } if (skinMap.path) { for (let ii = 0; ii < skinMap.path.length; ii++) { - let constraint = skeletonData.findPathConstraint(skinMap.path[ii]); - if (constraint == null) throw new Error("Skin path constraint not found: " + skinMap.path[i]); + const constraint = skeletonData.findPathConstraint(skinMap.path[ii]); + + if (constraint == null) throw new Error(`Skin path constraint not found: ${skinMap.path[i]}`); skin.constraints.push(constraint); } } - for (let slotName in skinMap.attachments) { - let slot = skeletonData.findSlot(slotName); - if (slot == null) throw new Error("Slot not found: " + slotName); - let slotMap = skinMap.attachments[slotName]; - for (let entryName in slotMap) { - let attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData); + for (const slotName in skinMap.attachments) { + const slot = skeletonData.findSlot(slotName); + + if (slot == null) throw new Error(`Slot not found: ${slotName}`); + const slotMap = skinMap.attachments[slotName]; + + for (const entryName in slotMap) { + const attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData); + if (attachment != null) skin.setAttachment(slot.index, entryName, attachment); } } skeletonData.skins.push(skin); - if (skin.name == "default") skeletonData.defaultSkin = skin; + if (skin.name == 'default') skeletonData.defaultSkin = skin; } } // Linked meshes. for (let i = 0, n = this.linkedMeshes.length; i < n; i++) { - let linkedMesh = this.linkedMeshes[i]; - let skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); - if (skin == null) throw new Error("Skin not found: " + linkedMesh.skin); - let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); - if (parent == null) throw new Error("Parent mesh not found: " + linkedMesh.parent); + const linkedMesh = this.linkedMeshes[i]; + const skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + + if (skin == null) throw new Error(`Skin not found: ${linkedMesh.skin}`); + const parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + + if (parent == null) throw new Error(`Parent mesh not found: ${linkedMesh.parent}`); linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? parent : linkedMesh.mesh; - linkedMesh.mesh.setParentMesh( parent); + linkedMesh.mesh.setParentMesh(parent); // linkedMesh.mesh.updateUVs(); } this.linkedMeshes.length = 0; // Events. if (root.events) { - for (let eventName in root.events) { - let eventMap = root.events[eventName]; - let data = new EventData(eventName); - data.intValue = this.getValue(eventMap, "int", 0); - data.floatValue = this.getValue(eventMap, "float", 0); - data.stringValue = this.getValue(eventMap, "string", ""); - data.audioPath = this.getValue(eventMap, "audio", null); + for (const eventName in root.events) { + const eventMap = root.events[eventName]; + const data = new EventData(eventName); + + data.intValue = this.getValue(eventMap, 'int', 0); + data.floatValue = this.getValue(eventMap, 'float', 0); + data.stringValue = this.getValue(eventMap, 'string', ''); + data.audioPath = this.getValue(eventMap, 'audio', null); if (data.audioPath != null) { - data.volume = this.getValue(eventMap, "volume", 1); - data.balance = this.getValue(eventMap, "balance", 0); + data.volume = this.getValue(eventMap, 'volume', 1); + data.balance = this.getValue(eventMap, 'balance', 0); } skeletonData.events.push(data); } @@ -303,8 +333,9 @@ export class SkeletonJson { // Animations. if (root.animations) { - for (let animationName in root.animations) { - let animationMap = root.animations[animationName]; + for (const animationName in root.animations) { + const animationMap = root.animations[animationName]; + this.readAnimation(animationMap, animationName, skeletonData); } } @@ -312,137 +343,168 @@ export class SkeletonJson { return skeletonData; } - readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment { - let scale = this.scale; - name = this.getValue(map, "name", name); + readAttachment(map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment { + const scale = this.scale; + + name = this.getValue(map, 'name', name); - let type = this.getValue(map, "type", "region"); + const type = this.getValue(map, 'type', 'region'); switch (type) { - case "region": { - let path = this.getValue(map, "path", name); - let region = this.attachmentLoader.newRegionAttachment(skin, name, path); + case 'region': { + const path = this.getValue(map, 'path', name); + const region = this.attachmentLoader.newRegionAttachment(skin, name, path); + if (region == null) return null; region.path = path; - region.x = this.getValue(map, "x", 0) * scale; - region.y = this.getValue(map, "y", 0) * scale; - region.scaleX = this.getValue(map, "scaleX", 1); - region.scaleY = this.getValue(map, "scaleY", 1); - region.rotation = this.getValue(map, "rotation", 0); + region.x = this.getValue(map, 'x', 0) * scale; + region.y = this.getValue(map, 'y', 0) * scale; + region.scaleX = this.getValue(map, 'scaleX', 1); + region.scaleY = this.getValue(map, 'scaleY', 1); + region.rotation = this.getValue(map, 'rotation', 0); region.width = map.width * scale; region.height = map.height * scale; - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) region.color.setFromString(color); // region.updateOffset(); return region; } - case "boundingbox": { - let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + case 'boundingbox': { + const box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + if (box == null) return null; this.readVertices(map, box, map.vertexCount << 1); - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) box.color.setFromString(color); + return box; } - case "mesh": - case "linkedmesh": { - let path = this.getValue(map, "path", name); - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + case 'mesh': + case 'linkedmesh': { + const path = this.getValue(map, 'path', name); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (mesh == null) return null; mesh.path = path; - let color = this.getValue(map, "color", null); + const color = this.getValue(map, 'color', null); + if (color != null) mesh.color.setFromString(color); - mesh.width = this.getValue(map, "width", 0) * scale; - mesh.height = this.getValue(map, "height", 0) * scale; + mesh.width = this.getValue(map, 'width', 0) * scale; + mesh.height = this.getValue(map, 'height', 0) * scale; + + const parent: string = this.getValue(map, 'parent', null); - let parent: string = this.getValue(map, "parent", null); if (parent != null) { - this.linkedMeshes.push(new LinkedMesh(mesh, this.getValue(map, "skin", null), slotIndex, parent, this.getValue(map, "deform", true))); + this.linkedMeshes.push(new LinkedMesh(mesh, this.getValue(map, 'skin', null), slotIndex, parent, this.getValue(map, 'deform', true))); + return mesh; } - let uvs: Array = map.uvs; + const uvs: Array = map.uvs; + this.readVertices(map, mesh, uvs.length); mesh.triangles = map.triangles; mesh.regionUVs = new Float32Array(uvs); // mesh.updateUVs(); - mesh.edges = this.getValue(map, "edges", null); - mesh.hullLength = this.getValue(map, "hull", 0) * 2; + mesh.edges = this.getValue(map, 'edges', null); + mesh.hullLength = this.getValue(map, 'hull', 0) * 2; + return mesh; } - case "path": { - let path = this.attachmentLoader.newPathAttachment(skin, name); + case 'path': { + const path = this.attachmentLoader.newPathAttachment(skin, name); + if (path == null) return null; - path.closed = this.getValue(map, "closed", false); - path.constantSpeed = this.getValue(map, "constantSpeed", true); + path.closed = this.getValue(map, 'closed', false); + path.constantSpeed = this.getValue(map, 'constantSpeed', true); + + const vertexCount = map.vertexCount; - let vertexCount = map.vertexCount; this.readVertices(map, path, vertexCount << 1); - let lengths: Array = Utils.newArray(vertexCount / 3, 0); - for (let i = 0; i < map.lengths.length; i++) - lengths[i] = map.lengths[i] * scale; + const lengths: Array = Utils.newArray(vertexCount / 3, 0); + + for (let i = 0; i < map.lengths.length; i++) lengths[i] = map.lengths[i] * scale; path.lengths = lengths; - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) path.color.setFromString(color); + return path; } - case "point": { - let point = this.attachmentLoader.newPointAttachment(skin, name); + case 'point': { + const point = this.attachmentLoader.newPointAttachment(skin, name); + if (point == null) return null; - point.x = this.getValue(map, "x", 0) * scale; - point.y = this.getValue(map, "y", 0) * scale; - point.rotation = this.getValue(map, "rotation", 0); + point.x = this.getValue(map, 'x', 0) * scale; + point.y = this.getValue(map, 'y', 0) * scale; + point.rotation = this.getValue(map, 'rotation', 0); + + const color = this.getValue(map, 'color', null); - let color = this.getValue(map, "color", null); if (color != null) point.color.setFromString(color); + return point; } - case "clipping": { - let clip = this.attachmentLoader.newClippingAttachment(skin, name); + case 'clipping': { + const clip = this.attachmentLoader.newClippingAttachment(skin, name); + if (clip == null) return null; - let end = this.getValue(map, "end", null); + const end = this.getValue(map, 'end', null); + if (end != null) { - let slot = skeletonData.findSlot(end); - if (slot == null) throw new Error("Clipping end slot not found: " + end); + const slot = skeletonData.findSlot(end); + + if (slot == null) throw new Error(`Clipping end slot not found: ${end}`); clip.endSlot = slot; } - let vertexCount = map.vertexCount; + const vertexCount = map.vertexCount; + this.readVertices(map, clip, vertexCount << 1); - let color: string = this.getValue(map, "color", null); + const color: string = this.getValue(map, 'color', null); + if (color != null) clip.color.setFromString(color); + return clip; } } + return null; } - readVertices (map: any, attachment: VertexAttachment, verticesLength: number) { - let scale = this.scale; + readVertices(map: any, attachment: VertexAttachment, verticesLength: number) { + const scale = this.scale; + attachment.worldVerticesLength = verticesLength; - let vertices: Array = map.vertices; + const vertices: Array = map.vertices; + if (verticesLength == vertices.length) { - let scaledVertices = Utils.toFloatArray(vertices); + const scaledVertices = Utils.toFloatArray(vertices); + if (scale != 1) { - for (let i = 0, n = vertices.length; i < n; i++) - scaledVertices[i] *= scale; + for (let i = 0, n = vertices.length; i < n; i++) scaledVertices[i] *= scale; } attachment.vertices = scaledVertices; + return; } - let weights = new Array(); - let bones = new Array(); - for (let i = 0, n = vertices.length; i < n;) { - let boneCount = vertices[i++]; + const weights = new Array(); + const bones = new Array(); + + for (let i = 0, n = vertices.length; i < n; ) { + const boneCount = vertices[i++]; + bones.push(boneCount); for (let nn = i + boneCount * 4; i < nn; i += 4) { bones.push(vertices[i]); @@ -455,100 +517,113 @@ export class SkeletonJson { attachment.vertices = Utils.toFloatArray(weights); } - readAnimation (map: any, name: string, skeletonData: SkeletonData) { - let scale = this.scale; - let timelines = new Array(); + readAnimation(map: any, name: string, skeletonData: SkeletonData) { + const scale = this.scale; + const timelines = new Array(); let duration = 0; // Slot timelines. if (map.slots) { - for (let slotName in map.slots) { - let slotMap = map.slots[slotName]; - let slotIndex = skeletonData.findSlotIndex(slotName); - if (slotIndex == -1) throw new Error("Slot not found: " + slotName); - for (let timelineName in slotMap) { - let timelineMap = slotMap[timelineName]; - if (timelineName == "attachment") { - let timeline = new AttachmentTimeline(timelineMap.length); + for (const slotName in map.slots) { + const slotMap = map.slots[slotName]; + const slotIndex = skeletonData.findSlotIndex(slotName); + + if (slotIndex == -1) throw new Error(`Slot not found: ${slotName}`); + for (const timelineName in slotMap) { + const timelineMap = slotMap[timelineName]; + + if (timelineName == 'attachment') { + const timeline = new AttachmentTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - timeline.setFrame(frameIndex++, this.getValue(valueMap, "time", 0), valueMap.name); + const valueMap = timelineMap[i]; + + timeline.setFrame(frameIndex++, this.getValue(valueMap, 'time', 0), valueMap.name); } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); - } else if (timelineName == "color") { - let timeline = new ColorTimeline(timelineMap.length); + } else if (timelineName == 'color') { + const timeline = new ColorTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - let color = new Color(); - color.setFromString(valueMap.color || "ffffffff"); - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), color.r, color.g, color.b, color.a); + const valueMap = timelineMap[i]; + const color = new Color(); + + color.setFromString(valueMap.color || 'ffffffff'); + timeline.setFrame(frameIndex, this.getValue(valueMap, 'time', 0), color.r, color.g, color.b, color.a); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * ColorTimeline.ENTRIES]); + } else if (timelineName == 'twoColor') { + const timeline = new TwoColorTimeline(timelineMap.length); - } else if (timelineName == "twoColor") { - let timeline = new TwoColorTimeline(timelineMap.length); timeline.slotIndex = slotIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - let light = new Color(); - let dark = new Color(); + const valueMap = timelineMap[i]; + const light = new Color(); + const dark = new Color(); + light.setFromString(valueMap.light); dark.setFromString(valueMap.dark); - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), light.r, light.g, light.b, light.a, dark.r, dark.g, dark.b); + timeline.setFrame(frameIndex, this.getValue(valueMap, 'time', 0), light.r, light.g, light.b, light.a, dark.r, dark.g, dark.b); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TwoColorTimeline.ENTRIES]); - - } else - throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } else throw new Error(`Invalid timeline type for a slot: ${timelineName} (${slotName})`); } } } // Bone timelines. if (map.bones) { - for (let boneName in map.bones) { - let boneMap = map.bones[boneName]; - let boneIndex = skeletonData.findBoneIndex(boneName); - if (boneIndex == -1) throw new Error("Bone not found: " + boneName); - for (let timelineName in boneMap) { - let timelineMap = boneMap[timelineName]; - if (timelineName === "rotate") { - let timeline = new RotateTimeline(timelineMap.length); + for (const boneName in map.bones) { + const boneMap = map.bones[boneName]; + const boneIndex = skeletonData.findBoneIndex(boneName); + + if (boneIndex == -1) throw new Error(`Bone not found: ${boneName}`); + for (const timelineName in boneMap) { + const timelineMap = boneMap[timelineName]; + + if (timelineName === 'rotate') { + const timeline = new RotateTimeline(timelineMap.length); + timeline.boneIndex = boneIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), this.getValue(valueMap, "angle", 0)); + const valueMap = timelineMap[i]; + + timeline.setFrame(frameIndex, this.getValue(valueMap, 'time', 0), this.getValue(valueMap, 'angle', 0)); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * RotateTimeline.ENTRIES]); - - } else if (timelineName === "translate" || timelineName === "scale" || timelineName === "shear") { + } else if (timelineName === 'translate' || timelineName === 'scale' || timelineName === 'shear') { let timeline: TranslateTimeline = null; - let timelineScale = 1, defaultValue = 0; - if (timelineName === "scale") { + let timelineScale = 1; + let defaultValue = 0; + + if (timelineName === 'scale') { timeline = new ScaleTimeline(timelineMap.length); defaultValue = 1; - } else if (timelineName === "shear") - timeline = new ShearTimeline(timelineMap.length); + } else if (timelineName === 'shear') timeline = new ShearTimeline(timelineMap.length); else { timeline = new TranslateTimeline(timelineMap.length); timelineScale = scale; @@ -556,34 +631,45 @@ export class SkeletonJson { timeline.boneIndex = boneIndex; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - let x = this.getValue(valueMap, "x", defaultValue), y = this.getValue(valueMap, "y", defaultValue); - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), x * timelineScale, y * timelineScale); + const valueMap = timelineMap[i]; + const x = this.getValue(valueMap, 'x', defaultValue); + const y = this.getValue(valueMap, 'y', defaultValue); + + timeline.setFrame(frameIndex, this.getValue(valueMap, 'time', 0), x * timelineScale, y * timelineScale); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TranslateTimeline.ENTRIES]); - - } else - throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } else throw new Error(`Invalid timeline type for a bone: ${timelineName} (${boneName})`); } } } // IK constraint timelines. if (map.ik) { - for (let constraintName in map.ik) { - let constraintMap = map.ik[constraintName]; - let constraint = skeletonData.findIkConstraint(constraintName); - let timeline = new IkConstraintTimeline(constraintMap.length); + for (const constraintName in map.ik) { + const constraintMap = map.ik[constraintName]; + const constraint = skeletonData.findIkConstraint(constraintName); + const timeline = new IkConstraintTimeline(constraintMap.length); + timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(constraint); let frameIndex = 0; + for (let i = 0; i < constraintMap.length; i++) { - let valueMap = constraintMap[i]; - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), this.getValue(valueMap, "mix", 1), this.getValue(valueMap, "softness", 0) * scale, - this.getValue(valueMap, "bendPositive", true) ? 1 : -1, this.getValue(valueMap, "compress", false), this.getValue(valueMap, "stretch", false)); + const valueMap = constraintMap[i]; + + timeline.setFrame( + frameIndex, + this.getValue(valueMap, 'time', 0), + this.getValue(valueMap, 'mix', 1), + this.getValue(valueMap, 'softness', 0) * scale, + this.getValue(valueMap, 'bendPositive', true) ? 1 : -1, + this.getValue(valueMap, 'compress', false), + this.getValue(valueMap, 'stretch', false) + ); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -594,38 +680,50 @@ export class SkeletonJson { // Transform constraint timelines. if (map.transform) { - for (let constraintName in map.transform) { - let constraintMap = map.transform[constraintName]; - let constraint = skeletonData.findTransformConstraint(constraintName); - let timeline = new TransformConstraintTimeline(constraintMap.length); + for (const constraintName in map.transform) { + const constraintMap = map.transform[constraintName]; + const constraint = skeletonData.findTransformConstraint(constraintName); + const timeline = new TransformConstraintTimeline(constraintMap.length); + timeline.transformConstraintIndex = skeletonData.transformConstraints.indexOf(constraint); let frameIndex = 0; + for (let i = 0; i < constraintMap.length; i++) { - let valueMap = constraintMap[i]; - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), this.getValue(valueMap, "rotateMix", 1), - this.getValue(valueMap, "translateMix", 1), this.getValue(valueMap, "scaleMix", 1), this.getValue(valueMap, "shearMix", 1)); + const valueMap = constraintMap[i]; + + timeline.setFrame( + frameIndex, + this.getValue(valueMap, 'time', 0), + this.getValue(valueMap, 'rotateMix', 1), + this.getValue(valueMap, 'translateMix', 1), + this.getValue(valueMap, 'scaleMix', 1), + this.getValue(valueMap, 'shearMix', 1) + ); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); - duration = Math.max(duration, - timeline.frames[(timeline.getFrameCount() - 1) * TransformConstraintTimeline.ENTRIES]); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TransformConstraintTimeline.ENTRIES]); } } // Path constraint timelines. if (map.path) { - for (let constraintName in map.path) { - let constraintMap = map.path[constraintName]; - let index = skeletonData.findPathConstraintIndex(constraintName); - if (index == -1) throw new Error("Path constraint not found: " + constraintName); - let data = skeletonData.pathConstraints[index]; - for (let timelineName in constraintMap) { - let timelineMap = constraintMap[timelineName]; - if (timelineName === "position" || timelineName === "spacing") { + for (const constraintName in map.path) { + const constraintMap = map.path[constraintName]; + const index = skeletonData.findPathConstraintIndex(constraintName); + + if (index == -1) throw new Error(`Path constraint not found: ${constraintName}`); + const data = skeletonData.pathConstraints[index]; + + for (const timelineName in constraintMap) { + const timelineMap = constraintMap[timelineName]; + + if (timelineName === 'position' || timelineName === 'spacing') { let timeline: PathConstraintPositionTimeline = null; let timelineScale = 1; - if (timelineName === "spacing") { + + if (timelineName === 'spacing') { timeline = new PathConstraintSpacingTimeline(timelineMap.length); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale; } else { @@ -634,29 +732,31 @@ export class SkeletonJson { } timeline.pathConstraintIndex = index; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), this.getValue(valueMap, timelineName, 0) * timelineScale); + const valueMap = timelineMap[i]; + + timeline.setFrame(frameIndex, this.getValue(valueMap, 'time', 0), this.getValue(valueMap, timelineName, 0) * timelineScale); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); - duration = Math.max(duration, - timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintPositionTimeline.ENTRIES]); - } else if (timelineName === "mix") { - let timeline = new PathConstraintMixTimeline(timelineMap.length); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintPositionTimeline.ENTRIES]); + } else if (timelineName === 'mix') { + const timeline = new PathConstraintMixTimeline(timelineMap.length); + timeline.pathConstraintIndex = index; let frameIndex = 0; + for (let i = 0; i < timelineMap.length; i++) { - let valueMap = timelineMap[i]; - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), this.getValue(valueMap, "rotateMix", 1), - this.getValue(valueMap, "translateMix", 1)); + const valueMap = timelineMap[i]; + + timeline.setFrame(frameIndex, this.getValue(valueMap, 'time', 0), this.getValue(valueMap, 'rotateMix', 1), this.getValue(valueMap, 'translateMix', 1)); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.push(timeline); - duration = Math.max(duration, - timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintMixTimeline.ENTRIES]); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintMixTimeline.ENTRIES]); } } } @@ -664,54 +764,58 @@ export class SkeletonJson { // Deform timelines. if (map.deform) { - for (let deformName in map.deform) { - let deformMap = map.deform[deformName]; - let skin = skeletonData.findSkin(deformName); + for (const deformName in map.deform) { + const deformMap = map.deform[deformName]; + const skin = skeletonData.findSkin(deformName); + if (skin == null) { - if (settings.FAIL_ON_NON_EXISTING_SKIN) { - throw new Error("Skin not found: " + deformName); - } else { - continue; - } + if (settings.FAIL_ON_NON_EXISTING_SKIN) { + throw new Error(`Skin not found: ${deformName}`); + } else { + continue; + } } - for (let slotName in deformMap) { - let slotMap = deformMap[slotName]; - let slotIndex = skeletonData.findSlotIndex(slotName); - if (slotIndex == -1) throw new Error("Slot not found: " + slotMap.name); - for (let timelineName in slotMap) { - let timelineMap = slotMap[timelineName]; - let attachment = skin.getAttachment(slotIndex, timelineName); - if (attachment == null) throw new Error("Deform attachment not found: " + timelineMap.name); - let weighted = attachment.bones != null; - let vertices = attachment.vertices; - let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; - - let timeline = new DeformTimeline(timelineMap.length); + for (const slotName in deformMap) { + const slotMap = deformMap[slotName]; + const slotIndex = skeletonData.findSlotIndex(slotName); + + if (slotIndex == -1) throw new Error(`Slot not found: ${slotMap.name}`); + for (const timelineName in slotMap) { + const timelineMap = slotMap[timelineName]; + const attachment = skin.getAttachment(slotIndex, timelineName); + + if (attachment == null) throw new Error(`Deform attachment not found: ${timelineMap.name}`); + const weighted = attachment.bones != null; + const vertices = attachment.vertices; + const deformLength = weighted ? (vertices.length / 3) * 2 : vertices.length; + + const timeline = new DeformTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; timeline.attachment = attachment; let frameIndex = 0; + for (let j = 0; j < timelineMap.length; j++) { - let valueMap = timelineMap[j]; + const valueMap = timelineMap[j]; let deform: ArrayLike; - let verticesValue: Array = this.getValue(valueMap, "vertices", null); - if (verticesValue == null) - deform = weighted ? Utils.newFloatArray(deformLength) : vertices; + const verticesValue: Array = this.getValue(valueMap, 'vertices', null); + + if (verticesValue == null) deform = weighted ? Utils.newFloatArray(deformLength) : vertices; else { deform = Utils.newFloatArray(deformLength); - let start = this.getValue(valueMap, "offset", 0); + const start = this.getValue(valueMap, 'offset', 0); + Utils.arrayCopy(verticesValue, 0, deform, start, verticesValue.length); if (scale != 1) { - for (let i = start, n = i + verticesValue.length; i < n; i++) - deform[i] *= scale; + for (let i = start, n = i + verticesValue.length; i < n; i++) deform[i] *= scale; } if (!weighted) { - for (let i = 0; i < deformLength; i++) - deform[i] += vertices[i]; + for (let i = 0; i < deformLength; i++) deform[i] += vertices[i]; } } - timeline.setFrame(frameIndex, this.getValue(valueMap, "time", 0), deform); + timeline.setFrame(frameIndex, this.getValue(valueMap, 'time', 0), deform); this.readCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -724,37 +828,40 @@ export class SkeletonJson { // Draw order timeline. let drawOrderNode = map.drawOrder; + if (drawOrderNode == null) drawOrderNode = map.draworder; if (drawOrderNode != null) { - let timeline = new DrawOrderTimeline(drawOrderNode.length); - let slotCount = skeletonData.slots.length; + const timeline = new DrawOrderTimeline(drawOrderNode.length); + const slotCount = skeletonData.slots.length; let frameIndex = 0; + for (let j = 0; j < drawOrderNode.length; j++) { - let drawOrderMap = drawOrderNode[j]; + const drawOrderMap = drawOrderNode[j]; let drawOrder: Array = null; - let offsets = this.getValue(drawOrderMap, "offsets", null); + const offsets = this.getValue(drawOrderMap, 'offsets', null); + if (offsets != null) { drawOrder = Utils.newArray(slotCount, -1); - let unchanged = Utils.newArray(slotCount - offsets.length, 0); - let originalIndex = 0, unchangedIndex = 0; + const unchanged = Utils.newArray(slotCount - offsets.length, 0); + let originalIndex = 0; + let unchangedIndex = 0; + for (let i = 0; i < offsets.length; i++) { - let offsetMap = offsets[i]; - let slotIndex = skeletonData.findSlotIndex(offsetMap.slot); - if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap.slot); + const offsetMap = offsets[i]; + const slotIndex = skeletonData.findSlotIndex(offsetMap.slot); + + if (slotIndex == -1) throw new Error(`Slot not found: ${offsetMap.slot}`); // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + offsetMap.offset] = originalIndex++; } // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. - for (let i = slotCount - 1; i >= 0; i--) - if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; + for (let i = slotCount - 1; i >= 0; i--) if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; } - timeline.setFrame(frameIndex++, this.getValue(drawOrderMap, "time", 0), drawOrder); + timeline.setFrame(frameIndex++, this.getValue(drawOrderMap, 'time', 0), drawOrder); } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); @@ -762,19 +869,22 @@ export class SkeletonJson { // Event timeline. if (map.events) { - let timeline = new EventTimeline(map.events.length); + const timeline = new EventTimeline(map.events.length); let frameIndex = 0; + for (let i = 0; i < map.events.length; i++) { - let eventMap = map.events[i]; - let eventData = skeletonData.findEvent(eventMap.name); - if (eventData == null) throw new Error("Event not found: " + eventMap.name); - let event = new Event(Utils.toSinglePrecision(this.getValue(eventMap, "time", 0)), eventData); - event.intValue = this.getValue(eventMap, "int", eventData.intValue); - event.floatValue = this.getValue(eventMap, "float", eventData.floatValue); - event.stringValue = this.getValue(eventMap, "string", eventData.stringValue); + const eventMap = map.events[i]; + const eventData = skeletonData.findEvent(eventMap.name); + + if (eventData == null) throw new Error(`Event not found: ${eventMap.name}`); + const event = new Event(Utils.toSinglePrecision(this.getValue(eventMap, 'time', 0)), eventData); + + event.intValue = this.getValue(eventMap, 'int', eventData.intValue); + event.floatValue = this.getValue(eventMap, 'float', eventData.floatValue); + event.stringValue = this.getValue(eventMap, 'string', eventData.stringValue); if (event.data.audioPath != null) { - event.volume = this.getValue(eventMap, "volume", 1); - event.balance = this.getValue(eventMap, "balance", 0); + event.volume = this.getValue(eventMap, 'volume', 1); + event.balance = this.getValue(eventMap, 'balance', 0); } timeline.setFrame(frameIndex++, event); } @@ -783,76 +893,77 @@ export class SkeletonJson { } if (isNaN(duration)) { - throw new Error("Error while parsing animation, duration is NaN"); + throw new Error('Error while parsing animation, duration is NaN'); } skeletonData.animations.push(new Animation(name, timelines, duration)); } - readCurve (map: any, timeline: CurveTimeline, frameIndex: number) { - if (!map.hasOwnProperty("curve")) return; - if (map.curve === "stepped") - timeline.setStepped(frameIndex); + readCurve(map: any, timeline: CurveTimeline, frameIndex: number) { + if (!map.hasOwnProperty('curve')) return; + if (map.curve === 'stepped') timeline.setStepped(frameIndex); else { - let curve: number = map.curve; - timeline.setCurve(frameIndex, curve, this.getValue(map, "c2", 0), this.getValue(map, "c3", 1), this.getValue(map, "c4", 1)); + const curve: number = map.curve; + + timeline.setCurve(frameIndex, curve, this.getValue(map, 'c2', 0), this.getValue(map, 'c3', 1), this.getValue(map, 'c4', 1)); } } - getValue (map: any, prop: string, defaultValue: any) { + getValue(map: any, prop: string, defaultValue: any) { return map[prop] !== undefined ? map[prop] : defaultValue; } - static blendModeFromString (str: string) { + static blendModeFromString(str: string) { str = str.toLowerCase(); - if (str == "normal") return BLEND_MODES.NORMAL; - if (str == "additive") return BLEND_MODES.ADD; - if (str == "multiply") return BLEND_MODES.MULTIPLY; - if (str == "screen") return BLEND_MODES.SCREEN; + if (str == 'normal') return BLEND_MODES.NORMAL; + if (str == 'additive') return BLEND_MODES.ADD; + if (str == 'multiply') return BLEND_MODES.MULTIPLY; + if (str == 'screen') return BLEND_MODES.SCREEN; throw new Error(`Unknown blend mode: ${str}`); } - static positionModeFromString (str: string) { + static positionModeFromString(str: string) { str = str.toLowerCase(); - if (str == "fixed") return PositionMode.Fixed; - if (str == "percent") return PositionMode.Percent; + if (str == 'fixed') return PositionMode.Fixed; + if (str == 'percent') return PositionMode.Percent; throw new Error(`Unknown position mode: ${str}`); } - static spacingModeFromString (str: string) { + static spacingModeFromString(str: string) { str = str.toLowerCase(); - if (str == "length") return SpacingMode.Length; - if (str == "fixed") return SpacingMode.Fixed; - if (str == "percent") return SpacingMode.Percent; + if (str == 'length') return SpacingMode.Length; + if (str == 'fixed') return SpacingMode.Fixed; + if (str == 'percent') return SpacingMode.Percent; throw new Error(`Unknown position mode: ${str}`); } - static rotateModeFromString (str: string) { + static rotateModeFromString(str: string) { str = str.toLowerCase(); - if (str == "tangent") return RotateMode.Tangent; - if (str == "chain") return RotateMode.Chain; - if (str == "chainscale") return RotateMode.ChainScale; + if (str == 'tangent') return RotateMode.Tangent; + if (str == 'chain') return RotateMode.Chain; + if (str == 'chainscale') return RotateMode.ChainScale; throw new Error(`Unknown rotate mode: ${str}`); } static transformModeFromString(str: string) { str = str.toLowerCase(); - if (str == "normal") return TransformMode.Normal; - if (str == "onlytranslation") return TransformMode.OnlyTranslation; - if (str == "norotationorreflection") return TransformMode.NoRotationOrReflection; - if (str == "noscale") return TransformMode.NoScale; - if (str == "noscaleorreflection") return TransformMode.NoScaleOrReflection; + if (str == 'normal') return TransformMode.Normal; + if (str == 'onlytranslation') return TransformMode.OnlyTranslation; + if (str == 'norotationorreflection') return TransformMode.NoRotationOrReflection; + if (str == 'noscale') return TransformMode.NoScale; + if (str == 'noscaleorreflection') return TransformMode.NoScaleOrReflection; throw new Error(`Unknown transform mode: ${str}`); } } class LinkedMesh { - parent: string; skin: string; + parent: string; + skin: string; slotIndex: number; mesh: MeshAttachment; inheritDeform: boolean; - constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { + constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; diff --git a/packages/runtime-3.8/src/core/Skin.ts b/packages/runtime-3.8/src/core/Skin.ts index a7be47d5..eb71c657 100644 --- a/packages/runtime-3.8/src/core/Skin.ts +++ b/packages/runtime-3.8/src/core/Skin.ts @@ -1,15 +1,15 @@ -import {Attachment, MeshAttachment} from './attachments'; -import {BoneData} from "./BoneData"; -import {ConstraintData} from "./Constraint"; -import {Skeleton} from "./Skeleton"; +import { Attachment, MeshAttachment } from './attachments'; +import type { BoneData } from './BoneData'; +import type { ConstraintData } from './Constraint'; +import type { Skeleton } from './Skeleton'; -import type {Map, ISkin} from '@pixi-spine/base'; +import type { Map, ISkin } from '@pixi-spine/base'; /** * @public */ export class SkinEntry { - constructor(public slotIndex: number, public name: string, public attachment: Attachment) { } + constructor(public slotIndex: number, public name: string, public attachment: Attachment) {} } /** @@ -21,23 +21,25 @@ export class Skin implements ISkin { bones = Array(); constraints = new Array(); - constructor (name: string) { - if (name == null) throw new Error("name cannot be null."); + constructor(name: string) { + if (name == null) throw new Error('name cannot be null.'); this.name = name; } - setAttachment (slotIndex: number, name: string, attachment: Attachment) { - if (attachment == null) throw new Error("attachment cannot be null."); - let attachments = this.attachments; + setAttachment(slotIndex: number, name: string, attachment: Attachment) { + if (attachment == null) throw new Error('attachment cannot be null.'); + const attachments = this.attachments; + if (slotIndex >= attachments.length) attachments.length = slotIndex + 1; - if (!attachments[slotIndex]) attachments[slotIndex] = { }; + if (!attachments[slotIndex]) attachments[slotIndex] = {}; attachments[slotIndex][name] = attachment; } - addSkin (skin: Skin) { - for(let i = 0; i < skin.bones.length; i++) { - let bone = skin.bones[i]; + addSkin(skin: Skin) { + for (let i = 0; i < skin.bones.length; i++) { + const bone = skin.bones[i]; let contained = false; + for (let j = 0; j < this.bones.length; j++) { if (this.bones[j] == bone) { contained = true; @@ -47,9 +49,10 @@ export class Skin implements ISkin { if (!contained) this.bones.push(bone); } - for(let i = 0; i < skin.constraints.length; i++) { - let constraint = skin.constraints[i]; + for (let i = 0; i < skin.constraints.length; i++) { + const constraint = skin.constraints[i]; let contained = false; + for (let j = 0; j < this.constraints.length; j++) { if (this.constraints[j] == constraint) { contained = true; @@ -59,17 +62,20 @@ export class Skin implements ISkin { if (!contained) this.constraints.push(constraint); } - let attachments = skin.getAttachments(); + const attachments = skin.getAttachments(); + for (let i = 0; i < attachments.length; i++) { - var attachment = attachments[i]; + const attachment = attachments[i]; + this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment); } } - copySkin (skin: Skin) { - for(let i = 0; i < skin.bones.length; i++) { - let bone = skin.bones[i]; + copySkin(skin: Skin) { + for (let i = 0; i < skin.bones.length; i++) { + const bone = skin.bones[i]; let contained = false; + for (let j = 0; j < this.bones.length; j++) { if (this.bones[j] == bone) { contained = true; @@ -79,9 +85,10 @@ export class Skin implements ISkin { if (!contained) this.bones.push(bone); } - for(let i = 0; i < skin.constraints.length; i++) { - let constraint = skin.constraints[i]; + for (let i = 0; i < skin.constraints.length; i++) { + const constraint = skin.constraints[i]; let contained = false; + for (let j = 0; j < this.constraints.length; j++) { if (this.constraints[j] == constraint) { contained = true; @@ -91,9 +98,11 @@ export class Skin implements ISkin { if (!contained) this.constraints.push(constraint); } - let attachments = skin.getAttachments(); + const attachments = skin.getAttachments(); + for (let i = 0; i < attachments.length; i++) { - var attachment = attachments[i]; + const attachment = attachments[i]; + if (attachment.attachment == null) continue; if (attachment.attachment instanceof MeshAttachment) { attachment.attachment = attachment.attachment.newLinkedMesh(); @@ -106,58 +115,71 @@ export class Skin implements ISkin { } /** @return May be null. */ - getAttachment (slotIndex: number, name: string): Attachment { - let dictionary = this.attachments[slotIndex]; + getAttachment(slotIndex: number, name: string): Attachment { + const dictionary = this.attachments[slotIndex]; + return dictionary ? dictionary[name] : null; } - removeAttachment (slotIndex: number, name: string) { - let dictionary = this.attachments[slotIndex]; + removeAttachment(slotIndex: number, name: string) { + const dictionary = this.attachments[slotIndex]; + if (dictionary) dictionary[name] = null; } - getAttachments (): Array { - let entries = new Array(); - for (var i = 0; i < this.attachments.length; i++) { - let slotAttachments = this.attachments[i]; + getAttachments(): Array { + const entries = new Array(); + + for (let i = 0; i < this.attachments.length; i++) { + const slotAttachments = this.attachments[i]; + if (slotAttachments) { - for (let name in slotAttachments) { - let attachment = slotAttachments[name]; + for (const name in slotAttachments) { + const attachment = slotAttachments[name]; + if (attachment) entries.push(new SkinEntry(i, name, attachment)); } } } + return entries; } - getAttachmentsForSlot (slotIndex: number, attachments: Array) { - let slotAttachments = this.attachments[slotIndex]; + getAttachmentsForSlot(slotIndex: number, attachments: Array) { + const slotAttachments = this.attachments[slotIndex]; + if (slotAttachments) { - for (let name in slotAttachments) { - let attachment = slotAttachments[name]; + for (const name in slotAttachments) { + const attachment = slotAttachments[name]; + if (attachment) attachments.push(new SkinEntry(slotIndex, name, attachment)); } } } - clear () { + clear() { this.attachments.length = 0; this.bones.length = 0; this.constraints.length = 0; } /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ - attachAll (skeleton: Skeleton, oldSkin: Skin) { + attachAll(skeleton: Skeleton, oldSkin: Skin) { let slotIndex = 0; + for (let i = 0; i < skeleton.slots.length; i++) { - let slot = skeleton.slots[i]; - let slotAttachment = slot.getAttachment(); + const slot = skeleton.slots[i]; + const slotAttachment = slot.getAttachment(); + if (slotAttachment && slotIndex < oldSkin.attachments.length) { - let dictionary = oldSkin.attachments[slotIndex]; - for (let key in dictionary) { - let skinAttachment:Attachment = dictionary[key]; + const dictionary = oldSkin.attachments[slotIndex]; + + for (const key in dictionary) { + const skinAttachment: Attachment = dictionary[key]; + if (slotAttachment == skinAttachment) { - let attachment = this.getAttachment(slotIndex, key); + const attachment = this.getAttachment(slotIndex, key); + if (attachment != null) slot.setAttachment(attachment); break; } diff --git a/packages/runtime-3.8/src/core/Slot.ts b/packages/runtime-3.8/src/core/Slot.ts index c5cc025e..ae8ba827 100644 --- a/packages/runtime-3.8/src/core/Slot.ts +++ b/packages/runtime-3.8/src/core/Slot.ts @@ -1,15 +1,14 @@ +import { Color, ISlot } from '@pixi-spine/base'; -import {Color, ISlot} from '@pixi-spine/base'; - -import type {Attachment} from './attachments/Attachment'; -import type {Bone} from './Bone'; -import type {SlotData} from './SlotData'; +import type { Attachment } from './attachments/Attachment'; +import type { Bone } from './Bone'; +import type { SlotData } from './SlotData'; /** * @public */ export class Slot implements ISlot { - //this is canon + // this is canon blendMode: number; data: SlotData; bone: Bone; @@ -20,9 +19,9 @@ export class Slot implements ISlot { attachmentState: number; deform = new Array(); - constructor (data: SlotData, bone: Bone) { - if (data == null) throw new Error("data cannot be null."); - if (bone == null) throw new Error("bone cannot be null."); + constructor(data: SlotData, bone: Bone) { + if (data == null) throw new Error('data cannot be null.'); + if (bone == null) throw new Error('bone cannot be null.'); this.data = data; this.bone = bone; this.color = new Color(); @@ -33,33 +32,32 @@ export class Slot implements ISlot { } /** @return May be null. */ - getAttachment (): Attachment { + getAttachment(): Attachment { return this.attachment; } /** Sets the attachment and if it changed, resets {@link #getAttachmentTime()} and clears {@link #getAttachmentVertices()}. * @param attachment May be null. */ - setAttachment (attachment: Attachment) { + setAttachment(attachment: Attachment) { if (this.attachment == attachment) return; this.attachment = attachment; this.attachmentTime = this.bone.skeleton.time; this.deform.length = 0; } - setAttachmentTime (time: number) { + setAttachmentTime(time: number) { this.attachmentTime = this.bone.skeleton.time - time; } /** Returns the time since the attachment was set. */ - getAttachmentTime (): number { + getAttachmentTime(): number { return this.bone.skeleton.time - this.attachmentTime; } - setToSetupPose () { + setToSetupPose() { this.color.setFromColor(this.data.color); if (this.darkColor != null) this.darkColor.setFromColor(this.data.darkColor); - if (this.data.attachmentName == null) - this.attachment = null; + if (this.data.attachmentName == null) this.attachment = null; else { this.attachment = null; this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName)); diff --git a/packages/runtime-3.8/src/core/SlotData.ts b/packages/runtime-3.8/src/core/SlotData.ts index 9a40db86..4d5ca4ef 100644 --- a/packages/runtime-3.8/src/core/SlotData.ts +++ b/packages/runtime-3.8/src/core/SlotData.ts @@ -1,8 +1,8 @@ -import {Color} from '@pixi-spine/base'; +import { Color } from '@pixi-spine/base'; -import type {ISlotData} from '@pixi-spine/base'; -import type {BLEND_MODES} from '@pixi/constants'; -import {BoneData} from "./BoneData"; +import type { ISlotData } from '@pixi-spine/base'; +import type { BLEND_MODES } from '@pixi/constants'; +import type { BoneData } from './BoneData'; /** * @public @@ -16,10 +16,10 @@ export class SlotData implements ISlotData { attachmentName: string; blendMode: BLEND_MODES; - constructor (index: number, name: string, boneData: BoneData) { - if (index < 0) throw new Error("index must be >= 0."); - if (name == null) throw new Error("name cannot be null."); - if (boneData == null) throw new Error("boneData cannot be null."); + constructor(index: number, name: string, boneData: BoneData) { + if (index < 0) throw new Error('index must be >= 0.'); + if (name == null) throw new Error('name cannot be null.'); + if (boneData == null) throw new Error('boneData cannot be null.'); this.index = index; this.name = name; this.boneData = boneData; diff --git a/packages/runtime-3.8/src/core/TransformConstraint.ts b/packages/runtime-3.8/src/core/TransformConstraint.ts index f7286050..72398b22 100644 --- a/packages/runtime-3.8/src/core/TransformConstraint.ts +++ b/packages/runtime-3.8/src/core/TransformConstraint.ts @@ -1,8 +1,8 @@ -import {Updatable} from "./Updatable"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {Bone} from "./Bone"; -import {MathUtils, Vector2} from "@pixi-spine/base"; -import {Skeleton} from "./Skeleton"; +import type { Updatable } from './Updatable'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { Bone } from './Bone'; +import { MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Skeleton } from './Skeleton'; /** * @public @@ -19,20 +19,19 @@ export class TransformConstraint implements Updatable { active = false; constructor(data: TransformConstraintData, skeleton: Skeleton) { - if (data == null) throw new Error("data cannot be null."); - if (skeleton == null) throw new Error("skeleton cannot be null."); + if (data == null) throw new Error('data cannot be null.'); + if (skeleton == null) throw new Error('skeleton cannot be null.'); this.data = data; this.rotateMix = data.rotateMix; this.translateMix = data.translateMix; this.scaleMix = data.scaleMix; this.shearMix = data.shearMix; this.bones = new Array(); - for (let i = 0; i < data.bones.length; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0; i < data.bones.length; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findBone(data.target.name); } - isActive () { + isActive() { return this.active; } @@ -42,43 +41,46 @@ export class TransformConstraint implements Updatable { update() { if (this.data.local) { - if (this.data.relative) - this.applyRelativeLocal(); - else - this.applyAbsoluteLocal(); - - } else { - if (this.data.relative) - this.applyRelativeWorld(); - else - this.applyAbsoluteWorld(); - } + if (this.data.relative) this.applyRelativeLocal(); + else this.applyAbsoluteLocal(); + } else if (this.data.relative) this.applyRelativeWorld(); + else this.applyAbsoluteWorld(); } applyAbsoluteWorld() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; - let targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect; - let offsetShearY = this.data.offsetShearY * degRadReflect; - let bones = this.bones; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + const targetMat = target.matrix; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; let modified = false; - let mat = bone.matrix; + const mat = bone.matrix; if (rotateMix != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) - r += MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; r *= rotateMix; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -87,7 +89,8 @@ export class TransformConstraint implements Updatable { } if (translateMix != 0) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += (temp.x - mat.tx) * translateMix; mat.ty += (temp.y - mat.ty) * translateMix; @@ -97,6 +100,7 @@ export class TransformConstraint implements Updatable { if (scaleMix > 0) { let s = Math.sqrt(mat.a * mat.a + mat.b * mat.b); let ts = Math.sqrt(ta * ta + tc * tc); + if (s > 0.00001) s = (s + (ts - s + this.data.offsetScaleX) * scaleMix) / s; mat.a *= s; mat.b *= s; @@ -109,15 +113,16 @@ export class TransformConstraint implements Updatable { } if (shearMix > 0) { - let b = mat.c, d = mat.d; - let by = Math.atan2(d, b); + const b = mat.c; + const d = mat.d; + const by = Math.atan2(d, b); let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(mat.b, mat.a)); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) - r += MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; r = by + (r + offsetShearY) * shearMix; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; modified = true; @@ -128,28 +133,39 @@ export class TransformConstraint implements Updatable { } applyRelativeWorld() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; - let targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect, - offsetShearY = this.data.offsetShearY * degRadReflect; - let bones = this.bones; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + const targetMat = target.matrix; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; let modified = false; - let mat = bone.matrix; + const mat = bone.matrix; if (rotateMix != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; else if (r < -MathUtils.PI) r += MathUtils.PI2; r *= rotateMix; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -158,7 +174,8 @@ export class TransformConstraint implements Updatable { } if (translateMix != 0) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += temp.x * translateMix; mat.ty += temp.y * translateMix; @@ -167,6 +184,7 @@ export class TransformConstraint implements Updatable { if (scaleMix > 0) { let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * scaleMix + 1; + mat.a *= s; mat.b *= s; s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * scaleMix + 1; @@ -177,12 +195,15 @@ export class TransformConstraint implements Updatable { if (shearMix > 0) { let r = Math.atan2(td, tb) - Math.atan2(tc, ta); - if (r > MathUtils.PI) - r -= MathUtils.PI2; + + if (r > MathUtils.PI) r -= MathUtils.PI2; else if (r < -MathUtils.PI) r += MathUtils.PI2; - let b = mat.c, d = mat.d; + const b = mat.c; + const d = mat.d; + r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * shearMix; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; modified = true; @@ -193,37 +214,50 @@ export class TransformConstraint implements Updatable { } applyAbsoluteLocal() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + if (!target.appliedValid) target.updateAppliedTransform(); - let bones = this.bones; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (!bone.appliedValid) bone.updateAppliedTransform(); let rotation = bone.arotation; + if (rotateMix != 0) { let r = target.arotation - rotation + this.data.offsetRotation; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; rotation += r * rotateMix; } - let x = bone.ax, y = bone.ay; + let x = bone.ax; + let y = bone.ay; + if (translateMix != 0) { x += (target.ax - x + this.data.offsetX) * translateMix; y += (target.ay - y + this.data.offsetY) * translateMix; } - let scaleX = bone.ascaleX, scaleY = bone.ascaleY; + let scaleX = bone.ascaleX; + let scaleY = bone.ascaleY; + if (scaleMix > 0) { if (scaleX > 0.00001) scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * scaleMix) / scaleX; if (scaleY > 0.00001) scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * scaleMix) / scaleY; } - let shearY = bone.ashearY; + const shearY = bone.ashearY; + if (shearMix > 0) { let r = target.ashearY - shearY + this.data.offsetShearY; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; bone.shearY += r * shearMix; } @@ -233,31 +267,42 @@ export class TransformConstraint implements Updatable { } applyRelativeLocal() { - let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, - shearMix = this.shearMix; - let target = this.target; + const rotateMix = this.rotateMix; + const translateMix = this.translateMix; + const scaleMix = this.scaleMix; + const shearMix = this.shearMix; + const target = this.target; + if (!target.appliedValid) target.updateAppliedTransform(); - let bones = this.bones; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (!bone.appliedValid) bone.updateAppliedTransform(); let rotation = bone.arotation; + if (rotateMix != 0) rotation += (target.arotation + this.data.offsetRotation) * rotateMix; - let x = bone.ax, y = bone.ay; + let x = bone.ax; + let y = bone.ay; + if (translateMix != 0) { x += (target.ax + this.data.offsetX) * translateMix; y += (target.ay + this.data.offsetY) * translateMix; } - let scaleX = bone.ascaleX, scaleY = bone.ascaleY; + let scaleX = bone.ascaleX; + let scaleY = bone.ascaleY; + if (scaleMix > 0) { - if (scaleX > 0.00001) scaleX *= ((target.ascaleX - 1 + this.data.offsetScaleX) * scaleMix) + 1; - if (scaleY > 0.00001) scaleY *= ((target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix) + 1; + if (scaleX > 0.00001) scaleX *= (target.ascaleX - 1 + this.data.offsetScaleX) * scaleMix + 1; + if (scaleY > 0.00001) scaleY *= (target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix + 1; } let shearY = bone.ashearY; + if (shearMix > 0) shearY += (target.ashearY + this.data.offsetShearY) * shearMix; bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); diff --git a/packages/runtime-3.8/src/core/TransformConstraintData.ts b/packages/runtime-3.8/src/core/TransformConstraintData.ts index 9854288f..5873ad63 100644 --- a/packages/runtime-3.8/src/core/TransformConstraintData.ts +++ b/packages/runtime-3.8/src/core/TransformConstraintData.ts @@ -1,5 +1,5 @@ -import {BoneData} from './BoneData'; -import {ConstraintData} from './Constraint'; +import type { BoneData } from './BoneData'; +import { ConstraintData } from './Constraint'; /** * @public @@ -7,12 +7,20 @@ import {ConstraintData} from './Constraint'; export class TransformConstraintData extends ConstraintData { bones = new Array(); target: BoneData; - rotateMix = 0; translateMix = 0; scaleMix = 0; shearMix = 0; - offsetRotation = 0; offsetX = 0; offsetY = 0; offsetScaleX = 0; offsetScaleY = 0; offsetShearY = 0; + rotateMix = 0; + translateMix = 0; + scaleMix = 0; + shearMix = 0; + offsetRotation = 0; + offsetX = 0; + offsetY = 0; + offsetScaleX = 0; + offsetScaleY = 0; + offsetShearY = 0; relative = false; local = false; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } diff --git a/packages/runtime-3.8/src/core/VertexEffect.ts b/packages/runtime-3.8/src/core/VertexEffect.ts index a3a9cc9f..98ef8cf1 100644 --- a/packages/runtime-3.8/src/core/VertexEffect.ts +++ b/packages/runtime-3.8/src/core/VertexEffect.ts @@ -1,5 +1,5 @@ -import type {Skeleton} from "./Skeleton"; -import type {Color, Vector2} from "@pixi-spine/base"; +import type { Skeleton } from './Skeleton'; +import type { Color, Vector2 } from '@pixi-spine/base'; /** * @public diff --git a/packages/runtime-3.8/src/core/attachments/Attachment.ts b/packages/runtime-3.8/src/core/attachments/Attachment.ts index 4b7756c6..9e11c811 100644 --- a/packages/runtime-3.8/src/core/attachments/Attachment.ts +++ b/packages/runtime-3.8/src/core/attachments/Attachment.ts @@ -1,7 +1,7 @@ -import {AttachmentType, Utils} from '@pixi-spine/base'; -import type {IAttachment, ArrayLike} from '@pixi-spine/base'; +import { AttachmentType, Utils } from '@pixi-spine/base'; +import type { IAttachment, ArrayLike } from '@pixi-spine/base'; -import type {Slot} from '../Slot'; +import type { Slot } from '../Slot'; /** * @public @@ -11,11 +11,11 @@ export abstract class Attachment implements IAttachment { type: AttachmentType; constructor(name: string) { - if (name == null) throw new Error("name cannot be null."); + if (name == null) throw new Error('name cannot be null.'); this.name = name; } - abstract copy (): Attachment; + abstract copy(): Attachment; } /** @@ -43,40 +43,57 @@ export abstract class VertexAttachment extends Attachment { * @param count The number of world vertex values to output. Must be <= {@link #getWorldVerticesLength()} - start. * @param worldVertices The output world vertices. Must have a length >= offset + count. * @param offset The worldVertices index to begin writing values. */ - computeWorldVertices (slot: Slot, start: number, count: number, worldVertices: ArrayLike, offset: number, stride: number) { + computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: ArrayLike, offset: number, stride: number) { count = offset + (count >> 1) * stride; - let skeleton = slot.bone.skeleton; - let deformArray = slot.deform; + const skeleton = slot.bone.skeleton; + const deformArray = slot.deform; let vertices = this.vertices; - let bones = this.bones; + const bones = this.bones; + if (bones == null) { if (deformArray.length > 0) vertices = deformArray; - let mat = slot.bone.matrix; - let x = mat.tx; - let y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const mat = slot.bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + for (let v = start, w = offset; w < count; v += 2, w += stride) { - let vx = vertices[v], vy = vertices[v + 1]; + const vx = vertices[v]; + const vy = vertices[v + 1]; + worldVertices[w] = vx * a + vy * b + x; worldVertices[w + 1] = vx * c + vy * d + y; } + return; } - let v = 0, skip = 0; + let v = 0; + let skip = 0; + for (let i = 0; i < start; i += 2) { - let n = bones[v]; + const n = bones[v]; + v += n + 1; skip += n; } - let skeletonBones = skeleton.bones; + const skeletonBones = skeleton.bones; + if (deformArray.length == 0) { for (let w = offset, b = skip * 3; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b]; + const vy = vertices[b + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } @@ -84,14 +101,20 @@ export abstract class VertexAttachment extends Attachment { worldVertices[w + 1] = wy; } } else { - let deform = deformArray; + const deform = deformArray; + for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3, f += 2) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b] + deform[f]; + const vy = vertices[b + 1] + deform[f + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } @@ -101,18 +124,16 @@ export abstract class VertexAttachment extends Attachment { } } - copyTo (attachment: VertexAttachment) { + copyTo(attachment: VertexAttachment) { if (this.bones != null) { attachment.bones = new Array(this.bones.length); Utils.arrayCopy(this.bones, 0, attachment.bones, 0, this.bones.length); - } else - attachment.bones = null; + } else attachment.bones = null; if (this.vertices != null) { attachment.vertices = Utils.newFloatArray(this.vertices.length); Utils.arrayCopy(this.vertices, 0, attachment.vertices, 0, this.vertices.length); - } else - attachment.vertices = null; + } else attachment.vertices = null; attachment.worldVerticesLength = this.worldVerticesLength; attachment.deformAttachment = this.deformAttachment; diff --git a/packages/runtime-3.8/src/core/attachments/AttachmentLoader.ts b/packages/runtime-3.8/src/core/attachments/AttachmentLoader.ts index 788b17b7..21cb94e4 100644 --- a/packages/runtime-3.8/src/core/attachments/AttachmentLoader.ts +++ b/packages/runtime-3.8/src/core/attachments/AttachmentLoader.ts @@ -1,24 +1,23 @@ - -import {Skin} from '../Skin'; -import type {RegionAttachment} from './RegionAttachment'; -import type {MeshAttachment} from './MeshAttachment'; -import type {BoundingBoxAttachment} from './BoundingBoxAttachment'; -import type {PathAttachment} from './PathAttachment'; -import type {PointAttachment} from './PointAttachment'; -import type {ClippingAttachment} from './ClippingAttachment'; +import type { Skin } from '../Skin'; +import type { RegionAttachment } from './RegionAttachment'; +import type { MeshAttachment } from './MeshAttachment'; +import type { BoundingBoxAttachment } from './BoundingBoxAttachment'; +import type { PathAttachment } from './PathAttachment'; +import type { PointAttachment } from './PointAttachment'; +import type { ClippingAttachment } from './ClippingAttachment'; /** * @public */ export interface AttachmentLoader { /** @return May be null to not load an attachment. */ - newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment; + newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment; /** @return May be null to not load an attachment. */ - newMeshAttachment (skin: Skin, name: string, path: string): MeshAttachment; + newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment; /** @return May be null to not load an attachment. */ - newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment; + newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment; /** @return May be null to not load an attachment */ newPathAttachment(skin: Skin, name: string): PathAttachment; diff --git a/packages/runtime-3.8/src/core/attachments/BoundingBoxAttachment.ts b/packages/runtime-3.8/src/core/attachments/BoundingBoxAttachment.ts index 24500b94..de6247a9 100644 --- a/packages/runtime-3.8/src/core/attachments/BoundingBoxAttachment.ts +++ b/packages/runtime-3.8/src/core/attachments/BoundingBoxAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color} from '@pixi-spine/base'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color } from '@pixi-spine/base'; /** * @public @@ -8,14 +8,16 @@ export class BoundingBoxAttachment extends VertexAttachment { type = AttachmentType.BoundingBox; color = new Color(1, 1, 1, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new BoundingBoxAttachment(this.name); + copy(): Attachment { + const copy = new BoundingBoxAttachment(this.name); + this.copyTo(copy); copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-3.8/src/core/attachments/ClippingAttachment.ts b/packages/runtime-3.8/src/core/attachments/ClippingAttachment.ts index 4f3021d4..de55d549 100644 --- a/packages/runtime-3.8/src/core/attachments/ClippingAttachment.ts +++ b/packages/runtime-3.8/src/core/attachments/ClippingAttachment.ts @@ -1,6 +1,6 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IClippingAttachment} from '@pixi-spine/base'; -import type {SlotData} from '../SlotData'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IClippingAttachment } from '@pixi-spine/base'; +import type { SlotData } from '../SlotData'; /** * @public @@ -12,15 +12,17 @@ export class ClippingAttachment extends VertexAttachment implements IClippingAtt // Nonessential. color = new Color(0.2275, 0.2275, 0.8078, 1); // ce3a3aff - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new ClippingAttachment(this.name); + copy(): Attachment { + const copy = new ClippingAttachment(this.name); + this.copyTo(copy); copy.endSlot = this.endSlot; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-3.8/src/core/attachments/MeshAttachment.ts b/packages/runtime-3.8/src/core/attachments/MeshAttachment.ts index 16fa3ad3..bf1e5723 100644 --- a/packages/runtime-3.8/src/core/attachments/MeshAttachment.ts +++ b/packages/runtime-3.8/src/core/attachments/MeshAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IMeshAttachment, TextureRegion, Utils} from '@pixi-spine/base'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IMeshAttachment, TextureRegion, Utils } from '@pixi-spine/base'; /** * @public @@ -19,16 +19,16 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment private parentMesh: MeshAttachment; tempColor = new Color(0, 0, 0, 0); - constructor (name: string) { + constructor(name: string) { super(name); } - getParentMesh () { + getParentMesh() { return this.parentMesh; } /** @param parentMesh May be null. */ - setParentMesh (parentMesh: MeshAttachment) { + setParentMesh(parentMesh: MeshAttachment) { this.parentMesh = parentMesh; if (parentMesh != null) { this.bones = parentMesh.bones; @@ -37,14 +37,15 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment this.regionUVs = parentMesh.regionUVs; this.triangles = parentMesh.triangles; this.hullLength = parentMesh.hullLength; - this.worldVerticesLength = parentMesh.worldVerticesLength + this.worldVerticesLength = parentMesh.worldVerticesLength; } } - copy (): Attachment { + copy(): Attachment { if (this.parentMesh != null) return this.newLinkedMesh(); - let copy = new MeshAttachment(this.name); + const copy = new MeshAttachment(this.name); + copy.region = this.region; copy.path = this.path; copy.color.setFromColor(this.color); @@ -67,14 +68,16 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment return copy; } - newLinkedMesh (): MeshAttachment { - let copy = new MeshAttachment(this.name); + newLinkedMesh(): MeshAttachment { + const copy = new MeshAttachment(this.name); + copy.region = this.region; copy.path = this.path; copy.color.setFromColor(this.color); copy.deformAttachment = this.deformAttachment; copy.setParentMesh(this.parentMesh != null ? this.parentMesh : this); // copy.updateUVs(); + return copy; } } diff --git a/packages/runtime-3.8/src/core/attachments/PathAttachment.ts b/packages/runtime-3.8/src/core/attachments/PathAttachment.ts index 5f380961..97930c4a 100644 --- a/packages/runtime-3.8/src/core/attachments/PathAttachment.ts +++ b/packages/runtime-3.8/src/core/attachments/PathAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from "./Attachment"; -import {AttachmentType, Color, Utils} from "@pixi-spine/base"; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, Utils } from '@pixi-spine/base'; /** * @public @@ -7,21 +7,24 @@ import {AttachmentType, Color, Utils} from "@pixi-spine/base"; export class PathAttachment extends VertexAttachment { type = AttachmentType.Path; lengths: Array; - closed = false; constantSpeed = false; + closed = false; + constantSpeed = false; color = new Color(1, 1, 1, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new PathAttachment(this.name); + copy(): Attachment { + const copy = new PathAttachment(this.name); + this.copyTo(copy); copy.lengths = new Array(this.lengths.length); Utils.arrayCopy(this.lengths, 0, copy.lengths, 0, this.lengths.length); copy.closed = closed; copy.constantSpeed = this.constantSpeed; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-3.8/src/core/attachments/PointAttachment.ts b/packages/runtime-3.8/src/core/attachments/PointAttachment.ts index 77030105..c82b68c6 100644 --- a/packages/runtime-3.8/src/core/attachments/PointAttachment.ts +++ b/packages/runtime-3.8/src/core/attachments/PointAttachment.ts @@ -1,40 +1,48 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, MathUtils, Vector2} from "@pixi-spine/base"; -import type {Bone} from '../Bone'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Bone } from '../Bone'; /** * @public */ export class PointAttachment extends VertexAttachment { type = AttachmentType.Point; - x: number; y: number; rotation: number; + x: number; + y: number; + rotation: number; color = new Color(0.38, 0.94, 0, 1); - constructor (name: string) { + constructor(name: string) { super(name); } computeWorldPosition(bone: Bone, point: Vector2) { const mat = bone.matrix; + point.x = this.x * mat.a + this.y * mat.c + bone.worldX; point.y = this.x * mat.b + this.y * mat.d + bone.worldY; + return point; } computeWorldRotation(bone: Bone) { const mat = bone.matrix; - let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation); - let x = cos * mat.a + sin * mat.c; - let y = cos * mat.b + sin * mat.d; + const cos = MathUtils.cosDeg(this.rotation); + const sin = MathUtils.sinDeg(this.rotation); + const x = cos * mat.a + sin * mat.c; + const y = cos * mat.b + sin * mat.d; + return Math.atan2(y, x) * MathUtils.radDeg; } - copy (): Attachment { - let copy = new PointAttachment(this.name); + copy(): Attachment { + const copy = new PointAttachment(this.name); + copy.x = this.x; copy.y = this.y; copy.rotation = this.rotation; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-3.8/src/core/attachments/RegionAttachment.ts b/packages/runtime-3.8/src/core/attachments/RegionAttachment.ts index 3ac45218..f9d07eef 100644 --- a/packages/runtime-3.8/src/core/attachments/RegionAttachment.ts +++ b/packages/runtime-3.8/src/core/attachments/RegionAttachment.ts @@ -1,7 +1,7 @@ -import {Attachment} from './Attachment'; -import {AttachmentType, ArrayLike, Color, TextureRegion, Utils, IRegionAttachment} from "@pixi-spine/base"; +import { Attachment } from './Attachment'; +import { AttachmentType, ArrayLike, Color, TextureRegion, Utils, IRegionAttachment } from '@pixi-spine/base'; -import type {Bone} from '../Bone'; +import type { Bone } from '../Bone'; import { Slot } from '../Slot'; /** @@ -78,24 +78,25 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { } updateOffset(): void { - let regionScaleX = this.width / this.region.originalWidth * this.scaleX; - let regionScaleY = this.height / this.region.originalHeight * this.scaleY; - let localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX; - let localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY; - let localX2 = localX + this.region.width * regionScaleX; - let localY2 = localY + this.region.height * regionScaleY; - let radians = this.rotation * Math.PI / 180; - let cos = Math.cos(radians); - let sin = Math.sin(radians); - let localXCos = localX * cos + this.x; - let localXSin = localX * sin; - let localYCos = localY * cos + this.y; - let localYSin = localY * sin; - let localX2Cos = localX2 * cos + this.x; - let localX2Sin = localX2 * sin; - let localY2Cos = localY2 * cos + this.y; - let localY2Sin = localY2 * sin; - let offset = this.offset; + const regionScaleX = (this.width / this.region.originalWidth) * this.scaleX; + const regionScaleY = (this.height / this.region.originalHeight) * this.scaleY; + const localX = (-this.width / 2) * this.scaleX + this.region.offsetX * regionScaleX; + const localY = (-this.height / 2) * this.scaleY + this.region.offsetY * regionScaleY; + const localX2 = localX + this.region.width * regionScaleX; + const localY2 = localY + this.region.height * regionScaleY; + const radians = (this.rotation * Math.PI) / 180; + const cos = Math.cos(radians); + const sin = Math.sin(radians); + const localXCos = localX * cos + this.x; + const localXSin = localX * sin; + const localYCos = localY * cos + this.y; + const localYSin = localY * sin; + const localX2Cos = localX2 * cos + this.x; + const localX2Sin = localX2 * sin; + const localY2Cos = localY2 * cos + this.y; + const localY2Sin = localY2 * sin; + const offset = this.offset; + offset[RegionAttachment.OX1] = localXCos - localYSin; offset[RegionAttachment.OY1] = localYCos + localXSin; offset[RegionAttachment.OX2] = localXCos - localY2Sin; @@ -108,7 +109,8 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { setRegion(region: TextureRegion): void { this.region = region; - let uvs = this.uvs; + const uvs = this.uvs; + if (region.rotate) { uvs[2] = region.u; uvs[3] = region.v2; @@ -131,11 +133,16 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { } computeWorldVertices(bone: Bone | Slot, worldVertices: ArrayLike, offset: number, stride: number) { - let vertexOffset = this.offset; - let mat = bone instanceof Slot? bone.bone.matrix : bone.matrix; - let x = mat.tx, y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let offsetX = 0, offsetY = 0; + const vertexOffset = this.offset; + const mat = bone instanceof Slot ? bone.bone.matrix : bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let offsetX = 0; + let offsetY = 0; offsetX = vertexOffset[RegionAttachment.OX1]; offsetY = vertexOffset[RegionAttachment.OY1]; @@ -161,8 +168,9 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { worldVertices[offset + 1] = offsetX * c + offsetY * d + y; } - copy (): Attachment { - let copy = new RegionAttachment(this.name); + copy(): Attachment { + const copy = new RegionAttachment(this.name); + copy.region = this.region; copy.rendererObject = this.rendererObject; copy.path = this.path; @@ -176,6 +184,7 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { Utils.arrayCopy(this.uvs, 0, copy.uvs, 0, 8); Utils.arrayCopy(this.offset, 0, copy.offset, 0, 8); copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-3.8/src/core/vertexeffects/JitterEffect.ts b/packages/runtime-3.8/src/core/vertexeffects/JitterEffect.ts index 800105e1..a8dccd58 100644 --- a/packages/runtime-3.8/src/core/vertexeffects/JitterEffect.ts +++ b/packages/runtime-3.8/src/core/vertexeffects/JitterEffect.ts @@ -1,6 +1,6 @@ -import {VertexEffect} from "../VertexEffect"; -import type {Skeleton} from "../Skeleton"; -import {Color, MathUtils, Vector2} from "@pixi-spine/base"; +import type { VertexEffect } from '../VertexEffect'; +import type { Skeleton } from '../Skeleton'; +import { Color, MathUtils, Vector2 } from '@pixi-spine/base'; /** * @public @@ -9,19 +9,17 @@ export class JitterEffect implements VertexEffect { jitterX = 0; jitterY = 0; - constructor (jitterX: number, jitterY: number) { + constructor(jitterX: number, jitterY: number) { this.jitterX = jitterX; this.jitterY = jitterY; } - begin(skeleton: Skeleton): void { - } + begin(skeleton: Skeleton): void {} transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { position.x += MathUtils.randomTriangular(-this.jitterX, this.jitterY); position.y += MathUtils.randomTriangular(-this.jitterX, this.jitterY); } - end(): void { - } + end(): void {} } diff --git a/packages/runtime-3.8/src/core/vertexeffects/SwirlEffect.ts b/packages/runtime-3.8/src/core/vertexeffects/SwirlEffect.ts index 416b969c..46d78bce 100644 --- a/packages/runtime-3.8/src/core/vertexeffects/SwirlEffect.ts +++ b/packages/runtime-3.8/src/core/vertexeffects/SwirlEffect.ts @@ -1,6 +1,6 @@ -import {VertexEffect} from "../VertexEffect"; -import type {Skeleton} from "../Skeleton"; -import {Color, MathUtils, PowOut, Vector2} from "@pixi-spine/base"; +import type { VertexEffect } from '../VertexEffect'; +import type { Skeleton } from '../Skeleton'; +import { Color, MathUtils, PowOut, Vector2 } from '@pixi-spine/base'; /** * @public @@ -14,7 +14,7 @@ export class SwirlEffect implements VertexEffect { private worldX = 0; private worldY = 0; - constructor (radius: number) { + constructor(radius: number) { this.radius = radius; } @@ -24,19 +24,20 @@ export class SwirlEffect implements VertexEffect { } transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { - let radAngle = this.angle * MathUtils.degreesToRadians; - let x = position.x - this.worldX; - let y = position.y - this.worldY; - let dist = Math.sqrt(x * x + y * y); + const radAngle = this.angle * MathUtils.degreesToRadians; + const x = position.x - this.worldX; + const y = position.y - this.worldY; + const dist = Math.sqrt(x * x + y * y); + if (dist < this.radius) { - let theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); - let cos = Math.cos(theta); - let sin = Math.sin(theta); + const theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); + const cos = Math.cos(theta); + const sin = Math.sin(theta); + position.x = cos * x - sin * y + this.worldX; position.y = sin * x + cos * y + this.worldY; } } - end(): void { - } + end(): void {} } diff --git a/packages/runtime-4.0/package.json b/packages/runtime-4.0/package.json index 35c44f3d..9d1a43d6 100644 --- a/packages/runtime-4.0/package.json +++ b/packages/runtime-4.0/package.json @@ -2,21 +2,35 @@ "name": "@pixi-spine/runtime-4.0", "version": "3.1.2", "description": "Pixi runtime for spine 4.0 models", - "main": "lib/runtime-4.0.js", - "module": "lib/runtime-4.0.es.js", - "bundle": "dist/runtime-4.0.js", - "namespace": "PIXI.spine40", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "peerDependencies": { - "@pixi/constants": "^6.1.0", - "@pixi/math": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine40", + "bundle": "dist/runtime-4.0.js", + "bundleModule": "dist/runtime-4.0.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2" + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,13 +53,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/runtime-4.0/rollup.config.js b/packages/runtime-4.0/rollup.config.js deleted file mode 100644 index 8b1fc303..00000000 --- a/packages/runtime-4.0/rollup.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine.base', - }, -}); diff --git a/packages/runtime-4.0/rollup.config.mjs b/packages/runtime-4.0/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/packages/runtime-4.0/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/packages/runtime-4.0/src/Spine.ts b/packages/runtime-4.0/src/Spine.ts index 6faecd39..b3a2b909 100644 --- a/packages/runtime-4.0/src/Spine.ts +++ b/packages/runtime-4.0/src/Spine.ts @@ -1,8 +1,8 @@ -import {SpineBase} from '@pixi-spine/base'; -import {Skeleton} from "./core/Skeleton"; -import {SkeletonData} from "./core/SkeletonData"; -import {AnimationState} from "./core/AnimationState"; -import {AnimationStateData} from "./core/AnimationStateData"; +import { SpineBase } from '@pixi-spine/base'; +import { Skeleton } from './core/Skeleton'; +import type { SkeletonData } from './core/SkeletonData'; +import { AnimationState } from './core/AnimationState'; +import { AnimationStateData } from './core/AnimationStateData'; /** * @public diff --git a/packages/runtime-4.0/src/core/Animation.ts b/packages/runtime-4.0/src/core/Animation.ts index 5f98c176..b59afccb 100644 --- a/packages/runtime-4.0/src/core/Animation.ts +++ b/packages/runtime-4.0/src/core/Animation.ts @@ -1,11 +1,11 @@ -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; -import {Attachment, VertexAttachment} from "./attachments"; -import {NumberArrayLike, IAnimation, ITimeline, MathUtils, MixBlend, StringSet, Utils, MixDirection} from '@pixi-spine/base'; -import {Slot} from "./Slot"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; +import { Attachment, VertexAttachment } from './attachments'; +import { NumberArrayLike, IAnimation, ITimeline, MathUtils, MixBlend, StringSet, Utils, MixDirection } from '@pixi-spine/base'; +import type { Slot } from './Slot'; +import type { IkConstraint } from './IkConstraint'; +import type { TransformConstraint } from './TransformConstraint'; +import type { PathConstraint } from './PathConstraint'; /** * A simple container for a list of timelines and a name. * @public @@ -19,24 +19,23 @@ export class Animation implements IAnimation { /** The duration of the animation in seconds, which is the highest time of all keys in the timeline. */ duration: number; - constructor (name: string, timelines: Array, duration: number) { - if (!name) throw new Error("name cannot be null."); + constructor(name: string, timelines: Array, duration: number) { + if (!name) throw new Error('name cannot be null.'); this.name = name; this.setTimelines(timelines); this.duration = duration; } - setTimelines (timelines: Array) { - if (!timelines) throw new Error("timelines cannot be null."); + setTimelines(timelines: Array) { + if (!timelines) throw new Error('timelines cannot be null.'); this.timelines = timelines; this.timelineIds = new StringSet(); - for (var i = 0; i < timelines.length; i++) - this.timelineIds.addAll(timelines[i].getPropertyIds()); + for (let i = 0; i < timelines.length; i++) this.timelineIds.addAll(timelines[i].getPropertyIds()); } - hasTimeline (ids: string[]): boolean { - for (let i = 0; i < ids.length; i++) - if (this.timelineIds.contains(ids[i])) return true; + hasTimeline(ids: string[]): boolean { + for (let i = 0; i < ids.length; i++) if (this.timelineIds.contains(ids[i])) return true; + return false; } @@ -45,17 +44,17 @@ export class Animation implements IAnimation { * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. * @param loop If true, the animation repeats after {@link #getDuration()}. * @param events May be null to ignore fired events. */ - apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - if (!skeleton) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + if (!skeleton) throw new Error('skeleton cannot be null.'); if (loop && this.duration != 0) { time %= this.duration; if (lastTime > 0) lastTime %= this.duration; } - let timelines = this.timelines; - for (let i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); + const timelines = this.timelines; + + for (let i = 0, n = timelines.length; i < n; i++) timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); } } @@ -83,8 +82,8 @@ const Property = { pathConstraintPosition: 16, pathConstraintSpacing: 17, - pathConstraintMix: 18 -} + pathConstraintMix: 18, +}; /** The interface for all timelines. * @public @@ -93,40 +92,42 @@ export abstract class Timeline implements ITimeline { propertyIds: string[] = null; frames: NumberArrayLike = null; - constructor (frameCount: number, propertyIds: string[]) { + constructor(frameCount: number, propertyIds: string[]) { this.propertyIds = propertyIds; this.frames = Utils.newFloatArray(frameCount * this.getFrameEntries()); } - getPropertyIds () { + getPropertyIds() { return this.propertyIds; } - getFrameEntries (): number { + getFrameEntries(): number { return 1; } - getFrameCount () { + getFrameCount() { return this.frames.length / this.getFrameEntries(); } - getDuration (): number { + getDuration(): number { return this.frames[this.frames.length - this.getFrameEntries()]; } - abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; + abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection): void; + + static search1(frames: NumberArrayLike, time: number) { + const n = frames.length; + + for (let i = 1; i < n; i++) if (frames[i] > time) return i - 1; - static search1 (frames: NumberArrayLike, time: number) { - let n = frames.length; - for (let i = 1; i < n; i++) - if (frames[i] > time) return i - 1; return n - 1; } - static search (frames: NumberArrayLike, time: number, step: number) { - let n = frames.length; - for (let i = step; i < n; i += step) - if (frames[i] > time) return i - step; + static search(frames: NumberArrayLike, time: number, step: number) { + const n = frames.length; + + for (let i = step; i < n; i += step) if (frames[i] > time) return i - step; + return n - step; } } @@ -153,28 +154,30 @@ export interface SlotTimeline { export abstract class CurveTimeline extends Timeline { protected curves: NumberArrayLike = null; // type, x, y, ... - constructor (frameCount: number, bezierCount: number, propertyIds: string[]) { + constructor(frameCount: number, bezierCount: number, propertyIds: string[]) { super(frameCount, propertyIds); - this.curves = Utils.newFloatArray(frameCount + bezierCount * 18/*BEZIER_SIZE*/); - this.curves[frameCount - 1] = 1/*STEPPED*/; + this.curves = Utils.newFloatArray(frameCount + bezierCount * 18 /* BEZIER_SIZE*/); + this.curves[frameCount - 1] = 1 /* STEPPED*/; } /** Sets the specified key frame to linear interpolation. */ - setLinear (frame: number) { - this.curves[frame] = 0/*LINEAR*/; + setLinear(frame: number) { + this.curves[frame] = 0 /* LINEAR*/; } /** Sets the specified key frame to stepped interpolation. */ - setStepped (frame: number) { - this.curves[frame] = 1/*STEPPED*/; + setStepped(frame: number) { + this.curves[frame] = 1 /* STEPPED*/; } /** Shrinks the storage for Bezier curves, for use when bezierCount (specified in the constructor) was larger * than the actual number of Bezier curves. */ - shrink (bezierCount: number) { - let size = this.getFrameCount() + bezierCount * 18/*BEZIER_SIZE*/; + shrink(bezierCount: number) { + const size = this.getFrameCount() + bezierCount * 18; /* BEZIER_SIZE*/ + if (this.curves.length > size) { - let newCurves = Utils.newFloatArray(size); + const newCurves = Utils.newFloatArray(size); + Utils.arrayCopy(this.curves, 0, newCurves, 0, size); this.curves = newCurves; } @@ -194,17 +197,23 @@ export abstract class CurveTimeline extends Timeline { * @param cy2 The value for the second Bezier handle. * @param time2 The time for the second key. * @param value2 The value for the second key. */ - setBezier (bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, - cy2: number, time2: number, value2: number) { - let curves = this.curves; - let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/; - if (value == 0) curves[frame] = 2/*BEZIER*/ + i; - let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = (value1 - cy1 * 2 + cy2) * 0.03; - let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006; - let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy; - let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667; - let x = time1 + dx, y = value1 + dy; - for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) { + setBezier(bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, cy2: number, time2: number, value2: number) { + const curves = this.curves; + let i = this.getFrameCount() + bezier * 18; /* BEZIER_SIZE*/ + + if (value == 0) curves[frame] = 2 /* BEZIER*/ + i; + const tmpx = (time1 - cx1 * 2 + cx2) * 0.03; + const tmpy = (value1 - cy1 * 2 + cy2) * 0.03; + const dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006; + const dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006; + let ddx = tmpx * 2 + dddx; + let ddy = tmpy * 2 + dddy; + let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667; + let dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667; + let x = time1 + dx; + let y = value1 + dy; + + for (let n = i + 18 /* BEZIER_SIZE*/; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; dx += ddx; @@ -220,49 +229,58 @@ export abstract class CurveTimeline extends Timeline { * @param frameIndex The index into {@link #getFrames()} for the values of the frame before time. * @param valueOffset The offset from frameIndex to the value this curve is used for. * @param i The index of the Bezier segments. See {@link #getCurveType(int)}. */ - getBezierValue (time: number, frameIndex: number, valueOffset: number, i: number) { - let curves = this.curves; + getBezierValue(time: number, frameIndex: number, valueOffset: number, i: number) { + const curves = this.curves; + if (curves[i] > time) { - let x = this.frames[frameIndex], y = this.frames[frameIndex + valueOffset]; - return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y); + const x = this.frames[frameIndex]; + const y = this.frames[frameIndex + valueOffset]; + + return y + ((time - x) / (curves[i] - x)) * (curves[i + 1] - y); } - let n = i + 18/*BEZIER_SIZE*/; + const n = i + 18; /* BEZIER_SIZE*/ + for (i += 2; i < n; i += 2) { if (curves[i] >= time) { - let x = curves[i - 2], y = curves[i - 1]; - return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y); + const x = curves[i - 2]; + const y = curves[i - 1]; + + return y + ((time - x) / (curves[i] - x)) * (curves[i + 1] - y); } } frameIndex += this.getFrameEntries(); - let x = curves[n - 2], y = curves[n - 1]; - return y + (time - x) / (this.frames[frameIndex] - x) * (this.frames[frameIndex + valueOffset] - y); + const x = curves[n - 2]; + const y = curves[n - 1]; + + return y + ((time - x) / (this.frames[frameIndex] - x)) * (this.frames[frameIndex + valueOffset] - y); } } /** * @public */ export abstract class CurveTimeline1 extends CurveTimeline { - constructor (frameCount: number, bezierCount: number, propertyId: string) { + constructor(frameCount: number, bezierCount: number, propertyId: string) { super(frameCount, bezierCount, [propertyId]); } - getFrameEntries () { - return 2/*ENTRIES*/; + getFrameEntries() { + return 2 /* ENTRIES*/; } /** Sets the time and value for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ - setFrame (frame: number, time: number, value: number) { + setFrame(frame: number, time: number, value: number) { frame <<= 1; this.frames[frame] = time; - this.frames[frame + 1/*VALUE*/] = value; + this.frames[frame + 1 /* VALUE*/] = value; } /** Returns the interpolated value for the specified time. */ - getCurveValue (time: number) { - let frames = this.frames; + getCurveValue(time: number) { + const frames = this.frames; let i = frames.length - 2; + for (let ii = 2; ii <= i; ii += 2) { if (frames[ii] > time) { i = ii - 2; @@ -270,15 +288,19 @@ export abstract class CurveTimeline1 extends CurveTimeline { } } - let curveType = this.curves[i >> 1]; + const curveType = this.curves[i >> 1]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i], value = frames[i + 1/*VALUE*/]; - return value + (time - before) / (frames[i + 2/*ENTRIES*/] - before) * (frames[i + 2/*ENTRIES*/ + 1/*VALUE*/] - value); - case 1/*STEPPED*/: - return frames[i + 1/*VALUE*/]; + case 0 /* LINEAR*/: + const before = frames[i]; + const value = frames[i + 1 /* VALUE*/]; + + return value + ((time - before) / (frames[i + 2 /* ENTRIES*/] - before)) * (frames[i + 2 /* ENTRIES*/ + 1 /* VALUE*/] - value); + case 1 /* STEPPED*/: + return frames[i + 1 /* VALUE*/]; } - return this.getBezierValue(time, i, 1/*VALUE*/, curveType - 2/*BEZIER*/); + + return this.getBezierValue(time, i, 1 /* VALUE*/, curveType - 2 /* BEZIER*/); } } @@ -288,22 +310,22 @@ export abstract class CurveTimeline1 extends CurveTimeline { export abstract class CurveTimeline2 extends CurveTimeline { /** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}. * @param propertyIds Unique identifiers for the properties the timeline modifies. */ - constructor (frameCount: number, bezierCount: number, propertyId1: string, propertyId2: string) { + constructor(frameCount: number, bezierCount: number, propertyId1: string, propertyId2: string) { super(frameCount, bezierCount, [propertyId1, propertyId2]); } - getFrameEntries () { - return 3/*ENTRIES*/; + getFrameEntries() { + return 3 /* ENTRIES*/; } /** Sets the time and values for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ - setFrame (frame: number, time: number, value1: number, value2: number) { - frame *= 3/*ENTRIES*/; + setFrame(frame: number, time: number, value1: number, value2: number) { + frame *= 3 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*VALUE1*/] = value1; - this.frames[frame + 2/*VALUE2*/] = value2; + this.frames[frame + 1 /* VALUE1*/] = value1; + this.frames[frame + 2 /* VALUE2*/] = value2; } } @@ -313,28 +335,33 @@ export abstract class CurveTimeline2 extends CurveTimeline { export class RotateTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.rotate + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.rotate}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation; + return; case MixBlend.first: bone.rotation += (bone.data.rotation - bone.rotation) * alpha; } + return; } let r = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation + r * alpha; @@ -354,51 +381,56 @@ export class RotateTimeline extends CurveTimeline1 implements BoneTimeline { export class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, - Property.x + "|" + boneIndex, - Property.y + "|" + boneIndex, - ); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.x}|${boneIndex}`, `${Property.y}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.x = bone.data.x; bone.y = bone.data.y; + return; case MixBlend.first: bone.x += (bone.data.x - bone.x) * alpha; bone.y += (bone.data.y - bone.y) * alpha; } + return; } - let x = 0, y = 0; - let i = Timeline.search(frames, time, 3/*ENTRIES*/); - let curveType = this.curves[i / 3/*ENTRIES*/]; + let x = 0; + let y = 0; + const i = Timeline.search(frames, time, 3 /* ENTRIES*/); + const curveType = this.curves[i / 3 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; - let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before); - x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t; - y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; + const t = (time - before) / (frames[i + 3 /* ENTRIES*/] - before); + + x += (frames[i + 3 /* ENTRIES*/ + 1 /* VALUE1*/] - x) * t; + y += (frames[i + 3 /* ENTRIES*/ + 2 /* VALUE2*/] - y) * t; break; - case 1/*STEPPED*/: - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; + case 1 /* STEPPED*/: + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; break; default: - x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + x = this.getBezierValue(time, i, 1 /* VALUE1*/, curveType - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 2 /* VALUE2*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } switch (blend) { @@ -424,28 +456,33 @@ export class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { export class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.x + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.x}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.x = bone.data.x; + return; case MixBlend.first: bone.x += (bone.data.x - bone.x) * alpha; } + return; } - let x = this.getCurveValue(time); + const x = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.x = bone.data.x + x * alpha; @@ -466,28 +503,33 @@ export class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { export class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.y + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.y}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.y = bone.data.y; + return; case MixBlend.first: bone.y += (bone.data.y - bone.y) * alpha; } + return; } - let y = this.getCurveValue(time); + const y = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.y = bone.data.y + y * alpha; @@ -508,51 +550,56 @@ export class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, - Property.scaleX + "|" + boneIndex, - Property.scaleY + "|" + boneIndex - ); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.scaleX}|${boneIndex}`, `${Property.scaleY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleX = bone.data.scaleX; bone.scaleY = bone.data.scaleY; + return; case MixBlend.first: bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; } + return; } - let x, y; - let i = Timeline.search(frames, time, 3/*ENTRIES*/); - let curveType = this.curves[i / 3/*ENTRIES*/]; + let x; + let y; + const i = Timeline.search(frames, time, 3 /* ENTRIES*/); + const curveType = this.curves[i / 3 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; - let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before); - x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t; - y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; + const t = (time - before) / (frames[i + 3 /* ENTRIES*/] - before); + + x += (frames[i + 3 /* ENTRIES*/ + 1 /* VALUE1*/] - x) * t; + y += (frames[i + 3 /* ENTRIES*/ + 2 /* VALUE2*/] - y) * t; break; - case 1/*STEPPED*/: - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; + case 1 /* STEPPED*/: + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; break; default: - x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + x = this.getBezierValue(time, i, 1 /* VALUE1*/, curveType - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 2 /* VALUE2*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } x *= bone.data.scaleX; y *= bone.data.scaleY; @@ -566,7 +613,9 @@ export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { bone.scaleY = y; } } else { - let bx = 0, by = 0; + let bx = 0; + let by = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -616,36 +665,40 @@ export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { export class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.scaleX + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.scaleX}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleX = bone.data.scaleX; + return; case MixBlend.first: bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; } + return; } - let x = this.getCurveValue(time) * bone.data.scaleX; + const x = this.getCurveValue(time) * bone.data.scaleX; + if (alpha == 1) { - if (blend == MixBlend.add) - bone.scaleX += x - bone.data.scaleX; - else - bone.scaleX = x; + if (blend == MixBlend.add) bone.scaleX += x - bone.data.scaleX; + else bone.scaleX = x; } else { // Mixing out uses sign of setup or current pose, else use sign of key. let bx = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -685,36 +738,40 @@ export class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { export class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.scaleY + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.scaleY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleY = bone.data.scaleY; + return; case MixBlend.first: bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; } + return; } - let y = this.getCurveValue(time) * bone.data.scaleY; + const y = this.getCurveValue(time) * bone.data.scaleY; + if (alpha == 1) { - if (blend == MixBlend.add) - bone.scaleY += y - bone.data.scaleY; - else - bone.scaleY = y; + if (blend == MixBlend.add) bone.scaleY += y - bone.data.scaleY; + else bone.scaleY = y; } else { // Mixing out uses sign of setup or current pose, else use sign of key. let by = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -754,51 +811,56 @@ export class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { export class ShearTimeline extends CurveTimeline2 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, - Property.shearX + "|" + boneIndex, - Property.shearY + "|" + boneIndex - ); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.shearX}|${boneIndex}`, `${Property.shearY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX; bone.shearY = bone.data.shearY; + return; case MixBlend.first: bone.shearX += (bone.data.shearX - bone.shearX) * alpha; bone.shearY += (bone.data.shearY - bone.shearY) * alpha; } + return; } - let x = 0, y = 0; - let i = Timeline.search(frames, time, 3/*ENTRIES*/); - let curveType = this.curves[i / 3/*ENTRIES*/]; + let x = 0; + let y = 0; + const i = Timeline.search(frames, time, 3 /* ENTRIES*/); + const curveType = this.curves[i / 3 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; - let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before); - x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t; - y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; + const t = (time - before) / (frames[i + 3 /* ENTRIES*/] - before); + + x += (frames[i + 3 /* ENTRIES*/ + 1 /* VALUE1*/] - x) * t; + y += (frames[i + 3 /* ENTRIES*/ + 2 /* VALUE2*/] - y) * t; break; - case 1/*STEPPED*/: - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; + case 1 /* STEPPED*/: + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; break; default: - x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + x = this.getBezierValue(time, i, 1 /* VALUE1*/, curveType - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 2 /* VALUE2*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } switch (blend) { @@ -824,28 +886,33 @@ export class ShearTimeline extends CurveTimeline2 implements BoneTimeline { export class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.shearX + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.shearX}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX; + return; case MixBlend.first: bone.shearX += (bone.data.shearX - bone.shearX) * alpha; } + return; } - let x = this.getCurveValue(time); + const x = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX + x * alpha; @@ -866,28 +933,33 @@ export class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { export class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.shearY + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.shearY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearY = bone.data.shearY; + return; case MixBlend.first: bone.shearY += (bone.data.shearY - bone.shearY) * alpha; } + return; } - let y = this.getCurveValue(time); + const y = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.shearY = bone.data.shearY + y * alpha; @@ -908,77 +980,83 @@ export class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { export class RGBATimeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex, - Property.alpha + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`, `${Property.alpha}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 5/*ENTRIES*/; + getFrameEntries() { + return 5 /* ENTRIES*/; } /** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number, a: number) { - frame *= 5/*ENTRIES*/; + setFrame(frame: number, time: number, r: number, g: number, b: number, a: number) { + frame *= 5 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; - this.frames[frame + 4/*A*/] = a; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; + this.frames[frame + 4 /* A*/] = a; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let color = slot.color; + const frames = this.frames; + const color = slot.color; + if (time < frames[0]) { - let setup = slot.data.color; + const setup = slot.data.color; + switch (blend) { case MixBlend.setup: color.setFromColor(setup); + return; case MixBlend.first: - color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, - (setup.a - color.a) * alpha); + color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, (setup.a - color.a) * alpha); } + return; } - let r = 0, g = 0, b = 0, a = 0; - let i = Timeline.search(frames, time, 5/*ENTRIES*/); - let curveType = this.curves[i / 5/*ENTRIES*/]; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + const i = Timeline.search(frames, time, 5 /* ENTRIES*/); + const curveType = this.curves[i / 5 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; - let t = (time - before) / (frames[i + 5/*ENTRIES*/] - before); - r += (frames[i + 5/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 5/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 5/*ENTRIES*/ + 3/*B*/] - b) * t; - a += (frames[i + 5/*ENTRIES*/ + 4/*A*/] - a) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; + const t = (time - before) / (frames[i + 5 /* ENTRIES*/] - before); + + r += (frames[i + 5 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 5 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 5 /* ENTRIES*/ + 3 /* B*/] - b) * t; + a += (frames[i + 5 /* ENTRIES*/ + 4 /* A*/] - a) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + a = this.getBezierValue(time, i, 4 /* A*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); } - if (alpha == 1) - color.set(r, g, b, a); + if (alpha == 1) color.set(r, g, b, a); else { if (blend == MixBlend.setup) color.setFromColor(slot.data.color); color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); @@ -992,71 +1070,79 @@ export class RGBATimeline extends CurveTimeline implements SlotTimeline { export class RGBTimeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 4/*ENTRIES*/; + getFrameEntries() { + return 4 /* ENTRIES*/; } /** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number) { + setFrame(frame: number, time: number, r: number, g: number, b: number) { frame <<= 2; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let color = slot.color; + const frames = this.frames; + const color = slot.color; + if (time < frames[0]) { - let setup = slot.data.color; + const setup = slot.data.color; + switch (blend) { case MixBlend.setup: color.r = setup.r; color.g = setup.g; color.b = setup.b; + return; case MixBlend.first: color.r += (setup.r - color.r) * alpha; color.g += (setup.g - color.g) * alpha; color.b += (setup.b - color.b) * alpha; } + return; } - let r = 0, g = 0, b = 0; - let i = Timeline.search(frames, time, 4/*ENTRIES*/); - let curveType = this.curves[i >> 2]; + let r = 0; + let g = 0; + let b = 0; + const i = Timeline.search(frames, time, 4 /* ENTRIES*/); + const curveType = this.curves[i >> 2]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before); - r += (frames[i + 4/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 4/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 4/*ENTRIES*/ + 3/*B*/] - b) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + const t = (time - before) / (frames[i + 4 /* ENTRIES*/] - before); + + r += (frames[i + 4 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 4 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 4 /* ENTRIES*/ + 3 /* B*/] - b) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); } if (alpha == 1) { color.r = r; @@ -1064,7 +1150,8 @@ export class RGBTimeline extends CurveTimeline implements SlotTimeline { color.b = b; } else { if (blend == MixBlend.setup) { - let setup = slot.data.color; + const setup = slot.data.color; + color.r = setup.r; color.g = setup.g; color.b = setup.b; @@ -1082,31 +1169,37 @@ export class RGBTimeline extends CurveTimeline implements SlotTimeline { export class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, Property.alpha + "|" + slotIndex); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, `${Property.alpha}|${slotIndex}`); this.slotIndex = slotIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let color = slot.color; - if (time < this.frames[0]) { // Time is before first frame. - let setup = slot.data.color; + const color = slot.color; + + if (time < this.frames[0]) { + // Time is before first frame. + const setup = slot.data.color; + switch (blend) { case MixBlend.setup: color.a = setup.a; + return; case MixBlend.first: color.a += (setup.a - color.a) * alpha; } + return; } - let a = this.getCurveValue(time); - if (alpha == 1) - color.a = a; + const a = this.getCurveValue(time); + + if (alpha == 1) color.a = a; else { if (blend == MixBlend.setup) color.a = slot.data.color.a; color.a += (a - color.a) * alpha; @@ -1117,99 +1210,110 @@ export class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { /** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. * @public * */ -export class RGBA2Timeline extends CurveTimeline implements SlotTimeline{ +export class RGBA2Timeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex, - Property.alpha + "|" + slotIndex, - Property.rgb2 + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`, `${Property.alpha}|${slotIndex}`, `${Property.rgb2}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 8/*ENTRIES*/; + getFrameEntries() { + return 8 /* ENTRIES*/; } /** Sets the time in seconds, light, and dark colors for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { + setFrame(frame: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { frame <<= 3; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; - this.frames[frame + 4/*A*/] = a; - this.frames[frame + 5/*R2*/] = r2; - this.frames[frame + 6/*G2*/] = g2; - this.frames[frame + 7/*B2*/] = b2; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; + this.frames[frame + 4 /* A*/] = a; + this.frames[frame + 5 /* R2*/] = r2; + this.frames[frame + 6 /* G2*/] = g2; + this.frames[frame + 7 /* B2*/] = b2; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let light = slot.color, dark = slot.darkColor; + const frames = this.frames; + const light = slot.color; + const dark = slot.darkColor; + if (time < frames[0]) { - let setupLight = slot.data.color, setupDark = slot.data.darkColor; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + switch (blend) { case MixBlend.setup: light.setFromColor(setupLight); dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; + return; case MixBlend.first: - light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, - (setupLight.a - light.a) * alpha); + light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); dark.r += (setupDark.r - dark.r) * alpha; dark.g += (setupDark.g - dark.g) * alpha; dark.b += (setupDark.b - dark.b) * alpha; } + return; } - let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0; - let i = Timeline.search(frames, time, 8/*ENTRIES*/); - let curveType = this.curves[i >> 3]; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + let r2 = 0; + let g2 = 0; + let b2 = 0; + const i = Timeline.search(frames, time, 8 /* ENTRIES*/); + const curveType = this.curves[i >> 3]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; - r2 = frames[i + 5/*R2*/]; - g2 = frames[i + 6/*G2*/]; - b2 = frames[i + 7/*B2*/]; - let t = (time - before) / (frames[i + 8/*ENTRIES*/] - before); - r += (frames[i + 8/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 8/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 8/*ENTRIES*/ + 3/*B*/] - b) * t; - a += (frames[i + 8/*ENTRIES*/ + 4/*A*/] - a) * t; - r2 += (frames[i + 8/*ENTRIES*/ + 5/*R2*/] - r2) * t; - g2 += (frames[i + 8/*ENTRIES*/ + 6/*G2*/] - g2) * t; - b2 += (frames[i + 8/*ENTRIES*/ + 7/*B2*/] - b2) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; + r2 = frames[i + 5 /* R2*/]; + g2 = frames[i + 6 /* G2*/]; + b2 = frames[i + 7 /* B2*/]; + const t = (time - before) / (frames[i + 8 /* ENTRIES*/] - before); + + r += (frames[i + 8 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 8 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 8 /* ENTRIES*/ + 3 /* B*/] - b) * t; + a += (frames[i + 8 /* ENTRIES*/ + 4 /* A*/] - a) * t; + r2 += (frames[i + 8 /* ENTRIES*/ + 5 /* R2*/] - r2) * t; + g2 += (frames[i + 8 /* ENTRIES*/ + 6 /* G2*/] - g2) * t; + b2 += (frames[i + 8 /* ENTRIES*/ + 7 /* B2*/] - b2) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; - r2 = frames[i + 5/*R2*/]; - g2 = frames[i + 6/*G2*/]; - b2 = frames[i + 7/*B2*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; + r2 = frames[i + 5 /* R2*/]; + g2 = frames[i + 6 /* G2*/]; + b2 = frames[i + 7 /* B2*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); - r2 = this.getBezierValue(time, i, 5/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/); - g2 = this.getBezierValue(time, i, 6/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/); - b2 = this.getBezierValue(time, i, 7/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 6 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + a = this.getBezierValue(time, i, 4 /* A*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); + r2 = this.getBezierValue(time, i, 5 /* R2*/, curveType + 18 /* BEZIER_SIZE*/ * 4 - 2 /* BEZIER*/); + g2 = this.getBezierValue(time, i, 6 /* G2*/, curveType + 18 /* BEZIER_SIZE*/ * 5 - 2 /* BEZIER*/); + b2 = this.getBezierValue(time, i, 7 /* B2*/, curveType + 18 /* BEZIER_SIZE*/ * 6 - 2 /* BEZIER*/); } if (alpha == 1) { @@ -1220,7 +1324,8 @@ export class RGBA2Timeline extends CurveTimeline implements SlotTimeline{ } else { if (blend == MixBlend.setup) { light.setFromColor(slot.data.color); - let setupDark = slot.data.darkColor; + const setupDark = slot.data.darkColor; + dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; @@ -1236,41 +1341,43 @@ export class RGBA2Timeline extends CurveTimeline implements SlotTimeline{ /** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. * @public * */ -export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ +export class RGB2Timeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex, - Property.rgb2 + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`, `${Property.rgb2}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 7/*ENTRIES*/; + getFrameEntries() { + return 7 /* ENTRIES*/; } /** Sets the time in seconds, light, and dark colors for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number, r2: number, g2: number, b2: number) { - frame *= 7/*ENTRIES*/; + setFrame(frame: number, time: number, r: number, g: number, b: number, r2: number, g2: number, b2: number) { + frame *= 7 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; - this.frames[frame + 4/*R2*/] = r2; - this.frames[frame + 5/*G2*/] = g2; - this.frames[frame + 6/*B2*/] = b2; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; + this.frames[frame + 4 /* R2*/] = r2; + this.frames[frame + 5 /* G2*/] = g2; + this.frames[frame + 6 /* B2*/] = b2; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let light = slot.color, dark = slot.darkColor; + const frames = this.frames; + const light = slot.color; + const dark = slot.darkColor; + if (time < frames[0]) { - let setupLight = slot.data.color, setupDark = slot.data.darkColor; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + switch (blend) { case MixBlend.setup: light.r = setupLight.r; @@ -1279,6 +1386,7 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; + return; case MixBlend.first: light.r += (setupLight.r - light.r) * alpha; @@ -1288,44 +1396,53 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ dark.g += (setupDark.g - dark.g) * alpha; dark.b += (setupDark.b - dark.b) * alpha; } + return; } - let r = 0, g = 0, b = 0, r2 = 0, g2 = 0, b2 = 0; - let i = Timeline.search(frames, time, 7/*ENTRIES*/); - let curveType = this.curves[i / 7/*ENTRIES*/]; + let r = 0; + let g = 0; + let b = 0; + let r2 = 0; + let g2 = 0; + let b2 = 0; + const i = Timeline.search(frames, time, 7 /* ENTRIES*/); + const curveType = this.curves[i / 7 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - r2 = frames[i + 4/*R2*/]; - g2 = frames[i + 5/*G2*/]; - b2 = frames[i + 6/*B2*/]; - let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before); - r += (frames[i + 7/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 7/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 7/*ENTRIES*/ + 3/*B*/] - b) * t; - r2 += (frames[i + 7/*ENTRIES*/ + 4/*R2*/] - r2) * t; - g2 += (frames[i + 7/*ENTRIES*/ + 5/*G2*/] - g2) * t; - b2 += (frames[i + 7/*ENTRIES*/ + 6/*B2*/] - b2) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + r2 = frames[i + 4 /* R2*/]; + g2 = frames[i + 5 /* G2*/]; + b2 = frames[i + 6 /* B2*/]; + const t = (time - before) / (frames[i + 7 /* ENTRIES*/] - before); + + r += (frames[i + 7 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 7 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 7 /* ENTRIES*/ + 3 /* B*/] - b) * t; + r2 += (frames[i + 7 /* ENTRIES*/ + 4 /* R2*/] - r2) * t; + g2 += (frames[i + 7 /* ENTRIES*/ + 5 /* G2*/] - g2) * t; + b2 += (frames[i + 7 /* ENTRIES*/ + 6 /* B2*/] - b2) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - r2 = frames[i + 4/*R2*/]; - g2 = frames[i + 5/*G2*/]; - b2 = frames[i + 6/*B2*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + r2 = frames[i + 4 /* R2*/]; + g2 = frames[i + 5 /* G2*/]; + b2 = frames[i + 6 /* B2*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - r2 = this.getBezierValue(time, i, 4/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); - g2 = this.getBezierValue(time, i, 5/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/); - b2 = this.getBezierValue(time, i, 6/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + r2 = this.getBezierValue(time, i, 4 /* R2*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); + g2 = this.getBezierValue(time, i, 5 /* G2*/, curveType + 18 /* BEZIER_SIZE*/ * 4 - 2 /* BEZIER*/); + b2 = this.getBezierValue(time, i, 6 /* B2*/, curveType + 18 /* BEZIER_SIZE*/ * 5 - 2 /* BEZIER*/); } if (alpha == 1) { @@ -1337,7 +1454,9 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ dark.b = b2; } else { if (blend == MixBlend.setup) { - let setupLight = slot.data.color, setupDark = slot.data.darkColor; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + light.r = setupLight.r; light.g = setupLight.g; light.b = setupLight.b; @@ -1364,42 +1483,43 @@ export class AttachmentTimeline extends Timeline implements SlotTimeline { /** The attachment name for each key frame. May contain null values to clear the attachment. */ attachmentNames: Array; - constructor (frameCount: number, slotIndex: number) { - super(frameCount, [ - Property.attachment + "|" + slotIndex - ]); + constructor(frameCount: number, slotIndex: number) { + super(frameCount, [`${Property.attachment}|${slotIndex}`]); this.slotIndex = slotIndex; this.attachmentNames = new Array(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the attachment name for the specified key frame. */ - setFrame (frame: number, time: number, attachmentName: string) { + setFrame(frame: number, time: number, attachmentName: string) { this.frames[frame] = time; this.attachmentNames[frame] = attachmentName; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; if (direction == MixDirection.mixOut) { if (blend == MixBlend.setup) this.setAttachment(skeleton, slot, slot.data.attachmentName); + return; } if (time < this.frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName); + return; } this.setAttachment(skeleton, slot, this.attachmentNames[Timeline.search1(this.frames, time)]); } - setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string) { + setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string) { slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); } } @@ -1416,39 +1536,43 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { /** The vertices for each key frame. */ vertices: Array = null; - constructor (frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) { - super(frameCount, bezierCount, [ - Property.deform + "|" + slotIndex + "|" + attachment.id - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) { + super(frameCount, bezierCount, [`${Property.deform}|${slotIndex}|${attachment.id}`]); this.slotIndex = slotIndex; this.attachment = attachment; this.vertices = new Array(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the vertices for the specified key frame. * @param vertices Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights. */ - setFrame (frame: number, time: number, vertices: NumberArrayLike) { + setFrame(frame: number, time: number, vertices: NumberArrayLike) { this.frames[frame] = time; this.vertices[frame] = vertices; } /** @param value1 Ignored (0 is used for a deform timeline). * @param value2 Ignored (1 is used for a deform timeline). */ - setBezier (bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, - cy2: number, time2: number, value2: number) { - let curves = this.curves; - let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/; - if (value == 0) curves[frame] = 2/*BEZIER*/ + i; - let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = cy2 * 0.03 - cy1 * 0.06; - let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = (cy1 - cy2 + 0.33333333) * 0.018; - let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy; - let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = cy1 * 0.3 + tmpy + dddy * 0.16666667; - let x = time1 + dx, y = dy; - for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) { + setBezier(bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, cy2: number, time2: number, value2: number) { + const curves = this.curves; + let i = this.getFrameCount() + bezier * 18; /* BEZIER_SIZE*/ + + if (value == 0) curves[frame] = 2 /* BEZIER*/ + i; + const tmpx = (time1 - cx1 * 2 + cx2) * 0.03; + const tmpy = cy2 * 0.03 - cy1 * 0.06; + const dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006; + const dddy = (cy1 - cy2 + 0.33333333) * 0.018; + let ddx = tmpx * 2 + dddx; + let ddy = tmpy * 2 + dddy; + let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667; + let dy = cy1 * 0.3 + tmpy + dddy * 0.16666667; + let x = time1 + dx; + let y = dy; + + for (let n = i + 18 /* BEZIER_SIZE*/; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; dx += ddx; @@ -1460,174 +1584,202 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { } } - getCurvePercent (time: number, frame: number) { - let curves = this.curves; + getCurvePercent(time: number, frame: number) { + const curves = this.curves; let i = curves[frame]; + switch (i) { - case 0/*LINEAR*/: - let x = this.frames[frame]; + case 0 /* LINEAR*/: + const x = this.frames[frame]; + return (time - x) / (this.frames[frame + this.getFrameEntries()] - x); - case 1/*STEPPED*/: + case 1 /* STEPPED*/: return 0; } - i -= 2/*BEZIER*/; + i -= 2 /* BEZIER*/; if (curves[i] > time) { - let x = this.frames[frame]; - return curves[i + 1] * (time - x) / (curves[i] - x); + const x = this.frames[frame]; + + return (curves[i + 1] * (time - x)) / (curves[i] - x); } - let n = i + 18/*BEZIER_SIZE*/; + const n = i + 18; /* BEZIER_SIZE*/ + for (i += 2; i < n; i += 2) { if (curves[i] >= time) { - let x = curves[i - 2], y = curves[i - 1]; - return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y); + const x = curves[i - 2]; + const y = curves[i - 1]; + + return y + ((time - x) / (curves[i] - x)) * (curves[i + 1] - y); } } - let x = curves[n - 2], y = curves[n - 1]; - return y + (1 - y) * (time - x) / (this.frames[frame + this.getFrameEntries()] - x); + const x = curves[n - 2]; + const y = curves[n - 1]; + + return y + ((1 - y) * (time - x)) / (this.frames[frame + this.getFrameEntries()] - x); } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot: Slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot: Slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let slotAttachment: Attachment = slot.getAttachment(); + const slotAttachment: Attachment = slot.getAttachment(); + if (!(slotAttachment instanceof VertexAttachment) || (slotAttachment).deformAttachment != this.attachment) return; - let deform: Array = slot.deform; + const deform: Array = slot.deform; + if (deform.length == 0) blend = MixBlend.setup; - let vertices = this.vertices; - let vertexCount = vertices[0].length; + const vertices = this.vertices; + const vertexCount = vertices[0].length; + + const frames = this.frames; - let frames = this.frames; if (time < frames[0]) { - let vertexAttachment = slotAttachment; + const vertexAttachment = slotAttachment; + switch (blend) { case MixBlend.setup: deform.length = 0; + return; case MixBlend.first: if (alpha == 1) { deform.length = 0; + return; } deform.length = vertexCount; if (!vertexAttachment.bones) { // Unweighted vertex positions. - let setupVertices = vertexAttachment.vertices; - for (var i = 0; i < vertexCount; i++) - deform[i] += (setupVertices[i] - deform[i]) * alpha; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) deform[i] += (setupVertices[i] - deform[i]) * alpha; } else { // Weighted deform offsets. alpha = 1 - alpha; - for (var i = 0; i < vertexCount; i++) - deform[i] *= alpha; + for (let i = 0; i < vertexCount; i++) deform[i] *= alpha; } } + return; } deform.length = vertexCount; - if (time >= frames[frames.length - 1]) { // Time is after last frame. - let lastVertices = vertices[frames.length - 1]; + if (time >= frames[frames.length - 1]) { + // Time is after last frame. + const lastVertices = vertices[frames.length - 1]; + if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i] - setupVertices[i]; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i] - setupVertices[i]; } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i]; + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i]; } - } else - Utils.arrayCopy(lastVertices, 0, deform, 0, vertexCount); + } else Utils.arrayCopy(lastVertices, 0, deform, 0, vertexCount); } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let setup = setupVertices[i]; + const setup = setupVertices[i]; + deform[i] = setup + (lastVertices[i] - setup) * alpha; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] = lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] = lastVertices[i] * alpha; } break; } case MixBlend.first: case MixBlend.replace: - for (let i = 0; i < vertexCount; i++) - deform[i] += (lastVertices[i] - deform[i]) * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] += (lastVertices[i] - deform[i]) * alpha; break; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; - for (let i = 0; i < vertexCount; i++) - deform[i] += (lastVertices[i] - setupVertices[i]) * alpha; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) deform[i] += (lastVertices[i] - setupVertices[i]) * alpha; } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i] * alpha; } } } + return; } // Interpolate between the previous frame and the current frame. - let frame = Timeline.search1(frames, time); - let percent = this.getCurvePercent(time, frame); - let prevVertices = vertices[frame]; - let nextVertices = vertices[frame + 1]; + const frame = Timeline.search1(frames, time); + const percent = this.getCurvePercent(time, frame); + const prevVertices = vertices[frame]; + const nextVertices = vertices[frame + 1]; if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i]; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += prev + (nextVertices[i] - prev) * percent; } } } else { for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] = prev + (nextVertices[i] - prev) * percent; } } } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i], setup = setupVertices[i]; + const prev = prevVertices[i]; + const setup = setupVertices[i]; + deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -1636,23 +1788,28 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { case MixBlend.first: case MixBlend.replace: for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha; } break; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -1665,54 +1822,57 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { * @public * */ export class EventTimeline extends Timeline { - static propertyIds = ["" + Property.event]; + static propertyIds = [`${Property.event}`]; /** The event for each key frame. */ events: Array = null; - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount, EventTimeline.propertyIds); this.events = new Array(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the event for the specified key frame. */ - setFrame (frame: number, event: Event) { + setFrame(frame: number, event: Event) { this.frames[frame] = event.time; this.events[frame] = event; } /** Fires events for frames > `lastTime` and <= `time`. */ - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { if (!firedEvents) return; - let frames = this.frames; - let frameCount = this.frames.length; + const frames = this.frames; + const frameCount = this.frames.length; - if (lastTime > time) { // Fire events after last time for looped animations. + if (lastTime > time) { + // Fire events after last time for looped animations. this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, blend, direction); lastTime = -1; - } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + } else if (lastTime >= frames[frameCount - 1]) + // Last time is after last frame. return; if (time < frames[0]) return; // Time is before first frame. let i = 0; - if (lastTime < frames[0]) - i = 0; + + if (lastTime < frames[0]) i = 0; else { i = Timeline.search1(frames, lastTime) + 1; - let frameTime = frames[i]; - while (i > 0) { // Fire multiple events with the same frame. + const frameTime = frames[i]; + + while (i > 0) { + // Fire multiple events with the same frame. if (frames[i - 1] != frameTime) break; i--; } } - for (; i < frameCount && time >= frames[i]; i++) - firedEvents.push(this.events[i]); + for (; i < frameCount && time >= frames[i]; i++) firedEvents.push(this.events[i]); } } @@ -1720,47 +1880,49 @@ export class EventTimeline extends Timeline { * @public * */ export class DrawOrderTimeline extends Timeline { - static propertyIds = ["" + Property.drawOrder]; + static propertyIds = [`${Property.drawOrder}`]; /** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */ drawOrders: Array> = null; - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount, DrawOrderTimeline.propertyIds); this.drawOrders = new Array>(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the draw order for the specified key frame. * @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose * draw order. */ - setFrame (frame: number, time: number, drawOrder: Array) { + setFrame(frame: number, time: number, drawOrder: Array) { this.frames[frame] = time; this.drawOrders[frame] = drawOrder; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { if (direction == MixDirection.mixOut) { if (blend == MixBlend.setup) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } if (time < this.frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } - let drawOrderToSetupIndex = this.drawOrders[Timeline.search1(this.frames, time)]; - if (!drawOrderToSetupIndex) - Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + const drawOrderToSetupIndex = this.drawOrders[Timeline.search1(this.frames, time)]; + + if (!drawOrderToSetupIndex) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); else { - let drawOrder: Array = skeleton.drawOrder; - let slots: Array = skeleton.slots; - for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) - drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + const drawOrder: Array = skeleton.drawOrder; + const slots: Array = skeleton.slots; + + for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) drawOrder[i] = slots[drawOrderToSetupIndex[i]]; } } } @@ -1771,35 +1933,35 @@ export class DrawOrderTimeline extends Timeline { * */ export class IkConstraintTimeline extends CurveTimeline { /** The index of the IK constraint slot in {@link Skeleton#ikConstraints} that will be changed. */ - ikConstraintIndex: number = 0; + ikConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, ikConstraintIndex: number) { - super(frameCount, bezierCount, [ - Property.ikConstraint + "|" + ikConstraintIndex - ]); + constructor(frameCount: number, bezierCount: number, ikConstraintIndex: number) { + super(frameCount, bezierCount, [`${Property.ikConstraint}|${ikConstraintIndex}`]); this.ikConstraintIndex = ikConstraintIndex; } - getFrameEntries () { - return 6/*ENTRIES*/; + getFrameEntries() { + return 6 /* ENTRIES*/; } /** Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame. */ - setFrame (frame: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) { - frame *= 6/*ENTRIES*/; + setFrame(frame: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) { + frame *= 6 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*MIX*/] = mix; - this.frames[frame + 2/*SOFTNESS*/] = softness; - this.frames[frame + 3/*BEND_DIRECTION*/] = bendDirection; - this.frames[frame + 4/*COMPRESS*/] = compress ? 1 : 0; - this.frames[frame + 5/*STRETCH*/] = stretch ? 1 : 0; + this.frames[frame + 1 /* MIX*/] = mix; + this.frames[frame + 2 /* SOFTNESS*/] = softness; + this.frames[frame + 3 /* BEND_DIRECTION*/] = bendDirection; + this.frames[frame + 4 /* COMPRESS*/] = compress ? 1 : 0; + this.frames[frame + 5 /* STRETCH*/] = stretch ? 1 : 0; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: @@ -1808,6 +1970,7 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.bendDirection = constraint.data.bendDirection; constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; + return; case MixBlend.first: constraint.mix += (constraint.data.mix - constraint.mix) * alpha; @@ -1816,28 +1979,33 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } + return; } - let mix = 0, softness = 0; - let i = Timeline.search(frames, time, 6/*ENTRIES*/) - let curveType = this.curves[i / 6/*ENTRIES*/]; + let mix = 0; + let softness = 0; + const i = Timeline.search(frames, time, 6 /* ENTRIES*/); + const curveType = this.curves[i / 6 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - mix = frames[i + 1/*MIX*/]; - softness = frames[i + 2/*SOFTNESS*/]; - let t = (time - before) / (frames[i + 6/*ENTRIES*/] - before); - mix += (frames[i + 6/*ENTRIES*/ + 1/*MIX*/] - mix) * t; - softness += (frames[i + 6/*ENTRIES*/ + 2/*SOFTNESS*/] - softness) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + mix = frames[i + 1 /* MIX*/]; + softness = frames[i + 2 /* SOFTNESS*/]; + const t = (time - before) / (frames[i + 6 /* ENTRIES*/] - before); + + mix += (frames[i + 6 /* ENTRIES*/ + 1 /* MIX*/] - mix) * t; + softness += (frames[i + 6 /* ENTRIES*/ + 2 /* SOFTNESS*/] - softness) * t; break; - case 1/*STEPPED*/: - mix = frames[i + 1/*MIX*/]; - softness = frames[i + 2/*SOFTNESS*/]; + case 1 /* STEPPED*/: + mix = frames[i + 1 /* MIX*/]; + softness = frames[i + 2 /* SOFTNESS*/]; break; default: - mix = this.getBezierValue(time, i, 1/*MIX*/, curveType - 2/*BEZIER*/); - softness = this.getBezierValue(time, i, 2/*SOFTNESS*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + mix = this.getBezierValue(time, i, 1 /* MIX*/, curveType - 2 /* BEZIER*/); + softness = this.getBezierValue(time, i, 2 /* SOFTNESS*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } if (blend == MixBlend.setup) { @@ -1849,17 +2017,17 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } else { - constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/]; - constraint.compress = frames[i + 4/*COMPRESS*/] != 0; - constraint.stretch = frames[i + 5/*STRETCH*/] != 0; + constraint.bendDirection = frames[i + 3 /* BEND_DIRECTION*/]; + constraint.compress = frames[i + 4 /* COMPRESS*/] != 0; + constraint.stretch = frames[i + 5 /* STRETCH*/] != 0; } } else { constraint.mix += (mix - constraint.mix) * alpha; constraint.softness += (softness - constraint.softness) * alpha; if (direction == MixDirection.mixIn) { - constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/]; - constraint.compress = frames[i + 4/*COMPRESS*/] != 0; - constraint.stretch = frames[i + 5/*STRETCH*/] != 0; + constraint.bendDirection = frames[i + 3 /* BEND_DIRECTION*/]; + constraint.compress = frames[i + 4 /* COMPRESS*/] != 0; + constraint.stretch = frames[i + 5 /* STRETCH*/] != 0; } } } @@ -1871,40 +2039,41 @@ export class IkConstraintTimeline extends CurveTimeline { * */ export class TransformConstraintTimeline extends CurveTimeline { /** The index of the transform constraint slot in {@link Skeleton#transformConstraints} that will be changed. */ - transformConstraintIndex: number = 0; + transformConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, transformConstraintIndex: number) { - super(frameCount, bezierCount, [ - Property.transformConstraint + "|" + transformConstraintIndex - ]); + constructor(frameCount: number, bezierCount: number, transformConstraintIndex: number) { + super(frameCount, bezierCount, [`${Property.transformConstraint}|${transformConstraintIndex}`]); this.transformConstraintIndex = transformConstraintIndex; } - getFrameEntries () { - return 7/*ENTRIES*/; + getFrameEntries() { + return 7 /* ENTRIES*/; } /** The time in seconds, rotate mix, translate mix, scale mix, and shear mix for the specified key frame. */ - setFrame (frame: number, time: number, mixRotate: number, mixX: number, mixY: number, mixScaleX: number, mixScaleY: number, - mixShearY: number) { - let frames = this.frames; - frame *= 7/*ENTRIES*/; + setFrame(frame: number, time: number, mixRotate: number, mixX: number, mixY: number, mixScaleX: number, mixScaleY: number, mixShearY: number) { + const frames = this.frames; + + frame *= 7 /* ENTRIES*/; frames[frame] = time; - frames[frame + 1/*ROTATE*/] = mixRotate; - frames[frame + 2/*X*/] = mixX; - frames[frame + 3/*Y*/] = mixY; - frames[frame + 4/*SCALEX*/] = mixScaleX; - frames[frame + 5/*SCALEY*/] = mixScaleY; - frames[frame + 6/*SHEARY*/] = mixShearY; + frames[frame + 1 /* ROTATE*/] = mixRotate; + frames[frame + 2 /* X*/] = mixX; + frames[frame + 3 /* Y*/] = mixY; + frames[frame + 4 /* SCALEX*/] = mixScaleX; + frames[frame + 5 /* SCALEY*/] = mixScaleY; + frames[frame + 6 /* SHEARY*/] = mixShearY; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { - let data = constraint.data; + const data = constraint.data; + switch (blend) { case MixBlend.setup: constraint.mixRotate = data.mixRotate; @@ -1913,6 +2082,7 @@ export class TransformConstraintTimeline extends CurveTimeline { constraint.mixScaleX = data.mixScaleX; constraint.mixScaleY = data.mixScaleY; constraint.mixShearY = data.mixShearY; + return; case MixBlend.first: constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha; @@ -1922,48 +2092,58 @@ export class TransformConstraintTimeline extends CurveTimeline { constraint.mixScaleY += (data.mixScaleY - constraint.mixScaleY) * alpha; constraint.mixShearY += (data.mixShearY - constraint.mixShearY) * alpha; } + return; } - let rotate, x, y, scaleX, scaleY, shearY; - let i = Timeline.search(frames, time, 7/*ENTRIES*/); - let curveType = this.curves[i / 7/*ENTRIES*/]; + let rotate; + let x; + let y; + let scaleX; + let scaleY; + let shearY; + const i = Timeline.search(frames, time, 7 /* ENTRIES*/); + const curveType = this.curves[i / 7 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; - scaleX = frames[i + 4/*SCALEX*/]; - scaleY = frames[i + 5/*SCALEY*/]; - shearY = frames[i + 6/*SHEARY*/]; - let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before); - rotate += (frames[i + 7/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t; - x += (frames[i + 7/*ENTRIES*/ + 2/*X*/] - x) * t; - y += (frames[i + 7/*ENTRIES*/ + 3/*Y*/] - y) * t; - scaleX += (frames[i + 7/*ENTRIES*/ + 4/*SCALEX*/] - scaleX) * t; - scaleY += (frames[i + 7/*ENTRIES*/ + 5/*SCALEY*/] - scaleY) * t; - shearY += (frames[i + 7/*ENTRIES*/ + 6/*SHEARY*/] - shearY) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; + scaleX = frames[i + 4 /* SCALEX*/]; + scaleY = frames[i + 5 /* SCALEY*/]; + shearY = frames[i + 6 /* SHEARY*/]; + const t = (time - before) / (frames[i + 7 /* ENTRIES*/] - before); + + rotate += (frames[i + 7 /* ENTRIES*/ + 1 /* ROTATE*/] - rotate) * t; + x += (frames[i + 7 /* ENTRIES*/ + 2 /* X*/] - x) * t; + y += (frames[i + 7 /* ENTRIES*/ + 3 /* Y*/] - y) * t; + scaleX += (frames[i + 7 /* ENTRIES*/ + 4 /* SCALEX*/] - scaleX) * t; + scaleY += (frames[i + 7 /* ENTRIES*/ + 5 /* SCALEY*/] - scaleY) * t; + shearY += (frames[i + 7 /* ENTRIES*/ + 6 /* SHEARY*/] - shearY) * t; break; - case 1/*STEPPED*/: - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; - scaleX = frames[i + 4/*SCALEX*/]; - scaleY = frames[i + 5/*SCALEY*/]; - shearY = frames[i + 6/*SHEARY*/]; + case 1 /* STEPPED*/: + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; + scaleX = frames[i + 4 /* SCALEX*/]; + scaleY = frames[i + 5 /* SCALEY*/]; + shearY = frames[i + 6 /* SHEARY*/]; break; default: - rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/); - x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - scaleX = this.getBezierValue(time, i, 4/*SCALEX*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); - scaleY = this.getBezierValue(time, i, 5/*SCALEY*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/); - shearY = this.getBezierValue(time, i, 6/*SHEARY*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/); + rotate = this.getBezierValue(time, i, 1 /* ROTATE*/, curveType - 2 /* BEZIER*/); + x = this.getBezierValue(time, i, 2 /* X*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 3 /* Y*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + scaleX = this.getBezierValue(time, i, 4 /* SCALEX*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); + scaleY = this.getBezierValue(time, i, 5 /* SCALEY*/, curveType + 18 /* BEZIER_SIZE*/ * 4 - 2 /* BEZIER*/); + shearY = this.getBezierValue(time, i, 6 /* SHEARY*/, curveType + 18 /* BEZIER_SIZE*/ * 5 - 2 /* BEZIER*/); } if (blend == MixBlend.setup) { - let data = constraint.data; + const data = constraint.data; + constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha; constraint.mixX = data.mixX + (x - data.mixX) * alpha; constraint.mixY = data.mixY + (y - data.mixY) * alpha; @@ -1986,35 +2166,37 @@ export class TransformConstraintTimeline extends CurveTimeline { * */ export class PathConstraintPositionTimeline extends CurveTimeline1 { /** The index of the path constraint slot in {@link Skeleton#pathConstraints} that will be changed. */ - pathConstraintIndex: number = 0; + pathConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) { - super(frameCount, bezierCount, Property.pathConstraintPosition + "|" + pathConstraintIndex); + constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) { + super(frameCount, bezierCount, `${Property.pathConstraintPosition}|${pathConstraintIndex}`); this.pathConstraintIndex = pathConstraintIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.position = constraint.data.position; + return; case MixBlend.first: constraint.position += (constraint.data.position - constraint.position) * alpha; } + return; } - let position = this.getCurveValue(time); + const position = this.getCurveValue(time); - if (blend == MixBlend.setup) - constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; - else - constraint.position += (position - constraint.position) * alpha; + if (blend == MixBlend.setup) constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; + else constraint.position += (position - constraint.position) * alpha; } } @@ -2025,33 +2207,35 @@ export class PathConstraintSpacingTimeline extends CurveTimeline1 { /** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */ pathConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) { - super(frameCount, bezierCount, Property.pathConstraintSpacing + "|" + pathConstraintIndex); + constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) { + super(frameCount, bezierCount, `${Property.pathConstraintSpacing}|${pathConstraintIndex}`); this.pathConstraintIndex = pathConstraintIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.spacing = constraint.data.spacing; + return; case MixBlend.first: constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha; } + return; } - let spacing = this.getCurveValue(time); + const spacing = this.getCurveValue(time); - if (blend == MixBlend.setup) - constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; - else - constraint.spacing += (spacing - constraint.spacing) * alpha; + if (blend == MixBlend.setup) constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; + else constraint.spacing += (spacing - constraint.spacing) * alpha; } } @@ -2063,73 +2247,82 @@ export class PathConstraintMixTimeline extends CurveTimeline { /** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */ pathConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) { - super(frameCount, bezierCount, [ - Property.pathConstraintMix + "|" + pathConstraintIndex - ]); + constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) { + super(frameCount, bezierCount, [`${Property.pathConstraintMix}|${pathConstraintIndex}`]); this.pathConstraintIndex = pathConstraintIndex; } - getFrameEntries () { - return 4/*ENTRIES*/; + getFrameEntries() { + return 4 /* ENTRIES*/; } - setFrame (frame: number, time: number, mixRotate: number, mixX: number, mixY: number) { - let frames = this.frames; + setFrame(frame: number, time: number, mixRotate: number, mixX: number, mixY: number) { + const frames = this.frames; + frame <<= 2; frames[frame] = time; - frames[frame + 1/*ROTATE*/] = mixRotate; - frames[frame + 2/*X*/] = mixX; - frames[frame + 3/*Y*/] = mixY; + frames[frame + 1 /* ROTATE*/] = mixRotate; + frames[frame + 2 /* X*/] = mixX; + frames[frame + 3 /* Y*/] = mixY; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.mixRotate = constraint.data.mixRotate; constraint.mixX = constraint.data.mixX; constraint.mixY = constraint.data.mixY; + return; case MixBlend.first: constraint.mixRotate += (constraint.data.mixRotate - constraint.mixRotate) * alpha; constraint.mixX += (constraint.data.mixX - constraint.mixX) * alpha; constraint.mixY += (constraint.data.mixY - constraint.mixY) * alpha; } + return; } - let rotate, x, y; - let i = Timeline.search(frames, time, 4/*ENTRIES*/); - let curveType = this.curves[i >> 2]; + let rotate; + let x; + let y; + const i = Timeline.search(frames, time, 4 /* ENTRIES*/); + const curveType = this.curves[i >> 2]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; - let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before); - rotate += (frames[i + 4/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t; - x += (frames[i + 4/*ENTRIES*/ + 2/*X*/] - x) * t; - y += (frames[i + 4/*ENTRIES*/ + 3/*Y*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; + const t = (time - before) / (frames[i + 4 /* ENTRIES*/] - before); + + rotate += (frames[i + 4 /* ENTRIES*/ + 1 /* ROTATE*/] - rotate) * t; + x += (frames[i + 4 /* ENTRIES*/ + 2 /* X*/] - x) * t; + y += (frames[i + 4 /* ENTRIES*/ + 3 /* Y*/] - y) * t; break; - case 1/*STEPPED*/: - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; + case 1 /* STEPPED*/: + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; break; default: - rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/); - x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); + rotate = this.getBezierValue(time, i, 1 /* ROTATE*/, curveType - 2 /* BEZIER*/); + x = this.getBezierValue(time, i, 2 /* X*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 3 /* Y*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); } if (blend == MixBlend.setup) { - let data = constraint.data; + const data = constraint.data; + constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha; constraint.mixX = data.mixX + (x - data.mixX) * alpha; constraint.mixY = data.mixY + (y - data.mixY) * alpha; diff --git a/packages/runtime-4.0/src/core/AnimationState.ts b/packages/runtime-4.0/src/core/AnimationState.ts index 38813d6b..aad2b11b 100644 --- a/packages/runtime-4.0/src/core/AnimationState.ts +++ b/packages/runtime-4.0/src/core/AnimationState.ts @@ -1,25 +1,9 @@ -import { - IAnimationState, - IAnimationStateListener, - ITrackEntry, - MathUtils, - MixBlend, - MixDirection, - Pool, - StringSet, - Utils -} from "@pixi-spine/base"; -import { - Animation, - AttachmentTimeline, - DrawOrderTimeline, - EventTimeline, - RotateTimeline, Timeline -} from './Animation'; -import {AnimationStateData} from "./AnimationStateData"; -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; -import type {Slot} from "./Slot"; +import { IAnimationState, IAnimationStateListener, ITrackEntry, MathUtils, MixBlend, MixDirection, Pool, StringSet, Utils } from '@pixi-spine/base'; +import { Animation, AttachmentTimeline, DrawOrderTimeline, EventTimeline, RotateTimeline, Timeline } from './Animation'; +import type { AnimationStateData } from './AnimationStateData'; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; +import type { Slot } from './Slot'; /** Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies * multiple animations on top of each other (layering). @@ -28,8 +12,9 @@ import type {Slot} from "./Slot"; * @public * */ export class AnimationState implements IAnimationState { - private static emptyAnimation (): Animation { - if (!_emptyAnimation) _emptyAnimation = new Animation("", [], 0); + private static emptyAnimation(): Animation { + if (!_emptyAnimation) _emptyAnimation = new Animation('', [], 0); + return _emptyAnimation; } @@ -54,16 +39,18 @@ export class AnimationState implements IAnimationState { trackEntryPool = new Pool(() => new TrackEntry()); - constructor (data: AnimationStateData) { + constructor(data: AnimationStateData) { this.data = data; } /** Increments each track entry {@link TrackEntry#trackTime()}, setting queued animations as current if needed. */ - update (delta: number) { + update(delta: number) { delta *= this.timeScale; - let tracks = this.tracks; + const tracks = this.tracks; + for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (!current) continue; current.animationLast = current.nextAnimationLast; @@ -79,9 +66,11 @@ export class AnimationState implements IAnimationState { } let next = current.next; + if (next) { // When the next entry's delay is passed, change to the next entry, preserving leftover time. - let nextTime = current.trackLast - next.delay; + const nextTime = current.trackLast - next.delay; + if (nextTime >= 0) { next.delay = 0; next.trackTime += current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale; @@ -102,6 +91,7 @@ export class AnimationState implements IAnimationState { if (current.mixingFrom && this.updateMixingFrom(current, delta)) { // End mixing from entries once all have completed. let from = current.mixingFrom; + current.mixingFrom = null; if (from) from.mixingTo = null; while (from) { @@ -117,11 +107,12 @@ export class AnimationState implements IAnimationState { } /** Returns true when all mixing from entries are complete. */ - updateMixingFrom (to: TrackEntry, delta: number): boolean { - let from = to.mixingFrom; + updateMixingFrom(to: TrackEntry, delta: number): boolean { + const from = to.mixingFrom; + if (!from) return true; - let finished = this.updateMixingFrom(from, delta); + const finished = this.updateMixingFrom(from, delta); from.animationLast = from.nextAnimationLast; from.trackLast = from.nextTrackLast; @@ -135,68 +126,75 @@ export class AnimationState implements IAnimationState { to.interruptAlpha = from.interruptAlpha; this.queue.end(from); } + return finished; } from.trackTime += delta * from.timeScale; to.mixTime += delta; + return false; } /** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the * animation state can be applied to multiple skeletons to pose them identically. * @returns True if any animations were applied. */ - apply (skeleton: Skeleton): boolean { - if (!skeleton) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton): boolean { + if (!skeleton) throw new Error('skeleton cannot be null.'); if (this.animationsChanged) this._animationsChanged(); - let events = this.events; - let tracks = this.tracks; + const events = this.events; + const tracks = this.tracks; let applied = false; for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (!current || current.delay > 0) continue; applied = true; - let blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; + const blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; // Apply mixing from entries first. let mix = current.alpha; - if (current.mixingFrom) - mix *= this.applyMixingFrom(current, skeleton, blend); - else if (current.trackTime >= current.trackEnd && !current.next) - mix = 0; + + if (current.mixingFrom) mix *= this.applyMixingFrom(current, skeleton, blend); + else if (current.trackTime >= current.trackEnd && !current.next) mix = 0; // Apply current entry. - let animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime; + const animationLast = current.animationLast; + const animationTime = current.getAnimationTime(); + let applyTime = animationTime; let applyEvents = events; + if (current.reverse) { applyTime = current.animation.duration - applyTime; applyEvents = null; } - let timelines = current.animation.timelines; - let timelineCount = timelines.length; + const timelines = current.animation.timelines; + const timelineCount = timelines.length; + if ((i == 0 && mix == 1) || blend == MixBlend.add) { for (let ii = 0; ii < timelineCount; ii++) { // Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets // to sometimes stop rendering when using color correction, as their RGBA values become NaN. // (https://github.com/pixijs/pixi-spine/issues/302) Utils.webkit602BugfixHelper(mix, blend); - var timeline = timelines[ii]; - if (timeline instanceof AttachmentTimeline) - this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true); - else - timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn); + const timeline = timelines[ii]; + + if (timeline instanceof AttachmentTimeline) this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true); + else timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn); } } else { - let timelineMode = current.timelineMode; + const timelineMode = current.timelineMode; + + const firstFrame = current.timelinesRotation.length != timelineCount << 1; - let firstFrame = current.timelinesRotation.length != timelineCount << 1; if (firstFrame) current.timelinesRotation.length = timelineCount << 1; for (let ii = 0; ii < timelineCount; ii++) { - let timeline = timelines[ii]; - let timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup; + const timeline = timelines[ii]; + const timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup; + if (timeline instanceof RotateTimeline) { this.applyRotateTimeline(timeline, skeleton, applyTime, mix, timelineBlend, current.timelinesRotation, ii << 1, firstFrame); } else if (timeline instanceof AttachmentTimeline) { @@ -217,27 +215,34 @@ export class AnimationState implements IAnimationState { // Set slots attachments to the setup pose, if needed. This occurs if an animation that is mixing out sets attachments so // subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or // the time is before the first key). - var setupState = this.unkeyedState + SETUP; - var slots = skeleton.slots; - for (var i = 0, n = skeleton.slots.length; i < n; i++) { - var slot = slots[i]; + const setupState = this.unkeyedState + SETUP; + const slots = skeleton.slots; + + for (let i = 0, n = skeleton.slots.length; i < n; i++) { + const slot = slots[i]; + if (slot.attachmentState == setupState) { - var attachmentName = slot.data.attachmentName; + const attachmentName = slot.data.attachmentName; + slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName)); } } this.unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot. this.queue.drain(); + return applied; } - applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { - let from = to.mixingFrom; + applyMixingFrom(to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { + const from = to.mixingFrom; + if (from.mixingFrom) this.applyMixingFrom(from, skeleton, blend); let mix = 0; - if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes. + + if (to.mixDuration == 0) { + // Single frame mix to undo mixingFrom changes. mix = 1; if (blend == MixBlend.first) blend = MixBlend.setup; } else { @@ -246,33 +251,37 @@ export class AnimationState implements IAnimationState { if (blend != MixBlend.first) blend = from.mixBlend; } - let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; - let timelines = from.animation.timelines; - let timelineCount = timelines.length; - let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix); - let animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime; + const attachments = mix < from.attachmentThreshold; + const drawOrder = mix < from.drawOrderThreshold; + const timelines = from.animation.timelines; + const timelineCount = timelines.length; + const alphaHold = from.alpha * to.interruptAlpha; + const alphaMix = alphaHold * (1 - mix); + const animationLast = from.animationLast; + const animationTime = from.getAnimationTime(); + let applyTime = animationTime; let events = null; - if (from.reverse) - applyTime = from.animation.duration - applyTime; - else if (mix < from.eventThreshold) - events = this.events; + + if (from.reverse) applyTime = from.animation.duration - applyTime; + else if (mix < from.eventThreshold) events = this.events; if (blend == MixBlend.add) { - for (let i = 0; i < timelineCount; i++) - timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut); + for (let i = 0; i < timelineCount; i++) timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut); } else { - let timelineMode = from.timelineMode; - let timelineHoldMix = from.timelineHoldMix; + const timelineMode = from.timelineMode; + const timelineHoldMix = from.timelineHoldMix; + + const firstFrame = from.timelinesRotation.length != timelineCount << 1; - let firstFrame = from.timelinesRotation.length != timelineCount << 1; if (firstFrame) from.timelinesRotation.length = timelineCount << 1; from.totalAlpha = 0; for (let i = 0; i < timelineCount; i++) { - let timeline = timelines[i]; + const timeline = timelines[i]; let direction = MixDirection.mixOut; let timelineBlend: MixBlend; let alpha = 0; + switch (timelineMode[i]) { case SUBSEQUENT: if (!drawOrder && timeline instanceof DrawOrderTimeline) continue; @@ -293,21 +302,19 @@ export class AnimationState implements IAnimationState { break; default: timelineBlend = MixBlend.setup; - let holdMix = timelineHoldMix[i]; + const holdMix = timelineHoldMix[i]; + alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration); break; } from.totalAlpha += alpha; - if (timeline instanceof RotateTimeline) - this.applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, from.timelinesRotation, i << 1, firstFrame); - else if (timeline instanceof AttachmentTimeline) - this.applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, attachments); + if (timeline instanceof RotateTimeline) this.applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, from.timelinesRotation, i << 1, firstFrame); + else if (timeline instanceof AttachmentTimeline) this.applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, attachments); else { // This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109 Utils.webkit602BugfixHelper(alpha, blend); - if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup) - direction = MixDirection.mixIn; + if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup) direction = MixDirection.mixIn; timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction); } } @@ -321,39 +328,50 @@ export class AnimationState implements IAnimationState { return mix; } - applyAttachmentTimeline (timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) { - var slot = skeleton.slots[timeline.slotIndex]; + applyAttachmentTimeline(timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) { + const slot = skeleton.slots[timeline.slotIndex]; + if (!slot.bone.active) return; - if (time < timeline.frames[0]) { // Time is before first frame. - if (blend == MixBlend.setup || blend == MixBlend.first) - this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments); - } else - this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments); + if (time < timeline.frames[0]) { + // Time is before first frame. + if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments); + } else this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments); // If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later. if (slot.attachmentState <= this.unkeyedState) slot.attachmentState = this.unkeyedState + SETUP; } - setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string, attachments: boolean) { + setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string, attachments: boolean) { slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName)); if (attachments) slot.attachmentState = this.unkeyedState + CURRENT; } - applyRotateTimeline (timeline: RotateTimeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend, - timelinesRotation: Array, i: number, firstFrame: boolean) { - + applyRotateTimeline( + timeline: RotateTimeline, + skeleton: Skeleton, + time: number, + alpha: number, + blend: MixBlend, + timelinesRotation: Array, + i: number, + firstFrame: boolean + ) { if (firstFrame) timelinesRotation[i] = 0; if (alpha == 1) { timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn); + return; } - let bone = skeleton.bones[timeline.boneIndex]; + const bone = skeleton.bones[timeline.boneIndex]; + if (!bone.active) return; - let frames = timeline.frames; - let r1 = 0, r2 = 0; + const frames = timeline.frames; + let r1 = 0; + let r2 = 0; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: @@ -370,12 +388,16 @@ export class AnimationState implements IAnimationState { } // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. - let total = 0, diff = r2 - r1; + let total = 0; + let diff = r2 - r1; + diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360; if (diff == 0) { total = timelinesRotation[i]; } else { - let lastTotal = 0, lastDiff = 0; + let lastTotal = 0; + let lastDiff = 0; + if (firstFrame) { lastTotal = 0; lastDiff = diff; @@ -383,14 +405,16 @@ export class AnimationState implements IAnimationState { lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. lastDiff = timelinesRotation[i + 1]; // Difference between bones. } - let current = diff > 0, dir = lastTotal >= 0; + const current = diff > 0; + let dir = lastTotal >= 0; // Detect cross at 0 (not 180). + if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) { // A cross after a 360 rotation is a loop. if (Math.abs(lastTotal) > 180) lastTotal += 360 * MathUtils.signum(lastTotal); dir = current; } - total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. + total = diff + lastTotal - (lastTotal % 360); // Store loops as part of lastTotal. if (dir != current) total += 360 * MathUtils.signum(lastTotal); timelinesRotation[i] = total; } @@ -398,16 +422,20 @@ export class AnimationState implements IAnimationState { bone.rotation = r1 + total * alpha; } - queueEvents (entry: TrackEntry, animationTime: number) { - let animationStart = entry.animationStart, animationEnd = entry.animationEnd; - let duration = animationEnd - animationStart; - let trackLastWrapped = entry.trackLast % duration; + queueEvents(entry: TrackEntry, animationTime: number) { + const animationStart = entry.animationStart; + const animationEnd = entry.animationEnd; + const duration = animationEnd - animationStart; + const trackLastWrapped = entry.trackLast % duration; // Queue events before complete. - let events = this.events; - let i = 0, n = events.length; + const events = this.events; + let i = 0; + const n = events.length; + for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < trackLastWrapped) break; if (event.time > animationEnd) continue; // Discard events outside animation start/end. this.queue.event(entry, event); @@ -415,15 +443,15 @@ export class AnimationState implements IAnimationState { // Queue complete if completed a loop iteration or the animation. let complete = false; - if (entry.loop) - complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; - else - complete = animationTime >= animationEnd && entry.animationLast < animationEnd; + + if (entry.loop) complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; + else complete = animationTime >= animationEnd && entry.animationLast < animationEnd; if (complete) this.queue.complete(entry); // Queue events after complete. for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < animationStart) continue; // Discard events outside animation start/end. this.queue.event(entry, event); } @@ -433,11 +461,11 @@ export class AnimationState implements IAnimationState { * * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose, * rather than leaving them in their current pose. */ - clearTracks () { - let oldDrainDisabled = this.queue.drainDisabled; + clearTracks() { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; - for (let i = 0, n = this.tracks.length; i < n; i++) - this.clearTrack(i); + for (let i = 0, n = this.tracks.length; i < n; i++) this.clearTrack(i); this.tracks.length = 0; this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); @@ -447,9 +475,10 @@ export class AnimationState implements IAnimationState { * * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose, * rather than leaving them in their current pose. */ - clearTrack (trackIndex: number) { + clearTrack(trackIndex: number) { if (trackIndex >= this.tracks.length) return; - let current = this.tracks[trackIndex]; + const current = this.tracks[trackIndex]; + if (!current) return; this.queue.end(current); @@ -457,8 +486,10 @@ export class AnimationState implements IAnimationState { this.clearNext(current); let entry = current; + while (true) { - let from = entry.mixingFrom; + const from = entry.mixingFrom; + if (!from) break; this.queue.end(from); entry.mixingFrom = null; @@ -471,8 +502,9 @@ export class AnimationState implements IAnimationState { this.queue.drain(); } - setCurrent (index: number, current: TrackEntry, interrupt: boolean) { - let from = this.expandToIndex(index); + setCurrent(index: number, current: TrackEntry, interrupt: boolean) { + const from = this.expandToIndex(index); + this.tracks[index] = current; current.previous = null; @@ -483,8 +515,7 @@ export class AnimationState implements IAnimationState { current.mixTime = 0; // Store the interrupted mix percentage. - if (from.mixingFrom && from.mixDuration > 0) - current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); + if (from.mixingFrom && from.mixDuration > 0) current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in. } @@ -495,9 +526,11 @@ export class AnimationState implements IAnimationState { /** Sets an animation by name. * * See {@link #setAnimationWith()}. */ - setAnimation (trackIndex: number, animationName: string, loop: boolean = false) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (!animation) throw new Error("Animation not found: " + animationName); + setAnimation(trackIndex: number, animationName: string, loop = false) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (!animation) throw new Error(`Animation not found: ${animationName}`); + return this.setAnimationWith(trackIndex, animation, loop); } @@ -507,10 +540,11 @@ export class AnimationState implements IAnimationState { * duration. In either case {@link TrackEntry#trackEnd} determines when the track is cleared. * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - setAnimationWith (trackIndex: number, animation: Animation, loop: boolean = false) { - if (!animation) throw new Error("animation cannot be null."); + setAnimationWith(trackIndex: number, animation: Animation, loop = false) { + if (!animation) throw new Error('animation cannot be null.'); let interrupt = true; let current = this.expandToIndex(trackIndex); + if (current) { if (current.nextTrackLast == -1) { // Don't mix from an entry that was never applied. @@ -520,21 +554,24 @@ export class AnimationState implements IAnimationState { this.clearNext(current); current = current.mixingFrom; interrupt = false; - } else - this.clearNext(current); + } else this.clearNext(current); } - let entry = this.trackEntry(trackIndex, animation, loop, current); + const entry = this.trackEntry(trackIndex, animation, loop, current); + this.setCurrent(trackIndex, entry, interrupt); this.queue.drain(); + return entry; } /** Queues an animation by name. * * See {@link #addAnimationWith()}. */ - addAnimation (trackIndex: number, animationName: string, loop: boolean = false, delay: number = 0) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (!animation) throw new Error("Animation not found: " + animationName); + addAnimation(trackIndex: number, animationName: string, loop = false, delay = 0) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (!animation) throw new Error(`Animation not found: ${animationName}`); + return this.addAnimationWith(trackIndex, animation, loop, delay); } @@ -546,16 +583,16 @@ export class AnimationState implements IAnimationState { * previous entry is looping, its next loop completion is used instead of its duration. * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - addAnimationWith (trackIndex: number, animation: Animation, loop: boolean = false, delay: number = 0) { - if (!animation) throw new Error("animation cannot be null."); + addAnimationWith(trackIndex: number, animation: Animation, loop = false, delay = 0) { + if (!animation) throw new Error('animation cannot be null.'); let last = this.expandToIndex(trackIndex); + if (last) { - while (last.next) - last = last.next; + while (last.next) last = last.next; } - let entry = this.trackEntry(trackIndex, animation, loop, last); + const entry = this.trackEntry(trackIndex, animation, loop, last); if (!last) { this.setCurrent(trackIndex, entry, true); @@ -567,6 +604,7 @@ export class AnimationState implements IAnimationState { } entry.delay = delay; + return entry; } @@ -584,10 +622,12 @@ export class AnimationState implements IAnimationState { * {@link TrackEntry#setMixDuration()}. Mixing from an empty animation causes the new animation to be applied more and * more over the mix duration. Properties keyed in the new animation transition from the value from lower tracks or from the * setup pose value if no lower tracks key the property to the value keyed in the new animation. */ - setEmptyAnimation (trackIndex: number, mixDuration: number = 0) { - let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation(), false); + setEmptyAnimation(trackIndex: number, mixDuration = 0) { + const entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation(), false); + entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } @@ -602,37 +642,43 @@ export class AnimationState implements IAnimationState { * loop completion is used instead of its duration. * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - addEmptyAnimation (trackIndex: number, mixDuration: number = 0, delay: number = 0) { - let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation(), false, delay); + addEmptyAnimation(trackIndex: number, mixDuration = 0, delay = 0) { + const entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation(), false, delay); + if (delay <= 0) entry.delay += entry.mixDuration - mixDuration; entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } /** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix * duration. */ - setEmptyAnimations (mixDuration: number = 0) { - let oldDrainDisabled = this.queue.drainDisabled; + setEmptyAnimations(mixDuration = 0) { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; for (let i = 0, n = this.tracks.length; i < n; i++) { - let current = this.tracks[i]; + const current = this.tracks[i]; + if (current) this.setEmptyAnimation(current.trackIndex, mixDuration); } this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); } - expandToIndex (index: number) { + expandToIndex(index: number) { if (index < this.tracks.length) return this.tracks[index]; Utils.ensureArrayCapacity(this.tracks, index + 1, null); this.tracks.length = index + 1; + return null; } /** @param last May be null. */ - trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) { - let entry = this.trackEntryPool.obtain(); + trackEntry(trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) { + const entry = this.trackEntryPool.obtain(); + entry.reset(); entry.trackIndex = trackIndex; entry.animation = animation; @@ -660,12 +706,14 @@ export class AnimationState implements IAnimationState { entry.mixTime = 0; entry.mixDuration = !last ? 0 : this.data.getMix(last.animation, animation); entry.mixBlend = MixBlend.replace; + return entry; } /** Removes the {@link TrackEntry#getNext() next entry} and all entries after it for the specified entry. */ - clearNext (entry: TrackEntry) { + clearNext(entry: TrackEntry) { let next = entry.next; + while (next) { this.queue.dispose(next); next = next.next; @@ -673,16 +721,17 @@ export class AnimationState implements IAnimationState { entry.next = null; } - _animationsChanged () { + _animationsChanged() { this.animationsChanged = false; this.propertyIDs.clear(); - let tracks = this.tracks; + const tracks = this.tracks; + for (let i = 0, n = tracks.length; i < n; i++) { let entry = tracks[i]; + if (!entry) continue; - while (entry.mixingFrom) - entry = entry.mixingFrom; + while (entry.mixingFrom) entry = entry.mixingFrom; do { if (!entry.mixingTo || entry.mixBlend != MixBlend.add) this.computeHold(entry); entry = entry.mixingTo; @@ -690,114 +739,126 @@ export class AnimationState implements IAnimationState { } } - computeHold (entry: TrackEntry) { - let to = entry.mixingTo; - let timelines = entry.animation.timelines; - let timelinesCount = entry.animation.timelines.length; - let timelineMode = entry.timelineMode; + computeHold(entry: TrackEntry) { + const to = entry.mixingTo; + const timelines = entry.animation.timelines; + const timelinesCount = entry.animation.timelines.length; + const timelineMode = entry.timelineMode; + timelineMode.length = timelinesCount; - let timelineHoldMix = entry.timelineHoldMix; + const timelineHoldMix = entry.timelineHoldMix; + timelineHoldMix.length = 0; - let propertyIDs = this.propertyIDs; + const propertyIDs = this.propertyIDs; if (to && to.holdPrevious) { - for (let i = 0; i < timelinesCount; i++) - timelineMode[i] = propertyIDs.addAll(timelines[i].getPropertyIds()) ? HOLD_FIRST : HOLD_SUBSEQUENT; + for (let i = 0; i < timelinesCount; i++) timelineMode[i] = propertyIDs.addAll(timelines[i].getPropertyIds()) ? HOLD_FIRST : HOLD_SUBSEQUENT; + return; } - outer: - for (let i = 0; i < timelinesCount; i++) { - let timeline = timelines[i]; - let ids = timeline.getPropertyIds(); - if (!propertyIDs.addAll(ids)) - timelineMode[i] = SUBSEQUENT; - else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline - || timeline instanceof EventTimeline || !to.animation.hasTimeline(ids)) { - timelineMode[i] = FIRST; - } else { - for (let next = to.mixingTo; next; next = next.mixingTo) { - if (next.animation.hasTimeline(ids)) continue; - if (entry.mixDuration > 0) { - timelineMode[i] = HOLD_MIX; - timelineHoldMix[i] = next; - continue outer; - } - break; + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < timelinesCount; i++) { + const timeline = timelines[i]; + const ids = timeline.getPropertyIds(); + + if (!propertyIDs.addAll(ids)) timelineMode[i] = SUBSEQUENT; + else if ( + !to || + timeline instanceof AttachmentTimeline || + timeline instanceof DrawOrderTimeline || + timeline instanceof EventTimeline || + !to.animation.hasTimeline(ids) + ) { + timelineMode[i] = FIRST; + } else { + for (let next = to.mixingTo; next; next = next.mixingTo) { + if (next.animation.hasTimeline(ids)) continue; + if (entry.mixDuration > 0) { + timelineMode[i] = HOLD_MIX; + timelineHoldMix[i] = next; + // eslint-disable-next-line no-labels + continue outer; } - timelineMode[i] = HOLD_FIRST; + break; } + timelineMode[i] = HOLD_FIRST; } + } } /** Returns the track entry for the animation currently playing on the track, or null if no animation is currently playing. */ - getCurrent (trackIndex: number) { + getCurrent(trackIndex: number) { if (trackIndex >= this.tracks.length) return null; + return this.tracks[trackIndex]; } /** Adds a listener to receive events for all track entries. */ - addListener (listener: AnimationStateListener) { - if (!listener) throw new Error("listener cannot be null."); + addListener(listener: AnimationStateListener) { + if (!listener) throw new Error('listener cannot be null.'); this.listeners.push(listener); } /** Removes the listener added with {@link #addListener()}. */ - removeListener (listener: AnimationStateListener) { - let index = this.listeners.indexOf(listener); + removeListener(listener: AnimationStateListener) { + const index = this.listeners.indexOf(listener); + if (index >= 0) this.listeners.splice(index, 1); } /** Removes all listeners added with {@link #addListener()}. */ - clearListeners () { + clearListeners() { this.listeners.length = 0; } /** Discards all listener notifications that have not yet been delivered. This can be useful to call from an * {@link AnimationStateListener} when it is known that further notifications that may have been already queued for delivery * are not wanted because new animations are being set. */ - clearListenerNotifications () { + clearListenerNotifications() { this.queue.clear(); } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; onEnd: (trackIndex: number) => any; - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; setAnimationByName(trackIndex: number, animationName: string, loop: boolean) { if (!AnimationState.deprecatedWarning1) { AnimationState.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on.'); } this.setAnimation(trackIndex, animationName, loop); } - private static deprecatedWarning2: boolean = false; + private static deprecatedWarning2 = false; addAnimationByName(trackIndex: number, animationName: string, loop: boolean, delay: number) { if (!AnimationState.deprecatedWarning2) { AnimationState.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on.'); } this.addAnimation(trackIndex, animationName, loop, delay); } - private static deprecatedWarning3: boolean = false; + private static deprecatedWarning3 = false; hasAnimation(animationName: string): boolean { - let animation = this.data.skeletonData.findAnimation(animationName); + const animation = this.data.skeletonData.findAnimation(animationName); + return animation !== null; } hasAnimationByName(animationName: string): boolean { if (!AnimationState.deprecatedWarning3) { AnimationState.deprecatedWarning3 = true; - console.warn("Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on.'); } + return this.hasAnimation(animationName); } } @@ -833,11 +894,11 @@ export class TrackEntry implements ITrackEntry { /** The index of the track where this track entry is either current or queued. * * See {@link AnimationState#getCurrent()}. */ - trackIndex: number = 0; + trackIndex = 0; /** If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its * duration. */ - loop: boolean = false; + loop = false; /** If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead * of being mixed out. @@ -850,43 +911,42 @@ export class TrackEntry implements ITrackEntry { * * Snapping will occur if `holdPrevious` is true and this animation does not key all the same properties as the * previous animation. */ - holdPrevious: boolean = false; + holdPrevious = false; - reverse: boolean = false; + reverse = false; /** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the * `eventThreshold`, event timelines are applied while this animation is being mixed out. Defaults to 0, so event * timelines are not applied while this animation is being mixed out. */ - eventThreshold: number = 0; + eventThreshold = 0; /** When the mix percentage ({@link #mixtime} / {@link #mixDuration}) is less than the * `attachmentThreshold`, attachment timelines are applied while this animation is being mixed out. Defaults to * 0, so attachment timelines are not applied while this animation is being mixed out. */ - attachmentThreshold: number = 0; + attachmentThreshold = 0; /** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the * `drawOrderThreshold`, draw order timelines are applied while this animation is being mixed out. Defaults to 0, * so draw order timelines are not applied while this animation is being mixed out. */ - drawOrderThreshold: number = 0; + drawOrderThreshold = 0; /** Seconds when this animation starts, both initially and after looping. Defaults to 0. * * When changing the `animationStart` time, it often makes sense to set {@link #animationLast} to the same * value to prevent timeline keys before the start time from triggering. */ - animationStart: number = 0; + animationStart = 0; /** Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will * loop back to {@link #animationStart} at this time. Defaults to the animation {@link Animation#duration}. */ - animationEnd: number = 0; - + animationEnd = 0; /** The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this * animation is applied, event timelines will fire all events between the `animationLast` time (exclusive) and * `animationTime` (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation * is applied. */ - animationLast: number = 0; + animationLast = 0; - nextAnimationLast: number = 0; + nextAnimationLast = 0; /** Seconds to postpone playing the animation. When this track entry is the current track entry, `delay` * postpones incrementing the {@link #trackTime}. When this track entry is queued, `delay` is the time from @@ -894,14 +954,15 @@ export class TrackEntry implements ITrackEntry { * track entry {@link TrackEntry#trackTime} >= this track entry's `delay`). * * {@link #timeScale} affects the delay. */ - delay: number = 0; + delay = 0; /** Current time in seconds this track entry has been the current track entry. The track time determines * {@link #animationTime}. The track time can be set to start the animation at a time other than 0, without affecting * looping. */ - trackTime: number = 0; + trackTime = 0; - trackLast: number = 0; nextTrackLast: number = 0; + trackLast = 0; + nextTrackLast = 0; /** The track time in seconds when this animation will be removed from the track. Defaults to the highest possible float * value, meaning the animation will be applied until a new animation is set or the track is cleared. If the track end time @@ -910,7 +971,7 @@ export class TrackEntry implements ITrackEntry { * * It may be desired to use {@link AnimationState#addEmptyAnimation()} rather than have the animation * abruptly cease being applied. */ - trackEnd: number = 0; + trackEnd = 0; /** Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or * faster. Defaults to 1. @@ -923,18 +984,18 @@ export class TrackEntry implements ITrackEntry { * the time scale is not 1, the delay may need to be adjusted. * * See AnimationState {@link AnimationState#timeScale} for affecting all animations. */ - timeScale: number = 0; + timeScale = 0; /** Values < 1 mix this animation with the skeleton's current pose (usually the pose resulting from lower tracks). Defaults * to 1, which overwrites the skeleton's current pose with this animation. * * Typically track 0 is used to completely pose the skeleton, then alpha is used on higher tracks. It doesn't make sense to * use alpha on track 0 if the skeleton pose is from the last frame render. */ - alpha: number = 0; + alpha = 0; /** Seconds from 0 to the {@link #getMixDuration()} when mixing from the previous animation to this animation. May be * slightly more than `mixDuration` when the mix is complete. */ - mixTime: number = 0; + mixTime = 0; /** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by AnimationStateData * {@link AnimationStateData#getMix()} based on the animation before this animation (if any). @@ -949,7 +1010,9 @@ export class TrackEntry implements ITrackEntry { * When using {@link AnimationState#addAnimation()} with a `delay` <= 0, note the * {@link #delay} is set using the mix duration from the {@link AnimationStateData}, not a mix duration set * afterward. */ - mixDuration: number = 0; interruptAlpha: number = 0; totalAlpha: number = 0; + mixDuration = 0; + interruptAlpha = 0; + totalAlpha = 0; /** Controls how properties keyed in the animation are mixed with lower tracks. Defaults to {@link MixBlend#replace}, which * replaces the values from the lower tracks with the animation values. {@link MixBlend#add} adds the animation values to @@ -962,7 +1025,7 @@ export class TrackEntry implements ITrackEntry { timelineHoldMix = new Array(); timelinesRotation = new Array(); - reset () { + reset() { this.next = null; this.previous = null; this.mixingFrom = null; @@ -977,16 +1040,19 @@ export class TrackEntry implements ITrackEntry { /** Uses {@link #trackTime} to compute the `animationTime`, which is between {@link #animationStart} * and {@link #animationEnd}. When the `trackTime` is 0, the `animationTime` is equal to the * `animationStart` time. */ - getAnimationTime () { + getAnimationTime() { if (this.loop) { - let duration = this.animationEnd - this.animationStart; + const duration = this.animationEnd - this.animationStart; + if (duration == 0) return this.animationStart; + return (this.trackTime % duration) + this.animationStart; } + return Math.min(this.trackTime + this.animationStart, this.animationEnd); } - setAnimationLast (animationLast: number) { + setAnimationLast(animationLast: number) { this.animationLast = animationLast; this.nextAnimationLast = animationLast; } @@ -994,7 +1060,7 @@ export class TrackEntry implements ITrackEntry { /** Returns true if at least one loop has been completed. * * See {@link AnimationStateListener#complete()}. */ - isComplete () { + isComplete() { return this.trackTime >= this.animationEnd - this.animationStart; } @@ -1005,20 +1071,22 @@ export class TrackEntry implements ITrackEntry { * the short way or the long way around. The two rotations likely change over time, so which direction is the short or long * way also changes. If the short way was always chosen, bones would flip to the other side when that direction became the * long way. TrackEntry chooses the short way the first time it is applied and remembers that direction. */ - resetRotationDirections () { + resetRotationDirections() { this.timelinesRotation.length = 0; } - getTrackComplete () { - let duration = this.animationEnd - this.animationStart; + getTrackComplete() { + const duration = this.animationEnd - this.animationStart; + if (duration != 0) { if (this.loop) return duration * (1 + ((this.trackTime / duration) | 0)); // Completion of next loop. if (this.trackTime < duration) return duration; // Before duration. } + return this.trackTime; // Next update. } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; @@ -1030,15 +1098,16 @@ export class TrackEntry implements ITrackEntry { get time() { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } + return this.trackTime; } set time(value: number) { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } this.trackTime = value; } @@ -1046,15 +1115,16 @@ export class TrackEntry implements ITrackEntry { get endTime() { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } + return this.trackTime; } set endTime(value: number) { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } this.trackTime = value; } @@ -1072,85 +1142,81 @@ export class EventQueue { drainDisabled = false; animState: AnimationState = null; - constructor (animState: AnimationState) { + constructor(animState: AnimationState) { this.animState = animState; } - start (entry: TrackEntry) { + start(entry: TrackEntry) { this.objects.push(EventType.start); this.objects.push(entry); this.animState.animationsChanged = true; } - interrupt (entry: TrackEntry) { + interrupt(entry: TrackEntry) { this.objects.push(EventType.interrupt); this.objects.push(entry); } - end (entry: TrackEntry) { + end(entry: TrackEntry) { this.objects.push(EventType.end); this.objects.push(entry); this.animState.animationsChanged = true; } - dispose (entry: TrackEntry) { + dispose(entry: TrackEntry) { this.objects.push(EventType.dispose); this.objects.push(entry); } - complete (entry: TrackEntry) { + complete(entry: TrackEntry) { this.objects.push(EventType.complete); this.objects.push(entry); } - event (entry: TrackEntry, event: Event) { + event(entry: TrackEntry, event: Event) { this.objects.push(EventType.event); this.objects.push(entry); this.objects.push(event); } - drain () { + drain() { if (this.drainDisabled) return; this.drainDisabled = true; - let objects = this.objects; - let listeners = this.animState.listeners; + const objects = this.objects; + const listeners = this.animState.listeners; for (let i = 0; i < objects.length; i += 2) { - let type = objects[i] as EventType; - let entry = objects[i + 1] as TrackEntry; + const type = objects[i] as EventType; + const entry = objects[i + 1] as TrackEntry; + switch (type) { case EventType.start: if (entry.listener && entry.listener.start) entry.listener.start(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].start) listeners[ii].start(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].start) listeners[ii].start(entry); break; case EventType.interrupt: if (entry.listener && entry.listener.interrupt) entry.listener.interrupt(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].interrupt) listeners[ii].interrupt(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].interrupt) listeners[ii].interrupt(entry); break; case EventType.end: if (entry.listener && entry.listener.end) entry.listener.end(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].end) listeners[ii].end(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].end) listeners[ii].end(entry); // Fall through. case EventType.dispose: if (entry.listener && entry.listener.dispose) entry.listener.dispose(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].dispose) listeners[ii].dispose(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].dispose) listeners[ii].dispose(entry); this.animState.trackEntryPool.free(entry); break; case EventType.complete: if (entry.listener && entry.listener.complete) entry.listener.complete(entry); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].complete) listeners[ii].complete(entry); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].complete) listeners[ii].complete(entry); break; case EventType.event: - let event = objects[i++ + 2] as Event; + const event = objects[i++ + 2] as Event; + if (entry.listener && entry.listener.event) entry.listener.event(entry, event); - for (let ii = 0; ii < listeners.length; ii++) - if (listeners[ii].event) listeners[ii].event(entry, event); + for (let ii = 0; ii < listeners.length; ii++) if (listeners[ii].event) listeners[ii].event(entry, event); break; } } @@ -1159,7 +1225,7 @@ export class EventQueue { this.drainDisabled = false; } - clear () { + clear() { this.objects.length = 0; } } @@ -1168,7 +1234,12 @@ export class EventQueue { * @public */ export enum EventType { - start, interrupt, end, dispose, complete, event + start, + interrupt, + end, + dispose, + complete, + event, } /** The interface to implement for receiving TrackEntry events. It is always safe to call AnimationState methods when receiving @@ -1204,23 +1275,17 @@ export interface AnimationStateListener extends IAnimationStateListener { * @public */ export abstract class AnimationStateAdapter implements AnimationStateListener { - start (entry: TrackEntry) { - } + start(entry: TrackEntry) {} - interrupt (entry: TrackEntry) { - } + interrupt(entry: TrackEntry) {} - end (entry: TrackEntry) { - } + end(entry: TrackEntry) {} - dispose (entry: TrackEntry) { - } + dispose(entry: TrackEntry) {} - complete (entry: TrackEntry) { - } + complete(entry: TrackEntry) {} - event (entry: TrackEntry, event: Event) { - } + event(entry: TrackEntry, event: Event) {} } /** 1. A previously applied timeline has set this property. diff --git a/packages/runtime-4.0/src/core/AnimationStateData.ts b/packages/runtime-4.0/src/core/AnimationStateData.ts index 6a5fd25d..6fb9601a 100644 --- a/packages/runtime-4.0/src/core/AnimationStateData.ts +++ b/packages/runtime-4.0/src/core/AnimationStateData.ts @@ -1,6 +1,6 @@ -import {SkeletonData} from "./SkeletonData"; -import {IAnimationStateData, Map} from '@pixi-spine/base'; -import type {Animation} from './Animation'; +import type { SkeletonData } from './SkeletonData'; +import type { IAnimationStateData, Map } from '@pixi-spine/base'; +import type { Animation } from './Animation'; /** Stores mix (crossfade) durations to be applied when {@link AnimationState} animations are changed. * @public @@ -9,42 +9,46 @@ export class AnimationStateData implements IAnimationStateData = { }; + animationToMixTime: Map = {}; /** The mix duration to use when no mix duration has been defined between two animations. */ defaultMix = 0; - constructor (skeletonData: SkeletonData) { - if (skeletonData == null) throw new Error("skeletonData cannot be null."); + constructor(skeletonData: SkeletonData) { + if (skeletonData == null) throw new Error('skeletonData cannot be null.'); this.skeletonData = skeletonData; } /** Sets a mix duration by animation name. * * See {@link #setMixWith()}. */ - setMix (fromName: string, toName: string, duration: number) { - let from = this.skeletonData.findAnimation(fromName); - if (from == null) throw new Error("Animation not found: " + fromName); - let to = this.skeletonData.findAnimation(toName); - if (to == null) throw new Error("Animation not found: " + toName); + setMix(fromName: string, toName: string, duration: number) { + const from = this.skeletonData.findAnimation(fromName); + + if (from == null) throw new Error(`Animation not found: ${fromName}`); + const to = this.skeletonData.findAnimation(toName); + + if (to == null) throw new Error(`Animation not found: ${toName}`); this.setMixWith(from, to, duration); } /** Sets the mix duration when changing from the specified animation to the other. * * See {@link TrackEntry#mixDuration}. */ - setMixWith (from: Animation, to: Animation, duration: number) { - if (from == null) throw new Error("from cannot be null."); - if (to == null) throw new Error("to cannot be null."); - let key = from.name + "." + to.name; + setMixWith(from: Animation, to: Animation, duration: number) { + if (from == null) throw new Error('from cannot be null.'); + if (to == null) throw new Error('to cannot be null.'); + const key = `${from.name}.${to.name}`; + this.animationToMixTime[key] = duration; } /** Returns the mix duration to use when changing from the specified animation to the other, or the {@link #defaultMix} if * no mix duration has been set. */ - getMix (from: Animation, to: Animation) { - let key = from.name + "." + to.name; - let value = this.animationToMixTime[key]; + getMix(from: Animation, to: Animation) { + const key = `${from.name}.${to.name}`; + const value = this.animationToMixTime[key]; + return value === undefined ? this.defaultMix : value; } } diff --git a/packages/runtime-4.0/src/core/AtlasAttachmentLoader.ts b/packages/runtime-4.0/src/core/AtlasAttachmentLoader.ts index 806d7525..b8f7a624 100644 --- a/packages/runtime-4.0/src/core/AtlasAttachmentLoader.ts +++ b/packages/runtime-4.0/src/core/AtlasAttachmentLoader.ts @@ -1,8 +1,6 @@ - -import {AttachmentLoader, RegionAttachment, MeshAttachment, BoundingBoxAttachment, - PathAttachment, PointAttachment, ClippingAttachment} from './attachments'; -import type {TextureAtlas} from "@pixi-spine/base"; -import type {Skin} from "./Skin"; +import { AttachmentLoader, RegionAttachment, MeshAttachment, BoundingBoxAttachment, PathAttachment, PointAttachment, ClippingAttachment } from './attachments'; +import type { TextureAtlas } from '@pixi-spine/base'; +import type { Skin } from './Skin'; /** * @public @@ -17,20 +15,26 @@ export class AtlasAttachmentLoader implements AttachmentLoader { /** @return May be null to not load an attachment. */ // @ts-ignore newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment { - let region = this.atlas.findRegion(path); - if (region == null) throw new Error("Region not found in atlas: " + path + " (region attachment: " + name + ")"); - let attachment = new RegionAttachment(name); + const region = this.atlas.findRegion(path); + + if (region == null) throw new Error(`Region not found in atlas: ${path} (region attachment: ${name})`); + const attachment = new RegionAttachment(name); + attachment.region = region; + return attachment; } /** @return May be null to not load an attachment. */ // @ts-ignore newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment { - let region = this.atlas.findRegion(path); - if (region == null) throw new Error("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); - let attachment = new MeshAttachment(name); + const region = this.atlas.findRegion(path); + + if (region == null) throw new Error(`Region not found in atlas: ${path} (mesh attachment: ${name})`); + const attachment = new MeshAttachment(name); + attachment.region = region; + return attachment; } diff --git a/packages/runtime-4.0/src/core/Bone.ts b/packages/runtime-4.0/src/core/Bone.ts index e97006d3..a0e60644 100644 --- a/packages/runtime-4.0/src/core/Bone.ts +++ b/packages/runtime-4.0/src/core/Bone.ts @@ -1,8 +1,8 @@ -import {Matrix} from '@pixi/math'; -import {Updatable} from "./Updatable"; -import {BoneData} from "./BoneData"; -import {Skeleton} from "./Skeleton"; -import {IBone, MathUtils, settings, TransformMode, Vector2} from "@pixi-spine/base"; +import { Matrix } from '@pixi/math'; +import type { Updatable } from './Updatable'; +import type { BoneData } from './BoneData'; +import type { Skeleton } from './Skeleton'; +import { IBone, MathUtils, settings, TransformMode, Vector2 } from '@pixi-spine/base'; /** Stores a bone's current pose. * @@ -12,7 +12,7 @@ import {IBone, MathUtils, settings, TransformMode, Vector2} from "@pixi-spine/ba * @public * */ export class Bone implements Updatable, IBone { - //be careful! Spine b,c is c,b in pixi matrix + // be careful! Spine b,c is c,b in pixi matrix matrix = new Matrix(); get worldX(): number { @@ -81,9 +81,9 @@ export class Bone implements Updatable, IBone { active = false; /** @param parent May be null. */ - constructor (data: BoneData, skeleton: Skeleton, parent: Bone) { - if (!data) throw new Error("data cannot be null."); - if (!skeleton) throw new Error("skeleton cannot be null."); + constructor(data: BoneData, skeleton: Skeleton, parent: Bone) { + if (!data) throw new Error('data cannot be null.'); + if (!skeleton) throw new Error('skeleton cannot be null.'); this.data = data; this.skeleton = skeleton; this.parent = parent; @@ -92,19 +92,19 @@ export class Bone implements Updatable, IBone { /** Returns false when the bone has not been computed because {@link BoneData#skinRequired} is true and the * {@link Skeleton#skin active skin} does not {@link Skin#bones contain} this bone. */ - isActive () { + isActive() { return this.active; } /** Computes the world transform using the parent bone and this bone's local applied transform. */ - update () { + update() { this.updateWorldTransformWith(this.ax, this.ay, this.arotation, this.ascaleX, this.ascaleY, this.ashearX, this.ashearY); } /** Computes the world transform using the parent bone and this bone's local transform. * * See {@link #updateWorldTransformWith()}. */ - updateWorldTransform () { + updateWorldTransform() { this.updateWorldTransformWith(this.x, this.y, this.rotation, this.scaleX, this.scaleY, this.shearX, this.shearY); } @@ -113,7 +113,7 @@ export class Bone implements Updatable, IBone { * * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine * Runtimes Guide. */ - updateWorldTransformWith (x: number, y: number, rotation: number, scaleX: number, scaleY: number, shearX: number, shearY: number) { + updateWorldTransformWith(x: number, y: number, rotation: number, scaleX: number, scaleY: number, shearX: number, shearY: number) { this.ax = x; this.ay = y; this.arotation = rotation; @@ -122,42 +122,53 @@ export class Bone implements Updatable, IBone { this.ashearX = shearX; this.ashearY = shearY; - let parent = this.parent; - let m = this.matrix; + const parent = this.parent; + const m = this.matrix; + + const sx = this.skeleton.scaleX; + const sy = settings.yDown ? -this.skeleton.scaleY : this.skeleton.scaleY; + + if (!parent) { + // Root bone. + const skeleton = this.skeleton; + const rotationY = rotation + 90 + shearY; - let sx = this.skeleton.scaleX; - let sy = settings.yDown? -this.skeleton.scaleY : this.skeleton.scaleY; - if (!parent) { // Root bone. - let skeleton = this.skeleton; - let rotationY = rotation + 90 + shearY; m.a = MathUtils.cosDeg(rotation + shearX) * scaleX * sx; m.c = MathUtils.cosDeg(rotationY) * scaleY * sx; m.b = MathUtils.sinDeg(rotation + shearX) * scaleX * sy; m.d = MathUtils.sinDeg(rotationY) * scaleY * sy; m.tx = x * sx + skeleton.x; m.ty = y * sy + skeleton.y; + return; } - let pa = parent.matrix.a, pb = parent.matrix.c, pc = parent.matrix.b, pd = parent.matrix.d; + let pa = parent.matrix.a; + let pb = parent.matrix.c; + let pc = parent.matrix.b; + let pd = parent.matrix.d; + m.tx = pa * x + pb * y + parent.matrix.tx; m.ty = pc * x + pd * y + parent.matrix.ty; switch (this.data.transformMode) { case TransformMode.Normal: { - let rotationY = rotation + 90 + shearY; - let la = MathUtils.cosDeg(rotation + shearX) * scaleX; - let lb = MathUtils.cosDeg(rotationY) * scaleY; - let lc = MathUtils.sinDeg(rotation + shearX) * scaleX; - let ld = MathUtils.sinDeg(rotationY) * scaleY; + const rotationY = rotation + 90 + shearY; + const la = MathUtils.cosDeg(rotation + shearX) * scaleX; + const lb = MathUtils.cosDeg(rotationY) * scaleY; + const lc = MathUtils.sinDeg(rotation + shearX) * scaleX; + const ld = MathUtils.sinDeg(rotationY) * scaleY; + m.a = pa * la + pb * lc; m.c = pa * lb + pb * ld; m.b = pc * la + pd * lc; m.d = pc * lb + pd * ld; + return; } case TransformMode.OnlyTranslation: { - let rotationY = rotation + 90 + shearY; + const rotationY = rotation + 90 + shearY; + m.a = MathUtils.cosDeg(rotation + shearX) * scaleX; m.c = MathUtils.cosDeg(rotationY) * scaleY; m.b = MathUtils.sinDeg(rotation + shearX) * scaleX; @@ -167,6 +178,7 @@ export class Bone implements Updatable, IBone { case TransformMode.NoRotationOrReflection: { let s = pa * pa + pc * pc; let prx = 0; + if (s > 0.0001) { s = Math.abs(pa * pd - pb * pc) / s; pa /= sx; @@ -179,12 +191,13 @@ export class Bone implements Updatable, IBone { pc = 0; prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg; } - let rx = rotation + shearX - prx; - let ry = rotation + shearY - prx + 90; - let la = MathUtils.cosDeg(rx) * scaleX; - let lb = MathUtils.cosDeg(ry) * scaleY; - let lc = MathUtils.sinDeg(rx) * scaleX; - let ld = MathUtils.sinDeg(ry) * scaleY; + const rx = rotation + shearX - prx; + const ry = rotation + shearY - prx + 90; + const la = MathUtils.cosDeg(rx) * scaleX; + const lb = MathUtils.cosDeg(ry) * scaleY; + const lc = MathUtils.sinDeg(rx) * scaleX; + const ld = MathUtils.sinDeg(ry) * scaleY; + m.a = pa * la - pb * lc; m.c = pa * lb - pb * ld; m.b = pc * la + pd * lc; @@ -193,24 +206,25 @@ export class Bone implements Updatable, IBone { } case TransformMode.NoScale: case TransformMode.NoScaleOrReflection: { - let cos = MathUtils.cosDeg(rotation); - let sin = MathUtils.sinDeg(rotation); + const cos = MathUtils.cosDeg(rotation); + const sin = MathUtils.sinDeg(rotation); let za = (pa * cos + pb * sin) / sx; let zc = (pc * cos + pd * sin) / sy; let s = Math.sqrt(za * za + zc * zc); + if (s > 0.00001) s = 1 / s; za *= s; zc *= s; s = Math.sqrt(za * za + zc * zc); - if (this.data.transformMode == TransformMode.NoScale - && (pa * pd - pb * pc < 0) != (sx < 0 != sy < 0)) s = -s; - let r = Math.PI / 2 + Math.atan2(zc, za); - let zb = Math.cos(r) * s; - let zd = Math.sin(r) * s; - let la = MathUtils.cosDeg(shearX) * scaleX; - let lb = MathUtils.cosDeg(90 + shearY) * scaleY; - let lc = MathUtils.sinDeg(shearX) * scaleX; - let ld = MathUtils.sinDeg(90 + shearY) * scaleY; + if (this.data.transformMode == TransformMode.NoScale && pa * pd - pb * pc < 0 != (sx < 0 != sy < 0)) s = -s; + const r = Math.PI / 2 + Math.atan2(zc, za); + const zb = Math.cos(r) * s; + const zd = Math.sin(r) * s; + const la = MathUtils.cosDeg(shearX) * scaleX; + const lb = MathUtils.cosDeg(90 + shearY) * scaleY; + const lc = MathUtils.sinDeg(shearX) * scaleX; + const ld = MathUtils.sinDeg(90 + shearY) * scaleY; + m.a = za * la + zb * lc; m.c = za * lb + zb * ld; m.b = zc * la + zd * lc; @@ -225,8 +239,9 @@ export class Bone implements Updatable, IBone { } /** Sets this bone's local transform to the setup pose. */ - setToSetupPose () { - let data = this.data; + setToSetupPose() { + const data = this.data; + this.x = data.x; this.y = data.y; this.rotation = data.rotation; @@ -237,24 +252,26 @@ export class Bone implements Updatable, IBone { } /** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */ - getWorldRotationX () { + getWorldRotationX() { return Math.atan2(this.matrix.b, this.matrix.a) * MathUtils.radDeg; } /** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */ - getWorldRotationY () { + getWorldRotationY() { return Math.atan2(this.matrix.d, this.matrix.c) * MathUtils.radDeg; } /** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */ - getWorldScaleX () { - let m = this.matrix; + getWorldScaleX() { + const m = this.matrix; + return Math.sqrt(m.a * m.a + m.b * m.b); } /** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */ - getWorldScaleY () { - let m = this.matrix; + getWorldScaleY() { + const m = this.matrix; + return Math.sqrt(m.c * m.c + m.d * m.d); } @@ -266,9 +283,10 @@ export class Bone implements Updatable, IBone { * * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after * calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */ - updateAppliedTransform () { - let parent = this.parent; - let m = this.matrix; + updateAppliedTransform() { + const parent = this.parent; + const m = this.matrix; + if (!parent) { this.ax = m.tx; this.ay = m.ty; @@ -277,25 +295,30 @@ export class Bone implements Updatable, IBone { this.ascaleY = Math.sqrt(m.c * m.c + m.d * m.d); this.ashearX = 0; this.ashearY = Math.atan2(m.a * m.c + m.b * m.d, m.a * m.d - m.b * m.c) * MathUtils.radDeg; + return; } - let pm = parent.matrix; - let pid = 1 / (pm.a * pm.d - pm.b * pm.c); - let dx = m.tx - pm.tx, dy = m.ty - pm.ty; - this.ax = (dx * pm.d * pid - dy * pm.c * pid); - this.ay = (dy * pm.a * pid - dx * pm.b * pid); - let ia = pid * pm.d; - let id = pid * pm.a; - let ib = pid * pm.c; - let ic = pid * pm.b; - let ra = ia * m.a - ib * m.b; - let rb = ia * m.c - ib * m.d; - let rc = id * m.b - ic * m.a; - let rd = id * m.d - ic * m.c; + const pm = parent.matrix; + const pid = 1 / (pm.a * pm.d - pm.b * pm.c); + const dx = m.tx - pm.tx; + const dy = m.ty - pm.ty; + + this.ax = dx * pm.d * pid - dy * pm.c * pid; + this.ay = dy * pm.a * pid - dx * pm.b * pid; + const ia = pid * pm.d; + const id = pid * pm.a; + const ib = pid * pm.c; + const ic = pid * pm.b; + const ra = ia * m.a - ib * m.b; + const rb = ia * m.c - ib * m.d; + const rc = id * m.b - ic * m.a; + const rd = id * m.d - ic * m.c; + this.ashearX = 0; this.ascaleX = Math.sqrt(ra * ra + rc * rc); if (this.ascaleX > 0.0001) { - let det = ra * rd - rb * rc; + const det = ra * rd - rb * rc; + this.ascaleY = det / this.ascaleX; this.ashearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg; this.arotation = Math.atan2(rc, ra) * MathUtils.radDeg; @@ -309,36 +332,49 @@ export class Bone implements Updatable, IBone { /** Transforms a point from world coordinates to the bone's local coordinates. */ worldToLocal(world: Vector2) { - let m = this.matrix; - let a = m.a, b = m.c, c = m.b, d = m.d; - let invDet = 1 / (a * d - b * c); - let x = world.x - m.tx, y = world.y - m.ty; - world.x = (x * d * invDet - y * b * invDet); - world.y = (y * a * invDet - x * c * invDet); + const m = this.matrix; + const a = m.a; + const b = m.c; + const c = m.b; + const d = m.d; + const invDet = 1 / (a * d - b * c); + const x = world.x - m.tx; + const y = world.y - m.ty; + + world.x = x * d * invDet - y * b * invDet; + world.y = y * a * invDet - x * c * invDet; + return world; } /** Transforms a point from the bone's local coordinates to world coordinates. */ localToWorld(local: Vector2) { - let m = this.matrix; - let x = local.x, y = local.y; + const m = this.matrix; + const x = local.x; + const y = local.y; + local.x = x * m.a + y * m.c + m.tx; local.y = x * m.b + y * m.d + m.ty; + return local; } /** Transforms a world rotation to a local rotation. */ - worldToLocalRotation (worldRotation: number) { - let sin = MathUtils.sinDeg(worldRotation), cos = MathUtils.cosDeg(worldRotation); - let mat = this.matrix; + worldToLocalRotation(worldRotation: number) { + const sin = MathUtils.sinDeg(worldRotation); + const cos = MathUtils.cosDeg(worldRotation); + const mat = this.matrix; + return Math.atan2(mat.a * sin - mat.b * cos, mat.d * cos - mat.c * sin) * MathUtils.radDeg; } /** Transforms a local rotation to a world rotation. */ - localToWorldRotation (localRotation: number) { + localToWorldRotation(localRotation: number) { localRotation -= this.rotation - this.shearX; - let sin = MathUtils.sinDeg(localRotation), cos = MathUtils.cosDeg(localRotation); - let mat = this.matrix; + const sin = MathUtils.sinDeg(localRotation); + const cos = MathUtils.cosDeg(localRotation); + const mat = this.matrix; + return Math.atan2(cos * mat.b + sin * mat.d, cos * mat.a + sin * mat.c) * MathUtils.radDeg; } @@ -346,10 +382,15 @@ export class Bone implements Updatable, IBone { *

* After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and {@link #update()} will * need to be called on any child bones, recursively. */ - rotateWorld (degrees: number) { - let mat = this.matrix; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees); + rotateWorld(degrees: number) { + const mat = this.matrix; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + const cos = MathUtils.cosDeg(degrees); + const sin = MathUtils.sinDeg(degrees); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; diff --git a/packages/runtime-4.0/src/core/BoneData.ts b/packages/runtime-4.0/src/core/BoneData.ts index eaf9fbd3..a7843992 100644 --- a/packages/runtime-4.0/src/core/BoneData.ts +++ b/packages/runtime-4.0/src/core/BoneData.ts @@ -1,4 +1,4 @@ -import {Color, TransformMode} from '@pixi-spine/base'; +import { Color, TransformMode } from '@pixi-spine/base'; /** Stores the setup pose for a {@link Bone}. * @public @@ -49,9 +49,9 @@ export class BoneData { * rendered at runtime. */ color = new Color(); - constructor (index: number, name: string, parent: BoneData) { - if (index < 0) throw new Error("index must be >= 0."); - if (name == null) throw new Error("name cannot be null."); + constructor(index: number, name: string, parent: BoneData) { + if (index < 0) throw new Error('index must be >= 0.'); + if (name == null) throw new Error('name cannot be null.'); this.index = index; this.name = name; this.parent = parent; diff --git a/packages/runtime-4.0/src/core/ConstraintData.ts b/packages/runtime-4.0/src/core/ConstraintData.ts index 40fe5bdd..172701cc 100644 --- a/packages/runtime-4.0/src/core/ConstraintData.ts +++ b/packages/runtime-4.0/src/core/ConstraintData.ts @@ -2,5 +2,5 @@ * @public * */ export abstract class ConstraintData { - constructor(public name: string, public order: number, public skinRequired: boolean) { } + constructor(public name: string, public order: number, public skinRequired: boolean) {} } diff --git a/packages/runtime-4.0/src/core/Event.ts b/packages/runtime-4.0/src/core/Event.ts index 585b39bd..59941ebe 100644 --- a/packages/runtime-4.0/src/core/Event.ts +++ b/packages/runtime-4.0/src/core/Event.ts @@ -1,5 +1,5 @@ -import {EventData} from "./EventData"; -import {IEvent} from "@pixi-spine/base"; +import type { EventData } from './EventData'; +import type { IEvent } from '@pixi-spine/base'; /** Stores the current pose values for an {@link Event}. * @@ -17,8 +17,8 @@ export class Event implements IEvent { volume: number; balance: number; - constructor (time: number, data: EventData) { - if (data == null) throw new Error("data cannot be null."); + constructor(time: number, data: EventData) { + if (data == null) throw new Error('data cannot be null.'); this.time = time; this.data = data; } diff --git a/packages/runtime-4.0/src/core/EventData.ts b/packages/runtime-4.0/src/core/EventData.ts index 574cc4fc..19dd9053 100644 --- a/packages/runtime-4.0/src/core/EventData.ts +++ b/packages/runtime-4.0/src/core/EventData.ts @@ -1,4 +1,4 @@ -import {IEventData} from "@pixi-spine/base"; +import type { IEventData } from '@pixi-spine/base'; /** Stores the setup pose values for an {@link Event}. * @@ -14,7 +14,7 @@ export class EventData implements IEventData { volume: number; balance: number; - constructor (name: string) { + constructor(name: string) { this.name = name; } } diff --git a/packages/runtime-4.0/src/core/IkConstraint.ts b/packages/runtime-4.0/src/core/IkConstraint.ts index d4c4d714..1643d25c 100644 --- a/packages/runtime-4.0/src/core/IkConstraint.ts +++ b/packages/runtime-4.0/src/core/IkConstraint.ts @@ -1,8 +1,8 @@ -import {Updatable} from "./Updatable"; -import {IkConstraintData} from "./IkConstraintData"; -import {Bone} from "./Bone"; -import {Skeleton} from "./Skeleton"; -import {MathUtils, settings, TransformMode} from "@pixi-spine/base"; +import type { Updatable } from './Updatable'; +import type { IkConstraintData } from './IkConstraintData'; +import type { Bone } from './Bone'; +import type { Skeleton } from './Skeleton'; +import { MathUtils, settings, TransformMode } from '@pixi-spine/base'; /** Stores the current pose for an IK constraint. An IK constraint adjusts the rotation of 1 or 2 constrained bones so the tip of * the last bone is as close to the target bone as possible. @@ -37,9 +37,9 @@ export class IkConstraint implements Updatable { softness = 0; active = false; - constructor (data: IkConstraintData, skeleton: Skeleton) { - if (!data) throw new Error("data cannot be null."); - if (!skeleton) throw new Error("skeleton cannot be null."); + constructor(data: IkConstraintData, skeleton: Skeleton) { + if (!data) throw new Error('data cannot be null.'); + if (!skeleton) throw new Error('skeleton cannot be null.'); this.data = data; this.mix = data.mix; this.softness = data.softness; @@ -48,19 +48,19 @@ export class IkConstraint implements Updatable { this.stretch = data.stretch; this.bones = new Array(); - for (let i = 0; i < data.bones.length; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0; i < data.bones.length; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findBone(data.target.name); } - isActive () { + isActive() { return this.active; } - update () { + update() { if (this.mix == 0) return; - let target = this.target; - let bones = this.bones; + const target = this.target; + const bones = this.bones; + switch (bones.length) { case 1: this.apply1(bones[0], target.worldX, target.worldY, this.compress, this.stretch, this.data.uniform, this.mix); @@ -72,45 +72,53 @@ export class IkConstraint implements Updatable { } /** Applies 1 bone IK. The target is specified in the world coordinate system. */ - apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { - let p = bone.parent.matrix; + apply1(bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { + const p = bone.parent.matrix; - let pa = p.a, pb = p.c, pc = p.b, pd = p.d; - let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0; + const pa = p.a; + let pb = p.c; + const pc = p.b; + let pd = p.d; + let rotationIK = -bone.ashearX - bone.arotation; + let tx = 0; + let ty = 0; - let skelX = bone.skeleton.scaleX; - let skelY = settings.yDown? -bone.skeleton.scaleY : bone.skeleton.scaleY; + const skelX = bone.skeleton.scaleX; + const skelY = settings.yDown ? -bone.skeleton.scaleY : bone.skeleton.scaleY; - switch(bone.data.transformMode) { + switch (bone.data.transformMode) { case TransformMode.OnlyTranslation: tx = targetX - bone.worldX; ty = targetY - bone.worldY; - //TODO: possible bug in spine-ts runtime! + // TODO: possible bug in spine-ts runtime! if (settings.yDown) { ty = -ty; } break; case TransformMode.NoRotationOrReflection: - let s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); - let sa = pa / skelX; - let sc = pc / skelY; + const s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); + const sa = pa / skelX; + const sc = pc / skelY; + pb = -sc * s * skelX; pd = sa * s * skelY; rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg; // Fall through default: - let x = targetX - p.tx, y = targetY - p.ty; - let d = pa * pd - pb * pc; + const x = targetX - p.tx; + const y = targetY - p.ty; + const d = pa * pd - pb * pc; + tx = (x * pd - y * pb) / d - bone.ax; ty = (y * pa - x * pc) / d - bone.ay; } rotationIK += Math.atan2(ty, tx) * MathUtils.radDeg; if (bone.ascaleX < 0) rotationIK += 180; - if (rotationIK > 180) - rotationIK -= 360; - else if (rotationIK < -180) - rotationIK += 360; - let sx = bone.ascaleX, sy = bone.ascaleY; + if (rotationIK > 180) rotationIK -= 360; + else if (rotationIK < -180) rotationIK += 360; + let sx = bone.ascaleX; + let sy = bone.ascaleY; + if (compress || stretch) { switch (bone.data.transformMode) { case TransformMode.NoScale: @@ -118,23 +126,34 @@ export class IkConstraint implements Updatable { tx = targetX - bone.worldX; ty = targetY - bone.worldY; } - let b = bone.data.length * sx, dd = Math.sqrt(tx * tx + ty * ty); - if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) { - let s = (dd / b - 1) * alpha + 1; + const b = bone.data.length * sx; + const dd = Math.sqrt(tx * tx + ty * ty); + + if ((compress && dd < b) || (stretch && dd > b && b > 0.0001)) { + const s = (dd / b - 1) * alpha + 1; + sx *= s; if (uniform) sy *= s; } } - bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, - bone.ashearY); + bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY); } /** Applies 2 bone IK. The target is specified in the world coordinate system. * @param child A direct descendant of the parent bone. */ - apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, uniform: boolean, softness: number, alpha: number) { - let px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX; - let pmat = parent.matrix; - let os1 = 0, os2 = 0, s2 = 0; + apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, uniform: boolean, softness: number, alpha: number) { + const px = parent.ax; + const py = parent.ay; + let psx = parent.ascaleX; + let psy = parent.ascaleY; + let sx = psx; + let sy = psy; + let csx = child.ascaleX; + const pmat = parent.matrix; + let os1 = 0; + let os2 = 0; + let s2 = 0; + if (psx < 0) { psx = -psx; os1 = 180; @@ -150,10 +169,17 @@ export class IkConstraint implements Updatable { if (csx < 0) { csx = -csx; os2 = 180; - } else - os2 = 0; - let cx = child.ax, cy = 0, cwx = 0, cwy = 0, a = pmat.a, b = pmat.c, c = pmat.b, d = pmat.d; - let u = Math.abs(psx - psy) <= 0.0001; + } else os2 = 0; + const cx = child.ax; + let cy = 0; + let cwx = 0; + let cwy = 0; + let a = pmat.a; + let b = pmat.c; + let c = pmat.b; + let d = pmat.d; + const u = Math.abs(psx - psy) <= 0.0001; + if (!u || stretch) { cy = 0; cwx = a * cx + pmat.tx; @@ -163,116 +189,147 @@ export class IkConstraint implements Updatable { cwx = a * cx + b * cy + pmat.tx; cwy = c * cx + d * cy + pmat.ty; } - let pp = parent.parent.matrix; + const pp = parent.parent.matrix; + a = pp.a; b = pp.c; c = pp.b; d = pp.d; - let id = 1 / (a * d - b * c), x = cwx - pp.tx, y = cwy - pp.ty; - let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; - let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2; + const id = 1 / (a * d - b * c); + let x = cwx - pp.tx; + let y = cwy - pp.ty; + const dx = (x * d - y * b) * id - px; + const dy = (y * a - x * c) * id - py; + const l1 = Math.sqrt(dx * dx + dy * dy); + let l2 = child.data.length * csx; + let a1; + let a2; + if (l1 < 0.0001) { this.apply1(parent, targetX, targetY, false, stretch, false, alpha); child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); + return; } x = targetX - pp.tx; y = targetY - pp.ty; - let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py; + let tx = (x * d - y * b) * id - px; + let ty = (y * a - x * c) * id - py; let dd = tx * tx + ty * ty; + if (softness != 0) { softness *= psx * (csx + 1) * 0.5; - let td = Math.sqrt(dd), sd = td - l1 - l2 * psx + softness; + const td = Math.sqrt(dd); + const sd = td - l1 - l2 * psx + softness; + if (sd > 0) { let p = Math.min(1, sd / (softness * 2)) - 1; + p = (sd - softness * (1 - p * p)) / td; tx -= p * tx; ty -= p * ty; dd = tx * tx + ty * ty; } } - outer: - if (u) { - l2 *= psx; - let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); - if (cos < -1) { - cos = -1; - a2 = Math.PI * bendDir; - } else if (cos > 1) { - cos = 1; - a2 = 0; - if (stretch) { - a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; - sx *= a; - if (uniform) sy *= a; - } - } else - a2 = Math.acos(cos) * bendDir; - a = l1 + l2 * cos; - b = l2 * Math.sin(a2); - a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); - } else { - a = psx * l2; - b = psy * l2; - let aa = a * a, bb = b * b, ta = Math.atan2(ty, tx); - c = bb * l1 * l1 + aa * dd - aa * bb; - let c1 = -2 * bb * l1, c2 = bb - aa; - d = c1 * c1 - 4 * c2 * c; - if (d >= 0) { - let q = Math.sqrt(d); - if (c1 < 0) q = -q; - q = -(c1 + q) * 0.5; - let r0 = q / c2, r1 = c / q; - let r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; - if (r * r <= dd) { - y = Math.sqrt(dd - r * r) * bendDir; - a1 = ta - Math.atan2(y, r); - a2 = Math.atan2(y / psy, (r - l1) / psx); - break outer; - } + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: if (u) { + l2 *= psx; + let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); + + if (cos < -1) { + cos = -1; + a2 = Math.PI * bendDir; + } else if (cos > 1) { + cos = 1; + a2 = 0; + if (stretch) { + a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; + sx *= a; + if (uniform) sy *= a; + } + } else a2 = Math.acos(cos) * bendDir; + a = l1 + l2 * cos; + b = l2 * Math.sin(a2); + a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); + } else { + a = psx * l2; + b = psy * l2; + const aa = a * a; + const bb = b * b; + const ta = Math.atan2(ty, tx); + + c = bb * l1 * l1 + aa * dd - aa * bb; + const c1 = -2 * bb * l1; + const c2 = bb - aa; + + d = c1 * c1 - 4 * c2 * c; + if (d >= 0) { + let q = Math.sqrt(d); + + if (c1 < 0) q = -q; + q = -(c1 + q) * 0.5; + const r0 = q / c2; + const r1 = c / q; + const r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; + + if (r * r <= dd) { + y = Math.sqrt(dd - r * r) * bendDir; + a1 = ta - Math.atan2(y, r); + a2 = Math.atan2(y / psy, (r - l1) / psx); + // eslint-disable-next-line no-labels + break outer; } - let minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0; - let maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; - c = -a * l1 / (aa - bb); - if (c >= -1 && c <= 1) { - c = Math.acos(c); - x = a * Math.cos(c) + l1; - y = b * Math.sin(c); - d = x * x + y * y; - if (d < minDist) { - minAngle = c; - minDist = d; - minX = x; - minY = y; - } - if (d > maxDist) { - maxAngle = c; - maxDist = d; - maxX = x; - maxY = y; - } + } + let minAngle = MathUtils.PI; + let minX = l1 - a; + let minDist = minX * minX; + let minY = 0; + let maxAngle = 0; + let maxX = l1 + a; + let maxDist = maxX * maxX; + let maxY = 0; + + c = (-a * l1) / (aa - bb); + if (c >= -1 && c <= 1) { + c = Math.acos(c); + x = a * Math.cos(c) + l1; + y = b * Math.sin(c); + d = x * x + y * y; + if (d < minDist) { + minAngle = c; + minDist = d; + minX = x; + minY = y; } - if (dd <= (minDist + maxDist) * 0.5) { - a1 = ta - Math.atan2(minY * bendDir, minX); - a2 = minAngle * bendDir; - } else { - a1 = ta - Math.atan2(maxY * bendDir, maxX); - a2 = maxAngle * bendDir; + if (d > maxDist) { + maxAngle = c; + maxDist = d; + maxX = x; + maxY = y; } } - let os = Math.atan2(cy, cx) * s2; + if (dd <= (minDist + maxDist) * 0.5) { + a1 = ta - Math.atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } else { + a1 = ta - Math.atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + const os = Math.atan2(cy, cx) * s2; let rotation = parent.arotation; + a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation; - if (a1 > 180) - a1 -= 360; - else if (a1 < -180) // + if (a1 > 180) a1 -= 360; + else if (a1 < -180) + // a1 += 360; parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, sy, 0, 0); rotation = child.arotation; a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation; - if (a2 > 180) - a2 -= 360; - else if (a2 < -180) // + if (a2 > 180) a2 -= 360; + else if (a2 < -180) + // a2 += 360; child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); } diff --git a/packages/runtime-4.0/src/core/IkConstraintData.ts b/packages/runtime-4.0/src/core/IkConstraintData.ts index dc2f6393..3f2aaf12 100644 --- a/packages/runtime-4.0/src/core/IkConstraintData.ts +++ b/packages/runtime-4.0/src/core/IkConstraintData.ts @@ -1,5 +1,5 @@ -import {ConstraintData} from "./ConstraintData"; -import {BoneData} from "./BoneData"; +import { ConstraintData } from './ConstraintData'; +import type { BoneData } from './BoneData'; /** Stores the setup pose for an {@link IkConstraint}. *

@@ -33,7 +33,7 @@ export class IkConstraintData extends ConstraintData { /** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */ softness = 0; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } diff --git a/packages/runtime-4.0/src/core/PathConstraint.ts b/packages/runtime-4.0/src/core/PathConstraint.ts index 11a05712..0ab56c88 100644 --- a/packages/runtime-4.0/src/core/PathConstraint.ts +++ b/packages/runtime-4.0/src/core/PathConstraint.ts @@ -1,10 +1,10 @@ -import {PathAttachment} from "./attachments"; -import {Updatable} from "./Updatable"; -import {PathConstraintData, SpacingMode} from "./PathConstraintData"; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Skeleton} from "./Skeleton"; -import {MathUtils, PositionMode, RotateMode, Utils} from "@pixi-spine/base"; +import { PathAttachment } from './attachments'; +import type { Updatable } from './Updatable'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import type { Bone } from './Bone'; +import type { Slot } from './Slot'; +import type { Skeleton } from './Skeleton'; +import { MathUtils, PositionMode, RotateMode, Utils } from '@pixi-spine/base'; /** Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the * constrained bones so they follow a {@link PathAttachment}. * @@ -48,12 +48,11 @@ export class PathConstraint implements Updatable { active = false; constructor(data: PathConstraintData, skeleton: Skeleton) { - if (!data) throw new Error("data cannot be null."); - if (!skeleton) throw new Error("skeleton cannot be null."); + if (!data) throw new Error('data cannot be null.'); + if (!skeleton) throw new Error('skeleton cannot be null.'); this.data = data; this.bones = new Array(); - for (let i = 0, n = data.bones.length; i < n; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0, n = data.bones.length; i < n; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findSlot(data.target.name); this.position = data.position; this.spacing = data.spacing; @@ -67,31 +66,39 @@ export class PathConstraint implements Updatable { } update() { - let attachment = this.target.getAttachment(); + const attachment = this.target.getAttachment(); + if (!(attachment instanceof PathAttachment)) return; - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY; + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + if (mixRotate == 0 && mixX == 0 && mixY == 0) return; - let data = this.data; - let tangents = data.rotateMode == RotateMode.Tangent, scale = data.rotateMode == RotateMode.ChainScale; + const data = this.data; + const tangents = data.rotateMode == RotateMode.Tangent; + const scale = data.rotateMode == RotateMode.ChainScale; - let bones = this.bones; - let boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1; - let spaces = Utils.setArraySize(this.spaces, spacesCount), - lengths: Array = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : null; - let spacing = this.spacing; + const bones = this.bones; + const boneCount = bones.length; + const spacesCount = tangents ? boneCount : boneCount + 1; + const spaces = Utils.setArraySize(this.spaces, spacesCount); + const lengths: Array = scale ? (this.lengths = Utils.setArraySize(this.lengths, boneCount)) : null; + const spacing = this.spacing; switch (data.spacingMode) { case SpacingMode.Percent: if (scale) { for (let i = 0, n = spacesCount - 1; i < n; i++) { - let bone = bones[i]; - let setupLength = bone.data.length; - if (setupLength < PathConstraint.epsilon) - lengths[i] = 0; + const bone = bones[i]; + const setupLength = bone.data.length; + + if (setupLength < PathConstraint.epsilon) lengths[i] = 0; else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + lengths[i] = Math.sqrt(x * x + y * y); } } @@ -100,63 +107,80 @@ export class PathConstraint implements Updatable { break; case SpacingMode.Proportional: let sum = 0; - for (let i = 0, n = spacesCount - 1; i < n;) { - let bone = bones[i]; - let setupLength = bone.data.length; + + for (let i = 0, n = spacesCount - 1; i < n; ) { + const bone = bones[i]; + const setupLength = bone.data.length; + if (setupLength < PathConstraint.epsilon) { if (scale) lengths[i] = 0; spaces[++i] = spacing; } else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; - let length = Math.sqrt(x * x + y * y); + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + const length = Math.sqrt(x * x + y * y); + if (scale) lengths[i] = length; spaces[++i] = length; sum += length; } } if (sum > 0) { - sum = spacesCount / sum * spacing; - for (let i = 1; i < spacesCount; i++) - spaces[i] *= sum; + sum = (spacesCount / sum) * spacing; + for (let i = 1; i < spacesCount; i++) spaces[i] *= sum; } break; default: - let lengthSpacing = data.spacingMode == SpacingMode.Length; - for (let i = 0, n = spacesCount - 1; i < n;) { - let bone = bones[i]; - let setupLength = bone.data.length; + const lengthSpacing = data.spacingMode == SpacingMode.Length; + + for (let i = 0, n = spacesCount - 1; i < n; ) { + const bone = bones[i]; + const setupLength = bone.data.length; + if (setupLength < PathConstraint.epsilon) { if (scale) lengths[i] = 0; spaces[++i] = spacing; } else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; - let length = Math.sqrt(x * x + y * y); + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + const length = Math.sqrt(x * x + y * y); + if (scale) lengths[i] = length; - spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + spaces[++i] = ((lengthSpacing ? setupLength + spacing : spacing) * length) / setupLength; } } } - let positions = this.computeWorldPositions(attachment, spacesCount, tangents); - let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; + const positions = this.computeWorldPositions(attachment, spacesCount, tangents); + let boneX = positions[0]; + let boneY = positions[1]; + let offsetRotation = data.offsetRotation; let tip = false; - if (offsetRotation == 0) - tip = data.rotateMode == RotateMode.Chain; + + if (offsetRotation == 0) tip = data.rotateMode == RotateMode.Chain; else { tip = false; - let p = this.target.bone.matrix; + const p = this.target.bone.matrix; + offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.degRad : -MathUtils.degRad; } for (let i = 0, p = 3; i < boneCount; i++, p += 3) { - let bone = bones[i]; - let mat = bone.matrix; + const bone = bones[i]; + const mat = bone.matrix; + mat.tx += (boneX - mat.tx) * mixX; mat.ty += (boneY - mat.ty) * mixY; - let x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; + const x = positions[p]; + const y = positions[p + 1]; + const dx = x - boneX; + const dy = y - boneY; + if (scale) { - let length = lengths[i]; + const length = lengths[i]; + if (length != 0) { - let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1; + const s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1; + mat.a *= s; mat.b *= s; } @@ -164,26 +188,32 @@ export class PathConstraint implements Updatable { boneX = x; boneY = y; if (mixRotate > 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d, r = 0, cos = 0, sin = 0; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let r = 0; + let cos = 0; + let sin = 0; + if (tangents) - r = positions[p - 1]; - else if (spaces[i + 1] == 0) - r = positions[p + 2]; - else - r = Math.atan2(dy, dx); + if (tangents) r = positions[p - 1]; + else if (spaces[i + 1] == 0) r = positions[p + 2]; + else r = Math.atan2(dy, dx); r -= Math.atan2(c, a); if (tip) { cos = Math.cos(r); sin = Math.sin(r); - let length = bone.data.length; + const length = bone.data.length; + boneX += (length * (cos * a - sin * c) - dx) * mixRotate; boneY += (length * (sin * a + cos * c) - dy) * mixRotate; } else { r += offsetRotation; } - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= mixRotate; cos = Math.cos(r); @@ -198,20 +228,26 @@ export class PathConstraint implements Updatable { } computeWorldPositions(path: PathAttachment, spacesCount: number, tangents: boolean) { - let target = this.target; + const target = this.target; let position = this.position; - let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), - world: Array = null; - let closed = path.closed; - let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE; + const spaces = this.spaces; + const out = Utils.setArraySize(this.positions, spacesCount * 3 + 2); + let world: Array = null; + const closed = path.closed; + let verticesLength = path.worldVerticesLength; + let curveCount = verticesLength / 6; + let prevCurve = PathConstraint.NONE; if (!path.constantSpeed) { - let lengths = path.lengths; + const lengths = path.lengths; + curveCount -= closed ? 1 : 2; - let pathLength = lengths[curveCount]; + const pathLength = lengths[curveCount]; + if (this.data.positionMode == PositionMode.Percent) position *= pathLength; let multiplier; + switch (this.data.spacingMode) { case SpacingMode.Percent: multiplier = pathLength; @@ -224,7 +260,8 @@ export class PathConstraint implements Updatable { } world = Utils.setArraySize(this.world, 8); for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i] * multiplier; + const space = spaces[i] * multiplier; + position += space; let p = position; @@ -250,12 +287,13 @@ export class PathConstraint implements Updatable { // Determine curve containing position. for (; ; curve++) { - let length = lengths[curve]; + const length = lengths[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = lengths[curve - 1]; + const prev = lengths[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -265,12 +303,11 @@ export class PathConstraint implements Updatable { if (closed && curve == curveCount) { path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); path.computeWorldVertices(target, 0, 4, world, 4, 2); - } else - path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); + } else path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); } - this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, - tangents || (i > 0 && space == 0)); + this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0)); } + return out; } @@ -290,10 +327,25 @@ export class PathConstraint implements Updatable { } // Curve lengths. - let curves = Utils.setArraySize(this.curves, curveCount); + const curves = Utils.setArraySize(this.curves, curveCount); let pathLength = 0; - let x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; - let tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0; + let x1 = world[0]; + let y1 = world[1]; + let cx1 = 0; + let cy1 = 0; + let cx2 = 0; + let cy2 = 0; + let x2 = 0; + let y2 = 0; + let tmpx = 0; + let tmpy = 0; + let dddfx = 0; + let dddfy = 0; + let ddfx = 0; + let ddfy = 0; + let dfx = 0; + let dfy = 0; + for (let i = 0, w = 2; i < curveCount; i++, w += 6) { cx1 = world[w]; cy1 = world[w + 1]; @@ -329,6 +381,7 @@ export class PathConstraint implements Updatable { if (this.data.positionMode == PositionMode.Percent) position *= pathLength; let multiplier; + switch (this.data.spacingMode) { case SpacingMode.Percent: multiplier = pathLength; @@ -340,10 +393,12 @@ export class PathConstraint implements Updatable { multiplier = 1; } - let segments = this.segments; + const segments = this.segments; let curveLength = 0; + for (let i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i] * multiplier; + const space = spaces[i] * multiplier; + position += space; let p = position; @@ -361,12 +416,13 @@ export class PathConstraint implements Updatable { // Determine curve containing position. for (; ; curve++) { - let length = curves[curve]; + const length = curves[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = curves[curve - 1]; + const prev = curves[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -376,6 +432,7 @@ export class PathConstraint implements Updatable { if (curve != prevCurve) { prevCurve = curve; let ii = curve * 6; + x1 = world[ii]; y1 = world[ii + 1]; cx1 = world[ii + 2]; @@ -416,53 +473,85 @@ export class PathConstraint implements Updatable { // Weight by segment length. p *= curveLength; for (; ; segment++) { - let length = segments[segment]; + const length = segments[segment]; + if (p > length) continue; - if (segment == 0) - p /= length; + if (segment == 0) p /= length; else { - let prev = segments[segment - 1]; + const prev = segments[segment - 1]; + p = segment + (p - prev) / (length - prev); } break; } this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0)); } + return out; } addBeforePosition(p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx); + const x1 = temp[i]; + const y1 = temp[i + 1]; + const dx = temp[i + 2] - x1; + const dy = temp[i + 3] - y1; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } addAfterPosition(p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx); + const x1 = temp[i + 2]; + const y1 = temp[i + 3]; + const dx = x1 - temp[i]; + const dy = y1 - temp[i + 1]; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } - addCurvePosition(p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, - out: Array, o: number, tangents: boolean) { + addCurvePosition( + p: number, + x1: number, + y1: number, + cx1: number, + cy1: number, + cx2: number, + cy2: number, + x2: number, + y2: number, + out: Array, + o: number, + tangents: boolean + ) { if (p == 0 || isNaN(p)) { out[o] = x1; out[o + 1] = y1; out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1); + return; } - let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; - let ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; - let x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + const tt = p * p; + const ttt = tt * p; + const u = 1 - p; + const uu = u * u; + const uuu = uu * u; + const ut = u * p; + const ut3 = ut * 3; + const uut3 = u * ut3; + const utt3 = ut3 * p; + const x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt; + const y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + out[o] = x; out[o + 1] = y; if (tangents) { - if (p < 0.001) - out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1); - else - out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + if (p < 0.001) out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1); + else out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); } } } diff --git a/packages/runtime-4.0/src/core/PathConstraintData.ts b/packages/runtime-4.0/src/core/PathConstraintData.ts index 00e282ee..5f453619 100644 --- a/packages/runtime-4.0/src/core/PathConstraintData.ts +++ b/packages/runtime-4.0/src/core/PathConstraintData.ts @@ -1,7 +1,7 @@ -import {ConstraintData} from "./ConstraintData"; -import type {SlotData} from "./SlotData"; -import type {BoneData} from "./BoneData"; -import { PositionMode, RotateMode } from "@pixi-spine/base"; +import { ConstraintData } from './ConstraintData'; +import type { SlotData } from './SlotData'; +import type { BoneData } from './BoneData'; +import type { PositionMode, RotateMode } from '@pixi-spine/base'; /** Stores the setup pose for a {@link PathConstraint}. * @@ -9,7 +9,6 @@ import { PositionMode, RotateMode } from "@pixi-spine/base"; * @public * */ export class PathConstraintData extends ConstraintData { - /** The bones that will be modified by this path constraint. */ bones = new Array(); @@ -38,7 +37,7 @@ export class PathConstraintData extends ConstraintData { mixX = 0; mixY = 0; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } @@ -49,5 +48,8 @@ export class PathConstraintData extends ConstraintData { * @public * */ export enum SpacingMode { - Length, Fixed, Percent, Proportional + Length, + Fixed, + Percent, + Proportional, } diff --git a/packages/runtime-4.0/src/core/Skeleton.ts b/packages/runtime-4.0/src/core/Skeleton.ts index d6983edf..cb3bcdce 100644 --- a/packages/runtime-4.0/src/core/Skeleton.ts +++ b/packages/runtime-4.0/src/core/Skeleton.ts @@ -1,13 +1,13 @@ -import {Attachment, RegionAttachment, MeshAttachment, PathAttachment} from './attachments'; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Updatable} from "./Updatable"; -import {SkeletonData} from "./SkeletonData"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; -import {Skin} from "./Skin"; -import {Color, MathUtils, NumberArrayLike, settings, Utils, Vector2, ISkeleton} from "@pixi-spine/base"; +import { Attachment, RegionAttachment, MeshAttachment, PathAttachment } from './attachments'; +import { Bone } from './Bone'; +import { Slot } from './Slot'; +import type { Updatable } from './Updatable'; +import type { SkeletonData } from './SkeletonData'; +import { IkConstraint } from './IkConstraint'; +import { TransformConstraint } from './TransformConstraint'; +import { PathConstraint } from './PathConstraint'; +import type { Skin } from './Skin'; +import { Color, MathUtils, NumberArrayLike, settings, Utils, Vector2, ISkeleton } from '@pixi-spine/base'; /** Stores the current pose for a skeleton. * @@ -64,18 +64,19 @@ export class Skeleton implements ISkeleton { /** Sets the skeleton Y position, which is added to the root bone worldY position. */ y = 0; - constructor (data: SkeletonData) { - if (!data) throw new Error("data cannot be null."); + constructor(data: SkeletonData) { + if (!data) throw new Error('data cannot be null.'); this.data = data; this.bones = new Array(); for (let i = 0; i < data.bones.length; i++) { - let boneData = data.bones[i]; + const boneData = data.bones[i]; let bone: Bone; - if (!boneData.parent) - bone = new Bone(boneData, this, null); + + if (!boneData.parent) bone = new Bone(boneData, this, null); else { - let parent = this.bones[boneData.parent.index]; + const parent = this.bones[boneData.parent.index]; + bone = new Bone(boneData, this, parent); parent.children.push(bone); } @@ -85,28 +86,32 @@ export class Skeleton implements ISkeleton { this.slots = new Array(); this.drawOrder = new Array(); for (let i = 0; i < data.slots.length; i++) { - let slotData = data.slots[i]; - let bone = this.bones[slotData.boneData.index]; - let slot = new Slot(slotData, bone); + const slotData = data.slots[i]; + const bone = this.bones[slotData.boneData.index]; + const slot = new Slot(slotData, bone); + this.slots.push(slot); this.drawOrder.push(slot); } this.ikConstraints = new Array(); for (let i = 0; i < data.ikConstraints.length; i++) { - let ikConstraintData = data.ikConstraints[i]; + const ikConstraintData = data.ikConstraints[i]; + this.ikConstraints.push(new IkConstraint(ikConstraintData, this)); } this.transformConstraints = new Array(); for (let i = 0; i < data.transformConstraints.length; i++) { - let transformConstraintData = data.transformConstraints[i]; + const transformConstraintData = data.transformConstraints[i]; + this.transformConstraints.push(new TransformConstraint(transformConstraintData, this)); } this.pathConstraints = new Array(); for (let i = 0; i < data.pathConstraints.length; i++) { - let pathConstraintData = data.pathConstraints[i]; + const pathConstraintData = data.pathConstraints[i]; + this.pathConstraints.push(new PathConstraint(pathConstraintData, this)); } @@ -116,21 +121,26 @@ export class Skeleton implements ISkeleton { /** Caches information about bones and constraints. Must be called if the {@link #getSkin()} is modified or if bones, * constraints, or weighted path attachments are added or removed. */ - updateCache () { - let updateCache = this._updateCache; + updateCache() { + const updateCache = this._updateCache; + updateCache.length = 0; - let bones = this.bones; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + bone.sorted = bone.data.skinRequired; bone.active = !bone.sorted; } if (this.skin) { - let skinBones = this.skin.bones; + const skinBones = this.skin.bones; + for (let i = 0, n = this.skin.bones.length; i < n; i++) { let bone = this.bones[skinBones[i].index]; + do { bone.sorted = false; bone.active = true; @@ -140,57 +150,67 @@ export class Skeleton implements ISkeleton { } // IK first, lowest hierarchy depth first. - let ikConstraints = this.ikConstraints; - let transformConstraints = this.transformConstraints; - let pathConstraints = this.pathConstraints; - let ikCount = ikConstraints.length, transformCount = transformConstraints.length, pathCount = pathConstraints.length; - let constraintCount = ikCount + transformCount + pathCount; - - outer: - for (let i = 0; i < constraintCount; i++) { - for (let ii = 0; ii < ikCount; ii++) { - let constraint = ikConstraints[ii]; - if (constraint.data.order == i) { - this.sortIkConstraint(constraint); - continue outer; - } + const ikConstraints = this.ikConstraints; + const transformConstraints = this.transformConstraints; + const pathConstraints = this.pathConstraints; + const ikCount = ikConstraints.length; + const transformCount = transformConstraints.length; + const pathCount = pathConstraints.length; + const constraintCount = ikCount + transformCount + pathCount; + + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < constraintCount; i++) { + for (let ii = 0; ii < ikCount; ii++) { + const constraint = ikConstraints[ii]; + + if (constraint.data.order == i) { + this.sortIkConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < transformCount; ii++) { - let constraint = transformConstraints[ii]; - if (constraint.data.order == i) { - this.sortTransformConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < transformCount; ii++) { + const constraint = transformConstraints[ii]; + + if (constraint.data.order == i) { + this.sortTransformConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < pathCount; ii++) { - let constraint = pathConstraints[ii]; - if (constraint.data.order == i) { - this.sortPathConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < pathCount; ii++) { + const constraint = pathConstraints[ii]; + + if (constraint.data.order == i) { + this.sortPathConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } } + } - for (let i = 0, n = bones.length; i < n; i++) - this.sortBone(bones[i]); + for (let i = 0, n = bones.length; i < n; i++) this.sortBone(bones[i]); } - sortIkConstraint (constraint: IkConstraint) { + sortIkConstraint(constraint: IkConstraint) { constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; - let target = constraint.target; + const target = constraint.target; + this.sortBone(target); - let constrained = constraint.bones; - let parent = constrained[0]; + const constrained = constraint.bones; + const parent = constrained[0]; + this.sortBone(parent); if (constrained.length == 1) { this._updateCache.push(constraint); this.sortReset(parent.children); } else { - let child = constrained[constrained.length - 1]; + const child = constrained[constrained.length - 1]; + this.sortBone(child); this._updateCache.push(constraint); @@ -200,46 +220,46 @@ export class Skeleton implements ISkeleton { } } - sortPathConstraint (constraint: PathConstraint) { + sortPathConstraint(constraint: PathConstraint) { constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; - let slot = constraint.target; - let slotIndex = slot.data.index; - let slotBone = slot.bone; + const slot = constraint.target; + const slotIndex = slot.data.index; + const slotBone = slot.bone; + if (this.skin) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone); - if (this.data.defaultSkin && this.data.defaultSkin != this.skin) - this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); - for (let i = 0, n = this.data.skins.length; i < n; i++) - this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + if (this.data.defaultSkin && this.data.defaultSkin != this.skin) this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); + for (let i = 0, n = this.data.skins.length; i < n; i++) this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + + const attachment = slot.getAttachment(); - let attachment = slot.getAttachment(); if (attachment instanceof PathAttachment) this.sortPathConstraintAttachmentWith(attachment, slotBone); - let constrained = constraint.bones; - let boneCount = constrained.length; - for (let i = 0; i < boneCount; i++) - this.sortBone(constrained[i]); + const constrained = constraint.bones; + const boneCount = constrained.length; + + for (let i = 0; i < boneCount; i++) this.sortBone(constrained[i]); this._updateCache.push(constraint); - for (let i = 0; i < boneCount; i++) - this.sortReset(constrained[i].children); - for (let i = 0; i < boneCount; i++) - constrained[i].sorted = true; + for (let i = 0; i < boneCount; i++) this.sortReset(constrained[i].children); + for (let i = 0; i < boneCount; i++) constrained[i].sorted = true; } - sortTransformConstraint (constraint: TransformConstraint) { + sortTransformConstraint(constraint: TransformConstraint) { constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; this.sortBone(constraint.target); - let constrained = constraint.bones; - let boneCount = constrained.length; + const constrained = constraint.bones; + const boneCount = constrained.length; + if (constraint.data.local) { for (let i = 0; i < boneCount; i++) { - let child = constrained[i]; + const child = constrained[i]; + this.sortBone(child.parent); this.sortBone(child); } @@ -251,47 +271,49 @@ export class Skeleton implements ISkeleton { this._updateCache.push(constraint); - for (let i = 0; i < boneCount; i++) - this.sortReset(constrained[i].children); - for (let i = 0; i < boneCount; i++) - constrained[i].sorted = true; + for (let i = 0; i < boneCount; i++) this.sortReset(constrained[i].children); + for (let i = 0; i < boneCount; i++) constrained[i].sorted = true; } - sortPathConstraintAttachment (skin: Skin, slotIndex: number, slotBone: Bone) { - let attachments = skin.attachments[slotIndex]; + sortPathConstraintAttachment(skin: Skin, slotIndex: number, slotBone: Bone) { + const attachments = skin.attachments[slotIndex]; + if (!attachments) return; - for (let key in attachments) { + for (const key in attachments) { this.sortPathConstraintAttachmentWith(attachments[key], slotBone); } } - sortPathConstraintAttachmentWith (attachment: Attachment, slotBone: Bone) { + sortPathConstraintAttachmentWith(attachment: Attachment, slotBone: Bone) { if (!(attachment instanceof PathAttachment)) return; - let pathBones = (attachment).bones; - if (!pathBones) - this.sortBone(slotBone); + const pathBones = (attachment).bones; + + if (!pathBones) this.sortBone(slotBone); else { - let bones = this.bones; - for (let i = 0, n = pathBones.length; i < n;) { + const bones = this.bones; + + for (let i = 0, n = pathBones.length; i < n; ) { let nn = pathBones[i++]; + nn += i; - while (i < nn) - this.sortBone(bones[pathBones[i++]]); + while (i < nn) this.sortBone(bones[pathBones[i++]]); } } } - sortBone (bone: Bone) { + sortBone(bone: Bone) { if (bone.sorted) return; - let parent = bone.parent; + const parent = bone.parent; + if (parent) this.sortBone(parent); bone.sorted = true; this._updateCache.push(bone); } - sortReset (bones: Array) { + sortReset(bones: Array) { for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (!bone.active) continue; if (bone.sorted) this.sortReset(bone.children); bone.sorted = false; @@ -302,10 +324,12 @@ export class Skeleton implements ISkeleton { * * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine * Runtimes Guide. */ - updateWorldTransform () { - let bones = this.bones; + updateWorldTransform() { + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + bone.ax = bone.x; bone.ay = bone.y; bone.arotation = bone.rotation; @@ -315,54 +339,63 @@ export class Skeleton implements ISkeleton { bone.ashearY = bone.shearY; } - let updateCache = this._updateCache; - for (let i = 0, n = updateCache.length; i < n; i++) - updateCache[i].update(); + const updateCache = this._updateCache; + + for (let i = 0, n = updateCache.length; i < n; i++) updateCache[i].update(); } - updateWorldTransformWith (parent: Bone) { + updateWorldTransformWith(parent: Bone) { // Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection. - let rootBone = this.getRootBone(); - let pa = parent.matrix.a, pb = parent.matrix.c, pc = parent.matrix.b, pd = parent.matrix.d; + const rootBone = this.getRootBone(); + const pa = parent.matrix.a; + const pb = parent.matrix.c; + const pc = parent.matrix.b; + const pd = parent.matrix.d; + rootBone.matrix.tx = pa * this.x + pb * this.y + parent.worldX; rootBone.matrix.ty = pc * this.x + pd * this.y + parent.worldY; - let rotationY = rootBone.rotation + 90 + rootBone.shearY; - let la = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; - let lb = MathUtils.cosDeg(rotationY) * rootBone.scaleY; - let lc = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; - let ld = MathUtils.sinDeg(rotationY) * rootBone.scaleY; + const rotationY = rootBone.rotation + 90 + rootBone.shearY; + const la = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; + const lb = MathUtils.cosDeg(rotationY) * rootBone.scaleY; + const lc = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; + const ld = MathUtils.sinDeg(rotationY) * rootBone.scaleY; const sx = this.scaleX; - const sy = settings.yDown? -this.scaleY : this.scaleY; + const sy = settings.yDown ? -this.scaleY : this.scaleY; + rootBone.matrix.a = (pa * la + pb * lc) * sx; rootBone.matrix.c = (pa * lb + pb * ld) * sx; rootBone.matrix.b = (pc * la + pd * lc) * sy; rootBone.matrix.d = (pc * lb + pd * ld) * sy; // Update everything except root bone. - let updateCache = this._updateCache; + const updateCache = this._updateCache; + for (let i = 0, n = updateCache.length; i < n; i++) { - let updatable = updateCache[i]; + const updatable = updateCache[i]; + if (updatable != rootBone) updatable.update(); } } /** Sets the bones, constraints, and slots to their setup pose values. */ - setToSetupPose () { + setToSetupPose() { this.setBonesToSetupPose(); this.setSlotsToSetupPose(); } /** Sets the bones and constraints to their setup pose values. */ - setBonesToSetupPose () { - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - bones[i].setToSetupPose(); + setBonesToSetupPose() { + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) bones[i].setToSetupPose(); + + const ikConstraints = this.ikConstraints; - let ikConstraints = this.ikConstraints; for (let i = 0, n = ikConstraints.length; i < n; i++) { - let constraint = ikConstraints[i]; + const constraint = ikConstraints[i]; + constraint.mix = constraint.data.mix; constraint.softness = constraint.data.softness; constraint.bendDirection = constraint.data.bendDirection; @@ -370,10 +403,12 @@ export class Skeleton implements ISkeleton { constraint.stretch = constraint.data.stretch; } - let transformConstraints = this.transformConstraints; + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; - let data = constraint.data; + const constraint = transformConstraints[i]; + const data = constraint.data; + constraint.mixRotate = data.mixRotate; constraint.mixX = data.mixX; constraint.mixY = data.mixY; @@ -382,10 +417,12 @@ export class Skeleton implements ISkeleton { constraint.mixShearY = data.mixShearY; } - let pathConstraints = this.pathConstraints; + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; - let data = constraint.data; + const constraint = pathConstraints[i]; + const data = constraint.data; + constraint.position = data.position; constraint.spacing = data.spacing; constraint.mixRotate = data.mixRotate; @@ -395,67 +432,77 @@ export class Skeleton implements ISkeleton { } /** Sets the slots and draw order to their setup pose values. */ - setSlotsToSetupPose () { - let slots = this.slots; + setSlotsToSetupPose() { + const slots = this.slots; + Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length); - for (let i = 0, n = slots.length; i < n; i++) - slots[i].setToSetupPose(); + for (let i = 0, n = slots.length; i < n; i++) slots[i].setToSetupPose(); } /** @returns May return null. */ - getRootBone () { + getRootBone() { if (this.bones.length == 0) return null; + return this.bones[0]; } /** @returns May be null. */ - findBone (boneName: string) { - if (!boneName) throw new Error("boneName cannot be null."); - let bones = this.bones; + findBone(boneName: string) { + if (!boneName) throw new Error('boneName cannot be null.'); + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (bone.data.name == boneName) return bone; } + return null; } /** @returns -1 if the bone was not found. */ - findBoneIndex (boneName: string) { - if (!boneName) throw new Error("boneName cannot be null."); - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - if (bones[i].data.name == boneName) return i; + findBoneIndex(boneName: string) { + if (!boneName) throw new Error('boneName cannot be null.'); + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) if (bones[i].data.name == boneName) return i; + return -1; } /** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it * repeatedly. * @returns May be null. */ - findSlot (slotName: string) { - if (!slotName) throw new Error("slotName cannot be null."); - let slots = this.slots; + findSlot(slotName: string) { + if (!slotName) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) return slot; } + return null; } /** @returns -1 if the bone was not found. */ - findSlotIndex (slotName: string) { - if (!slotName) throw new Error("slotName cannot be null."); - let slots = this.slots; - for (let i = 0, n = slots.length; i < n; i++) - if (slots[i].data.name == slotName) return i; + findSlotIndex(slotName: string) { + if (!slotName) throw new Error('slotName cannot be null.'); + const slots = this.slots; + + for (let i = 0, n = slots.length; i < n; i++) if (slots[i].data.name == slotName) return i; + return -1; } /** Sets a skin by name. * * See {@link #setSkin()}. */ - setSkinByName (skinName: string) { - let skin = this.data.findSkin(skinName); - if (!skin) throw new Error("Skin not found: " + skinName); + setSkinByName(skinName: string) { + const skin = this.data.findSkin(skinName); + + if (!skin) throw new Error(`Skin not found: ${skinName}`); this.setSkin(skin); } @@ -469,18 +516,20 @@ export class Skeleton implements ISkeleton { * {@link #setSlotsToSetupPose()}. Also, often {@link AnimationState#apply()} is called before the next time the * skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin. * @param newSkin May be null. */ - setSkin (newSkin: Skin) { + setSkin(newSkin: Skin) { if (newSkin == this.skin) return; if (newSkin) { - if (this.skin) - newSkin.attachAll(this, this.skin); + if (this.skin) newSkin.attachAll(this, this.skin); else { - let slots = this.slots; + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; - let name = slot.data.attachmentName; + const slot = slots[i]; + const name = slot.data.attachmentName; + if (name) { - let attachment: Attachment = newSkin.getAttachment(i, name); + const attachment: Attachment = newSkin.getAttachment(i, name); + if (attachment) slot.setAttachment(attachment); } } @@ -490,13 +539,12 @@ export class Skeleton implements ISkeleton { this.updateCache(); } - /** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment * name. * * See {@link #getAttachment()}. * @returns May be null. */ - getAttachmentByName (slotName: string, attachmentName: string): Attachment { + getAttachmentByName(slotName: string, attachmentName: string): Attachment { return this.getAttachment(this.data.findSlot(slotName).index, attachmentName); } @@ -505,83 +553,99 @@ export class Skeleton implements ISkeleton { * * See [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide. * @returns May be null. */ - getAttachment (slotIndex: number, attachmentName: string): Attachment { - if (!attachmentName) throw new Error("attachmentName cannot be null."); + getAttachment(slotIndex: number, attachmentName: string): Attachment { + if (!attachmentName) throw new Error('attachmentName cannot be null.'); if (this.skin) { - let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName); + const attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment) return attachment; } if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; } /** A convenience method to set an attachment by finding the slot with {@link #findSlot()}, finding the attachment with * {@link #getAttachment()}, then setting the slot's {@link Slot#attachment}. * @param attachmentName May be null to clear the slot's attachment. */ - setAttachment (slotName: string, attachmentName: string) { - if (!slotName) throw new Error("slotName cannot be null."); - let slots = this.slots; + setAttachment(slotName: string, attachmentName: string) { + if (!slotName) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) { let attachment: Attachment = null; + if (attachmentName) { attachment = this.getAttachment(i, attachmentName); - if (!attachment) throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName); + if (!attachment) throw new Error(`Attachment not found: ${attachmentName}, for slot: ${slotName}`); } slot.setAttachment(attachment); + return; } } - throw new Error("Slot not found: " + slotName); + throw new Error(`Slot not found: ${slotName}`); } - /** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method * than to call it repeatedly. * @return May be null. */ - findIkConstraint (constraintName: string) { - if (!constraintName) throw new Error("constraintName cannot be null."); - let ikConstraints = this.ikConstraints; + findIkConstraint(constraintName: string) { + if (!constraintName) throw new Error('constraintName cannot be null.'); + const ikConstraints = this.ikConstraints; + for (let i = 0, n = ikConstraints.length; i < n; i++) { - let ikConstraint = ikConstraints[i]; + const ikConstraint = ikConstraints[i]; + if (ikConstraint.data.name == constraintName) return ikConstraint; } + return null; } /** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of * this method than to call it repeatedly. * @return May be null. */ - findTransformConstraint (constraintName: string) { - if (!constraintName) throw new Error("constraintName cannot be null."); - let transformConstraints = this.transformConstraints; + findTransformConstraint(constraintName: string) { + if (!constraintName) throw new Error('constraintName cannot be null.'); + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; + const constraint = transformConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } /** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method * than to call it repeatedly. * @return May be null. */ - findPathConstraint (constraintName: string) { - if (!constraintName) throw new Error("constraintName cannot be null."); - let pathConstraints = this.pathConstraints; + findPathConstraint(constraintName: string) { + if (!constraintName) throw new Error('constraintName cannot be null.'); + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; + const constraint = pathConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } /** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose as `{ x: number, y: number, width: number, height: number }`. * Note that this method will create temporary objects which can add to garbage collection pressure. Use `getBounds()` if garbage collection is a concern. */ - getBoundsRect () { - let offset = new Vector2(); - let size = new Vector2(); + getBoundsRect() { + const offset = new Vector2(); + const size = new Vector2(); + this.getBounds(offset, size); + return { x: offset.x, y: offset.y, width: size.x, height: size.y }; } @@ -589,30 +653,39 @@ export class Skeleton implements ISkeleton { * @param offset An output value, the distance from the skeleton origin to the bottom left corner of the AABB. * @param size An output value, the width and height of the AABB. * @param temp Working memory to temporarily store attachments' computed world vertices. */ - getBounds (offset: Vector2, size: Vector2, temp: Array = new Array(2)) { - if (!offset) throw new Error("offset cannot be null."); - if (!size) throw new Error("size cannot be null."); - let drawOrder = this.drawOrder; - let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; + getBounds(offset: Vector2, size: Vector2, temp: Array = new Array(2)) { + if (!offset) throw new Error('offset cannot be null.'); + if (!size) throw new Error('size cannot be null.'); + const drawOrder = this.drawOrder; + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + let maxX = Number.NEGATIVE_INFINITY; + let maxY = Number.NEGATIVE_INFINITY; + for (let i = 0, n = drawOrder.length; i < n; i++) { - let slot = drawOrder[i]; + const slot = drawOrder[i]; + if (!slot.bone.active) continue; let verticesLength = 0; let vertices: NumberArrayLike = null; - let attachment = slot.getAttachment(); + const attachment = slot.getAttachment(); + if (attachment instanceof RegionAttachment) { verticesLength = 8; vertices = Utils.setArraySize(temp, verticesLength, 0); (attachment).computeWorldVertices(slot.bone, vertices, 0, 2); } else if (attachment instanceof MeshAttachment) { - let mesh = (attachment); + const mesh = attachment; + verticesLength = mesh.worldVerticesLength; vertices = Utils.setArraySize(temp, verticesLength, 0); mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); } if (vertices) { for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) { - let x = vertices[ii], y = vertices[ii + 1]; + const x = vertices[ii]; + const y = vertices[ii + 1]; + minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); @@ -625,7 +698,7 @@ export class Skeleton implements ISkeleton { } /** Increments the skeleton's {@link #time}. */ - update (delta: number) { + update(delta: number) { this.time += delta; } @@ -636,7 +709,7 @@ export class Skeleton implements ISkeleton { set flipX(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleX = value ? 1.0 : -1.0; } @@ -648,10 +721,10 @@ export class Skeleton implements ISkeleton { set flipY(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleY = value ? 1.0 : -1.0; } - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; } diff --git a/packages/runtime-4.0/src/core/SkeletonBinary.ts b/packages/runtime-4.0/src/core/SkeletonBinary.ts index 494291cc..4337caec 100644 --- a/packages/runtime-4.0/src/core/SkeletonBinary.ts +++ b/packages/runtime-4.0/src/core/SkeletonBinary.ts @@ -1,29 +1,46 @@ -import type {Attachment, AttachmentLoader, MeshAttachment, VertexAttachment} from './attachments'; +import type { Attachment, AttachmentLoader, MeshAttachment, VertexAttachment } from './attachments'; import { - AlphaTimeline, Animation, - AttachmentTimeline, CurveTimeline, CurveTimeline1, CurveTimeline2, DeformTimeline, DrawOrderTimeline, EventTimeline, + AlphaTimeline, + Animation, + AttachmentTimeline, + CurveTimeline, + CurveTimeline1, + CurveTimeline2, + DeformTimeline, + DrawOrderTimeline, + EventTimeline, IkConstraintTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, - PathConstraintSpacingTimeline, RGB2Timeline, RGBA2Timeline, RGBATimeline, RGBTimeline, + PathConstraintSpacingTimeline, + RGB2Timeline, + RGBA2Timeline, + RGBATimeline, + RGBTimeline, RotateTimeline, - ScaleTimeline, ScaleXTimeline, ScaleYTimeline, - ShearTimeline, ShearXTimeline, ShearYTimeline, + ScaleTimeline, + ScaleXTimeline, + ScaleYTimeline, + ShearTimeline, + ShearXTimeline, + ShearYTimeline, Timeline, TransformConstraintTimeline, - TranslateTimeline, TranslateXTimeline, TranslateYTimeline + TranslateTimeline, + TranslateXTimeline, + TranslateYTimeline, } from './Animation'; -import {Event} from './Event'; -import {SkeletonData} from './SkeletonData'; -import {SlotData} from './SlotData'; -import {BoneData} from './BoneData'; -import {IkConstraintData} from './IkConstraintData'; -import {TransformConstraintData} from './TransformConstraintData'; -import {PathConstraintData, SpacingMode} from './PathConstraintData'; -import {Skin} from './Skin'; -import {EventData} from './EventData'; -import {AttachmentType, BinaryInput, Color, PositionMode, Utils} from '@pixi-spine/base'; -import {BLEND_MODES} from '@pixi/constants'; +import { Event } from './Event'; +import { SkeletonData } from './SkeletonData'; +import { SlotData } from './SlotData'; +import { BoneData } from './BoneData'; +import { IkConstraintData } from './IkConstraintData'; +import { TransformConstraintData } from './TransformConstraintData'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import { Skin } from './Skin'; +import { EventData } from './EventData'; +import { AttachmentType, BinaryInput, Color, PositionMode, Utils } from '@pixi-spine/base'; +import { BLEND_MODES } from '@pixi/constants'; /** Loads skeleton data in the Spine binary format. * @@ -33,7 +50,7 @@ import {BLEND_MODES} from '@pixi/constants'; * @public * */ export class SkeletonBinary { - static BlendModeValues = [ BLEND_MODES.NORMAL, BLEND_MODES.ADD, BLEND_MODES.MULTIPLY, BLEND_MODES.SCREEN]; + static BlendModeValues = [BLEND_MODES.NORMAL, BLEND_MODES.ADD, BLEND_MODES.MULTIPLY, BLEND_MODES.SCREEN]; /** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at * runtime than were used in Spine. * @@ -43,25 +60,27 @@ export class SkeletonBinary { attachmentLoader: AttachmentLoader = null; private linkedMeshes = new Array(); - constructor (attachmentLoader: AttachmentLoader) { + constructor(attachmentLoader: AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - readSkeletonData (binary: Uint8Array): SkeletonData { - let scale = this.scale; + readSkeletonData(binary: Uint8Array): SkeletonData { + const scale = this.scale; - let skeletonData = new SkeletonData(); - skeletonData.name = ""; // BOZO + const skeletonData = new SkeletonData(); - let input = new BinaryInput(binary); + skeletonData.name = ''; // BOZO + + const input = new BinaryInput(binary); + + const lowHash = input.readInt32(); + const highHash = input.readInt32(); - let lowHash = input.readInt32(); - let highHash = input.readInt32(); skeletonData.hash = highHash == 0 && lowHash == 0 ? null : highHash.toString(16) + lowHash.toString(16); skeletonData.version = input.readString(); - if (skeletonData.version.substr(0, 3) !== '4.0') - { - let error = `Spine 4.0 loader cant load version ${skeletonData.version}. Please configure your pixi-spine bundle`; + if (skeletonData.version.substr(0, 3) !== '4.0') { + const error = `Spine 4.0 loader cant load version ${skeletonData.version}. Please configure your pixi-spine bundle`; + console.error(error); } skeletonData.x = input.readFloat(); @@ -69,7 +88,8 @@ export class SkeletonBinary { skeletonData.width = input.readFloat(); skeletonData.height = input.readFloat(); - let nonessential = input.readBoolean(); + const nonessential = input.readBoolean(); + if (nonessential) { skeletonData.fps = input.readFloat(); @@ -79,16 +99,17 @@ export class SkeletonBinary { let n = 0; // Strings. - n = input.readInt(true) - for (let i = 0; i < n; i++) - input.strings.push(input.readString()); + + n = input.readInt(true); + for (let i = 0; i < n; i++) input.strings.push(input.readString()); // Bones. - n = input.readInt(true) + n = input.readInt(true); for (let i = 0; i < n; i++) { - let name = input.readString(); - let parent = i == 0 ? null : skeletonData.bones[input.readInt(true)]; - let data = new BoneData(i, name, parent); + const name = input.readString(); + const parent = i == 0 ? null : skeletonData.bones[input.readInt(true)]; + const data = new BoneData(i, name, parent); + data.rotation = input.readFloat(); data.x = input.readFloat() * scale; data.y = input.readFloat() * scale; @@ -106,13 +127,15 @@ export class SkeletonBinary { // Slots. n = input.readInt(true); for (let i = 0; i < n; i++) { - let slotName = input.readString(); - let boneData = skeletonData.bones[input.readInt(true)]; - let data = new SlotData(i, slotName, boneData); + const slotName = input.readString(); + const boneData = skeletonData.bones[input.readInt(true)]; + const data = new SlotData(i, slotName, boneData); + Color.rgba8888ToColor(data.color, input.readInt32()); - let darkColor = input.readInt32(); - if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor); + const darkColor = input.readInt32(); + + if (darkColor != -1) Color.rgb888ToColor((data.darkColor = new Color()), darkColor); data.attachmentName = input.readStringRef(); data.blendMode = SkeletonBinary.BlendModeValues[input.readInt(true)]; @@ -122,12 +145,12 @@ export class SkeletonBinary { // IK constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let data = new IkConstraintData(input.readString()); + const data = new IkConstraintData(input.readString()); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.bones[input.readInt(true)]; data.mix = input.readFloat(); data.softness = input.readFloat() * scale; @@ -141,12 +164,12 @@ export class SkeletonBinary { // Transform constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let data = new TransformConstraintData(input.readString()); + const data = new TransformConstraintData(input.readString()); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.bones[input.readInt(true)]; data.local = input.readBoolean(); data.relative = input.readBoolean(); @@ -168,12 +191,12 @@ export class SkeletonBinary { // Path constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let data = new PathConstraintData(input.readString()); + const data = new PathConstraintData(input.readString()); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.slots[input.readInt(true)]; data.positionMode = input.readInt(true); data.spacingMode = input.readInt(true); @@ -190,7 +213,8 @@ export class SkeletonBinary { } // Default skin. - let defaultSkin = this.readSkin(input, skeletonData, true, nonessential); + const defaultSkin = this.readSkin(input, skeletonData, true, nonessential); + if (defaultSkin) { skeletonData.defaultSkin = defaultSkin; skeletonData.skins.push(defaultSkin); @@ -199,18 +223,19 @@ export class SkeletonBinary { // Skins. { let i = skeletonData.skins.length; - Utils.setArraySize(skeletonData.skins, n = i + input.readInt(true)); - for (; i < n; i++) - skeletonData.skins[i] = this.readSkin(input, skeletonData, false, nonessential); + + Utils.setArraySize(skeletonData.skins, (n = i + input.readInt(true))); + for (; i < n; i++) skeletonData.skins[i] = this.readSkin(input, skeletonData, false, nonessential); } // Linked meshes. n = this.linkedMeshes.length; for (let i = 0; i < n; i++) { - let linkedMesh = this.linkedMeshes[i]; - let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); - let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); - linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? parent as VertexAttachment : linkedMesh.mesh; + const linkedMesh = this.linkedMeshes[i]; + const skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + const parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + + linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? (parent as VertexAttachment) : linkedMesh.mesh; linkedMesh.mesh.setParentMesh(parent as MeshAttachment); // linkedMesh.mesh.updateUVs(); } @@ -219,7 +244,8 @@ export class SkeletonBinary { // Events. n = input.readInt(true); for (let i = 0; i < n; i++) { - let data = new EventData(input.readStringRef()); + const data = new EventData(input.readStringRef()); + data.intValue = input.readInt(false); data.floatValue = input.readFloat(); data.stringValue = input.readString(); @@ -233,66 +259,67 @@ export class SkeletonBinary { // Animations. n = input.readInt(true); - for (let i = 0; i < n; i++) - skeletonData.animations.push(this.readAnimation(input, input.readString(), skeletonData)); + for (let i = 0; i < n; i++) skeletonData.animations.push(this.readAnimation(input, input.readString(), skeletonData)); + return skeletonData; } - private readSkin (input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin { + private readSkin(input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin { let skin = null; let slotCount = 0; if (defaultSkin) { - slotCount = input.readInt(true) + slotCount = input.readInt(true); if (slotCount == 0) return null; - skin = new Skin("default"); + skin = new Skin('default'); } else { skin = new Skin(input.readStringRef()); skin.bones.length = input.readInt(true); - for (let i = 0, n = skin.bones.length; i < n; i++) - skin.bones[i] = skeletonData.bones[input.readInt(true)]; + for (let i = 0, n = skin.bones.length; i < n; i++) skin.bones[i] = skeletonData.bones[input.readInt(true)]; - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]); - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]); - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]); slotCount = input.readInt(true); } for (let i = 0; i < slotCount; i++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let name = input.readStringRef(); - let attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + const name = input.readStringRef(); + const attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + if (attachment) skin.setAttachment(slotIndex, name, attachment); } } + return skin; } - private readAttachment (input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment { - let scale = this.scale; + private readAttachment(input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment { + const scale = this.scale; let name = input.readStringRef(); + if (!name) name = attachmentName; switch (input.readByte()) { case AttachmentType.Region: { let path = input.readStringRef(); - let rotation = input.readFloat(); - let x = input.readFloat(); - let y = input.readFloat(); - let scaleX = input.readFloat(); - let scaleY = input.readFloat(); - let width = input.readFloat(); - let height = input.readFloat(); - let color = input.readInt32(); + const rotation = input.readFloat(); + const x = input.readFloat(); + const y = input.readFloat(); + const scaleX = input.readFloat(); + const scaleY = input.readFloat(); + const width = input.readFloat(); + const height = input.readFloat(); + const color = input.readInt32(); if (!path) path = name; - let region = this.attachmentLoader.newRegionAttachment(skin, name, path); + const region = this.attachmentLoader.newRegionAttachment(skin, name, path); + if (!region) return null; region.path = path; region.x = x * scale; @@ -304,31 +331,36 @@ export class SkeletonBinary { region.height = height * scale; Color.rgba8888ToColor(region.color, color); // region.updateOffset(); + return region; } case AttachmentType.BoundingBox: { - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let color = nonessential ? input.readInt32() : 0; + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const color = nonessential ? input.readInt32() : 0; + + const box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); - let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); if (!box) return null; box.worldVerticesLength = vertexCount << 1; box.vertices = vertices.vertices; box.bones = vertices.bones; if (nonessential) Color.rgba8888ToColor(box.color, color); + return box; } case AttachmentType.Mesh: { let path = input.readStringRef(); - let color = input.readInt32(); - let vertexCount = input.readInt(true); - let uvs = this.readFloatArray(input, vertexCount << 1, 1); - let triangles = this.readShortArray(input); - let vertices = this.readVertices(input, vertexCount); - let hullLength = input.readInt(true); + const color = input.readInt32(); + const vertexCount = input.readInt(true); + const uvs = this.readFloatArray(input, vertexCount << 1, 1); + const triangles = this.readShortArray(input); + const vertices = this.readVertices(input, vertexCount); + const hullLength = input.readInt(true); let edges = null; - let width = 0, height = 0; + let width = 0; + let height = 0; + if (nonessential) { edges = this.readShortArray(input); width = input.readFloat(); @@ -336,7 +368,8 @@ export class SkeletonBinary { } if (!path) path = name; - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; mesh.path = path; Color.rgba8888ToColor(mesh.color, color); @@ -352,22 +385,26 @@ export class SkeletonBinary { mesh.width = width * scale; mesh.height = height * scale; } + return mesh; } case AttachmentType.LinkedMesh: { let path = input.readStringRef(); - let color = input.readInt32(); - let skinName = input.readStringRef(); - let parent = input.readStringRef(); - let inheritDeform = input.readBoolean(); - let width = 0, height = 0; + const color = input.readInt32(); + const skinName = input.readStringRef(); + const parent = input.readStringRef(); + const inheritDeform = input.readBoolean(); + let width = 0; + let height = 0; + if (nonessential) { width = input.readFloat(); height = input.readFloat(); } if (!path) path = name; - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; mesh.path = path; Color.rgba8888ToColor(mesh.color, color); @@ -376,19 +413,21 @@ export class SkeletonBinary { mesh.height = height * scale; } this.linkedMeshes.push(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform)); + return mesh; } case AttachmentType.Path: { - let closed = input.readBoolean(); - let constantSpeed = input.readBoolean(); - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let lengths = Utils.newArray(vertexCount / 3, 0); - for (let i = 0, n = lengths.length; i < n; i++) - lengths[i] = input.readFloat() * scale; - let color = nonessential ? input.readInt32() : 0; - - let path = this.attachmentLoader.newPathAttachment(skin, name); + const closed = input.readBoolean(); + const constantSpeed = input.readBoolean(); + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const lengths = Utils.newArray(vertexCount / 3, 0); + + for (let i = 0, n = lengths.length; i < n; i++) lengths[i] = input.readFloat() * scale; + const color = nonessential ? input.readInt32() : 0; + + const path = this.attachmentLoader.newPathAttachment(skin, name); + if (!path) return null; path.closed = closed; path.constantSpeed = constantSpeed; @@ -397,53 +436,63 @@ export class SkeletonBinary { path.bones = vertices.bones; path.lengths = lengths; if (nonessential) Color.rgba8888ToColor(path.color, color); + return path; } case AttachmentType.Point: { - let rotation = input.readFloat(); - let x = input.readFloat(); - let y = input.readFloat(); - let color = nonessential ? input.readInt32() : 0; + const rotation = input.readFloat(); + const x = input.readFloat(); + const y = input.readFloat(); + const color = nonessential ? input.readInt32() : 0; + + const point = this.attachmentLoader.newPointAttachment(skin, name); - let point = this.attachmentLoader.newPointAttachment(skin, name); if (!point) return null; point.x = x * scale; point.y = y * scale; point.rotation = rotation; if (nonessential) Color.rgba8888ToColor(point.color, color); + return point; } case AttachmentType.Clipping: { - let endSlotIndex = input.readInt(true); - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let color = nonessential ? input.readInt32() : 0; + const endSlotIndex = input.readInt(true); + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const color = nonessential ? input.readInt32() : 0; + + const clip = this.attachmentLoader.newClippingAttachment(skin, name); - let clip = this.attachmentLoader.newClippingAttachment(skin, name); if (!clip) return null; clip.endSlot = skeletonData.slots[endSlotIndex]; clip.worldVerticesLength = vertexCount << 1; clip.vertices = vertices.vertices; clip.bones = vertices.bones; if (nonessential) Color.rgba8888ToColor(clip.color, color); + return clip; } } + return null; } - private readVertices (input: BinaryInput, vertexCount: number): Vertices { - let scale = this.scale; - let verticesLength = vertexCount << 1; - let vertices = new Vertices(); + private readVertices(input: BinaryInput, vertexCount: number): Vertices { + const scale = this.scale; + const verticesLength = vertexCount << 1; + const vertices = new Vertices(); + if (!input.readBoolean()) { vertices.vertices = this.readFloatArray(input, verticesLength, scale); + return vertices; } - let weights = new Array(); - let bonesArray = new Array(); + const weights = new Array(); + const bonesArray = new Array(); + for (let i = 0; i < vertexCount; i++) { - let boneCount = input.readInt(true); + const boneCount = input.readInt(true); + bonesArray.push(boneCount); for (let ii = 0; ii < boneCount; ii++) { bonesArray.push(input.readInt(true)); @@ -454,52 +503,56 @@ export class SkeletonBinary { } vertices.vertices = Utils.toFloatArray(weights); vertices.bones = bonesArray; + return vertices; } - private readFloatArray (input: BinaryInput, n: number, scale: number): number[] { - let array = new Array(n); + private readFloatArray(input: BinaryInput, n: number, scale: number): number[] { + const array = new Array(n); + if (scale == 1) { - for (let i = 0; i < n; i++) - array[i] = input.readFloat(); + for (let i = 0; i < n; i++) array[i] = input.readFloat(); } else { - for (let i = 0; i < n; i++) - array[i] = input.readFloat() * scale; + for (let i = 0; i < n; i++) array[i] = input.readFloat() * scale; } + return array; } - private readShortArray (input: BinaryInput): number[] { - let n = input.readInt(true); - let array = new Array(n); - for (let i = 0; i < n; i++) - array[i] = input.readShort(); + private readShortArray(input: BinaryInput): number[] { + const n = input.readInt(true); + const array = new Array(n); + + for (let i = 0; i < n; i++) array[i] = input.readShort(); + return array; } - private readAnimation (input: BinaryInput, name: string, skeletonData: SkeletonData): Animation { + private readAnimation(input: BinaryInput, name: string, skeletonData: SkeletonData): Animation { input.readInt(true); // Number of timelines. - let timelines = new Array(); - let scale = this.scale; + const timelines = new Array(); + const scale = this.scale; // Slot timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let timelineType = input.readByte(); - let frameCount = input.readInt(true); - let frameLast = frameCount - 1; + const timelineType = input.readByte(); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; + switch (timelineType) { case SLOT_ATTACHMENT: { - let timeline = new AttachmentTimeline(frameCount, slotIndex); - for (let frame = 0; frame < frameCount; frame++) - timeline.setFrame(frame, input.readFloat(), input.readStringRef()); + const timeline = new AttachmentTimeline(frameCount, slotIndex); + + for (let frame = 0; frame < frameCount; frame++) timeline.setFrame(frame, input.readFloat(), input.readStringRef()); timelines.push(timeline); break; } case SLOT_RGBA: { - let bezierCount = input.readInt(true); - let timeline = new RGBATimeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGBATimeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -511,11 +564,11 @@ export class SkeletonBinary { timeline.setFrame(frame, time, r, g, b, a); if (frame == frameLast) break; - let time2 = input.readFloat(); - let r2 = input.readUnsignedByte() / 255.0; - let g2 = input.readUnsignedByte() / 255.0; - let b2 = input.readUnsignedByte() / 255.0; - let a2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const r2 = input.readUnsignedByte() / 255.0; + const g2 = input.readUnsignedByte() / 255.0; + const b2 = input.readUnsignedByte() / 255.0; + const a2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -537,8 +590,8 @@ export class SkeletonBinary { break; } case SLOT_RGB: { - let bezierCount = input.readInt(true); - let timeline = new RGBTimeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGBTimeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -549,10 +602,10 @@ export class SkeletonBinary { timeline.setFrame(frame, time, r, g, b); if (frame == frameLast) break; - let time2 = input.readFloat(); - let r2 = input.readUnsignedByte() / 255.0; - let g2 = input.readUnsignedByte() / 255.0; - let b2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const r2 = input.readUnsignedByte() / 255.0; + const g2 = input.readUnsignedByte() / 255.0; + const b2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -572,8 +625,8 @@ export class SkeletonBinary { break; } case SLOT_RGBA2: { - let bezierCount = input.readInt(true); - let timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -587,14 +640,14 @@ export class SkeletonBinary { for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2); if (frame == frameLast) break; - let time2 = input.readFloat(); - let nr = input.readUnsignedByte() / 255.0; - let ng = input.readUnsignedByte() / 255.0; - let nb = input.readUnsignedByte() / 255.0; - let na = input.readUnsignedByte() / 255.0; - let nr2 = input.readUnsignedByte() / 255.0; - let ng2 = input.readUnsignedByte() / 255.0; - let nb2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const nr = input.readUnsignedByte() / 255.0; + const ng = input.readUnsignedByte() / 255.0; + const nb = input.readUnsignedByte() / 255.0; + const na = input.readUnsignedByte() / 255.0; + const nr2 = input.readUnsignedByte() / 255.0; + const ng2 = input.readUnsignedByte() / 255.0; + const nb2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -622,8 +675,8 @@ export class SkeletonBinary { break; } case SLOT_RGB2: { - let bezierCount = input.readInt(true); - let timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -636,13 +689,13 @@ export class SkeletonBinary { for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, r, g, b, r2, g2, b2); if (frame == frameLast) break; - let time2 = input.readFloat(); - let nr = input.readUnsignedByte() / 255.0; - let ng = input.readUnsignedByte() / 255.0; - let nb = input.readUnsignedByte() / 255.0; - let nr2 = input.readUnsignedByte() / 255.0; - let ng2 = input.readUnsignedByte() / 255.0; - let nb2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const nr = input.readUnsignedByte() / 255.0; + const ng = input.readUnsignedByte() / 255.0; + const nb = input.readUnsignedByte() / 255.0; + const nr2 = input.readUnsignedByte() / 255.0; + const ng2 = input.readUnsignedByte() / 255.0; + const nb2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -668,13 +721,16 @@ export class SkeletonBinary { break; } case SLOT_ALPHA: { - let timeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex); - let time = input.readFloat(), a = input.readUnsignedByte() / 255; + const timeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex); + let time = input.readFloat(); + let a = input.readUnsignedByte() / 255; + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, a); if (frame == frameLast) break; - let time2 = input.readFloat(); - let a2 = input.readUnsignedByte() / 255; + const time2 = input.readFloat(); + const a2 = input.readUnsignedByte() / 255; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -694,9 +750,13 @@ export class SkeletonBinary { // Bone timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let boneIndex = input.readInt(true); + const boneIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true); + const type = input.readByte(); + const frameCount = input.readInt(true); + const bezierCount = input.readInt(true); + switch (type) { case BONE_ROTATE: timelines.push(readTimeline1(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1)); @@ -733,13 +793,21 @@ export class SkeletonBinary { // IK constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1; - let timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index); - let time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale; + const index = input.readInt(true); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; + const timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index); + let time = input.readFloat(); + let mix = input.readFloat(); + let softness = input.readFloat() * scale; + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean()); if (frame == frameLast) break; - let time2 = input.readFloat(), mix2 = input.readFloat(), softness2 = input.readFloat() * scale; + const time2 = input.readFloat(); + const mix2 = input.readFloat(); + const softness2 = input.readFloat() * scale; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -757,15 +825,29 @@ export class SkeletonBinary { // Transform constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1; - let timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index); - let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat(), - mixScaleX = input.readFloat(), mixScaleY = input.readFloat(), mixShearY = input.readFloat(); + const index = input.readInt(true); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; + const timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index); + let time = input.readFloat(); + let mixRotate = input.readFloat(); + let mixX = input.readFloat(); + let mixY = input.readFloat(); + let mixScaleX = input.readFloat(); + let mixScaleY = input.readFloat(); + let mixShearY = input.readFloat(); + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY); if (frame == frameLast) break; - let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(), mixY2 = input.readFloat(), - mixScaleX2 = input.readFloat(), mixScaleY2 = input.readFloat(), mixShearY2 = input.readFloat(); + const time2 = input.readFloat(); + const mixRotate2 = input.readFloat(); + const mixX2 = input.readFloat(); + const mixY2 = input.readFloat(); + const mixScaleX2 = input.readFloat(); + const mixScaleY2 = input.readFloat(); + const mixShearY2 = input.readFloat(); + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -791,28 +873,44 @@ export class SkeletonBinary { // Path constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true); - let data = skeletonData.pathConstraints[index]; + const index = input.readInt(true); + const data = skeletonData.pathConstraints[index]; + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { switch (input.readByte()) { case PATH_POSITION: - timelines - .push(readTimeline1(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index), - data.positionMode == PositionMode.Fixed ? scale : 1)); + timelines.push( + readTimeline1( + input, + new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index), + data.positionMode == PositionMode.Fixed ? scale : 1 + ) + ); break; case PATH_SPACING: - timelines - .push(readTimeline1(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index), - data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1)); + timelines.push( + readTimeline1( + input, + new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index), + data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1 + ) + ); break; case PATH_MIX: - let timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index); - let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat(); + const timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index); + let time = input.readFloat(); + let mixRotate = input.readFloat(); + let mixX = input.readFloat(); + let mixY = input.readFloat(); + for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY); if (frame == frameLast) break; - let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(), - mixY2 = input.readFloat(); + const time2 = input.readFloat(); + const mixRotate2 = input.readFloat(); + const mixX2 = input.readFloat(); + const mixY2 = input.readFloat(); + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -834,47 +932,52 @@ export class SkeletonBinary { // Deform timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let skin = skeletonData.skins[input.readInt(true)]; + const skin = skeletonData.skins[input.readInt(true)]; + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let iii = 0, nnn = input.readInt(true); iii < nnn; iii++) { - let attachmentName = input.readStringRef(); - let attachment = skin.getAttachment(slotIndex, attachmentName) as VertexAttachment; - let weighted = attachment.bones; - let vertices = attachment.vertices; - let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + const attachmentName = input.readStringRef(); + const attachment = skin.getAttachment(slotIndex, attachmentName) as VertexAttachment; + const weighted = attachment.bones; + const vertices = attachment.vertices; + const deformLength = weighted ? (vertices.length / 3) * 2 : vertices.length; - let frameCount = input.readInt(true); - let frameLast = frameCount - 1; - let bezierCount = input.readInt(true); - let timeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; + const bezierCount = input.readInt(true); + const timeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment); let time = input.readFloat(); + for (let frame = 0, bezier = 0; ; frame++) { let deform; let end = input.readInt(true); - if (end == 0) - deform = weighted ? Utils.newFloatArray(deformLength) : vertices; + + if (end == 0) deform = weighted ? Utils.newFloatArray(deformLength) : vertices; else { deform = Utils.newFloatArray(deformLength); - let start = input.readInt(true); + const start = input.readInt(true); + end += start; if (scale == 1) { - for (let v = start; v < end; v++) - deform[v] = input.readFloat(); + // eslint-disable-next-line max-depth + for (let v = start; v < end; v++) deform[v] = input.readFloat(); } else { - for (let v = start; v < end; v++) - deform[v] = input.readFloat() * scale; + // eslint-disable-next-line max-depth + for (let v = start; v < end; v++) deform[v] = input.readFloat() * scale; } if (!weighted) { - for (let v = 0, vn = deform.length; v < vn; v++) - deform[v] += vertices[v]; + // eslint-disable-next-line max-depth + for (let v = 0, vn = deform.length; v < vn; v++) deform[v] += vertices[v]; } } timeline.setFrame(frame, time, deform); if (frame == frameLast) break; - let time2 = input.readFloat(); + const time2 = input.readFloat(); + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -890,45 +993,50 @@ export class SkeletonBinary { } // Draw order timeline. - let drawOrderCount = input.readInt(true); + const drawOrderCount = input.readInt(true); + if (drawOrderCount > 0) { - let timeline = new DrawOrderTimeline(drawOrderCount); - let slotCount = skeletonData.slots.length; + const timeline = new DrawOrderTimeline(drawOrderCount); + const slotCount = skeletonData.slots.length; + for (let i = 0; i < drawOrderCount; i++) { - let time = input.readFloat(); - let offsetCount = input.readInt(true); - let drawOrder = Utils.newArray(slotCount, 0); - for (let ii = slotCount - 1; ii >= 0; ii--) - drawOrder[ii] = -1; - let unchanged = Utils.newArray(slotCount - offsetCount, 0); - let originalIndex = 0, unchangedIndex = 0; + const time = input.readFloat(); + const offsetCount = input.readInt(true); + const drawOrder = Utils.newArray(slotCount, 0); + + for (let ii = slotCount - 1; ii >= 0; ii--) drawOrder[ii] = -1; + const unchanged = Utils.newArray(slotCount - offsetCount, 0); + let originalIndex = 0; + let unchangedIndex = 0; + for (let ii = 0; ii < offsetCount; ii++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; + + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + input.readInt(true)] = originalIndex++; } // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. - for (let ii = slotCount - 1; ii >= 0; ii--) - if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + for (let ii = slotCount - 1; ii >= 0; ii--) if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; timeline.setFrame(i, time, drawOrder); } timelines.push(timeline); } // Event timeline. - let eventCount = input.readInt(true); + const eventCount = input.readInt(true); + if (eventCount > 0) { - let timeline = new EventTimeline(eventCount); + const timeline = new EventTimeline(eventCount); + for (let i = 0; i < eventCount; i++) { - let time = input.readFloat(); - let eventData = skeletonData.events[input.readInt(true)]; - let event = new Event(time, eventData); + const time = input.readFloat(); + const eventData = skeletonData.events[input.readInt(true)]; + const event = new Event(time, eventData); + event.intValue = input.readInt(false); event.floatValue = input.readFloat(); event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue; @@ -942,19 +1050,21 @@ export class SkeletonBinary { } let duration = 0; - for (let i = 0, n = timelines.length; i < n; i++) - duration = Math.max(duration, timelines[i].getDuration()); + + for (let i = 0, n = timelines.length; i < n; i++) duration = Math.max(duration, timelines[i].getDuration()); + return new Animation(name, timelines, duration); } } class LinkedMesh { - parent: string; skin: string; + parent: string; + skin: string; slotIndex: number; mesh: MeshAttachment; inheritDeform: boolean; - constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { + constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; @@ -964,15 +1074,19 @@ class LinkedMesh { } class Vertices { - constructor (public bones: Array = null, public vertices: Array | Float32Array = null) { } + constructor(public bones: Array = null, public vertices: Array | Float32Array = null) {} } -function readTimeline1 (input: BinaryInput, timeline: CurveTimeline1, scale: number): CurveTimeline1 { - let time = input.readFloat(), value = input.readFloat() * scale; +function readTimeline1(input: BinaryInput, timeline: CurveTimeline1, scale: number): CurveTimeline1 { + let time = input.readFloat(); + let value = input.readFloat() * scale; + for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) { timeline.setFrame(frame, time, value); if (frame == frameLast) break; - let time2 = input.readFloat(), value2 = input.readFloat() * scale; + const time2 = input.readFloat(); + const value2 = input.readFloat() * scale; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -983,15 +1097,22 @@ function readTimeline1 (input: BinaryInput, timeline: CurveTimeline1, scale: num time = time2; value = value2; } + return timeline; } -function readTimeline2 (input: BinaryInput, timeline: CurveTimeline2, scale: number): CurveTimeline2 { - let time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale; +function readTimeline2(input: BinaryInput, timeline: CurveTimeline2, scale: number): CurveTimeline2 { + let time = input.readFloat(); + let value1 = input.readFloat() * scale; + let value2 = input.readFloat() * scale; + for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) { timeline.setFrame(frame, time, value1, value2); if (frame == frameLast) break; - let time2 = input.readFloat(), nvalue1 = input.readFloat() * scale, nvalue2 = input.readFloat() * scale; + const time2 = input.readFloat(); + const nvalue1 = input.readFloat() * scale; + const nvalue2 = input.readFloat() * scale; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -1004,11 +1125,22 @@ function readTimeline2 (input: BinaryInput, timeline: CurveTimeline2, scale: num value1 = nvalue1; value2 = nvalue2; } + return timeline; } -function setBezier (input: BinaryInput, timeline: CurveTimeline, bezier: number, frame: number, value: number, - time1: number, time2: number, value1: number, value2: number, scale: number) { +function setBezier( + input: BinaryInput, + timeline: CurveTimeline, + bezier: number, + frame: number, + value: number, + time1: number, + time2: number, + value1: number, + value2: number, + scale: number +) { timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), input.readFloat() * scale, time2, value2); } @@ -1035,6 +1167,6 @@ const PATH_SPACING = 1; const PATH_MIX = 2; // @ts-ignore -const CURVE_LINEAR = 0; -const CURVE_STEPPED = 1; -const CURVE_BEZIER = 2; +const CURVE_LINEAR = 0; +const CURVE_STEPPED = 1; +const CURVE_BEZIER = 2; diff --git a/packages/runtime-4.0/src/core/SkeletonBounds.ts b/packages/runtime-4.0/src/core/SkeletonBounds.ts index cce4ffe0..126d81aa 100644 --- a/packages/runtime-4.0/src/core/SkeletonBounds.ts +++ b/packages/runtime-4.0/src/core/SkeletonBounds.ts @@ -1,8 +1,8 @@ -import {BoundingBoxAttachment} from "./attachments"; -import {SkeletonBoundsBase} from "@pixi-spine/base"; +import type { BoundingBoxAttachment } from './attachments'; +import { SkeletonBoundsBase } from '@pixi-spine/base'; /** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are * provided along with convenience methods for doing hit detection. * @public * */ - export class SkeletonBounds extends SkeletonBoundsBase{}; \ No newline at end of file +export class SkeletonBounds extends SkeletonBoundsBase {} diff --git a/packages/runtime-4.0/src/core/SkeletonData.ts b/packages/runtime-4.0/src/core/SkeletonData.ts index 8c056774..f034b83f 100644 --- a/packages/runtime-4.0/src/core/SkeletonData.ts +++ b/packages/runtime-4.0/src/core/SkeletonData.ts @@ -1,12 +1,12 @@ -import type {ISkeletonData} from "@pixi-spine/base"; -import type {Animation} from "./Animation"; -import {BoneData} from "./BoneData"; -import {SlotData} from "./SlotData"; -import {Skin} from "./Skin"; -import {EventData} from "./EventData"; -import {IkConstraintData} from "./IkConstraintData"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {PathConstraintData} from "./PathConstraintData"; +import type { ISkeletonData } from '@pixi-spine/base'; +import type { Animation } from './Animation'; +import type { BoneData } from './BoneData'; +import type { SlotData } from './SlotData'; +import type { Skin } from './Skin'; +import type { EventData } from './EventData'; +import type { IkConstraintData } from './IkConstraintData'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { PathConstraintData } from './PathConstraintData'; /** Stores the setup pose and all of the stateless data for a skeleton. * @@ -15,7 +15,6 @@ import {PathConstraintData} from "./PathConstraintData"; * @public * */ export class SkeletonData implements ISkeletonData { - /** The skeleton's name, which by default is the name of the skeleton data file, if possible. May be null. */ name: string = null; @@ -48,16 +47,16 @@ export class SkeletonData implements ISkeletonData(); /** The X coordinate of the skeleton's axis aligned bounding box in the setup pose. */ - x: number = 0; + x = 0; /** The Y coordinate of the skeleton's axis aligned bounding box in the setup pose. */ - y: number = 0; + y = 0; /** The width of the skeleton's axis aligned bounding box in the setup pose. */ - width: number = 0; + width = 0; /** The height of the skeleton's axis aligned bounding box in the setup pose. */ - height: number = 0; + height = 0; /** The Spine version used to export the skeleton data, or null. */ version: string = null; @@ -78,130 +77,157 @@ export class SkeletonData implements ISkeletonData(); - constructor (attachmentLoader: AttachmentLoader) { + constructor(attachmentLoader: AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - readSkeletonData (json: string | any): SkeletonData { - let scale = this.scale; - let skeletonData = new SkeletonData(); - let root = typeof (json) === "string" ? JSON.parse(json) : json; + readSkeletonData(json: string | any): SkeletonData { + const scale = this.scale; + const skeletonData = new SkeletonData(); + const root = typeof json === 'string' ? JSON.parse(json) : json; // Skeleton - let skeletonMap = root.skeleton; + const skeletonMap = root.skeleton; + if (skeletonMap) { skeletonData.hash = skeletonMap.hash; skeletonData.version = skeletonMap.spine; if (skeletonData.version.substr(0, 3) !== '4.0') { - let error = `Spine 4.0 loader cant load version ${skeletonMap.spine}. Please configure your pixi-spine bundle`; + const error = `Spine 4.0 loader cant load version ${skeletonMap.spine}. Please configure your pixi-spine bundle`; + console.error(error); } skeletonData.x = skeletonMap.x; @@ -71,27 +90,30 @@ export class SkeletonJson { // Bones if (root.bones) { for (let i = 0; i < root.bones.length; i++) { - let boneMap = root.bones[i]; + const boneMap = root.bones[i]; let parent: BoneData = null; - let parentName: string = getValue(boneMap, "parent", null); + const parentName: string = getValue(boneMap, 'parent', null); + if (parentName != null) { parent = skeletonData.findBone(parentName); - if (parent == null) throw new Error("Parent bone not found: " + parentName); + if (parent == null) throw new Error(`Parent bone not found: ${parentName}`); } - let data = new BoneData(skeletonData.bones.length, boneMap.name, parent); - data.length = getValue(boneMap, "length", 0) * scale; - data.x = getValue(boneMap, "x", 0) * scale; - data.y = getValue(boneMap, "y", 0) * scale; - data.rotation = getValue(boneMap, "rotation", 0); - data.scaleX = getValue(boneMap, "scaleX", 1); - data.scaleY = getValue(boneMap, "scaleY", 1); - data.shearX = getValue(boneMap, "shearX", 0); - data.shearY = getValue(boneMap, "shearY", 0); - data.transformMode = Utils.enumValue(TransformMode, getValue(boneMap, "transform", "Normal")); - data.skinRequired = getValue(boneMap, "skin", false); - - let color = getValue(boneMap, "color", null); + const data = new BoneData(skeletonData.bones.length, boneMap.name, parent); + + data.length = getValue(boneMap, 'length', 0) * scale; + data.x = getValue(boneMap, 'x', 0) * scale; + data.y = getValue(boneMap, 'y', 0) * scale; + data.rotation = getValue(boneMap, 'rotation', 0); + data.scaleX = getValue(boneMap, 'scaleX', 1); + data.scaleY = getValue(boneMap, 'scaleY', 1); + data.shearX = getValue(boneMap, 'shearX', 0); + data.shearY = getValue(boneMap, 'shearY', 0); + data.transformMode = Utils.enumValue(TransformMode, getValue(boneMap, 'transform', 'Normal')); + data.skinRequired = getValue(boneMap, 'skin', false); + + const color = getValue(boneMap, 'color', null); + if (color) data.color.setFromString(color); skeletonData.bones.push(data); @@ -101,18 +123,20 @@ export class SkeletonJson { // Slots. if (root.slots) { for (let i = 0; i < root.slots.length; i++) { - let slotMap = root.slots[i]; - let boneData = skeletonData.findBone(slotMap.bone); - let data = new SlotData(skeletonData.slots.length, slotMap.name, boneData); + const slotMap = root.slots[i]; + const boneData = skeletonData.findBone(slotMap.bone); + const data = new SlotData(skeletonData.slots.length, slotMap.name, boneData); + + const color: string = getValue(slotMap, 'color', null); - let color: string = getValue(slotMap, "color", null); if (color) data.color.setFromString(color); - let dark: string = getValue(slotMap, "dark", null); + const dark: string = getValue(slotMap, 'dark', null); + if (dark) data.darkColor = Color.fromString(dark); - data.attachmentName = getValue(slotMap, "attachment", null); - data.blendMode = SkeletonJson.blendModeFromString(getValue(slotMap, "blend", "normal")); + data.attachmentName = getValue(slotMap, 'attachment', null); + data.blendMode = SkeletonJson.blendModeFromString(getValue(slotMap, 'blend', 'normal')); skeletonData.slots.push(data); } } @@ -120,26 +144,28 @@ export class SkeletonJson { // IK constraints if (root.ik) { for (let i = 0; i < root.ik.length; i++) { - let constraintMap = root.ik[i]; - let data = new IkConstraintData(constraintMap.name); - data.order = getValue(constraintMap, "order", 0); - data.skinRequired = getValue(constraintMap, "skin", false); + const constraintMap = root.ik[i]; + const data = new IkConstraintData(constraintMap.name); + + data.order = getValue(constraintMap, 'order', 0); + data.skinRequired = getValue(constraintMap, 'skin', false); for (let ii = 0; ii < constraintMap.bones.length; ii++) { - let boneName = constraintMap.bones[ii]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("IK bone not found: " + boneName); + const boneName = constraintMap.bones[ii]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`IK bone not found: ${boneName}`); data.bones.push(bone); } data.target = skeletonData.findBone(constraintMap.target); - data.mix = getValue(constraintMap, "mix", 1); - data.softness = getValue(constraintMap, "softness", 0) * scale; - data.bendDirection = getValue(constraintMap, "bendPositive", true) ? 1 : -1; - data.compress = getValue(constraintMap, "compress", false); - data.stretch = getValue(constraintMap, "stretch", false); - data.uniform = getValue(constraintMap, "uniform", false); + data.mix = getValue(constraintMap, 'mix', 1); + data.softness = getValue(constraintMap, 'softness', 0) * scale; + data.bendDirection = getValue(constraintMap, 'bendPositive', true) ? 1 : -1; + data.compress = getValue(constraintMap, 'compress', false); + data.stretch = getValue(constraintMap, 'stretch', false); + data.uniform = getValue(constraintMap, 'uniform', false); skeletonData.ikConstraints.push(data); } @@ -148,37 +174,40 @@ export class SkeletonJson { // Transform constraints. if (root.transform) { for (let i = 0; i < root.transform.length; i++) { - let constraintMap = root.transform[i]; - let data = new TransformConstraintData(constraintMap.name); - data.order = getValue(constraintMap, "order", 0); - data.skinRequired = getValue(constraintMap, "skin", false); + const constraintMap = root.transform[i]; + const data = new TransformConstraintData(constraintMap.name); + + data.order = getValue(constraintMap, 'order', 0); + data.skinRequired = getValue(constraintMap, 'skin', false); for (let ii = 0; ii < constraintMap.bones.length; ii++) { - let boneName = constraintMap.bones[ii]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); + const boneName = constraintMap.bones[ii]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`Transform constraint bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findBone(targetName); - if (data.target == null) throw new Error("Transform constraint target bone not found: " + targetName); - - data.local = getValue(constraintMap, "local", false); - data.relative = getValue(constraintMap, "relative", false); - data.offsetRotation = getValue(constraintMap, "rotation", 0); - data.offsetX = getValue(constraintMap, "x", 0) * scale; - data.offsetY = getValue(constraintMap, "y", 0) * scale; - data.offsetScaleX = getValue(constraintMap, "scaleX", 0); - data.offsetScaleY = getValue(constraintMap, "scaleY", 0); - data.offsetShearY = getValue(constraintMap, "shearY", 0); - - data.mixRotate = getValue(constraintMap, "mixRotate", 1); - data.mixX = getValue(constraintMap, "mixX", 1); - data.mixY = getValue(constraintMap, "mixY", data.mixX); - data.mixScaleX = getValue(constraintMap, "mixScaleX", 1); - data.mixScaleY = getValue(constraintMap, "mixScaleY", data.mixScaleX); - data.mixShearY = getValue(constraintMap, "mixShearY", 1); + if (data.target == null) throw new Error(`Transform constraint target bone not found: ${targetName}`); + + data.local = getValue(constraintMap, 'local', false); + data.relative = getValue(constraintMap, 'relative', false); + data.offsetRotation = getValue(constraintMap, 'rotation', 0); + data.offsetX = getValue(constraintMap, 'x', 0) * scale; + data.offsetY = getValue(constraintMap, 'y', 0) * scale; + data.offsetScaleX = getValue(constraintMap, 'scaleX', 0); + data.offsetScaleY = getValue(constraintMap, 'scaleY', 0); + data.offsetShearY = getValue(constraintMap, 'shearY', 0); + + data.mixRotate = getValue(constraintMap, 'mixRotate', 1); + data.mixX = getValue(constraintMap, 'mixX', 1); + data.mixY = getValue(constraintMap, 'mixY', data.mixX); + data.mixScaleX = getValue(constraintMap, 'mixScaleX', 1); + data.mixScaleY = getValue(constraintMap, 'mixScaleY', data.mixScaleX); + data.mixShearY = getValue(constraintMap, 'mixShearY', 1); skeletonData.transformConstraints.push(data); } @@ -187,33 +216,36 @@ export class SkeletonJson { // Path constraints. if (root.path) { for (let i = 0; i < root.path.length; i++) { - let constraintMap = root.path[i]; - let data = new PathConstraintData(constraintMap.name); - data.order = getValue(constraintMap, "order", 0); - data.skinRequired = getValue(constraintMap, "skin", false); + const constraintMap = root.path[i]; + const data = new PathConstraintData(constraintMap.name); + + data.order = getValue(constraintMap, 'order', 0); + data.skinRequired = getValue(constraintMap, 'skin', false); for (let ii = 0; ii < constraintMap.bones.length; ii++) { - let boneName = constraintMap.bones[ii]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); + const boneName = constraintMap.bones[ii]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`Transform constraint bone not found: ${boneName}`); data.bones.push(bone); } - let targetName: string = constraintMap.target; + const targetName: string = constraintMap.target; + data.target = skeletonData.findSlot(targetName); - if (data.target == null) throw new Error("Path target slot not found: " + targetName); + if (data.target == null) throw new Error(`Path target slot not found: ${targetName}`); - data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, "positionMode", "Percent")); - data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, "spacingMode", "Length")); - data.rotateMode = Utils.enumValue(RotateMode, getValue(constraintMap, "rotateMode", "Tangent")); - data.offsetRotation = getValue(constraintMap, "rotation", 0); - data.position = getValue(constraintMap, "position", 0); + data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, 'positionMode', 'Percent')); + data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, 'spacingMode', 'Length')); + data.rotateMode = Utils.enumValue(RotateMode, getValue(constraintMap, 'rotateMode', 'Tangent')); + data.offsetRotation = getValue(constraintMap, 'rotation', 0); + data.position = getValue(constraintMap, 'position', 0); if (data.positionMode == PositionMode.Fixed) data.position *= scale; - data.spacing = getValue(constraintMap, "spacing", 0); + data.spacing = getValue(constraintMap, 'spacing', 0); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale; - data.mixRotate = getValue(constraintMap, "mixRotate", 1); - data.mixX = getValue(constraintMap, "mixX", 1); - data.mixY = getValue(constraintMap, "mixY", data.mixX); + data.mixRotate = getValue(constraintMap, 'mixRotate', 1); + data.mixX = getValue(constraintMap, 'mixX', 1); + data.mixY = getValue(constraintMap, 'mixY', data.mixX); skeletonData.pathConstraints.push(data); } @@ -222,78 +254,87 @@ export class SkeletonJson { // Skins. if (root.skins) { for (let i = 0; i < root.skins.length; i++) { - let skinMap = root.skins[i] - let skin = new Skin(skinMap.name); + const skinMap = root.skins[i]; + const skin = new Skin(skinMap.name); if (skinMap.bones) { for (let ii = 0; ii < skinMap.bones.length; ii++) { - let bone = skeletonData.findBone(skinMap.bones[ii]); - if (bone == null) throw new Error("Skin bone not found: " + skinMap.bones[i]); + const bone = skeletonData.findBone(skinMap.bones[ii]); + + if (bone == null) throw new Error(`Skin bone not found: ${skinMap.bones[i]}`); skin.bones.push(bone); } } if (skinMap.ik) { for (let ii = 0; ii < skinMap.ik.length; ii++) { - let constraint = skeletonData.findIkConstraint(skinMap.ik[ii]); - if (constraint == null) throw new Error("Skin IK constraint not found: " + skinMap.ik[i]); + const constraint = skeletonData.findIkConstraint(skinMap.ik[ii]); + + if (constraint == null) throw new Error(`Skin IK constraint not found: ${skinMap.ik[i]}`); skin.constraints.push(constraint); } } if (skinMap.transform) { for (let ii = 0; ii < skinMap.transform.length; ii++) { - let constraint = skeletonData.findTransformConstraint(skinMap.transform[ii]); - if (constraint == null) throw new Error("Skin transform constraint not found: " + skinMap.transform[i]); + const constraint = skeletonData.findTransformConstraint(skinMap.transform[ii]); + + if (constraint == null) throw new Error(`Skin transform constraint not found: ${skinMap.transform[i]}`); skin.constraints.push(constraint); } } if (skinMap.path) { for (let ii = 0; ii < skinMap.path.length; ii++) { - let constraint = skeletonData.findPathConstraint(skinMap.path[ii]); - if (constraint == null) throw new Error("Skin path constraint not found: " + skinMap.path[i]); + const constraint = skeletonData.findPathConstraint(skinMap.path[ii]); + + if (constraint == null) throw new Error(`Skin path constraint not found: ${skinMap.path[i]}`); skin.constraints.push(constraint); } } - for (let slotName in skinMap.attachments) { - let slot = skeletonData.findSlot(slotName); - if (slot == null) throw new Error("Slot not found: " + slotName); - let slotMap = skinMap.attachments[slotName]; - for (let entryName in slotMap) { - let attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData); + for (const slotName in skinMap.attachments) { + const slot = skeletonData.findSlot(slotName); + + if (slot == null) throw new Error(`Slot not found: ${slotName}`); + const slotMap = skinMap.attachments[slotName]; + + for (const entryName in slotMap) { + const attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData); + if (attachment) skin.setAttachment(slot.index, entryName, attachment); } } skeletonData.skins.push(skin); - if (skin.name == "default") skeletonData.defaultSkin = skin; + if (skin.name == 'default') skeletonData.defaultSkin = skin; } } // Linked meshes. for (let i = 0, n = this.linkedMeshes.length; i < n; i++) { - let linkedMesh = this.linkedMeshes[i]; - let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); - let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + const linkedMesh = this.linkedMeshes[i]; + const skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + const parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? parent : linkedMesh.mesh; - linkedMesh.mesh.setParentMesh( parent); + linkedMesh.mesh.setParentMesh(parent); // linkedMesh.mesh.updateUVs(); } this.linkedMeshes.length = 0; // Events. if (root.events) { - for (let eventName in root.events) { - let eventMap = root.events[eventName]; - let data = new EventData(eventName); - data.intValue = getValue(eventMap, "int", 0); - data.floatValue = getValue(eventMap, "float", 0); - data.stringValue = getValue(eventMap, "string", ""); - data.audioPath = getValue(eventMap, "audio", null); + for (const eventName in root.events) { + const eventMap = root.events[eventName]; + const data = new EventData(eventName); + + data.intValue = getValue(eventMap, 'int', 0); + data.floatValue = getValue(eventMap, 'float', 0); + data.stringValue = getValue(eventMap, 'string', ''); + data.audioPath = getValue(eventMap, 'audio', null); if (data.audioPath) { - data.volume = getValue(eventMap, "volume", 1); - data.balance = getValue(eventMap, "balance", 0); + data.volume = getValue(eventMap, 'volume', 1); + data.balance = getValue(eventMap, 'balance', 0); } skeletonData.events.push(data); } @@ -301,8 +342,9 @@ export class SkeletonJson { // Animations. if (root.animations) { - for (let animationName in root.animations) { - let animationMap = root.animations[animationName]; + for (const animationName in root.animations) { + const animationMap = root.animations[animationName]; + this.readAnimation(animationMap, animationName, skeletonData); } } @@ -310,135 +352,166 @@ export class SkeletonJson { return skeletonData; } - readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment { - let scale = this.scale; - name = getValue(map, "name", name); + readAttachment(map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment { + const scale = this.scale; + + name = getValue(map, 'name', name); + + switch (getValue(map, 'type', 'region')) { + case 'region': { + const path = getValue(map, 'path', name); + const region = this.attachmentLoader.newRegionAttachment(skin, name, path); - switch (getValue(map, "type", "region")) { - case "region": { - let path = getValue(map, "path", name); - let region = this.attachmentLoader.newRegionAttachment(skin, name, path); if (!region) return null; region.path = path; - region.x = getValue(map, "x", 0) * scale; - region.y = getValue(map, "y", 0) * scale; - region.scaleX = getValue(map, "scaleX", 1); - region.scaleY = getValue(map, "scaleY", 1); - region.rotation = getValue(map, "rotation", 0); + region.x = getValue(map, 'x', 0) * scale; + region.y = getValue(map, 'y', 0) * scale; + region.scaleX = getValue(map, 'scaleX', 1); + region.scaleY = getValue(map, 'scaleY', 1); + region.rotation = getValue(map, 'rotation', 0); region.width = map.width * scale; region.height = map.height * scale; - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) region.color.setFromString(color); // region.updateOffset(); return region; } - case "boundingbox": { - let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + case 'boundingbox': { + const box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + if (!box) return null; this.readVertices(map, box, map.vertexCount << 1); - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) box.color.setFromString(color); + return box; } - case "mesh": - case "linkedmesh": { - let path = getValue(map, "path", name); - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + case 'mesh': + case 'linkedmesh': { + const path = getValue(map, 'path', name); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; mesh.path = path; - let color = getValue(map, "color", null); + const color = getValue(map, 'color', null); + if (color) mesh.color.setFromString(color); - mesh.width = getValue(map, "width", 0) * scale; - mesh.height = getValue(map, "height", 0) * scale; + mesh.width = getValue(map, 'width', 0) * scale; + mesh.height = getValue(map, 'height', 0) * scale; + + const parent: string = getValue(map, 'parent', null); - let parent: string = getValue(map, "parent", null); if (parent) { - this.linkedMeshes.push(new LinkedMesh(mesh, getValue(map, "skin", null), slotIndex, parent, getValue(map, "deform", true))); + this.linkedMeshes.push(new LinkedMesh(mesh, getValue(map, 'skin', null), slotIndex, parent, getValue(map, 'deform', true))); + return mesh; } - let uvs: Array = map.uvs; + const uvs: Array = map.uvs; + this.readVertices(map, mesh, uvs.length); mesh.triangles = map.triangles; mesh.regionUVs = new Float32Array(uvs); // mesh.updateUVs(); - mesh.edges = getValue(map, "edges", null); - mesh.hullLength = getValue(map, "hull", 0) * 2; + mesh.edges = getValue(map, 'edges', null); + mesh.hullLength = getValue(map, 'hull', 0) * 2; + return mesh; } - case "path": { - let path = this.attachmentLoader.newPathAttachment(skin, name); + case 'path': { + const path = this.attachmentLoader.newPathAttachment(skin, name); + if (!path) return null; - path.closed = getValue(map, "closed", false); - path.constantSpeed = getValue(map, "constantSpeed", true); + path.closed = getValue(map, 'closed', false); + path.constantSpeed = getValue(map, 'constantSpeed', true); + + const vertexCount = map.vertexCount; - let vertexCount = map.vertexCount; this.readVertices(map, path, vertexCount << 1); - let lengths: Array = Utils.newArray(vertexCount / 3, 0); - for (let i = 0; i < map.lengths.length; i++) - lengths[i] = map.lengths[i] * scale; + const lengths: Array = Utils.newArray(vertexCount / 3, 0); + + for (let i = 0; i < map.lengths.length; i++) lengths[i] = map.lengths[i] * scale; path.lengths = lengths; - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) path.color.setFromString(color); + return path; } - case "point": { - let point = this.attachmentLoader.newPointAttachment(skin, name); + case 'point': { + const point = this.attachmentLoader.newPointAttachment(skin, name); + if (!point) return null; - point.x = getValue(map, "x", 0) * scale; - point.y = getValue(map, "y", 0) * scale; - point.rotation = getValue(map, "rotation", 0); + point.x = getValue(map, 'x', 0) * scale; + point.y = getValue(map, 'y', 0) * scale; + point.rotation = getValue(map, 'rotation', 0); + + const color = getValue(map, 'color', null); - let color = getValue(map, "color", null); if (color) point.color.setFromString(color); + return point; } - case "clipping": { - let clip = this.attachmentLoader.newClippingAttachment(skin, name); + case 'clipping': { + const clip = this.attachmentLoader.newClippingAttachment(skin, name); + if (!clip) return null; - let end = getValue(map, "end", null); + const end = getValue(map, 'end', null); + if (end != null) { - let slot = skeletonData.findSlot(end); - if (slot == null) throw new Error("Clipping end slot not found: " + end); + const slot = skeletonData.findSlot(end); + + if (slot == null) throw new Error(`Clipping end slot not found: ${end}`); clip.endSlot = slot; } - let vertexCount = map.vertexCount; + const vertexCount = map.vertexCount; + this.readVertices(map, clip, vertexCount << 1); - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) clip.color.setFromString(color); + return clip; } } + return null; } - readVertices (map: any, attachment: VertexAttachment, verticesLength: number) { - let scale = this.scale; + readVertices(map: any, attachment: VertexAttachment, verticesLength: number) { + const scale = this.scale; + attachment.worldVerticesLength = verticesLength; - let vertices: Array = map.vertices; + const vertices: Array = map.vertices; + if (verticesLength == vertices.length) { - let scaledVertices = Utils.toFloatArray(vertices); + const scaledVertices = Utils.toFloatArray(vertices); + if (scale != 1) { - for (let i = 0, n = vertices.length; i < n; i++) - scaledVertices[i] *= scale; + for (let i = 0, n = vertices.length; i < n; i++) scaledVertices[i] *= scale; } attachment.vertices = scaledVertices; + return; } - let weights = new Array(); - let bones = new Array(); - for (let i = 0, n = vertices.length; i < n;) { - let boneCount = vertices[i++]; + const weights = new Array(); + const bones = new Array(); + + for (let i = 0, n = vertices.length; i < n; ) { + const boneCount = vertices[i++]; + bones.push(boneCount); for (let nn = i + boneCount * 4; i < nn; i += 4) { bones.push(vertices[i]); @@ -451,44 +524,50 @@ export class SkeletonJson { attachment.vertices = Utils.toFloatArray(weights); } - readAnimation (map: any, name: string, skeletonData: SkeletonData) { - let scale = this.scale; - let timelines = new Array(); + readAnimation(map: any, name: string, skeletonData: SkeletonData) { + const scale = this.scale; + const timelines = new Array(); // Slot timelines. if (map.slots) { - for (let slotName in map.slots) { - let slotMap = map.slots[slotName]; - let slotIndex = skeletonData.findSlot(slotName).index; - if (slotIndex == -1) throw new Error("Slot not found: " + slotName); - for (let timelineName in slotMap) { - let timelineMap = slotMap[timelineName]; + for (const slotName in map.slots) { + const slotMap = map.slots[slotName]; + const slotIndex = skeletonData.findSlot(slotName).index; + + if (slotIndex == -1) throw new Error(`Slot not found: ${slotName}`); + for (const timelineName in slotMap) { + const timelineMap = slotMap[timelineName]; + if (!timelineMap) continue; - let frames = timelineMap.length; - if (timelineName == "attachment") { - let timeline = new AttachmentTimeline(frames, slotIndex); + const frames = timelineMap.length; + + if (timelineName == 'attachment') { + const timeline = new AttachmentTimeline(frames, slotIndex); + for (let frame = 0; frame < frames; frame++) { - let keyMap = timelineMap[frame]; - timeline.setFrame(frame, getValue(keyMap, "time", 0), keyMap.name); + const keyMap = timelineMap[frame]; + + timeline.setFrame(frame, getValue(keyMap, 'time', 0), keyMap.name); } timelines.push(timeline); - - } else if (timelineName == "rgba") { - let timeline = new RGBATimeline(frames, frames << 2, slotIndex); + } else if (timelineName == 'rgba') { + const timeline = new RGBATimeline(frames, frames << 2, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.color); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b, color.a); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.color); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.color); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -501,23 +580,24 @@ export class SkeletonJson { } timelines.push(timeline); - - } else if (timelineName == "rgb") { - let timeline = new RGBTimeline(frames, frames * 3, slotIndex); + } else if (timelineName == 'rgb') { + const timeline = new RGBTimeline(frames, frames * 3, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.color); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.color); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.color); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -529,28 +609,29 @@ export class SkeletonJson { } timelines.push(timeline); - - } else if (timelineName == "alpha") { + } else if (timelineName == 'alpha') { timelines.push(readTimeline1(timelineMap, new AlphaTimeline(frames, frames, slotIndex), 0, 1)); - } else if (timelineName == "rgba2") { - let timeline = new RGBA2Timeline(frames, frames * 7, slotIndex); + } else if (timelineName == 'rgba2') { + const timeline = new RGBA2Timeline(frames, frames * 7, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.light); let color2 = Color.fromString(keyMap.dark); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.light); - let newColor2 = Color.fromString(nextMap.dark); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.light); + const newColor2 = Color.fromString(nextMap.dark); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -567,26 +648,27 @@ export class SkeletonJson { } timelines.push(timeline); - - } else if (timelineName == "rgb2") { - let timeline = new RGB2Timeline(frames, frames * 6, slotIndex); + } else if (timelineName == 'rgb2') { + const timeline = new RGB2Timeline(frames, frames * 6, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.light); let color2 = Color.fromString(keyMap.dark); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b, color2.r, color2.g, color2.b); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.light); - let newColor2 = Color.fromString(nextMap.dark); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.light); + const newColor2 = Color.fromString(nextMap.dark); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -602,51 +684,61 @@ export class SkeletonJson { } timelines.push(timeline); - } else - throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } else throw new Error(`Invalid timeline type for a slot: ${timelineName} (${slotName})`); } } } // Bone timelines. if (map.bones) { - for (let boneName in map.bones) { - let boneMap = map.bones[boneName]; - let boneIndex = skeletonData.findBone(boneName).index; - if (boneIndex == -1) throw new Error("Bone not found: " + boneName); - for (let timelineName in boneMap) { - let timelineMap = boneMap[timelineName]; - let frames = timelineMap.length; + for (const boneName in map.bones) { + const boneMap = map.bones[boneName]; + const boneIndex = skeletonData.findBone(boneName).index; + + if (boneIndex == -1) throw new Error(`Bone not found: ${boneName}`); + for (const timelineName in boneMap) { + const timelineMap = boneMap[timelineName]; + const frames = timelineMap.length; + if (frames == 0) continue; - if (timelineName === "rotate") { + if (timelineName === 'rotate') { timelines.push(readTimeline1(timelineMap, new RotateTimeline(frames, frames, boneIndex), 0, 1)); - } else if (timelineName === "translate") { - let timeline = new TranslateTimeline(frames, frames << 1, boneIndex); - timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, scale)); - } else if (timelineName === "translatex") { - let timeline = new TranslateXTimeline(frames, frames, boneIndex); + } else if (timelineName === 'translate') { + const timeline = new TranslateTimeline(frames, frames << 1, boneIndex); + + timelines.push(readTimeline2(timelineMap, timeline, 'x', 'y', 0, scale)); + } else if (timelineName === 'translatex') { + const timeline = new TranslateXTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, scale)); - } else if (timelineName === "translatey") { - let timeline = new TranslateYTimeline(frames, frames, boneIndex); + } else if (timelineName === 'translatey') { + const timeline = new TranslateYTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, scale)); - } else if (timelineName === "scale") { - let timeline = new ScaleTimeline(frames, frames << 1, boneIndex); - timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 1, 1)); - } else if (timelineName === "scalex") { - let timeline = new ScaleXTimeline(frames, frames, boneIndex); + } else if (timelineName === 'scale') { + const timeline = new ScaleTimeline(frames, frames << 1, boneIndex); + + timelines.push(readTimeline2(timelineMap, timeline, 'x', 'y', 1, 1)); + } else if (timelineName === 'scalex') { + const timeline = new ScaleXTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 1, 1)); - } else if (timelineName === "scaley") { - let timeline = new ScaleYTimeline(frames, frames, boneIndex); + } else if (timelineName === 'scaley') { + const timeline = new ScaleYTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 1, 1)); - } else if (timelineName === "shear") { - let timeline = new ShearTimeline(frames, frames << 1, boneIndex); - timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, 1)); - } else if (timelineName === "shearx") { - let timeline = new ShearXTimeline(frames, frames, boneIndex); + } else if (timelineName === 'shear') { + const timeline = new ShearTimeline(frames, frames << 1, boneIndex); + + timelines.push(readTimeline2(timelineMap, timeline, 'x', 'y', 0, 1)); + } else if (timelineName === 'shearx') { + const timeline = new ShearXTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, 1)); - } else if (timelineName === "sheary") { - let timeline = new ShearYTimeline(frames, frames, boneIndex); + } else if (timelineName === 'sheary') { + const timeline = new ShearYTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, 1)); } } @@ -655,31 +747,42 @@ export class SkeletonJson { // IK constraint timelines. if (map.ik) { - for (let constraintName in map.ik) { - let constraintMap = map.ik[constraintName]; + for (const constraintName in map.ik) { + const constraintMap = map.ik[constraintName]; let keyMap = constraintMap[0]; + if (!keyMap) continue; - let constraint = skeletonData.findIkConstraint(constraintName); - let constraintIndex = skeletonData.ikConstraints.indexOf(constraint); - let timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex); + const constraint = skeletonData.findIkConstraint(constraintName); + const constraintIndex = skeletonData.ikConstraints.indexOf(constraint); + const timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex); - let time = getValue(keyMap, "time", 0); - let mix = getValue(keyMap, "mix", 1); - let softness = getValue(keyMap, "softness", 0) * scale; + let time = getValue(keyMap, 'time', 0); + let mix = getValue(keyMap, 'mix', 1); + let softness = getValue(keyMap, 'softness', 0) * scale; for (let frame = 0, bezier = 0; ; frame++) { - timeline.setFrame(frame, time, mix, softness, getValue(keyMap, "bendPositive", true) ? 1 : -1, getValue(keyMap, "compress", false), getValue(keyMap, "stretch", false)); - let nextMap = constraintMap[frame + 1]; + timeline.setFrame( + frame, + time, + mix, + softness, + getValue(keyMap, 'bendPositive', true) ? 1 : -1, + getValue(keyMap, 'compress', false), + getValue(keyMap, 'stretch', false) + ); + const nextMap = constraintMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let mix2 = getValue(nextMap, "mix", 1); - let softness2 = getValue(nextMap, "softness", 0) * scale; - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const mix2 = getValue(nextMap, 'mix', 1); + const softness2 = getValue(nextMap, 'softness', 0) * scale; + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, scale); @@ -696,39 +799,42 @@ export class SkeletonJson { // Transform constraint timelines. if (map.transform) { - for (let constraintName in map.transform) { - let timelineMap = map.transform[constraintName]; + for (const constraintName in map.transform) { + const timelineMap = map.transform[constraintName]; let keyMap = timelineMap[0]; + if (!keyMap) continue; - let constraint = skeletonData.findTransformConstraint(constraintName); - let constraintIndex = skeletonData.transformConstraints.indexOf(constraint); - let timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length * 6, constraintIndex); + const constraint = skeletonData.findTransformConstraint(constraintName); + const constraintIndex = skeletonData.transformConstraints.indexOf(constraint); + const timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length * 6, constraintIndex); - let time = getValue(keyMap, "time", 0); - let mixRotate = getValue(keyMap, "mixRotate", 1); - let mixX = getValue(keyMap, "mixX", 1); - let mixY = getValue(keyMap, "mixY", mixX); - let mixScaleX = getValue(keyMap, "mixScaleX", 1); - let mixScaleY = getValue(keyMap, "mixScaleY", mixScaleX); - let mixShearY = getValue(keyMap, "mixShearY", 1); + let time = getValue(keyMap, 'time', 0); + let mixRotate = getValue(keyMap, 'mixRotate', 1); + let mixX = getValue(keyMap, 'mixX', 1); + let mixY = getValue(keyMap, 'mixY', mixX); + let mixScaleX = getValue(keyMap, 'mixScaleX', 1); + let mixScaleY = getValue(keyMap, 'mixScaleY', mixScaleX); + const mixShearY = getValue(keyMap, 'mixShearY', 1); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let mixRotate2 = getValue(nextMap, "mixRotate", 1); - let mixX2 = getValue(nextMap, "mixX", 1); - let mixY2 = getValue(nextMap, "mixY", mixX2); - let mixScaleX2 = getValue(nextMap, "mixScaleX", 1); - let mixScaleY2 = getValue(nextMap, "mixScaleY", mixScaleX2); - let mixShearY2 = getValue(nextMap, "mixShearY", 1); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const mixRotate2 = getValue(nextMap, 'mixRotate', 1); + const mixX2 = getValue(nextMap, 'mixX', 1); + const mixY2 = getValue(nextMap, 'mixY', mixX2); + const mixScaleX2 = getValue(nextMap, 'mixScaleX', 1); + const mixScaleY2 = getValue(nextMap, 'mixScaleY', mixScaleX2); + const mixShearY2 = getValue(nextMap, 'mixShearY', 1); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1); @@ -753,41 +859,52 @@ export class SkeletonJson { // Path constraint timelines. if (map.path) { - for (let constraintName in map.path) { - let constraintMap = map.path[constraintName]; - let constraintIndex = skeletonData.findPathConstraintIndex(constraintName); - if (constraintIndex == -1) throw new Error("Path constraint not found: " + constraintName); - let constraint = skeletonData.pathConstraints[constraintIndex]; - for (let timelineName in constraintMap) { - let timelineMap = constraintMap[timelineName]; + for (const constraintName in map.path) { + const constraintMap = map.path[constraintName]; + const constraintIndex = skeletonData.findPathConstraintIndex(constraintName); + + if (constraintIndex == -1) throw new Error(`Path constraint not found: ${constraintName}`); + const constraint = skeletonData.pathConstraints[constraintIndex]; + + for (const timelineName in constraintMap) { + const timelineMap = constraintMap[timelineName]; let keyMap = timelineMap[0]; + if (!keyMap) continue; - let frames = timelineMap.length; - if (timelineName === "position") { - let timeline = new PathConstraintPositionTimeline(frames, frames, constraintIndex); + const frames = timelineMap.length; + + if (timelineName === 'position') { + const timeline = new PathConstraintPositionTimeline(frames, frames, constraintIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.positionMode == PositionMode.Fixed ? scale : 1)); - } else if (timelineName === "spacing") { - let timeline = new PathConstraintSpacingTimeline(frames, frames, constraintIndex); - timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.spacingMode == SpacingMode.Length || constraint.spacingMode == SpacingMode.Fixed ? scale : 1)); - } else if (timelineName === "mix") { - let timeline = new PathConstraintMixTimeline(frames, frames * 3, constraintIndex); - let time = getValue(keyMap, "time", 0); - let mixRotate = getValue(keyMap, "mixRotate", 1); - let mixX = getValue(keyMap, "mixX", 1); - let mixY = getValue(keyMap, "mixY", mixX); + } else if (timelineName === 'spacing') { + const timeline = new PathConstraintSpacingTimeline(frames, frames, constraintIndex); + + timelines.push( + readTimeline1(timelineMap, timeline, 0, constraint.spacingMode == SpacingMode.Length || constraint.spacingMode == SpacingMode.Fixed ? scale : 1) + ); + } else if (timelineName === 'mix') { + const timeline = new PathConstraintMixTimeline(frames, frames * 3, constraintIndex); + let time = getValue(keyMap, 'time', 0); + let mixRotate = getValue(keyMap, 'mixRotate', 1); + let mixX = getValue(keyMap, 'mixX', 1); + let mixY = getValue(keyMap, 'mixY', mixX); + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let mixRotate2 = getValue(nextMap, "mixRotate", 1); - let mixX2 = getValue(nextMap, "mixX", 1); - let mixY2 = getValue(nextMap, "mixY", mixX2); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const mixRotate2 = getValue(nextMap, 'mixRotate', 1); + const mixX2 = getValue(nextMap, 'mixX', 1); + const mixY2 = getValue(nextMap, 'mixY', mixX2); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1); @@ -807,58 +924,63 @@ export class SkeletonJson { // Deform timelines. if (map.deform) { - for (let deformName in map.deform) { - let deformMap = map.deform[deformName]; - let skin = skeletonData.findSkin(deformName); + for (const deformName in map.deform) { + const deformMap = map.deform[deformName]; + const skin = skeletonData.findSkin(deformName); + if (skin == null) { - if (settings.FAIL_ON_NON_EXISTING_SKIN) { - throw new Error("Skin not found: " + deformName); - } else { - continue; - } + if (settings.FAIL_ON_NON_EXISTING_SKIN) { + throw new Error(`Skin not found: ${deformName}`); + } else { + continue; + } } - for (let slotName in deformMap) { - let slotMap = deformMap[slotName]; - let slotIndex = skeletonData.findSlot(slotName).index; - for (let timelineName in slotMap) { - let timelineMap = slotMap[timelineName]; + for (const slotName in deformMap) { + const slotMap = deformMap[slotName]; + const slotIndex = skeletonData.findSlot(slotName).index; + + for (const timelineName in slotMap) { + const timelineMap = slotMap[timelineName]; let keyMap = timelineMap[0]; + if (!keyMap) continue; - let attachment = skin.getAttachment(slotIndex, timelineName); - let weighted = attachment.bones; - let vertices = attachment.vertices; - let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + const attachment = skin.getAttachment(slotIndex, timelineName); + const weighted = attachment.bones; + const vertices = attachment.vertices; + const deformLength = weighted ? (vertices.length / 3) * 2 : vertices.length; + + const timeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment); + let time = getValue(keyMap, 'time', 0); - let timeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment); - let time = getValue(keyMap, "time", 0); for (let frame = 0, bezier = 0; ; frame++) { let deform: NumberArrayLike; - let verticesValue: Array = getValue(keyMap, "vertices", null); - if (!verticesValue) - deform = weighted ? Utils.newFloatArray(deformLength) : vertices; + const verticesValue: Array = getValue(keyMap, 'vertices', null); + + if (!verticesValue) deform = weighted ? Utils.newFloatArray(deformLength) : vertices; else { deform = Utils.newFloatArray(deformLength); - let start = getValue(keyMap, "offset", 0); + const start = getValue(keyMap, 'offset', 0); + Utils.arrayCopy(verticesValue, 0, deform, start, verticesValue.length); if (scale != 1) { - for (let i = start, n = i + verticesValue.length; i < n; i++) - deform[i] *= scale; + for (let i = start, n = i + verticesValue.length; i < n; i++) deform[i] *= scale; } if (!weighted) { - for (let i = 0; i < deformLength; i++) - deform[i] += vertices[i]; + for (let i = 0; i < deformLength; i++) deform[i] += vertices[i]; } } timeline.setFrame(frame, time, deform); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const curve = keyMap.curve; + if (curve) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1); time = time2; keyMap = nextMap; @@ -871,52 +993,56 @@ export class SkeletonJson { // Draw order timelines. if (map.drawOrder) { - let timeline = new DrawOrderTimeline(map.drawOrder.length); - let slotCount = skeletonData.slots.length; + const timeline = new DrawOrderTimeline(map.drawOrder.length); + const slotCount = skeletonData.slots.length; let frame = 0; + for (let i = 0; i < map.drawOrder.length; i++, frame++) { - let drawOrderMap = map.drawOrder[i]; + const drawOrderMap = map.drawOrder[i]; let drawOrder: Array = null; - let offsets = getValue(drawOrderMap, "offsets", null); + const offsets = getValue(drawOrderMap, 'offsets', null); + if (offsets) { drawOrder = Utils.newArray(slotCount, -1); - let unchanged = Utils.newArray(slotCount - offsets.length, 0); - let originalIndex = 0, unchangedIndex = 0; + const unchanged = Utils.newArray(slotCount - offsets.length, 0); + let originalIndex = 0; + let unchangedIndex = 0; + for (let ii = 0; ii < offsets.length; ii++) { - let offsetMap = offsets[ii]; - let slotIndex = skeletonData.findSlot(offsetMap.slot).index; + const offsetMap = offsets[ii]; + const slotIndex = skeletonData.findSlot(offsetMap.slot).index; // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; + + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + offsetMap.offset] = originalIndex++; } // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. - for (let ii = slotCount - 1; ii >= 0; ii--) - if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + for (let ii = slotCount - 1; ii >= 0; ii--) if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; } - timeline.setFrame(frame, getValue(drawOrderMap, "time", 0), drawOrder); + timeline.setFrame(frame, getValue(drawOrderMap, 'time', 0), drawOrder); } timelines.push(timeline); } // Event timelines. if (map.events) { - let timeline = new EventTimeline(map.events.length); + const timeline = new EventTimeline(map.events.length); let frame = 0; + for (let i = 0; i < map.events.length; i++, frame++) { - let eventMap = map.events[i]; - let eventData = skeletonData.findEvent(eventMap.name); - let event = new Event(Utils.toSinglePrecision(getValue(eventMap, "time", 0)), eventData); - event.intValue = getValue(eventMap, "int", eventData.intValue); - event.floatValue = getValue(eventMap, "float", eventData.floatValue); - event.stringValue = getValue(eventMap, "string", eventData.stringValue); + const eventMap = map.events[i]; + const eventData = skeletonData.findEvent(eventMap.name); + const event = new Event(Utils.toSinglePrecision(getValue(eventMap, 'time', 0)), eventData); + + event.intValue = getValue(eventMap, 'int', eventData.intValue); + event.floatValue = getValue(eventMap, 'float', eventData.floatValue); + event.stringValue = getValue(eventMap, 'string', eventData.stringValue); if (event.data.audioPath) { - event.volume = getValue(eventMap, "volume", 1); - event.balance = getValue(eventMap, "balance", 0); + event.volume = getValue(eventMap, 'volume', 1); + event.balance = getValue(eventMap, 'balance', 0); } timeline.setFrame(frame, event); } @@ -924,31 +1050,32 @@ export class SkeletonJson { } let duration = 0; - for (let i = 0, n = timelines.length; i < n; i++) - duration = Math.max(duration, timelines[i].getDuration()); + + for (let i = 0, n = timelines.length; i < n; i++) duration = Math.max(duration, timelines[i].getDuration()); if (isNaN(duration)) { - throw new Error("Error while parsing animation, duration is NaN"); + throw new Error('Error while parsing animation, duration is NaN'); } skeletonData.animations.push(new Animation(name, timelines, duration)); } - static blendModeFromString (str: string) { + static blendModeFromString(str: string) { str = str.toLowerCase(); - if (str == "normal") return BLEND_MODES.NORMAL; - if (str == "additive") return BLEND_MODES.ADD; - if (str == "multiply") return BLEND_MODES.MULTIPLY; - if (str == "screen") return BLEND_MODES.SCREEN; + if (str == 'normal') return BLEND_MODES.NORMAL; + if (str == 'additive') return BLEND_MODES.ADD; + if (str == 'multiply') return BLEND_MODES.MULTIPLY; + if (str == 'screen') return BLEND_MODES.SCREEN; throw new Error(`Unknown blend mode: ${str}`); } } class LinkedMesh { - parent: string; skin: string; + parent: string; + skin: string; slotIndex: number; mesh: MeshAttachment; inheritDeform: boolean; - constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { + constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; @@ -957,20 +1084,24 @@ class LinkedMesh { } } -function readTimeline1 (keys: any[], timeline: CurveTimeline1, defaultValue: number, scale: number) { +function readTimeline1(keys: any[], timeline: CurveTimeline1, defaultValue: number, scale: number) { let keyMap = keys[0]; - let time = getValue(keyMap, "time", 0); - let value = getValue(keyMap, "value", defaultValue) * scale; + let time = getValue(keyMap, 'time', 0); + let value = getValue(keyMap, 'value', defaultValue) * scale; let bezier = 0; + for (let frame = 0; ; frame++) { timeline.setFrame(frame, time, value); - let nextMap = keys[frame + 1]; + const nextMap = keys[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); + return timeline; } - let time2 = getValue(nextMap, "time", 0); - let value2 = getValue(nextMap, "value", defaultValue) * scale; + const time2 = getValue(nextMap, 'time', 0); + const value2 = getValue(nextMap, 'value', defaultValue) * scale; + if (keyMap.curve) bezier = readCurve(keyMap.curve, timeline, bezier, frame, 0, time, time2, value, value2, scale); time = time2; value = value2; @@ -978,23 +1109,27 @@ function readTimeline1 (keys: any[], timeline: CurveTimeline1, defaultValue: num } } -function readTimeline2 (keys: any[], timeline: CurveTimeline2, name1: string, name2: string, defaultValue: number, scale: number) { +function readTimeline2(keys: any[], timeline: CurveTimeline2, name1: string, name2: string, defaultValue: number, scale: number) { let keyMap = keys[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let value1 = getValue(keyMap, name1, defaultValue) * scale; let value2 = getValue(keyMap, name2, defaultValue) * scale; let bezier = 0; + for (let frame = 0; ; frame++) { timeline.setFrame(frame, time, value1, value2); - let nextMap = keys[frame + 1]; + const nextMap = keys[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); + return timeline; } - let time2 = getValue(nextMap, "time", 0); - let nvalue1 = getValue(nextMap, name1, defaultValue) * scale; - let nvalue2 = getValue(nextMap, name2, defaultValue) * scale; - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const nvalue1 = getValue(nextMap, name1, defaultValue) * scale; + const nvalue2 = getValue(nextMap, name2, defaultValue) * scale; + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale); @@ -1006,21 +1141,23 @@ function readTimeline2 (keys: any[], timeline: CurveTimeline2, name1: string, na } } -function readCurve (curve: any, timeline: CurveTimeline, bezier: number, frame: number, value: number, time1: number, time2: number, - value1: number, value2: number, scale: number) { - if (curve == "stepped") { +function readCurve(curve: any, timeline: CurveTimeline, bezier: number, frame: number, value: number, time1: number, time2: number, value1: number, value2: number, scale: number) { + if (curve == 'stepped') { timeline.setStepped(frame); + return bezier; } - let i = value << 2; - let cx1 = curve[i]; - let cy1 = curve[i + 1] * scale; - let cx2 = curve[i + 2]; - let cy2 = curve[i + 3] * scale; + const i = value << 2; + const cx1 = curve[i]; + const cy1 = curve[i + 1] * scale; + const cx2 = curve[i + 2]; + const cy2 = curve[i + 3] * scale; + timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2); + return bezier + 1; } -function getValue (map: any, property: string, defaultValue: any) { +function getValue(map: any, property: string, defaultValue: any) { return map[property] !== undefined ? map[property] : defaultValue; } diff --git a/packages/runtime-4.0/src/core/Skin.ts b/packages/runtime-4.0/src/core/Skin.ts index 2a2f797b..9171adcf 100644 --- a/packages/runtime-4.0/src/core/Skin.ts +++ b/packages/runtime-4.0/src/core/Skin.ts @@ -1,15 +1,15 @@ -import {Attachment, MeshAttachment} from './attachments'; -import {BoneData} from "./BoneData"; -import {ConstraintData} from "./ConstraintData"; -import {Skeleton} from "./Skeleton"; +import { Attachment, MeshAttachment } from './attachments'; +import type { BoneData } from './BoneData'; +import type { ConstraintData } from './ConstraintData'; +import type { Skeleton } from './Skeleton'; -import type {Map, ISkin} from '@pixi-spine/base'; +import type { Map, ISkin } from '@pixi-spine/base'; /** Stores an entry in the skin consisting of the slot index, name, and attachment * @public * **/ export class SkinEntry { - constructor(public slotIndex: number, public name: string, public attachment: Attachment) { } + constructor(public slotIndex: number, public name: string, public attachment: Attachment) {} } /** Stores attachments by slot index and attachment name. @@ -26,25 +26,27 @@ export class Skin implements ISkin { bones = Array(); constraints = new Array(); - constructor (name: string) { - if (!name) throw new Error("name cannot be null."); + constructor(name: string) { + if (!name) throw new Error('name cannot be null.'); this.name = name; } /** Adds an attachment to the skin for the specified slot index and name. */ - setAttachment (slotIndex: number, name: string, attachment: Attachment) { - if (!attachment) throw new Error("attachment cannot be null."); - let attachments = this.attachments; + setAttachment(slotIndex: number, name: string, attachment: Attachment) { + if (!attachment) throw new Error('attachment cannot be null.'); + const attachments = this.attachments; + if (slotIndex >= attachments.length) attachments.length = slotIndex + 1; - if (!attachments[slotIndex]) attachments[slotIndex] = { }; + if (!attachments[slotIndex]) attachments[slotIndex] = {}; attachments[slotIndex][name] = attachment; } /** Adds all attachments, bones, and constraints from the specified skin to this skin. */ - addSkin (skin: Skin) { - for(let i = 0; i < skin.bones.length; i++) { - let bone = skin.bones[i]; + addSkin(skin: Skin) { + for (let i = 0; i < skin.bones.length; i++) { + const bone = skin.bones[i]; let contained = false; + for (let ii = 0; ii < this.bones.length; ii++) { if (this.bones[ii] == bone) { contained = true; @@ -54,9 +56,10 @@ export class Skin implements ISkin { if (!contained) this.bones.push(bone); } - for(let i = 0; i < skin.constraints.length; i++) { - let constraint = skin.constraints[i]; + for (let i = 0; i < skin.constraints.length; i++) { + const constraint = skin.constraints[i]; let contained = false; + for (let ii = 0; ii < this.constraints.length; ii++) { if (this.constraints[ii] == constraint) { contained = true; @@ -66,19 +69,22 @@ export class Skin implements ISkin { if (!contained) this.constraints.push(constraint); } - let attachments = skin.getAttachments(); + const attachments = skin.getAttachments(); + for (let i = 0; i < attachments.length; i++) { - var attachment = attachments[i]; + const attachment = attachments[i]; + this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment); } } /** Adds all bones and constraints and copies of all attachments from the specified skin to this skin. Mesh attachments are not * copied, instead a new linked mesh is created. The attachment copies can be modified without affecting the originals. */ - copySkin (skin: Skin) { - for(let i = 0; i < skin.bones.length; i++) { - let bone = skin.bones[i]; + copySkin(skin: Skin) { + for (let i = 0; i < skin.bones.length; i++) { + const bone = skin.bones[i]; let contained = false; + for (let ii = 0; ii < this.bones.length; ii++) { if (this.bones[ii] == bone) { contained = true; @@ -88,9 +94,10 @@ export class Skin implements ISkin { if (!contained) this.bones.push(bone); } - for(let i = 0; i < skin.constraints.length; i++) { - let constraint = skin.constraints[i]; + for (let i = 0; i < skin.constraints.length; i++) { + const constraint = skin.constraints[i]; let contained = false; + for (let ii = 0; ii < this.constraints.length; ii++) { if (this.constraints[ii] == constraint) { contained = true; @@ -100,9 +107,11 @@ export class Skin implements ISkin { if (!contained) this.constraints.push(constraint); } - let attachments = skin.getAttachments(); + const attachments = skin.getAttachments(); + for (let i = 0; i < attachments.length; i++) { - var attachment = attachments[i]; + const attachment = attachments[i]; + if (!attachment.attachment) continue; if (attachment.attachment instanceof MeshAttachment) { attachment.attachment = attachment.attachment.newLinkedMesh(); @@ -115,62 +124,75 @@ export class Skin implements ISkin { } /** Returns the attachment for the specified slot index and name, or null. */ - getAttachment (slotIndex: number, name: string): Attachment { - let dictionary = this.attachments[slotIndex]; + getAttachment(slotIndex: number, name: string): Attachment { + const dictionary = this.attachments[slotIndex]; + return dictionary ? dictionary[name] : null; } /** Removes the attachment in the skin for the specified slot index and name, if any. */ - removeAttachment (slotIndex: number, name: string) { - let dictionary = this.attachments[slotIndex]; + removeAttachment(slotIndex: number, name: string) { + const dictionary = this.attachments[slotIndex]; + if (dictionary) dictionary[name] = null; } /** Returns all attachments in this skin. */ - getAttachments (): Array { - let entries = new Array(); - for (var i = 0; i < this.attachments.length; i++) { - let slotAttachments = this.attachments[i]; + getAttachments(): Array { + const entries = new Array(); + + for (let i = 0; i < this.attachments.length; i++) { + const slotAttachments = this.attachments[i]; + if (slotAttachments) { - for (let name in slotAttachments) { - let attachment = slotAttachments[name]; + for (const name in slotAttachments) { + const attachment = slotAttachments[name]; + if (attachment) entries.push(new SkinEntry(i, name, attachment)); } } } + return entries; } /** Returns all attachments in this skin for the specified slot index. */ - getAttachmentsForSlot (slotIndex: number, attachments: Array) { - let slotAttachments = this.attachments[slotIndex]; + getAttachmentsForSlot(slotIndex: number, attachments: Array) { + const slotAttachments = this.attachments[slotIndex]; + if (slotAttachments) { - for (let name in slotAttachments) { - let attachment = slotAttachments[name]; + for (const name in slotAttachments) { + const attachment = slotAttachments[name]; + if (attachment) attachments.push(new SkinEntry(slotIndex, name, attachment)); } } } /** Clears all attachments, bones, and constraints. */ - clear () { + clear() { this.attachments.length = 0; this.bones.length = 0; this.constraints.length = 0; } /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ - attachAll (skeleton: Skeleton, oldSkin: Skin) { + attachAll(skeleton: Skeleton, oldSkin: Skin) { let slotIndex = 0; + for (let i = 0; i < skeleton.slots.length; i++) { - let slot = skeleton.slots[i]; - let slotAttachment = slot.getAttachment(); + const slot = skeleton.slots[i]; + const slotAttachment = slot.getAttachment(); + if (slotAttachment && slotIndex < oldSkin.attachments.length) { - let dictionary = oldSkin.attachments[slotIndex]; - for (let key in dictionary) { - let skinAttachment:Attachment = dictionary[key]; + const dictionary = oldSkin.attachments[slotIndex]; + + for (const key in dictionary) { + const skinAttachment: Attachment = dictionary[key]; + if (slotAttachment == skinAttachment) { - let attachment = this.getAttachment(slotIndex, key); + const attachment = this.getAttachment(slotIndex, key); + if (attachment) slot.setAttachment(attachment); break; } diff --git a/packages/runtime-4.0/src/core/Slot.ts b/packages/runtime-4.0/src/core/Slot.ts index c6ab92fb..65b9825b 100644 --- a/packages/runtime-4.0/src/core/Slot.ts +++ b/packages/runtime-4.0/src/core/Slot.ts @@ -1,9 +1,9 @@ -import {Color, ISlot} from '@pixi-spine/base'; +import { Color, ISlot } from '@pixi-spine/base'; -import type {Attachment} from './attachments/Attachment'; -import type {Bone} from './Bone'; -import type {SlotData} from './SlotData'; -import type {Skeleton} from './Skeleton'; +import type { Attachment } from './attachments/Attachment'; +import type { Bone } from './Bone'; +import type { SlotData } from './SlotData'; +import type { Skeleton } from './Skeleton'; /** Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store * state for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared @@ -11,7 +11,7 @@ import type {Skeleton} from './Skeleton'; * @public * */ export class Slot implements ISlot { - //this is canon + // this is canon blendMode: number; /** The slot's setup pose data. */ data: SlotData; @@ -39,9 +39,9 @@ export class Slot implements ISlot { * See {@link VertexAttachment#computeWorldVertices()} and {@link DeformTimeline}. */ deform = new Array(); - constructor (data: SlotData, bone: Bone) { - if (data == null) throw new Error("data cannot be null."); - if (bone == null) throw new Error("bone cannot be null."); + constructor(data: SlotData, bone: Bone) { + if (data == null) throw new Error('data cannot be null.'); + if (bone == null) throw new Error('bone cannot be null.'); this.data = data; this.bone = bone; this.color = new Color(); @@ -52,40 +52,39 @@ export class Slot implements ISlot { } /** The skeleton this slot belongs to. */ - getSkeleton (): Skeleton { + getSkeleton(): Skeleton { return this.bone.skeleton; } /** The current attachment for the slot, or null if the slot has no attachment. */ - getAttachment (): Attachment { + getAttachment(): Attachment { return this.attachment; } /** Sets the slot's attachment and, if the attachment changed, resets {@link #attachmentTime} and clears {@link #deform}. * @param attachment May be null. */ - setAttachment (attachment: Attachment) { + setAttachment(attachment: Attachment) { if (this.attachment == attachment) return; this.attachment = attachment; this.attachmentTime = this.bone.skeleton.time; this.deform.length = 0; } - setAttachmentTime (time: number) { + setAttachmentTime(time: number) { this.attachmentTime = this.bone.skeleton.time - time; } /** The time that has elapsed since the last time the attachment was set or cleared. Relies on Skeleton * {@link Skeleton#time}. */ - getAttachmentTime (): number { + getAttachmentTime(): number { return this.bone.skeleton.time - this.attachmentTime; } /** Sets this slot to the setup pose. */ - setToSetupPose () { + setToSetupPose() { this.color.setFromColor(this.data.color); if (this.darkColor != null) this.darkColor.setFromColor(this.data.darkColor); - if (this.data.attachmentName == null) - this.attachment = null; + if (this.data.attachmentName == null) this.attachment = null; else { this.attachment = null; this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName)); diff --git a/packages/runtime-4.0/src/core/SlotData.ts b/packages/runtime-4.0/src/core/SlotData.ts index d5072e5d..06618e46 100644 --- a/packages/runtime-4.0/src/core/SlotData.ts +++ b/packages/runtime-4.0/src/core/SlotData.ts @@ -1,14 +1,13 @@ -import {Color} from '@pixi-spine/base'; +import { Color } from '@pixi-spine/base'; -import type {ISlotData} from '@pixi-spine/base'; -import type {BLEND_MODES} from '@pixi/constants'; -import {BoneData} from "./BoneData"; +import type { ISlotData } from '@pixi-spine/base'; +import type { BLEND_MODES } from '@pixi/constants'; +import type { BoneData } from './BoneData'; /** Stores the setup pose for a {@link Slot}. * @public * */ export class SlotData implements ISlotData { - /** The index of the slot in {@link Skeleton#getSlots()}. */ index: number; @@ -32,13 +31,12 @@ export class SlotData implements ISlotData { /** The blend mode for drawing the slot's attachment. */ blendMode: BLEND_MODES; - constructor (index: number, name: string, boneData: BoneData) { - if (index < 0) throw new Error("index must be >= 0."); - if (!name) throw new Error("name cannot be null."); - if (!boneData) throw new Error("boneData cannot be null."); + constructor(index: number, name: string, boneData: BoneData) { + if (index < 0) throw new Error('index must be >= 0.'); + if (!name) throw new Error('name cannot be null.'); + if (!boneData) throw new Error('boneData cannot be null.'); this.index = index; this.name = name; this.boneData = boneData; } } - diff --git a/packages/runtime-4.0/src/core/TransformConstraint.ts b/packages/runtime-4.0/src/core/TransformConstraint.ts index 842544a5..5d9101b8 100644 --- a/packages/runtime-4.0/src/core/TransformConstraint.ts +++ b/packages/runtime-4.0/src/core/TransformConstraint.ts @@ -1,8 +1,8 @@ -import {Updatable} from "./Updatable"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {Bone} from "./Bone"; -import {MathUtils, Vector2} from "@pixi-spine/base"; -import {Skeleton} from "./Skeleton"; +import type { Updatable } from './Updatable'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { Bone } from './Bone'; +import { MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Skeleton } from './Skeleton'; /** Stores the current pose for a transform constraint. A transform constraint adjusts the world transform of the constrained * bones to match that of the target bone. @@ -11,7 +11,6 @@ import {Skeleton} from "./Skeleton"; * @public * */ export class TransformConstraint implements Updatable { - /** The transform constraint's setup pose data. */ data: TransformConstraintData; @@ -21,14 +20,19 @@ export class TransformConstraint implements Updatable { /** The target bone whose world transform will be copied to the constrained bones. */ target: Bone; - mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0; + mixRotate = 0; + mixX = 0; + mixY = 0; + mixScaleX = 0; + mixScaleY = 0; + mixShearY = 0; temp = new Vector2(); active = false; - constructor (data: TransformConstraintData, skeleton: Skeleton) { - if (!data) throw new Error("data cannot be null."); - if (!skeleton) throw new Error("skeleton cannot be null."); + constructor(data: TransformConstraintData, skeleton: Skeleton) { + if (!data) throw new Error('data cannot be null.'); + if (!skeleton) throw new Error('skeleton cannot be null.'); this.data = data; this.mixRotate = data.mixRotate; this.mixX = data.mixX; @@ -37,57 +41,64 @@ export class TransformConstraint implements Updatable { this.mixScaleY = data.mixScaleY; this.mixShearY = data.mixShearY; this.bones = new Array(); - for (let i = 0; i < data.bones.length; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); + for (let i = 0; i < data.bones.length; i++) this.bones.push(skeleton.findBone(data.bones[i].name)); this.target = skeleton.findBone(data.target.name); } - isActive () { + isActive() { return this.active; } - update () { + update() { if (this.mixRotate == 0 && this.mixX == 0 && this.mixY == 0 && this.mixScaleX == 0 && this.mixScaleY == 0 && this.mixShearY == 0) return; if (this.data.local) { - if (this.data.relative) - this.applyRelativeLocal(); - else - this.applyAbsoluteLocal(); - } else { - if (this.data.relative) - this.applyRelativeWorld(); - else - this.applyAbsoluteWorld(); - } + if (this.data.relative) this.applyRelativeLocal(); + else this.applyAbsoluteLocal(); + } else if (this.data.relative) this.applyRelativeWorld(); + else this.applyAbsoluteWorld(); } - applyAbsoluteWorld () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - let translate = mixX != 0 || mixY != 0; + applyAbsoluteWorld() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; + const translate = mixX != 0 || mixY != 0; - let target = this.target; + const target = this.target; const targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect; - let offsetShearY = this.data.offsetShearY * degRadReflect; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; + + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; const mat = bone.matrix; if (mixRotate != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= mixRotate; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -95,7 +106,8 @@ export class TransformConstraint implements Updatable { } if (translate) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += (temp.x - mat.tx) * mixX; mat.ty += (temp.y - mat.ty) * mixY; @@ -103,62 +115,80 @@ export class TransformConstraint implements Updatable { if (mixScaleX != 0) { let s = Math.sqrt(mat.a * mat.a + mat.b * mat.b); + if (s != 0) s = (s + (Math.sqrt(ta * ta + tc * tc) - s + this.data.offsetScaleX) * mixScaleX) / s; mat.a *= s; mat.b *= s; } if (mixScaleY != 0) { let s = Math.sqrt(mat.c * mat.c + mat.d * mat.d); + if (s != 0) s = (s + (Math.sqrt(tb * tb + td * td) - s + this.data.offsetScaleY) * mixScaleY) / s; mat.c *= s; mat.d *= s; - } if (mixShearY > 0) { - let b = mat.c, d = mat.d; - let by = Math.atan2(d, b); + const b = mat.c; + const d = mat.d; + const by = Math.atan2(d, b); let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(mat.b, mat.a)); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r = by + (r + offsetShearY) * mixShearY; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; - } bone.updateAppliedTransform(); } } - applyRelativeWorld () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - let translate = mixX != 0 || mixY != 0; + applyRelativeWorld() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; + const translate = mixX != 0 || mixY != 0; - let target = this.target; - let targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect, offsetShearY = this.data.offsetShearY * degRadReflect; + const target = this.target; + const targetMat = target.matrix; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; + + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; const mat = bone.matrix; if (mixRotate != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= mixRotate; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -166,32 +196,39 @@ export class TransformConstraint implements Updatable { } if (translate) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += temp.x * mixX; mat.ty += temp.y * mixY; } if (mixScaleX != 0) { - let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * mixScaleX + 1; + const s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * mixScaleX + 1; + mat.a *= s; mat.b *= s; } if (mixScaleY != 0) { - let s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * mixScaleY + 1; + const s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * mixScaleY + 1; + mat.c *= s; mat.d *= s; } if (mixShearY > 0) { let r = Math.atan2(td, tb) - Math.atan2(tc, ta); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; - let b = mat.c, d = mat.d; + const b = mat.c; + const d = mat.d; + r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; } @@ -200,36 +237,47 @@ export class TransformConstraint implements Updatable { } } - applyAbsoluteLocal () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; + applyAbsoluteLocal() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; + + const target = this.target; - let target = this.target; + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; let rotation = bone.arotation; + if (mixRotate != 0) { let r = target.arotation - rotation + this.data.offsetRotation; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; rotation += r * mixRotate; } - let x = bone.ax, y = bone.ay; + let x = bone.ax; + let y = bone.ay; + x += (target.ax - x + this.data.offsetX) * mixX; y += (target.ay - y + this.data.offsetY) * mixY; - let scaleX = bone.ascaleX, scaleY = bone.ascaleY; - if (mixScaleX != 0 && scaleX != 0) - scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * mixScaleX) / scaleX; - if (mixScaleY != 0 && scaleY != 0) - scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * mixScaleY) / scaleY; + let scaleX = bone.ascaleX; + let scaleY = bone.ascaleY; + + if (mixScaleX != 0 && scaleX != 0) scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * mixScaleX) / scaleX; + if (mixScaleY != 0 && scaleY != 0) scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * mixScaleY) / scaleY; let shearY = bone.ashearY; + if (mixShearY != 0) { let r = target.ashearY - shearY + this.data.offsetShearY; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; shearY += r * mixShearY; } @@ -238,22 +286,27 @@ export class TransformConstraint implements Updatable { } } - applyRelativeLocal () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; + applyRelativeLocal() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; + + const target = this.target; - let target = this.target; + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; - - let rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate; - let x = bone.ax + (target.ax + this.data.offsetX) * mixX; - let y = bone.ay + (target.ay + this.data.offsetY) * mixY; - let scaleX = bone.ascaleX * (((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX) + 1); - let scaleY = bone.ascaleY * (((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY) + 1); - let shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY; + const bone = bones[i]; + + const rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate; + const x = bone.ax + (target.ax + this.data.offsetX) * mixX; + const y = bone.ay + (target.ay + this.data.offsetY) * mixY; + const scaleX = bone.ascaleX * ((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX + 1); + const scaleY = bone.ascaleY * ((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY + 1); + const shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY; bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); } diff --git a/packages/runtime-4.0/src/core/TransformConstraintData.ts b/packages/runtime-4.0/src/core/TransformConstraintData.ts index 61a9cd1d..91a60862 100644 --- a/packages/runtime-4.0/src/core/TransformConstraintData.ts +++ b/packages/runtime-4.0/src/core/TransformConstraintData.ts @@ -1,5 +1,5 @@ -import {BoneData} from './BoneData'; -import {ConstraintData} from './ConstraintData'; +import type { BoneData } from './BoneData'; +import { ConstraintData } from './ConstraintData'; /** Stores the setup pose for a {@link TransformConstraint}. * @@ -7,7 +7,6 @@ import {ConstraintData} from './ConstraintData'; * @public * */ export class TransformConstraintData extends ConstraintData { - /** The bones that will be modified by this transform constraint. */ bones = new Array(); @@ -42,7 +41,7 @@ export class TransformConstraintData extends ConstraintData { relative = false; local = false; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } diff --git a/packages/runtime-4.0/src/core/VertexEffect.ts b/packages/runtime-4.0/src/core/VertexEffect.ts index a3a9cc9f..98ef8cf1 100644 --- a/packages/runtime-4.0/src/core/VertexEffect.ts +++ b/packages/runtime-4.0/src/core/VertexEffect.ts @@ -1,5 +1,5 @@ -import type {Skeleton} from "./Skeleton"; -import type {Color, Vector2} from "@pixi-spine/base"; +import type { Skeleton } from './Skeleton'; +import type { Color, Vector2 } from '@pixi-spine/base'; /** * @public diff --git a/packages/runtime-4.0/src/core/attachments/Attachment.ts b/packages/runtime-4.0/src/core/attachments/Attachment.ts index 141cd6bd..fb8948fc 100644 --- a/packages/runtime-4.0/src/core/attachments/Attachment.ts +++ b/packages/runtime-4.0/src/core/attachments/Attachment.ts @@ -1,7 +1,7 @@ -import {AttachmentType, Utils} from '@pixi-spine/base'; -import type {IAttachment, ArrayLike} from '@pixi-spine/base'; +import { AttachmentType, Utils } from '@pixi-spine/base'; +import type { IAttachment, ArrayLike } from '@pixi-spine/base'; -import type {Slot} from '../Slot'; +import type { Slot } from '../Slot'; /** * The base class for all attachments. @@ -11,12 +11,12 @@ export abstract class Attachment implements IAttachment { name: string; type: AttachmentType; - constructor (name: string) { - if (!name) throw new Error("name cannot be null."); + constructor(name: string) { + if (!name) throw new Error('name cannot be null.'); this.name = name; } - abstract copy (): Attachment; + abstract copy(): Attachment; } /** @@ -47,7 +47,7 @@ export abstract class VertexAttachment extends Attachment { /** Deform keys for the deform attachment are also applied to this attachment. May be null if no deform keys should be applied. */ deformAttachment: VertexAttachment = this; - constructor (name: string) { + constructor(name: string) { super(name); } @@ -65,40 +65,57 @@ export abstract class VertexAttachment extends Attachment { * `stride` / 2. * @param offset The `worldVertices` index to begin writing values. * @param stride The number of `worldVertices` entries between the value pairs written. */ - computeWorldVertices (slot: Slot, start: number, count: number, worldVertices: ArrayLike, offset: number, stride: number) { + computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: ArrayLike, offset: number, stride: number) { count = offset + (count >> 1) * stride; - let skeleton = slot.bone.skeleton; - let deformArray = slot.deform; + const skeleton = slot.bone.skeleton; + const deformArray = slot.deform; let vertices = this.vertices; - let bones = this.bones; + const bones = this.bones; + if (!bones) { if (deformArray.length > 0) vertices = deformArray; - let mat = slot.bone.matrix; - let x = mat.tx; - let y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const mat = slot.bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + for (let v = start, w = offset; w < count; v += 2, w += stride) { - let vx = vertices[v], vy = vertices[v + 1]; + const vx = vertices[v]; + const vy = vertices[v + 1]; + worldVertices[w] = vx * a + vy * b + x; worldVertices[w + 1] = vx * c + vy * d + y; } + return; } - let v = 0, skip = 0; + let v = 0; + let skip = 0; + for (let i = 0; i < start; i += 2) { - let n = bones[v]; + const n = bones[v]; + v += n + 1; skip += n; } - let skeletonBones = skeleton.bones; + const skeletonBones = skeleton.bones; + if (deformArray.length == 0) { for (let w = offset, b = skip * 3; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b]; + const vy = vertices[b + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } @@ -106,14 +123,20 @@ export abstract class VertexAttachment extends Attachment { worldVertices[w + 1] = wy; } } else { - let deform = deformArray; + const deform = deformArray; + for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3, f += 2) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b] + deform[f]; + const vy = vertices[b + 1] + deform[f + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } @@ -124,18 +147,16 @@ export abstract class VertexAttachment extends Attachment { } /** Does not copy id (generated) or name (set on construction). **/ - copyTo (attachment: VertexAttachment) { + copyTo(attachment: VertexAttachment) { if (this.bones) { attachment.bones = new Array(this.bones.length); Utils.arrayCopy(this.bones, 0, attachment.bones, 0, this.bones.length); - } else - attachment.bones = null; + } else attachment.bones = null; if (this.vertices) { attachment.vertices = Utils.newFloatArray(this.vertices.length); Utils.arrayCopy(this.vertices, 0, attachment.vertices, 0, this.vertices.length); - } else - attachment.vertices = null; + } else attachment.vertices = null; attachment.worldVerticesLength = this.worldVerticesLength; attachment.deformAttachment = this.deformAttachment; diff --git a/packages/runtime-4.0/src/core/attachments/AttachmentLoader.ts b/packages/runtime-4.0/src/core/attachments/AttachmentLoader.ts index 522c2f37..21cb94e4 100644 --- a/packages/runtime-4.0/src/core/attachments/AttachmentLoader.ts +++ b/packages/runtime-4.0/src/core/attachments/AttachmentLoader.ts @@ -1,23 +1,23 @@ -import {Skin} from '../Skin'; -import type {RegionAttachment} from './RegionAttachment'; -import type {MeshAttachment} from './MeshAttachment'; -import type {BoundingBoxAttachment} from './BoundingBoxAttachment'; -import type {PathAttachment} from './PathAttachment'; -import type {PointAttachment} from './PointAttachment'; -import type {ClippingAttachment} from './ClippingAttachment'; +import type { Skin } from '../Skin'; +import type { RegionAttachment } from './RegionAttachment'; +import type { MeshAttachment } from './MeshAttachment'; +import type { BoundingBoxAttachment } from './BoundingBoxAttachment'; +import type { PathAttachment } from './PathAttachment'; +import type { PointAttachment } from './PointAttachment'; +import type { ClippingAttachment } from './ClippingAttachment'; /** * @public */ export interface AttachmentLoader { /** @return May be null to not load an attachment. */ - newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment; + newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment; /** @return May be null to not load an attachment. */ - newMeshAttachment (skin: Skin, name: string, path: string): MeshAttachment; + newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment; /** @return May be null to not load an attachment. */ - newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment; + newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment; /** @return May be null to not load an attachment */ newPathAttachment(skin: Skin, name: string): PathAttachment; diff --git a/packages/runtime-4.0/src/core/attachments/BoundingBoxAttachment.ts b/packages/runtime-4.0/src/core/attachments/BoundingBoxAttachment.ts index 24500b94..de6247a9 100644 --- a/packages/runtime-4.0/src/core/attachments/BoundingBoxAttachment.ts +++ b/packages/runtime-4.0/src/core/attachments/BoundingBoxAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color} from '@pixi-spine/base'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color } from '@pixi-spine/base'; /** * @public @@ -8,14 +8,16 @@ export class BoundingBoxAttachment extends VertexAttachment { type = AttachmentType.BoundingBox; color = new Color(1, 1, 1, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new BoundingBoxAttachment(this.name); + copy(): Attachment { + const copy = new BoundingBoxAttachment(this.name); + this.copyTo(copy); copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.0/src/core/attachments/ClippingAttachment.ts b/packages/runtime-4.0/src/core/attachments/ClippingAttachment.ts index 1d43e9eb..ea680cae 100644 --- a/packages/runtime-4.0/src/core/attachments/ClippingAttachment.ts +++ b/packages/runtime-4.0/src/core/attachments/ClippingAttachment.ts @@ -1,6 +1,6 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IClippingAttachment} from '@pixi-spine/base'; -import type {SlotData} from '../SlotData'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IClippingAttachment } from '@pixi-spine/base'; +import type { SlotData } from '../SlotData'; /** * @public @@ -14,15 +14,17 @@ export class ClippingAttachment extends VertexAttachment implements IClippingAtt * are not usually rendered at runtime. */ color = new Color(0.2275, 0.2275, 0.8078, 1); // ce3a3aff - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new ClippingAttachment(this.name); + copy(): Attachment { + const copy = new ClippingAttachment(this.name); + this.copyTo(copy); copy.endSlot = this.endSlot; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.0/src/core/attachments/MeshAttachment.ts b/packages/runtime-4.0/src/core/attachments/MeshAttachment.ts index b5b8c66c..e98dd7f8 100644 --- a/packages/runtime-4.0/src/core/attachments/MeshAttachment.ts +++ b/packages/runtime-4.0/src/core/attachments/MeshAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IMeshAttachment, TextureRegion, Utils} from '@pixi-spine/base'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IMeshAttachment, TextureRegion, Utils } from '@pixi-spine/base'; /** * @public @@ -37,19 +37,19 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment private parentMesh: MeshAttachment; tempColor = new Color(0, 0, 0, 0); - constructor (name: string) { + constructor(name: string) { super(name); } /** The parent mesh if this is a linked mesh, else null. A linked mesh shares the {@link #bones}, {@link #vertices}, * {@link #regionUVs}, {@link #triangles}, {@link #hullLength}, {@link #edges}, {@link #width}, and {@link #height} with the * parent mesh, but may have a different {@link #name} or {@link #path} (and therefore a different texture). */ - getParentMesh () { + getParentMesh() { return this.parentMesh; } /** @param parentMesh May be null. */ - setParentMesh (parentMesh: MeshAttachment) { + setParentMesh(parentMesh: MeshAttachment) { this.parentMesh = parentMesh; if (parentMesh) { this.bones = parentMesh.bones; @@ -58,14 +58,15 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment this.regionUVs = parentMesh.regionUVs; this.triangles = parentMesh.triangles; this.hullLength = parentMesh.hullLength; - this.worldVerticesLength = parentMesh.worldVerticesLength + this.worldVerticesLength = parentMesh.worldVerticesLength; } } - copy (): Attachment { + copy(): Attachment { if (this.parentMesh) return this.newLinkedMesh(); - let copy = new MeshAttachment(this.name); + const copy = new MeshAttachment(this.name); + copy.region = this.region; copy.path = this.path; copy.color.setFromColor(this.color); @@ -89,14 +90,16 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment } /** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. **/ - newLinkedMesh (): MeshAttachment { - let copy = new MeshAttachment(this.name); + newLinkedMesh(): MeshAttachment { + const copy = new MeshAttachment(this.name); + copy.region = this.region; copy.path = this.path; copy.color.setFromColor(this.color); copy.deformAttachment = this.deformAttachment; copy.setParentMesh(this.parentMesh ? this.parentMesh : this); // copy.updateUVs(); + return copy; } } diff --git a/packages/runtime-4.0/src/core/attachments/PathAttachment.ts b/packages/runtime-4.0/src/core/attachments/PathAttachment.ts index 11ffcebb..0d942398 100644 --- a/packages/runtime-4.0/src/core/attachments/PathAttachment.ts +++ b/packages/runtime-4.0/src/core/attachments/PathAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from "./Attachment"; -import {AttachmentType, Color, Utils} from "@pixi-spine/base"; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, Utils } from '@pixi-spine/base'; /** * @public @@ -21,18 +21,20 @@ export class PathAttachment extends VertexAttachment { * rendered at runtime. */ color = new Color(1, 1, 1, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new PathAttachment(this.name); + copy(): Attachment { + const copy = new PathAttachment(this.name); + this.copyTo(copy); copy.lengths = new Array(this.lengths.length); Utils.arrayCopy(this.lengths, 0, copy.lengths, 0, this.lengths.length); copy.closed = closed; copy.constantSpeed = this.constantSpeed; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.0/src/core/attachments/PointAttachment.ts b/packages/runtime-4.0/src/core/attachments/PointAttachment.ts index 23b76823..89fd5521 100644 --- a/packages/runtime-4.0/src/core/attachments/PointAttachment.ts +++ b/packages/runtime-4.0/src/core/attachments/PointAttachment.ts @@ -1,6 +1,6 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, MathUtils, Vector2} from "@pixi-spine/base"; -import type {Bone} from '../Bone'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Bone } from '../Bone'; /** * @public @@ -8,37 +8,45 @@ import type {Bone} from '../Bone'; export class PointAttachment extends VertexAttachment { type = AttachmentType.Point; - x: number; y: number; rotation: number; + x: number; + y: number; + rotation: number; /** The color of the point attachment as it was in Spine. Available only when nonessential data was exported. Point attachments * are not usually rendered at runtime. */ color = new Color(0.38, 0.94, 0, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - computeWorldPosition (bone: Bone, point: Vector2) { + computeWorldPosition(bone: Bone, point: Vector2) { const mat = bone.matrix; + point.x = this.x * mat.a + this.y * mat.c + bone.worldX; point.y = this.x * mat.b + this.y * mat.d + bone.worldY; + return point; } - computeWorldRotation (bone: Bone) { + computeWorldRotation(bone: Bone) { const mat = bone.matrix; - let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation); - let x = cos * mat.a + sin * mat.c; - let y = cos * mat.b + sin * mat.d; + const cos = MathUtils.cosDeg(this.rotation); + const sin = MathUtils.sinDeg(this.rotation); + const x = cos * mat.a + sin * mat.c; + const y = cos * mat.b + sin * mat.d; + return Math.atan2(y, x) * MathUtils.radDeg; } - copy (): Attachment { - let copy = new PointAttachment(this.name); + copy(): Attachment { + const copy = new PointAttachment(this.name); + copy.x = this.x; copy.y = this.y; copy.rotation = this.rotation; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.0/src/core/attachments/RegionAttachment.ts b/packages/runtime-4.0/src/core/attachments/RegionAttachment.ts index 2a1795f7..d6f67915 100644 --- a/packages/runtime-4.0/src/core/attachments/RegionAttachment.ts +++ b/packages/runtime-4.0/src/core/attachments/RegionAttachment.ts @@ -1,7 +1,7 @@ -import {Attachment} from './Attachment'; -import {AttachmentType, ArrayLike, Color, TextureRegion, Utils, IRegionAttachment} from "@pixi-spine/base"; +import { Attachment } from './Attachment'; +import { AttachmentType, ArrayLike, Color, TextureRegion, Utils, IRegionAttachment } from '@pixi-spine/base'; -import type {Bone} from '../Bone'; +import type { Bone } from '../Bone'; import { Slot } from '../Slot'; /** @@ -90,35 +90,35 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { * See {@link #updateOffset()}. */ offset = Utils.newFloatArray(8); - uvs = Utils.newFloatArray(8); tempColor = new Color(1, 1, 1, 1); - constructor (name:string) { + constructor(name: string) { super(name); } /** Calculates the {@link #offset} using the region settings. Must be called after changing region settings. */ - updateOffset () : void { - let regionScaleX = this.width / this.region.originalWidth * this.scaleX; - let regionScaleY = this.height / this.region.originalHeight * this.scaleY; - let localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX; - let localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY; - let localX2 = localX + this.region.width * regionScaleX; - let localY2 = localY + this.region.height * regionScaleY; - let radians = this.rotation * Math.PI / 180; - let cos = Math.cos(radians); - let sin = Math.sin(radians); - let localXCos = localX * cos + this.x; - let localXSin = localX * sin; - let localYCos = localY * cos + this.y; - let localYSin = localY * sin; - let localX2Cos = localX2 * cos + this.x; - let localX2Sin = localX2 * sin; - let localY2Cos = localY2 * cos + this.y; - let localY2Sin = localY2 * sin; - let offset = this.offset; + updateOffset(): void { + const regionScaleX = (this.width / this.region.originalWidth) * this.scaleX; + const regionScaleY = (this.height / this.region.originalHeight) * this.scaleY; + const localX = (-this.width / 2) * this.scaleX + this.region.offsetX * regionScaleX; + const localY = (-this.height / 2) * this.scaleY + this.region.offsetY * regionScaleY; + const localX2 = localX + this.region.width * regionScaleX; + const localY2 = localY + this.region.height * regionScaleY; + const radians = (this.rotation * Math.PI) / 180; + const cos = Math.cos(radians); + const sin = Math.sin(radians); + const localXCos = localX * cos + this.x; + const localXSin = localX * sin; + const localYCos = localY * cos + this.y; + const localYSin = localY * sin; + const localX2Cos = localX2 * cos + this.x; + const localX2Sin = localX2 * sin; + const localY2Cos = localY2 * cos + this.y; + const localY2Sin = localY2 * sin; + const offset = this.offset; + offset[RegionAttachment.OX1] = localXCos - localYSin; offset[RegionAttachment.OY1] = localYCos + localXSin; offset[RegionAttachment.OX2] = localXCos - localY2Sin; @@ -129,9 +129,10 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { offset[RegionAttachment.OY4] = localYCos + localX2Sin; } - setRegion (region: TextureRegion) : void { + setRegion(region: TextureRegion): void { this.region = region; - let uvs = this.uvs; + const uvs = this.uvs; + if (region.degrees == 90) { uvs[2] = region.u; uvs[3] = region.v2; @@ -160,12 +161,17 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { * @param worldVertices The output world vertices. Must have a length >= `offset` + 8. * @param offset The `worldVertices` index to begin writing values. * @param stride The number of `worldVertices` entries between the value pairs written. */ - computeWorldVertices (bone: Bone | Slot, worldVertices: ArrayLike, offset: number, stride: number) { - let vertexOffset = this.offset; - let mat = bone instanceof Slot? bone.bone.matrix : bone.matrix; - let x = mat.tx, y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let offsetX = 0, offsetY = 0; + computeWorldVertices(bone: Bone | Slot, worldVertices: ArrayLike, offset: number, stride: number) { + const vertexOffset = this.offset; + const mat = bone instanceof Slot ? bone.bone.matrix : bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let offsetX = 0; + let offsetY = 0; offsetX = vertexOffset[RegionAttachment.OX1]; offsetY = vertexOffset[RegionAttachment.OY1]; @@ -191,8 +197,9 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { worldVertices[offset + 1] = offsetX * c + offsetY * d + y; } - copy (): Attachment { - let copy = new RegionAttachment(this.name); + copy(): Attachment { + const copy = new RegionAttachment(this.name); + copy.region = this.region; copy.rendererObject = this.rendererObject; copy.path = this.path; @@ -206,6 +213,7 @@ export class RegionAttachment extends Attachment implements IRegionAttachment { Utils.arrayCopy(this.uvs, 0, copy.uvs, 0, 8); Utils.arrayCopy(this.offset, 0, copy.offset, 0, 8); copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.0/src/core/vertexeffects/JitterEffect.ts b/packages/runtime-4.0/src/core/vertexeffects/JitterEffect.ts index 6458d5d6..8a2454c0 100644 --- a/packages/runtime-4.0/src/core/vertexeffects/JitterEffect.ts +++ b/packages/runtime-4.0/src/core/vertexeffects/JitterEffect.ts @@ -1,6 +1,6 @@ -import {VertexEffect} from "../VertexEffect"; -import type {Skeleton} from "../Skeleton"; -import {Color, MathUtils, Vector2} from "@pixi-spine/base"; +import type { VertexEffect } from '../VertexEffect'; +import type { Skeleton } from '../Skeleton'; +import { Color, MathUtils, Vector2 } from '@pixi-spine/base'; /** * @public @@ -9,21 +9,19 @@ export class JitterEffect implements VertexEffect { jitterX = 0; jitterY = 0; - constructor (jitterX: number, jitterY: number) { + constructor(jitterX: number, jitterY: number) { this.jitterX = jitterX; this.jitterY = jitterY; } - //@ts-ignore - begin(skeleton: Skeleton): void { - } + // @ts-ignore + begin(skeleton: Skeleton): void {} - //@ts-ignore + // @ts-ignore transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { position.x += MathUtils.randomTriangular(-this.jitterX, this.jitterY); position.y += MathUtils.randomTriangular(-this.jitterX, this.jitterY); } - end(): void { - } + end(): void {} } diff --git a/packages/runtime-4.0/src/core/vertexeffects/SwirlEffect.ts b/packages/runtime-4.0/src/core/vertexeffects/SwirlEffect.ts index 495a01d5..61b1a0e3 100644 --- a/packages/runtime-4.0/src/core/vertexeffects/SwirlEffect.ts +++ b/packages/runtime-4.0/src/core/vertexeffects/SwirlEffect.ts @@ -1,6 +1,6 @@ -import {VertexEffect} from "../VertexEffect"; -import type {Skeleton} from "../Skeleton"; -import {Color, MathUtils, PowOut, Vector2} from "@pixi-spine/base"; +import type { VertexEffect } from '../VertexEffect'; +import type { Skeleton } from '../Skeleton'; +import { Color, MathUtils, PowOut, Vector2 } from '@pixi-spine/base'; /** * @public @@ -14,7 +14,7 @@ export class SwirlEffect implements VertexEffect { private worldX = 0; private worldY = 0; - constructor (radius: number) { + constructor(radius: number) { this.radius = radius; } @@ -23,21 +23,22 @@ export class SwirlEffect implements VertexEffect { this.worldY = skeleton.y + this.centerY; } - //@ts-ignore + // @ts-ignore transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { - let radAngle = this.angle * MathUtils.degreesToRadians; - let x = position.x - this.worldX; - let y = position.y - this.worldY; - let dist = Math.sqrt(x * x + y * y); + const radAngle = this.angle * MathUtils.degreesToRadians; + const x = position.x - this.worldX; + const y = position.y - this.worldY; + const dist = Math.sqrt(x * x + y * y); + if (dist < this.radius) { - let theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); - let cos = Math.cos(theta); - let sin = Math.sin(theta); + const theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); + const cos = Math.cos(theta); + const sin = Math.sin(theta); + position.x = cos * x - sin * y + this.worldX; position.y = sin * x + cos * y + this.worldY; } } - end(): void { - } + end(): void {} } diff --git a/packages/runtime-4.1/package.json b/packages/runtime-4.1/package.json index e658a478..0b017a8b 100644 --- a/packages/runtime-4.1/package.json +++ b/packages/runtime-4.1/package.json @@ -2,21 +2,35 @@ "name": "@pixi-spine/runtime-4.1", "version": "3.1.2", "description": "Pixi runtime for spine 4.1 models", - "main": "lib/runtime-4.1.js", - "module": "lib/runtime-4.1.es.js", - "bundle": "dist/runtime-4.1.js", - "namespace": "PIXI.spine41", + "main": "lib/index.js", + "module": "lib/index.mjs", "types": "./index.d.ts", - "peerDependencies": { - "@pixi/constants": "^6.1.0", - "@pixi/math": "^6.1.0" + "exports": { + ".": { + "import": "./lib/index.mjs", + "require": "./lib/index.js", + "types": "./index.d.ts" + } + }, + "extensionConfig": { + "namespace": "PIXI.spine41", + "bundle": "dist/runtime-4.1.js", + "bundleModule": "dist/runtime-4.1.mjs", + "globals": { + "@pixi-spine/base": "PIXI.spine" + } }, - "dependencies": { - "@pixi-spine/base": "~3.1.2" + "peerDependencies": { + "@pixi-spine/base": "*", + "@pixi/constants": "^7.0.0", + "@pixi/math": "^7.0.0" }, "scripts": { - "build": "rollup -c rollup.config.js --silent", - "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run" + "build": "run-p build:*", + "build:rollup": "rollup -c rollup.config.mjs --silent", + "build:types": "rimraf compile && tsc -p tsconfig-api.json && api-extractor run", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix" }, "repository": { "type": "git", @@ -39,13 +53,6 @@ "license": "SEE SPINE-LICENSE", "homepage": "https://github.com/pixijs/pixi-spine/#readme", "devDependencies": { - "@pixi-spine/eslint-config": "~1.0.0", - "@pixi-spine/rollup-config": "~1.0.0", - "chai": "~4.2.0", - "eslint": "~7.13.0", - "rimraf": "3.0.2", - "rollup": "^2.53.3", - "tslib": "~2.2.0", - "typescript": "~4.3.0" + "@pixi-spine/rollup-config": "*" } -} +} \ No newline at end of file diff --git a/packages/runtime-4.1/rollup.config.js b/packages/runtime-4.1/rollup.config.js deleted file mode 100644 index 8b1fc303..00000000 --- a/packages/runtime-4.1/rollup.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const {main} = require('@pixi-spine/rollup-config/main'); - -module.exports = main({ - globals: { - '@pixi-spine/base': 'PIXI.spine.base', - }, -}); diff --git a/packages/runtime-4.1/rollup.config.mjs b/packages/runtime-4.1/rollup.config.mjs new file mode 100644 index 00000000..22a21c92 --- /dev/null +++ b/packages/runtime-4.1/rollup.config.mjs @@ -0,0 +1,4 @@ +import configBuilder from '@pixi-spine/rollup-config'; +import pkg from './package.json' assert { type: 'json' }; + +export default configBuilder(pkg.extensionConfig, pkg); diff --git a/packages/runtime-4.1/src/Spine.ts b/packages/runtime-4.1/src/Spine.ts index 6faecd39..b3a2b909 100644 --- a/packages/runtime-4.1/src/Spine.ts +++ b/packages/runtime-4.1/src/Spine.ts @@ -1,8 +1,8 @@ -import {SpineBase} from '@pixi-spine/base'; -import {Skeleton} from "./core/Skeleton"; -import {SkeletonData} from "./core/SkeletonData"; -import {AnimationState} from "./core/AnimationState"; -import {AnimationStateData} from "./core/AnimationStateData"; +import { SpineBase } from '@pixi-spine/base'; +import { Skeleton } from './core/Skeleton'; +import type { SkeletonData } from './core/SkeletonData'; +import { AnimationState } from './core/AnimationState'; +import { AnimationStateData } from './core/AnimationStateData'; /** * @public diff --git a/packages/runtime-4.1/src/core/Animation.ts b/packages/runtime-4.1/src/core/Animation.ts index a9a576a0..f387a184 100644 --- a/packages/runtime-4.1/src/core/Animation.ts +++ b/packages/runtime-4.1/src/core/Animation.ts @@ -1,13 +1,12 @@ -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; -import {Attachment, VertexAttachment} from "./attachments"; -import {NumberArrayLike, IAnimation, ITimeline, MathUtils, - MixBlend, StringSet, Utils, MixDirection, IHasTextureRegion} from '@pixi-spine/base'; -import {Slot} from "./Slot"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; -import { SequenceMode, SequenceModeValues } from "./attachments/Sequence"; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; +import { Attachment, VertexAttachment } from './attachments'; +import { NumberArrayLike, IAnimation, ITimeline, MathUtils, MixBlend, StringSet, Utils, MixDirection, IHasTextureRegion } from '@pixi-spine/base'; +import type { Slot } from './Slot'; +import type { IkConstraint } from './IkConstraint'; +import type { TransformConstraint } from './TransformConstraint'; +import type { PathConstraint } from './PathConstraint'; +import { SequenceMode, SequenceModeValues } from './attachments/Sequence'; /** * A simple container for a list of timelines and a name. @@ -22,24 +21,23 @@ export class Animation implements IAnimation { /** The duration of the animation in seconds, which is the highest time of all keys in the timeline. */ duration: number; - constructor (name: string, timelines: Array, duration: number) { - if (!name) throw new Error("name cannot be null."); + constructor(name: string, timelines: Array, duration: number) { + if (!name) throw new Error('name cannot be null.'); this.name = name; this.setTimelines(timelines); this.duration = duration; } - setTimelines (timelines: Array) { - if (!timelines) throw new Error("timelines cannot be null."); + setTimelines(timelines: Array) { + if (!timelines) throw new Error('timelines cannot be null.'); this.timelines = timelines; this.timelineIds.clear(); - for (var i = 0; i < timelines.length; i++) - this.timelineIds.addAll(timelines[i].getPropertyIds()); + for (let i = 0; i < timelines.length; i++) this.timelineIds.addAll(timelines[i].getPropertyIds()); } - hasTimeline (ids: string[]): boolean { - for (let i = 0; i < ids.length; i++) - if (this.timelineIds.contains(ids[i])) return true; + hasTimeline(ids: string[]): boolean { + for (let i = 0; i < ids.length; i++) if (this.timelineIds.contains(ids[i])) return true; + return false; } @@ -48,17 +46,17 @@ export class Animation implements IAnimation { * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. * @param loop If true, the animation repeats after {@link #getDuration()}. * @param events May be null to ignore fired events. */ - apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - if (!skeleton) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + if (!skeleton) throw new Error('skeleton cannot be null.'); if (loop && this.duration != 0) { time %= this.duration; if (lastTime > 0) lastTime %= this.duration; } - let timelines = this.timelines; - for (let i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); + const timelines = this.timelines; + + for (let i = 0, n = timelines.length; i < n; i++) timelines[i].apply(skeleton, lastTime, time, events, alpha, blend, direction); } } @@ -88,8 +86,8 @@ const Property = { pathConstraintSpacing: 17, pathConstraintMix: 18, - sequence: 19 -} + sequence: 19, +}; /** The interface for all timelines. * @public @@ -98,40 +96,42 @@ export abstract class Timeline implements ITimeline { propertyIds: string[]; frames: NumberArrayLike; - constructor (frameCount: number, propertyIds: string[]) { + constructor(frameCount: number, propertyIds: string[]) { this.propertyIds = propertyIds; this.frames = Utils.newFloatArray(frameCount * this.getFrameEntries()); } - getPropertyIds () { + getPropertyIds() { return this.propertyIds; } - getFrameEntries (): number { + getFrameEntries(): number { return 1; } - getFrameCount () { + getFrameCount() { return this.frames.length / this.getFrameEntries(); } - getDuration (): number { + getDuration(): number { return this.frames[this.frames.length - this.getFrameEntries()]; } - abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array | null, alpha: number, blend: MixBlend, direction: MixDirection): void; + abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array | null, alpha: number, blend: MixBlend, direction: MixDirection): void; + + static search1(frames: NumberArrayLike, time: number) { + const n = frames.length; + + for (let i = 1; i < n; i++) if (frames[i] > time) return i - 1; - static search1 (frames: NumberArrayLike, time: number) { - let n = frames.length; - for (let i = 1; i < n; i++) - if (frames[i] > time) return i - 1; return n - 1; } - static search (frames: NumberArrayLike, time: number, step: number) { - let n = frames.length; - for (let i = step; i < n; i += step) - if (frames[i] > time) return i - step; + static search(frames: NumberArrayLike, time: number, step: number) { + const n = frames.length; + + for (let i = step; i < n; i += step) if (frames[i] > time) return i - step; + return n - step; } } @@ -158,28 +158,30 @@ export interface SlotTimeline { export abstract class CurveTimeline extends Timeline { protected curves: NumberArrayLike; // type, x, y, ... - constructor (frameCount: number, bezierCount: number, propertyIds: string[]) { + constructor(frameCount: number, bezierCount: number, propertyIds: string[]) { super(frameCount, propertyIds); - this.curves = Utils.newFloatArray(frameCount + bezierCount * 18/*BEZIER_SIZE*/); - this.curves[frameCount - 1] = 1/*STEPPED*/; + this.curves = Utils.newFloatArray(frameCount + bezierCount * 18 /* BEZIER_SIZE*/); + this.curves[frameCount - 1] = 1 /* STEPPED*/; } /** Sets the specified key frame to linear interpolation. */ - setLinear (frame: number) { - this.curves[frame] = 0/*LINEAR*/; + setLinear(frame: number) { + this.curves[frame] = 0 /* LINEAR*/; } /** Sets the specified key frame to stepped interpolation. */ - setStepped (frame: number) { - this.curves[frame] = 1/*STEPPED*/; + setStepped(frame: number) { + this.curves[frame] = 1 /* STEPPED*/; } /** Shrinks the storage for Bezier curves, for use when bezierCount (specified in the constructor) was larger * than the actual number of Bezier curves. */ - shrink (bezierCount: number) { - let size = this.getFrameCount() + bezierCount * 18/*BEZIER_SIZE*/; + shrink(bezierCount: number) { + const size = this.getFrameCount() + bezierCount * 18; /* BEZIER_SIZE*/ + if (this.curves.length > size) { - let newCurves = Utils.newFloatArray(size); + const newCurves = Utils.newFloatArray(size); + Utils.arrayCopy(this.curves, 0, newCurves, 0, size); this.curves = newCurves; } @@ -199,17 +201,23 @@ export abstract class CurveTimeline extends Timeline { * @param cy2 The value for the second Bezier handle. * @param time2 The time for the second key. * @param value2 The value for the second key. */ - setBezier (bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, - cy2: number, time2: number, value2: number) { - let curves = this.curves; - let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/; - if (value == 0) curves[frame] = 2/*BEZIER*/ + i; - let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = (value1 - cy1 * 2 + cy2) * 0.03; - let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006; - let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy; - let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667; - let x = time1 + dx, y = value1 + dy; - for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) { + setBezier(bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, cy2: number, time2: number, value2: number) { + const curves = this.curves; + let i = this.getFrameCount() + bezier * 18; /* BEZIER_SIZE*/ + + if (value == 0) curves[frame] = 2 /* BEZIER*/ + i; + const tmpx = (time1 - cx1 * 2 + cx2) * 0.03; + const tmpy = (value1 - cy1 * 2 + cy2) * 0.03; + const dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006; + const dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006; + let ddx = tmpx * 2 + dddx; + let ddy = tmpy * 2 + dddy; + let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667; + let dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667; + let x = time1 + dx; + let y = value1 + dy; + + for (let n = i + 18 /* BEZIER_SIZE*/; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; dx += ddx; @@ -225,49 +233,58 @@ export abstract class CurveTimeline extends Timeline { * @param frameIndex The index into {@link #getFrames()} for the values of the frame before time. * @param valueOffset The offset from frameIndex to the value this curve is used for. * @param i The index of the Bezier segments. See {@link #getCurveType(int)}. */ - getBezierValue (time: number, frameIndex: number, valueOffset: number, i: number) { - let curves = this.curves; + getBezierValue(time: number, frameIndex: number, valueOffset: number, i: number) { + const curves = this.curves; + if (curves[i] > time) { - let x = this.frames[frameIndex], y = this.frames[frameIndex + valueOffset]; - return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y); + const x = this.frames[frameIndex]; + const y = this.frames[frameIndex + valueOffset]; + + return y + ((time - x) / (curves[i] - x)) * (curves[i + 1] - y); } - let n = i + 18/*BEZIER_SIZE*/; + const n = i + 18; /* BEZIER_SIZE*/ + for (i += 2; i < n; i += 2) { if (curves[i] >= time) { - let x = curves[i - 2], y = curves[i - 1]; - return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y); + const x = curves[i - 2]; + const y = curves[i - 1]; + + return y + ((time - x) / (curves[i] - x)) * (curves[i + 1] - y); } } frameIndex += this.getFrameEntries(); - let x = curves[n - 2], y = curves[n - 1]; - return y + (time - x) / (this.frames[frameIndex] - x) * (this.frames[frameIndex + valueOffset] - y); + const x = curves[n - 2]; + const y = curves[n - 1]; + + return y + ((time - x) / (this.frames[frameIndex] - x)) * (this.frames[frameIndex + valueOffset] - y); } } /** * @public */ export abstract class CurveTimeline1 extends CurveTimeline { - constructor (frameCount: number, bezierCount: number, propertyId: string) { + constructor(frameCount: number, bezierCount: number, propertyId: string) { super(frameCount, bezierCount, [propertyId]); } - getFrameEntries () { - return 2/*ENTRIES*/; + getFrameEntries() { + return 2 /* ENTRIES*/; } /** Sets the time and value for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ - setFrame (frame: number, time: number, value: number) { + setFrame(frame: number, time: number, value: number) { frame <<= 1; this.frames[frame] = time; - this.frames[frame + 1/*VALUE*/] = value; + this.frames[frame + 1 /* VALUE*/] = value; } /** Returns the interpolated value for the specified time. */ - getCurveValue (time: number) { - let frames = this.frames; + getCurveValue(time: number) { + const frames = this.frames; let i = frames.length - 2; + for (let ii = 2; ii <= i; ii += 2) { if (frames[ii] > time) { i = ii - 2; @@ -275,15 +292,19 @@ export abstract class CurveTimeline1 extends CurveTimeline { } } - let curveType = this.curves[i >> 1]; + const curveType = this.curves[i >> 1]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i], value = frames[i + 1/*VALUE*/]; - return value + (time - before) / (frames[i + 2/*ENTRIES*/] - before) * (frames[i + 2/*ENTRIES*/ + 1/*VALUE*/] - value); - case 1/*STEPPED*/: - return frames[i + 1/*VALUE*/]; + case 0 /* LINEAR*/: + const before = frames[i]; + const value = frames[i + 1 /* VALUE*/]; + + return value + ((time - before) / (frames[i + 2 /* ENTRIES*/] - before)) * (frames[i + 2 /* ENTRIES*/ + 1 /* VALUE*/] - value); + case 1 /* STEPPED*/: + return frames[i + 1 /* VALUE*/]; } - return this.getBezierValue(time, i, 1/*VALUE*/, curveType - 2/*BEZIER*/); + + return this.getBezierValue(time, i, 1 /* VALUE*/, curveType - 2 /* BEZIER*/); } } @@ -293,22 +314,22 @@ export abstract class CurveTimeline1 extends CurveTimeline { export abstract class CurveTimeline2 extends CurveTimeline { /** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}. * @param propertyIds Unique identifiers for the properties the timeline modifies. */ - constructor (frameCount: number, bezierCount: number, propertyId1: string, propertyId2: string) { + constructor(frameCount: number, bezierCount: number, propertyId1: string, propertyId2: string) { super(frameCount, bezierCount, [propertyId1, propertyId2]); } - getFrameEntries () { - return 3/*ENTRIES*/; + getFrameEntries() { + return 3 /* ENTRIES*/; } /** Sets the time and values for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time The frame time in seconds. */ - setFrame (frame: number, time: number, value1: number, value2: number) { - frame *= 3/*ENTRIES*/; + setFrame(frame: number, time: number, value1: number, value2: number) { + frame *= 3 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*VALUE1*/] = value1; - this.frames[frame + 2/*VALUE2*/] = value2; + this.frames[frame + 1 /* VALUE1*/] = value1; + this.frames[frame + 2 /* VALUE2*/] = value2; } } @@ -318,28 +339,33 @@ export abstract class CurveTimeline2 extends CurveTimeline { export class RotateTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.rotate + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.rotate}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array | null, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array | null, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation; + return; case MixBlend.first: bone.rotation += (bone.data.rotation - bone.rotation) * alpha; } + return; } let r = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.rotation = bone.data.rotation + r * alpha; @@ -359,51 +385,56 @@ export class RotateTimeline extends CurveTimeline1 implements BoneTimeline { export class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, - Property.x + "|" + boneIndex, - Property.y + "|" + boneIndex, - ); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.x}|${boneIndex}`, `${Property.y}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.x = bone.data.x; bone.y = bone.data.y; + return; case MixBlend.first: bone.x += (bone.data.x - bone.x) * alpha; bone.y += (bone.data.y - bone.y) * alpha; } + return; } - let x = 0, y = 0; - let i = Timeline.search(frames, time, 3/*ENTRIES*/); - let curveType = this.curves[i / 3/*ENTRIES*/]; + let x = 0; + let y = 0; + const i = Timeline.search(frames, time, 3 /* ENTRIES*/); + const curveType = this.curves[i / 3 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; - let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before); - x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t; - y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; + const t = (time - before) / (frames[i + 3 /* ENTRIES*/] - before); + + x += (frames[i + 3 /* ENTRIES*/ + 1 /* VALUE1*/] - x) * t; + y += (frames[i + 3 /* ENTRIES*/ + 2 /* VALUE2*/] - y) * t; break; - case 1/*STEPPED*/: - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; + case 1 /* STEPPED*/: + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; break; default: - x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + x = this.getBezierValue(time, i, 1 /* VALUE1*/, curveType - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 2 /* VALUE2*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } switch (blend) { @@ -429,28 +460,33 @@ export class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { export class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.x + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.x}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.x = bone.data.x; + return; case MixBlend.first: bone.x += (bone.data.x - bone.x) * alpha; } + return; } - let x = this.getCurveValue(time); + const x = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.x = bone.data.x + x * alpha; @@ -471,28 +507,33 @@ export class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { export class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.y + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.y}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.y = bone.data.y; + return; case MixBlend.first: bone.y += (bone.data.y - bone.y) * alpha; } + return; } - let y = this.getCurveValue(time); + const y = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.y = bone.data.y + y * alpha; @@ -513,51 +554,56 @@ export class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, - Property.scaleX + "|" + boneIndex, - Property.scaleY + "|" + boneIndex - ); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.scaleX}|${boneIndex}`, `${Property.scaleY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleX = bone.data.scaleX; bone.scaleY = bone.data.scaleY; + return; case MixBlend.first: bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; } + return; } - let x, y; - let i = Timeline.search(frames, time, 3/*ENTRIES*/); - let curveType = this.curves[i / 3/*ENTRIES*/]; + let x; + let y; + const i = Timeline.search(frames, time, 3 /* ENTRIES*/); + const curveType = this.curves[i / 3 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; - let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before); - x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t; - y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; + const t = (time - before) / (frames[i + 3 /* ENTRIES*/] - before); + + x += (frames[i + 3 /* ENTRIES*/ + 1 /* VALUE1*/] - x) * t; + y += (frames[i + 3 /* ENTRIES*/ + 2 /* VALUE2*/] - y) * t; break; - case 1/*STEPPED*/: - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; + case 1 /* STEPPED*/: + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; break; default: - x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + x = this.getBezierValue(time, i, 1 /* VALUE1*/, curveType - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 2 /* VALUE2*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } x *= bone.data.scaleX; y *= bone.data.scaleY; @@ -571,7 +617,9 @@ export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { bone.scaleY = y; } } else { - let bx = 0, by = 0; + let bx = 0; + let by = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -621,36 +669,40 @@ export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { export class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.scaleX + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.scaleX}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleX = bone.data.scaleX; + return; case MixBlend.first: bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; } + return; } - let x = this.getCurveValue(time) * bone.data.scaleX; + const x = this.getCurveValue(time) * bone.data.scaleX; + if (alpha == 1) { - if (blend == MixBlend.add) - bone.scaleX += x - bone.data.scaleX; - else - bone.scaleX = x; + if (blend == MixBlend.add) bone.scaleX += x - bone.data.scaleX; + else bone.scaleX = x; } else { // Mixing out uses sign of setup or current pose, else use sign of key. let bx = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -690,36 +742,40 @@ export class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { export class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.scaleY + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.scaleY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.scaleY = bone.data.scaleY; + return; case MixBlend.first: bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; } + return; } - let y = this.getCurveValue(time) * bone.data.scaleY; + const y = this.getCurveValue(time) * bone.data.scaleY; + if (alpha == 1) { - if (blend == MixBlend.add) - bone.scaleY += y - bone.data.scaleY; - else - bone.scaleY = y; + if (blend == MixBlend.add) bone.scaleY += y - bone.data.scaleY; + else bone.scaleY = y; } else { // Mixing out uses sign of setup or current pose, else use sign of key. let by = 0; + if (direction == MixDirection.mixOut) { switch (blend) { case MixBlend.setup: @@ -759,51 +815,56 @@ export class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { export class ShearTimeline extends CurveTimeline2 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, - Property.shearX + "|" + boneIndex, - Property.shearY + "|" + boneIndex - ); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.shearX}|${boneIndex}`, `${Property.shearY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX; bone.shearY = bone.data.shearY; + return; case MixBlend.first: bone.shearX += (bone.data.shearX - bone.shearX) * alpha; bone.shearY += (bone.data.shearY - bone.shearY) * alpha; } + return; } - let x = 0, y = 0; - let i = Timeline.search(frames, time, 3/*ENTRIES*/); - let curveType = this.curves[i / 3/*ENTRIES*/]; + let x = 0; + let y = 0; + const i = Timeline.search(frames, time, 3 /* ENTRIES*/); + const curveType = this.curves[i / 3 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; - let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before); - x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t; - y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; + const t = (time - before) / (frames[i + 3 /* ENTRIES*/] - before); + + x += (frames[i + 3 /* ENTRIES*/ + 1 /* VALUE1*/] - x) * t; + y += (frames[i + 3 /* ENTRIES*/ + 2 /* VALUE2*/] - y) * t; break; - case 1/*STEPPED*/: - x = frames[i + 1/*VALUE1*/]; - y = frames[i + 2/*VALUE2*/]; + case 1 /* STEPPED*/: + x = frames[i + 1 /* VALUE1*/]; + y = frames[i + 2 /* VALUE2*/]; break; default: - x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + x = this.getBezierValue(time, i, 1 /* VALUE1*/, curveType - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 2 /* VALUE2*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } switch (blend) { @@ -829,28 +890,33 @@ export class ShearTimeline extends CurveTimeline2 implements BoneTimeline { export class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.shearX + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.shearX}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX; + return; case MixBlend.first: bone.shearX += (bone.data.shearX - bone.shearX) * alpha; } + return; } - let x = this.getCurveValue(time); + const x = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.shearX = bone.data.shearX + x * alpha; @@ -871,28 +937,33 @@ export class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { export class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { boneIndex = 0; - constructor (frameCount: number, bezierCount: number, boneIndex: number) { - super(frameCount, bezierCount, Property.shearY + "|" + boneIndex); + constructor(frameCount: number, bezierCount: number, boneIndex: number) { + super(frameCount, bezierCount, `${Property.shearY}|${boneIndex}`); this.boneIndex = boneIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let bone = skeleton.bones[this.boneIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: bone.shearY = bone.data.shearY; + return; case MixBlend.first: bone.shearY += (bone.data.shearY - bone.shearY) * alpha; } + return; } - let y = this.getCurveValue(time); + const y = this.getCurveValue(time); + switch (blend) { case MixBlend.setup: bone.shearY = bone.data.shearY + y * alpha; @@ -913,77 +984,83 @@ export class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { export class RGBATimeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex, - Property.alpha + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`, `${Property.alpha}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 5/*ENTRIES*/; + getFrameEntries() { + return 5 /* ENTRIES*/; } /** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number, a: number) { - frame *= 5/*ENTRIES*/; + setFrame(frame: number, time: number, r: number, g: number, b: number, a: number) { + frame *= 5 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; - this.frames[frame + 4/*A*/] = a; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; + this.frames[frame + 4 /* A*/] = a; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let color = slot.color; + const frames = this.frames; + const color = slot.color; + if (time < frames[0]) { - let setup = slot.data.color; + const setup = slot.data.color; + switch (blend) { case MixBlend.setup: color.setFromColor(setup); + return; case MixBlend.first: - color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, - (setup.a - color.a) * alpha); + color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, (setup.a - color.a) * alpha); } + return; } - let r = 0, g = 0, b = 0, a = 0; - let i = Timeline.search(frames, time, 5/*ENTRIES*/); - let curveType = this.curves[i / 5/*ENTRIES*/]; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + const i = Timeline.search(frames, time, 5 /* ENTRIES*/); + const curveType = this.curves[i / 5 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; - let t = (time - before) / (frames[i + 5/*ENTRIES*/] - before); - r += (frames[i + 5/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 5/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 5/*ENTRIES*/ + 3/*B*/] - b) * t; - a += (frames[i + 5/*ENTRIES*/ + 4/*A*/] - a) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; + const t = (time - before) / (frames[i + 5 /* ENTRIES*/] - before); + + r += (frames[i + 5 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 5 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 5 /* ENTRIES*/ + 3 /* B*/] - b) * t; + a += (frames[i + 5 /* ENTRIES*/ + 4 /* A*/] - a) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + a = this.getBezierValue(time, i, 4 /* A*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); } - if (alpha == 1) - color.set(r, g, b, a); + if (alpha == 1) color.set(r, g, b, a); else { if (blend == MixBlend.setup) color.setFromColor(slot.data.color); color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); @@ -997,71 +1074,79 @@ export class RGBATimeline extends CurveTimeline implements SlotTimeline { export class RGBTimeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 4/*ENTRIES*/; + getFrameEntries() { + return 4 /* ENTRIES*/; } /** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number) { + setFrame(frame: number, time: number, r: number, g: number, b: number) { frame <<= 2; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let color = slot.color; + const frames = this.frames; + const color = slot.color; + if (time < frames[0]) { - let setup = slot.data.color; + const setup = slot.data.color; + switch (blend) { case MixBlend.setup: color.r = setup.r; color.g = setup.g; color.b = setup.b; + return; case MixBlend.first: color.r += (setup.r - color.r) * alpha; color.g += (setup.g - color.g) * alpha; color.b += (setup.b - color.b) * alpha; } + return; } - let r = 0, g = 0, b = 0; - let i = Timeline.search(frames, time, 4/*ENTRIES*/); - let curveType = this.curves[i >> 2]; + let r = 0; + let g = 0; + let b = 0; + const i = Timeline.search(frames, time, 4 /* ENTRIES*/); + const curveType = this.curves[i >> 2]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before); - r += (frames[i + 4/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 4/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 4/*ENTRIES*/ + 3/*B*/] - b) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + const t = (time - before) / (frames[i + 4 /* ENTRIES*/] - before); + + r += (frames[i + 4 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 4 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 4 /* ENTRIES*/ + 3 /* B*/] - b) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); } if (alpha == 1) { color.r = r; @@ -1069,7 +1154,8 @@ export class RGBTimeline extends CurveTimeline implements SlotTimeline { color.b = b; } else { if (blend == MixBlend.setup) { - let setup = slot.data.color; + const setup = slot.data.color; + color.r = setup.r; color.g = setup.g; color.b = setup.b; @@ -1087,31 +1173,37 @@ export class RGBTimeline extends CurveTimeline implements SlotTimeline { export class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, Property.alpha + "|" + slotIndex); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, `${Property.alpha}|${slotIndex}`); this.slotIndex = slotIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let color = slot.color; - if (time < this.frames[0]) { // Time is before first frame. - let setup = slot.data.color; + const color = slot.color; + + if (time < this.frames[0]) { + // Time is before first frame. + const setup = slot.data.color; + switch (blend) { case MixBlend.setup: color.a = setup.a; + return; case MixBlend.first: color.a += (setup.a - color.a) * alpha; } + return; } - let a = this.getCurveValue(time); - if (alpha == 1) - color.a = a; + const a = this.getCurveValue(time); + + if (alpha == 1) color.a = a; else { if (blend == MixBlend.setup) color.a = slot.data.color.a; color.a += (a - color.a) * alpha; @@ -1122,99 +1214,110 @@ export class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { /** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. * @public * */ -export class RGBA2Timeline extends CurveTimeline implements SlotTimeline{ +export class RGBA2Timeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex, - Property.alpha + "|" + slotIndex, - Property.rgb2 + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`, `${Property.alpha}|${slotIndex}`, `${Property.rgb2}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 8/*ENTRIES*/; + getFrameEntries() { + return 8 /* ENTRIES*/; } /** Sets the time in seconds, light, and dark colors for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { + setFrame(frame: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) { frame <<= 3; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; - this.frames[frame + 4/*A*/] = a; - this.frames[frame + 5/*R2*/] = r2; - this.frames[frame + 6/*G2*/] = g2; - this.frames[frame + 7/*B2*/] = b2; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; + this.frames[frame + 4 /* A*/] = a; + this.frames[frame + 5 /* R2*/] = r2; + this.frames[frame + 6 /* G2*/] = g2; + this.frames[frame + 7 /* B2*/] = b2; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let light = slot.color, dark = slot.darkColor!; + const frames = this.frames; + const light = slot.color; + const dark = slot.darkColor; + if (time < frames[0]) { - let setupLight = slot.data.color, setupDark = slot.data.darkColor!; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + switch (blend) { case MixBlend.setup: light.setFromColor(setupLight); dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; + return; case MixBlend.first: - light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, - (setupLight.a - light.a) * alpha); + light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); dark.r += (setupDark.r - dark.r) * alpha; dark.g += (setupDark.g - dark.g) * alpha; dark.b += (setupDark.b - dark.b) * alpha; } + return; } - let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0; - let i = Timeline.search(frames, time, 8/*ENTRIES*/); - let curveType = this.curves[i >> 3]; + let r = 0; + let g = 0; + let b = 0; + let a = 0; + let r2 = 0; + let g2 = 0; + let b2 = 0; + const i = Timeline.search(frames, time, 8 /* ENTRIES*/); + const curveType = this.curves[i >> 3]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; - r2 = frames[i + 5/*R2*/]; - g2 = frames[i + 6/*G2*/]; - b2 = frames[i + 7/*B2*/]; - let t = (time - before) / (frames[i + 8/*ENTRIES*/] - before); - r += (frames[i + 8/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 8/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 8/*ENTRIES*/ + 3/*B*/] - b) * t; - a += (frames[i + 8/*ENTRIES*/ + 4/*A*/] - a) * t; - r2 += (frames[i + 8/*ENTRIES*/ + 5/*R2*/] - r2) * t; - g2 += (frames[i + 8/*ENTRIES*/ + 6/*G2*/] - g2) * t; - b2 += (frames[i + 8/*ENTRIES*/ + 7/*B2*/] - b2) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; + r2 = frames[i + 5 /* R2*/]; + g2 = frames[i + 6 /* G2*/]; + b2 = frames[i + 7 /* B2*/]; + const t = (time - before) / (frames[i + 8 /* ENTRIES*/] - before); + + r += (frames[i + 8 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 8 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 8 /* ENTRIES*/ + 3 /* B*/] - b) * t; + a += (frames[i + 8 /* ENTRIES*/ + 4 /* A*/] - a) * t; + r2 += (frames[i + 8 /* ENTRIES*/ + 5 /* R2*/] - r2) * t; + g2 += (frames[i + 8 /* ENTRIES*/ + 6 /* G2*/] - g2) * t; + b2 += (frames[i + 8 /* ENTRIES*/ + 7 /* B2*/] - b2) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - a = frames[i + 4/*A*/]; - r2 = frames[i + 5/*R2*/]; - g2 = frames[i + 6/*G2*/]; - b2 = frames[i + 7/*B2*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + a = frames[i + 4 /* A*/]; + r2 = frames[i + 5 /* R2*/]; + g2 = frames[i + 6 /* G2*/]; + b2 = frames[i + 7 /* B2*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); - r2 = this.getBezierValue(time, i, 5/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/); - g2 = this.getBezierValue(time, i, 6/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/); - b2 = this.getBezierValue(time, i, 7/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 6 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + a = this.getBezierValue(time, i, 4 /* A*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); + r2 = this.getBezierValue(time, i, 5 /* R2*/, curveType + 18 /* BEZIER_SIZE*/ * 4 - 2 /* BEZIER*/); + g2 = this.getBezierValue(time, i, 6 /* G2*/, curveType + 18 /* BEZIER_SIZE*/ * 5 - 2 /* BEZIER*/); + b2 = this.getBezierValue(time, i, 7 /* B2*/, curveType + 18 /* BEZIER_SIZE*/ * 6 - 2 /* BEZIER*/); } if (alpha == 1) { @@ -1225,7 +1328,8 @@ export class RGBA2Timeline extends CurveTimeline implements SlotTimeline{ } else { if (blend == MixBlend.setup) { light.setFromColor(slot.data.color); - let setupDark = slot.data.darkColor!; + const setupDark = slot.data.darkColor; + dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; @@ -1241,41 +1345,43 @@ export class RGBA2Timeline extends CurveTimeline implements SlotTimeline{ /** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. * @public * */ -export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ +export class RGB2Timeline extends CurveTimeline implements SlotTimeline { slotIndex = 0; - constructor (frameCount: number, bezierCount: number, slotIndex: number) { - super(frameCount, bezierCount, [ - Property.rgb + "|" + slotIndex, - Property.rgb2 + "|" + slotIndex - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number) { + super(frameCount, bezierCount, [`${Property.rgb}|${slotIndex}`, `${Property.rgb2}|${slotIndex}`]); this.slotIndex = slotIndex; } - getFrameEntries () { - return 7/*ENTRIES*/; + getFrameEntries() { + return 7 /* ENTRIES*/; } /** Sets the time in seconds, light, and dark colors for the specified key frame. */ - setFrame (frame: number, time: number, r: number, g: number, b: number, r2: number, g2: number, b2: number) { - frame *= 7/*ENTRIES*/; + setFrame(frame: number, time: number, r: number, g: number, b: number, r2: number, g2: number, b2: number) { + frame *= 7 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*R*/] = r; - this.frames[frame + 2/*G*/] = g; - this.frames[frame + 3/*B*/] = b; - this.frames[frame + 4/*R2*/] = r2; - this.frames[frame + 5/*G2*/] = g2; - this.frames[frame + 6/*B2*/] = b2; + this.frames[frame + 1 /* R*/] = r; + this.frames[frame + 2 /* G*/] = g; + this.frames[frame + 3 /* B*/] = b; + this.frames[frame + 4 /* R2*/] = r2; + this.frames[frame + 5 /* G2*/] = g2; + this.frames[frame + 6 /* B2*/] = b2; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let frames = this.frames; - let light = slot.color, dark = slot.darkColor!; + const frames = this.frames; + const light = slot.color; + const dark = slot.darkColor; + if (time < frames[0]) { - let setupLight = slot.data.color, setupDark = slot.data.darkColor!; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + switch (blend) { case MixBlend.setup: light.r = setupLight.r; @@ -1284,6 +1390,7 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; + return; case MixBlend.first: light.r += (setupLight.r - light.r) * alpha; @@ -1293,44 +1400,53 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ dark.g += (setupDark.g - dark.g) * alpha; dark.b += (setupDark.b - dark.b) * alpha; } + return; } - let r = 0, g = 0, b = 0, r2 = 0, g2 = 0, b2 = 0; - let i = Timeline.search(frames, time, 7/*ENTRIES*/); - let curveType = this.curves[i / 7/*ENTRIES*/]; + let r = 0; + let g = 0; + let b = 0; + let r2 = 0; + let g2 = 0; + let b2 = 0; + const i = Timeline.search(frames, time, 7 /* ENTRIES*/); + const curveType = this.curves[i / 7 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - r2 = frames[i + 4/*R2*/]; - g2 = frames[i + 5/*G2*/]; - b2 = frames[i + 6/*B2*/]; - let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before); - r += (frames[i + 7/*ENTRIES*/ + 1/*R*/] - r) * t; - g += (frames[i + 7/*ENTRIES*/ + 2/*G*/] - g) * t; - b += (frames[i + 7/*ENTRIES*/ + 3/*B*/] - b) * t; - r2 += (frames[i + 7/*ENTRIES*/ + 4/*R2*/] - r2) * t; - g2 += (frames[i + 7/*ENTRIES*/ + 5/*G2*/] - g2) * t; - b2 += (frames[i + 7/*ENTRIES*/ + 6/*B2*/] - b2) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + r2 = frames[i + 4 /* R2*/]; + g2 = frames[i + 5 /* G2*/]; + b2 = frames[i + 6 /* B2*/]; + const t = (time - before) / (frames[i + 7 /* ENTRIES*/] - before); + + r += (frames[i + 7 /* ENTRIES*/ + 1 /* R*/] - r) * t; + g += (frames[i + 7 /* ENTRIES*/ + 2 /* G*/] - g) * t; + b += (frames[i + 7 /* ENTRIES*/ + 3 /* B*/] - b) * t; + r2 += (frames[i + 7 /* ENTRIES*/ + 4 /* R2*/] - r2) * t; + g2 += (frames[i + 7 /* ENTRIES*/ + 5 /* G2*/] - g2) * t; + b2 += (frames[i + 7 /* ENTRIES*/ + 6 /* B2*/] - b2) * t; break; - case 1/*STEPPED*/: - r = frames[i + 1/*R*/]; - g = frames[i + 2/*G*/]; - b = frames[i + 3/*B*/]; - r2 = frames[i + 4/*R2*/]; - g2 = frames[i + 5/*G2*/]; - b2 = frames[i + 6/*B2*/]; + case 1 /* STEPPED*/: + r = frames[i + 1 /* R*/]; + g = frames[i + 2 /* G*/]; + b = frames[i + 3 /* B*/]; + r2 = frames[i + 4 /* R2*/]; + g2 = frames[i + 5 /* G2*/]; + b2 = frames[i + 6 /* B2*/]; break; default: - r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/); - g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - r2 = this.getBezierValue(time, i, 4/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); - g2 = this.getBezierValue(time, i, 5/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/); - b2 = this.getBezierValue(time, i, 6/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/); + r = this.getBezierValue(time, i, 1 /* R*/, curveType - 2 /* BEZIER*/); + g = this.getBezierValue(time, i, 2 /* G*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + b = this.getBezierValue(time, i, 3 /* B*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + r2 = this.getBezierValue(time, i, 4 /* R2*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); + g2 = this.getBezierValue(time, i, 5 /* G2*/, curveType + 18 /* BEZIER_SIZE*/ * 4 - 2 /* BEZIER*/); + b2 = this.getBezierValue(time, i, 6 /* B2*/, curveType + 18 /* BEZIER_SIZE*/ * 5 - 2 /* BEZIER*/); } if (alpha == 1) { @@ -1342,7 +1458,9 @@ export class RGB2Timeline extends CurveTimeline implements SlotTimeline{ dark.b = b2; } else { if (blend == MixBlend.setup) { - let setupLight = slot.data.color, setupDark = slot.data.darkColor!; + const setupLight = slot.data.color; + const setupDark = slot.data.darkColor; + light.r = setupLight.r; light.g = setupLight.g; light.b = setupLight.b; @@ -1369,42 +1487,43 @@ export class AttachmentTimeline extends Timeline implements SlotTimeline { /** The attachment name for each key frame. May contain null values to clear the attachment. */ attachmentNames: Array; - constructor (frameCount: number, slotIndex: number) { - super(frameCount, [ - Property.attachment + "|" + slotIndex - ]); + constructor(frameCount: number, slotIndex: number) { + super(frameCount, [`${Property.attachment}|${slotIndex}`]); this.slotIndex = slotIndex; this.attachmentNames = new Array(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the attachment name for the specified key frame. */ - setFrame (frame: number, time: number, attachmentName: string | null) { + setFrame(frame: number, time: number, attachmentName: string | null) { this.frames[frame] = time; this.attachmentNames[frame] = attachmentName; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; if (direction == MixDirection.mixOut) { if (blend == MixBlend.setup) this.setAttachment(skeleton, slot, slot.data.attachmentName); + return; } if (time < this.frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName); + return; } this.setAttachment(skeleton, slot, this.attachmentNames[Timeline.search1(this.frames, time)]); } - setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string | null) { + setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string | null) { slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); } } @@ -1421,39 +1540,43 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { /** The vertices for each key frame. */ vertices: Array; - constructor (frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) { - super(frameCount, bezierCount, [ - Property.deform + "|" + slotIndex + "|" + attachment.id - ]); + constructor(frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) { + super(frameCount, bezierCount, [`${Property.deform}|${slotIndex}|${attachment.id}`]); this.slotIndex = slotIndex; this.attachment = attachment; this.vertices = new Array(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the vertices for the specified key frame. * @param vertices Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights. */ - setFrame (frame: number, time: number, vertices: NumberArrayLike) { + setFrame(frame: number, time: number, vertices: NumberArrayLike) { this.frames[frame] = time; this.vertices[frame] = vertices; } /** @param value1 Ignored (0 is used for a deform timeline). * @param value2 Ignored (1 is used for a deform timeline). */ - setBezier (bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, - cy2: number, time2: number, value2: number) { - let curves = this.curves; - let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/; - if (value == 0) curves[frame] = 2/*BEZIER*/ + i; - let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = cy2 * 0.03 - cy1 * 0.06; - let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = (cy1 - cy2 + 0.33333333) * 0.018; - let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy; - let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = cy1 * 0.3 + tmpy + dddy * 0.16666667; - let x = time1 + dx, y = dy; - for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) { + setBezier(bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number, cy2: number, time2: number, value2: number) { + const curves = this.curves; + let i = this.getFrameCount() + bezier * 18; /* BEZIER_SIZE*/ + + if (value == 0) curves[frame] = 2 /* BEZIER*/ + i; + const tmpx = (time1 - cx1 * 2 + cx2) * 0.03; + const tmpy = cy2 * 0.03 - cy1 * 0.06; + const dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006; + const dddy = (cy1 - cy2 + 0.33333333) * 0.018; + let ddx = tmpx * 2 + dddx; + let ddy = tmpy * 2 + dddy; + let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667; + let dy = cy1 * 0.3 + tmpy + dddy * 0.16666667; + let x = time1 + dx; + let y = dy; + + for (let n = i + 18 /* BEZIER_SIZE*/; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; dx += ddx; @@ -1465,175 +1588,203 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { } } - getCurvePercent (time: number, frame: number) { - let curves = this.curves; + getCurvePercent(time: number, frame: number) { + const curves = this.curves; let i = curves[frame]; + switch (i) { - case 0/*LINEAR*/: - let x = this.frames[frame]; + case 0 /* LINEAR*/: + const x = this.frames[frame]; + return (time - x) / (this.frames[frame + this.getFrameEntries()] - x); - case 1/*STEPPED*/: + case 1 /* STEPPED*/: return 0; } - i -= 2/*BEZIER*/; + i -= 2 /* BEZIER*/; if (curves[i] > time) { - let x = this.frames[frame]; - return curves[i + 1] * (time - x) / (curves[i] - x); + const x = this.frames[frame]; + + return (curves[i + 1] * (time - x)) / (curves[i] - x); } - let n = i + 18/*BEZIER_SIZE*/; + const n = i + 18; /* BEZIER_SIZE*/ + for (i += 2; i < n; i += 2) { if (curves[i] >= time) { - let x = curves[i - 2], y = curves[i - 1]; - return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y); + const x = curves[i - 2]; + const y = curves[i - 1]; + + return y + ((time - x) / (curves[i] - x)) * (curves[i + 1] - y); } } - let x = curves[n - 2], y = curves[n - 1]; - return y + (1 - y) * (time - x) / (this.frames[frame + this.getFrameEntries()] - x); + const x = curves[n - 2]; + const y = curves[n - 1]; + + return y + ((1 - y) * (time - x)) / (this.frames[frame + this.getFrameEntries()] - x); } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot: Slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot: Slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let slotAttachment: Attachment | null = slot.getAttachment(); + const slotAttachment: Attachment | null = slot.getAttachment(); + if (!slotAttachment) return; if (!(slotAttachment instanceof VertexAttachment) || (slotAttachment).timelineAttachment != this.attachment) return; - let deform: Array = slot.deform; + const deform: Array = slot.deform; + if (deform.length == 0) blend = MixBlend.setup; - let vertices = this.vertices; - let vertexCount = vertices[0].length; + const vertices = this.vertices; + const vertexCount = vertices[0].length; + + const frames = this.frames; - let frames = this.frames; if (time < frames[0]) { switch (blend) { case MixBlend.setup: deform.length = 0; + return; case MixBlend.first: if (alpha == 1) { deform.length = 0; + return; } deform.length = vertexCount; - let vertexAttachment = slotAttachment; + const vertexAttachment = slotAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions. - let setupVertices = vertexAttachment.vertices; - for (var i = 0; i < vertexCount; i++) - deform[i] += (setupVertices[i] - deform[i]) * alpha; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) deform[i] += (setupVertices[i] - deform[i]) * alpha; } else { // Weighted deform offsets. alpha = 1 - alpha; - for (var i = 0; i < vertexCount; i++) - deform[i] *= alpha; + for (let i = 0; i < vertexCount; i++) deform[i] *= alpha; } } + return; } deform.length = vertexCount; - if (time >= frames[frames.length - 1]) { // Time is after last frame. - let lastVertices = vertices[frames.length - 1]; + if (time >= frames[frames.length - 1]) { + // Time is after last frame. + const lastVertices = vertices[frames.length - 1]; + if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i] - setupVertices[i]; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i] - setupVertices[i]; } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i]; + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i]; } - } else - Utils.arrayCopy(lastVertices, 0, deform, 0, vertexCount); + } else Utils.arrayCopy(lastVertices, 0, deform, 0, vertexCount); } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let setup = setupVertices[i]; + const setup = setupVertices[i]; + deform[i] = setup + (lastVertices[i] - setup) * alpha; } } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] = lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] = lastVertices[i] * alpha; } break; } case MixBlend.first: case MixBlend.replace: - for (let i = 0; i < vertexCount; i++) - deform[i] += (lastVertices[i] - deform[i]) * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] += (lastVertices[i] - deform[i]) * alpha; break; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; - for (let i = 0; i < vertexCount; i++) - deform[i] += (lastVertices[i] - setupVertices[i]) * alpha; + const setupVertices = vertexAttachment.vertices; + + for (let i = 0; i < vertexCount; i++) deform[i] += (lastVertices[i] - setupVertices[i]) * alpha; } else { // Weighted deform offsets, with alpha. - for (let i = 0; i < vertexCount; i++) - deform[i] += lastVertices[i] * alpha; + for (let i = 0; i < vertexCount; i++) deform[i] += lastVertices[i] * alpha; } } } + return; } // Interpolate between the previous frame and the current frame. - let frame = Timeline.search1(frames, time); - let percent = this.getCurvePercent(time, frame); - let prevVertices = vertices[frame]; - let nextVertices = vertices[frame + 1]; + const frame = Timeline.search1(frames, time); + const percent = this.getCurvePercent(time, frame); + const prevVertices = vertices[frame]; + const nextVertices = vertices[frame + 1]; if (alpha == 1) { if (blend == MixBlend.add) { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i]; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += prev + (nextVertices[i] - prev) * percent; } } } else { for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] = prev + (nextVertices[i] - prev) * percent; } } } else { switch (blend) { case MixBlend.setup: { - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i], setup = setupVertices[i]; + const prev = prevVertices[i]; + const setup = setupVertices[i]; + deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -1642,23 +1793,28 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { case MixBlend.first: case MixBlend.replace: for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha; } break; case MixBlend.add: - let vertexAttachment = slotAttachment as VertexAttachment; + const vertexAttachment = slotAttachment as VertexAttachment; + if (!vertexAttachment.bones) { // Unweighted vertex positions, with alpha. - let setupVertices = vertexAttachment.vertices; + const setupVertices = vertexAttachment.vertices; + for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha; } } else { // Weighted deform offsets, with alpha. for (let i = 0; i < vertexCount; i++) { - let prev = prevVertices[i]; + const prev = prevVertices[i]; + deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha; } } @@ -1671,54 +1827,57 @@ export class DeformTimeline extends CurveTimeline implements SlotTimeline { * @public * */ export class EventTimeline extends Timeline { - static propertyIds = ["" + Property.event]; + static propertyIds = [`${Property.event}`]; /** The event for each key frame. */ events: Array; - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount, EventTimeline.propertyIds); this.events = new Array(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the event for the specified key frame. */ - setFrame (frame: number, event: Event) { + setFrame(frame: number, event: Event) { this.frames[frame] = event.time; this.events[frame] = event; } /** Fires events for frames > `lastTime` and <= `time`. */ - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { if (!firedEvents) return; - let frames = this.frames; - let frameCount = this.frames.length; + const frames = this.frames; + const frameCount = this.frames.length; - if (lastTime > time) { // Fire events after last time for looped animations. + if (lastTime > time) { + // Fire events after last time for looped animations. this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, blend, direction); lastTime = -1; - } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + } else if (lastTime >= frames[frameCount - 1]) + // Last time is after last frame. return; if (time < frames[0]) return; // Time is before first frame. let i = 0; - if (lastTime < frames[0]) - i = 0; + + if (lastTime < frames[0]) i = 0; else { i = Timeline.search1(frames, lastTime) + 1; - let frameTime = frames[i]; - while (i > 0) { // Fire multiple events with the same frame. + const frameTime = frames[i]; + + while (i > 0) { + // Fire multiple events with the same frame. if (frames[i - 1] != frameTime) break; i--; } } - for (; i < frameCount && time >= frames[i]; i++) - firedEvents.push(this.events[i]); + for (; i < frameCount && time >= frames[i]; i++) firedEvents.push(this.events[i]); } } @@ -1726,48 +1885,50 @@ export class EventTimeline extends Timeline { * @public * */ export class DrawOrderTimeline extends Timeline { - static propertyIds = ["" + Property.drawOrder]; + static propertyIds = [`${Property.drawOrder}`]; /** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */ drawOrders: Array | null>; - constructor (frameCount: number) { + constructor(frameCount: number) { super(frameCount, DrawOrderTimeline.propertyIds); this.drawOrders = new Array | null>(frameCount); } - getFrameCount () { + getFrameCount() { return this.frames.length; } /** Sets the time in seconds and the draw order for the specified key frame. * @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose * draw order. */ - setFrame (frame: number, time: number, drawOrder: Array | null) { + setFrame(frame: number, time: number, drawOrder: Array | null) { this.frames[frame] = time; this.drawOrders[frame] = drawOrder; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { if (direction == MixDirection.mixOut) { if (blend == MixBlend.setup) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } if (time < this.frames[0]) { if (blend == MixBlend.setup || blend == MixBlend.first) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; } - let idx = Timeline.search1(this.frames, time); - let drawOrderToSetupIndex = this.drawOrders[idx]; - if (!drawOrderToSetupIndex) - Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + const idx = Timeline.search1(this.frames, time); + const drawOrderToSetupIndex = this.drawOrders[idx]; + + if (!drawOrderToSetupIndex) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); else { - let drawOrder: Array = skeleton.drawOrder; - let slots: Array = skeleton.slots; - for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) - drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + const drawOrder: Array = skeleton.drawOrder; + const slots: Array = skeleton.slots; + + for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++) drawOrder[i] = slots[drawOrderToSetupIndex[i]]; } } } @@ -1778,35 +1939,35 @@ export class DrawOrderTimeline extends Timeline { * */ export class IkConstraintTimeline extends CurveTimeline { /** The index of the IK constraint slot in {@link Skeleton#ikConstraints} that will be changed. */ - ikConstraintIndex: number = 0; + ikConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, ikConstraintIndex: number) { - super(frameCount, bezierCount, [ - Property.ikConstraint + "|" + ikConstraintIndex - ]); + constructor(frameCount: number, bezierCount: number, ikConstraintIndex: number) { + super(frameCount, bezierCount, [`${Property.ikConstraint}|${ikConstraintIndex}`]); this.ikConstraintIndex = ikConstraintIndex; } - getFrameEntries () { - return 6/*ENTRIES*/; + getFrameEntries() { + return 6 /* ENTRIES*/; } /** Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame. */ - setFrame (frame: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) { - frame *= 6/*ENTRIES*/; + setFrame(frame: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) { + frame *= 6 /* ENTRIES*/; this.frames[frame] = time; - this.frames[frame + 1/*MIX*/] = mix; - this.frames[frame + 2/*SOFTNESS*/] = softness; - this.frames[frame + 3/*BEND_DIRECTION*/] = bendDirection; - this.frames[frame + 4/*COMPRESS*/] = compress ? 1 : 0; - this.frames[frame + 5/*STRETCH*/] = stretch ? 1 : 0; + this.frames[frame + 1 /* MIX*/] = mix; + this.frames[frame + 2 /* SOFTNESS*/] = softness; + this.frames[frame + 3 /* BEND_DIRECTION*/] = bendDirection; + this.frames[frame + 4 /* COMPRESS*/] = compress ? 1 : 0; + this.frames[frame + 5 /* STRETCH*/] = stretch ? 1 : 0; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: @@ -1815,6 +1976,7 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.bendDirection = constraint.data.bendDirection; constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; + return; case MixBlend.first: constraint.mix += (constraint.data.mix - constraint.mix) * alpha; @@ -1823,28 +1985,33 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } + return; } - let mix = 0, softness = 0; - let i = Timeline.search(frames, time, 6/*ENTRIES*/) - let curveType = this.curves[i / 6/*ENTRIES*/]; + let mix = 0; + let softness = 0; + const i = Timeline.search(frames, time, 6 /* ENTRIES*/); + const curveType = this.curves[i / 6 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - mix = frames[i + 1/*MIX*/]; - softness = frames[i + 2/*SOFTNESS*/]; - let t = (time - before) / (frames[i + 6/*ENTRIES*/] - before); - mix += (frames[i + 6/*ENTRIES*/ + 1/*MIX*/] - mix) * t; - softness += (frames[i + 6/*ENTRIES*/ + 2/*SOFTNESS*/] - softness) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + mix = frames[i + 1 /* MIX*/]; + softness = frames[i + 2 /* SOFTNESS*/]; + const t = (time - before) / (frames[i + 6 /* ENTRIES*/] - before); + + mix += (frames[i + 6 /* ENTRIES*/ + 1 /* MIX*/] - mix) * t; + softness += (frames[i + 6 /* ENTRIES*/ + 2 /* SOFTNESS*/] - softness) * t; break; - case 1/*STEPPED*/: - mix = frames[i + 1/*MIX*/]; - softness = frames[i + 2/*SOFTNESS*/]; + case 1 /* STEPPED*/: + mix = frames[i + 1 /* MIX*/]; + softness = frames[i + 2 /* SOFTNESS*/]; break; default: - mix = this.getBezierValue(time, i, 1/*MIX*/, curveType - 2/*BEZIER*/); - softness = this.getBezierValue(time, i, 2/*SOFTNESS*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); + mix = this.getBezierValue(time, i, 1 /* MIX*/, curveType - 2 /* BEZIER*/); + softness = this.getBezierValue(time, i, 2 /* SOFTNESS*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); } if (blend == MixBlend.setup) { @@ -1856,17 +2023,17 @@ export class IkConstraintTimeline extends CurveTimeline { constraint.compress = constraint.data.compress; constraint.stretch = constraint.data.stretch; } else { - constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/]; - constraint.compress = frames[i + 4/*COMPRESS*/] != 0; - constraint.stretch = frames[i + 5/*STRETCH*/] != 0; + constraint.bendDirection = frames[i + 3 /* BEND_DIRECTION*/]; + constraint.compress = frames[i + 4 /* COMPRESS*/] != 0; + constraint.stretch = frames[i + 5 /* STRETCH*/] != 0; } } else { constraint.mix += (mix - constraint.mix) * alpha; constraint.softness += (softness - constraint.softness) * alpha; if (direction == MixDirection.mixIn) { - constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/]; - constraint.compress = frames[i + 4/*COMPRESS*/] != 0; - constraint.stretch = frames[i + 5/*STRETCH*/] != 0; + constraint.bendDirection = frames[i + 3 /* BEND_DIRECTION*/]; + constraint.compress = frames[i + 4 /* COMPRESS*/] != 0; + constraint.stretch = frames[i + 5 /* STRETCH*/] != 0; } } } @@ -1878,40 +2045,41 @@ export class IkConstraintTimeline extends CurveTimeline { * */ export class TransformConstraintTimeline extends CurveTimeline { /** The index of the transform constraint slot in {@link Skeleton#transformConstraints} that will be changed. */ - transformConstraintIndex: number = 0; + transformConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, transformConstraintIndex: number) { - super(frameCount, bezierCount, [ - Property.transformConstraint + "|" + transformConstraintIndex - ]); + constructor(frameCount: number, bezierCount: number, transformConstraintIndex: number) { + super(frameCount, bezierCount, [`${Property.transformConstraint}|${transformConstraintIndex}`]); this.transformConstraintIndex = transformConstraintIndex; } - getFrameEntries () { - return 7/*ENTRIES*/; + getFrameEntries() { + return 7 /* ENTRIES*/; } /** The time in seconds, rotate mix, translate mix, scale mix, and shear mix for the specified key frame. */ - setFrame (frame: number, time: number, mixRotate: number, mixX: number, mixY: number, mixScaleX: number, mixScaleY: number, - mixShearY: number) { - let frames = this.frames; - frame *= 7/*ENTRIES*/; + setFrame(frame: number, time: number, mixRotate: number, mixX: number, mixY: number, mixScaleX: number, mixScaleY: number, mixShearY: number) { + const frames = this.frames; + + frame *= 7 /* ENTRIES*/; frames[frame] = time; - frames[frame + 1/*ROTATE*/] = mixRotate; - frames[frame + 2/*X*/] = mixX; - frames[frame + 3/*Y*/] = mixY; - frames[frame + 4/*SCALEX*/] = mixScaleX; - frames[frame + 5/*SCALEY*/] = mixScaleY; - frames[frame + 6/*SHEARY*/] = mixShearY; + frames[frame + 1 /* ROTATE*/] = mixRotate; + frames[frame + 2 /* X*/] = mixX; + frames[frame + 3 /* Y*/] = mixY; + frames[frame + 4 /* SCALEX*/] = mixScaleX; + frames[frame + 5 /* SCALEY*/] = mixScaleY; + frames[frame + 6 /* SHEARY*/] = mixShearY; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { - let data = constraint.data; + const data = constraint.data; + switch (blend) { case MixBlend.setup: constraint.mixRotate = data.mixRotate; @@ -1920,6 +2088,7 @@ export class TransformConstraintTimeline extends CurveTimeline { constraint.mixScaleX = data.mixScaleX; constraint.mixScaleY = data.mixScaleY; constraint.mixShearY = data.mixShearY; + return; case MixBlend.first: constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha; @@ -1929,48 +2098,58 @@ export class TransformConstraintTimeline extends CurveTimeline { constraint.mixScaleY += (data.mixScaleY - constraint.mixScaleY) * alpha; constraint.mixShearY += (data.mixShearY - constraint.mixShearY) * alpha; } + return; } - let rotate, x, y, scaleX, scaleY, shearY; - let i = Timeline.search(frames, time, 7/*ENTRIES*/); - let curveType = this.curves[i / 7/*ENTRIES*/]; + let rotate; + let x; + let y; + let scaleX; + let scaleY; + let shearY; + const i = Timeline.search(frames, time, 7 /* ENTRIES*/); + const curveType = this.curves[i / 7 /* ENTRIES*/]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; - scaleX = frames[i + 4/*SCALEX*/]; - scaleY = frames[i + 5/*SCALEY*/]; - shearY = frames[i + 6/*SHEARY*/]; - let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before); - rotate += (frames[i + 7/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t; - x += (frames[i + 7/*ENTRIES*/ + 2/*X*/] - x) * t; - y += (frames[i + 7/*ENTRIES*/ + 3/*Y*/] - y) * t; - scaleX += (frames[i + 7/*ENTRIES*/ + 4/*SCALEX*/] - scaleX) * t; - scaleY += (frames[i + 7/*ENTRIES*/ + 5/*SCALEY*/] - scaleY) * t; - shearY += (frames[i + 7/*ENTRIES*/ + 6/*SHEARY*/] - shearY) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; + scaleX = frames[i + 4 /* SCALEX*/]; + scaleY = frames[i + 5 /* SCALEY*/]; + shearY = frames[i + 6 /* SHEARY*/]; + const t = (time - before) / (frames[i + 7 /* ENTRIES*/] - before); + + rotate += (frames[i + 7 /* ENTRIES*/ + 1 /* ROTATE*/] - rotate) * t; + x += (frames[i + 7 /* ENTRIES*/ + 2 /* X*/] - x) * t; + y += (frames[i + 7 /* ENTRIES*/ + 3 /* Y*/] - y) * t; + scaleX += (frames[i + 7 /* ENTRIES*/ + 4 /* SCALEX*/] - scaleX) * t; + scaleY += (frames[i + 7 /* ENTRIES*/ + 5 /* SCALEY*/] - scaleY) * t; + shearY += (frames[i + 7 /* ENTRIES*/ + 6 /* SHEARY*/] - shearY) * t; break; - case 1/*STEPPED*/: - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; - scaleX = frames[i + 4/*SCALEX*/]; - scaleY = frames[i + 5/*SCALEY*/]; - shearY = frames[i + 6/*SHEARY*/]; + case 1 /* STEPPED*/: + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; + scaleX = frames[i + 4 /* SCALEX*/]; + scaleY = frames[i + 5 /* SCALEY*/]; + shearY = frames[i + 6 /* SHEARY*/]; break; default: - rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/); - x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); - scaleX = this.getBezierValue(time, i, 4/*SCALEX*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/); - scaleY = this.getBezierValue(time, i, 5/*SCALEY*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/); - shearY = this.getBezierValue(time, i, 6/*SHEARY*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/); + rotate = this.getBezierValue(time, i, 1 /* ROTATE*/, curveType - 2 /* BEZIER*/); + x = this.getBezierValue(time, i, 2 /* X*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 3 /* Y*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); + scaleX = this.getBezierValue(time, i, 4 /* SCALEX*/, curveType + 18 /* BEZIER_SIZE*/ * 3 - 2 /* BEZIER*/); + scaleY = this.getBezierValue(time, i, 5 /* SCALEY*/, curveType + 18 /* BEZIER_SIZE*/ * 4 - 2 /* BEZIER*/); + shearY = this.getBezierValue(time, i, 6 /* SHEARY*/, curveType + 18 /* BEZIER_SIZE*/ * 5 - 2 /* BEZIER*/); } if (blend == MixBlend.setup) { - let data = constraint.data; + const data = constraint.data; + constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha; constraint.mixX = data.mixX + (x - data.mixX) * alpha; constraint.mixY = data.mixY + (y - data.mixY) * alpha; @@ -1993,35 +2172,37 @@ export class TransformConstraintTimeline extends CurveTimeline { * */ export class PathConstraintPositionTimeline extends CurveTimeline1 { /** The index of the path constraint slot in {@link Skeleton#pathConstraints} that will be changed. */ - pathConstraintIndex: number = 0; + pathConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) { - super(frameCount, bezierCount, Property.pathConstraintPosition + "|" + pathConstraintIndex); + constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) { + super(frameCount, bezierCount, `${Property.pathConstraintPosition}|${pathConstraintIndex}`); this.pathConstraintIndex = pathConstraintIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.position = constraint.data.position; + return; case MixBlend.first: constraint.position += (constraint.data.position - constraint.position) * alpha; } + return; } - let position = this.getCurveValue(time); + const position = this.getCurveValue(time); - if (blend == MixBlend.setup) - constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; - else - constraint.position += (position - constraint.position) * alpha; + if (blend == MixBlend.setup) constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; + else constraint.position += (position - constraint.position) * alpha; } } @@ -2032,33 +2213,35 @@ export class PathConstraintSpacingTimeline extends CurveTimeline1 { /** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */ pathConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) { - super(frameCount, bezierCount, Property.pathConstraintSpacing + "|" + pathConstraintIndex); + constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) { + super(frameCount, bezierCount, `${Property.pathConstraintSpacing}|${pathConstraintIndex}`); this.pathConstraintIndex = pathConstraintIndex; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.spacing = constraint.data.spacing; + return; case MixBlend.first: constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha; } + return; } - let spacing = this.getCurveValue(time); + const spacing = this.getCurveValue(time); - if (blend == MixBlend.setup) - constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; - else - constraint.spacing += (spacing - constraint.spacing) * alpha; + if (blend == MixBlend.setup) constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; + else constraint.spacing += (spacing - constraint.spacing) * alpha; } } @@ -2070,73 +2253,82 @@ export class PathConstraintMixTimeline extends CurveTimeline { /** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */ pathConstraintIndex = 0; - constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) { - super(frameCount, bezierCount, [ - Property.pathConstraintMix + "|" + pathConstraintIndex - ]); + constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) { + super(frameCount, bezierCount, [`${Property.pathConstraintMix}|${pathConstraintIndex}`]); this.pathConstraintIndex = pathConstraintIndex; } - getFrameEntries () { - return 4/*ENTRIES*/; + getFrameEntries() { + return 4 /* ENTRIES*/; } - setFrame (frame: number, time: number, mixRotate: number, mixX: number, mixY: number) { - let frames = this.frames; + setFrame(frame: number, time: number, mixRotate: number, mixX: number, mixY: number) { + const frames = this.frames; + frame <<= 2; frames[frame] = time; - frames[frame + 1/*ROTATE*/] = mixRotate; - frames[frame + 2/*X*/] = mixX; - frames[frame + 3/*Y*/] = mixY; + frames[frame + 1 /* ROTATE*/] = mixRotate; + frames[frame + 2 /* X*/] = mixX; + frames[frame + 3 /* Y*/] = mixY; } - apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (!constraint.active) return; - let frames = this.frames; + const frames = this.frames; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: constraint.mixRotate = constraint.data.mixRotate; constraint.mixX = constraint.data.mixX; constraint.mixY = constraint.data.mixY; + return; case MixBlend.first: constraint.mixRotate += (constraint.data.mixRotate - constraint.mixRotate) * alpha; constraint.mixX += (constraint.data.mixX - constraint.mixX) * alpha; constraint.mixY += (constraint.data.mixY - constraint.mixY) * alpha; } + return; } - let rotate, x, y; - let i = Timeline.search(frames, time, 4/*ENTRIES*/); - let curveType = this.curves[i >> 2]; + let rotate; + let x; + let y; + const i = Timeline.search(frames, time, 4 /* ENTRIES*/); + const curveType = this.curves[i >> 2]; + switch (curveType) { - case 0/*LINEAR*/: - let before = frames[i]; - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; - let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before); - rotate += (frames[i + 4/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t; - x += (frames[i + 4/*ENTRIES*/ + 2/*X*/] - x) * t; - y += (frames[i + 4/*ENTRIES*/ + 3/*Y*/] - y) * t; + case 0 /* LINEAR*/: + const before = frames[i]; + + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; + const t = (time - before) / (frames[i + 4 /* ENTRIES*/] - before); + + rotate += (frames[i + 4 /* ENTRIES*/ + 1 /* ROTATE*/] - rotate) * t; + x += (frames[i + 4 /* ENTRIES*/ + 2 /* X*/] - x) * t; + y += (frames[i + 4 /* ENTRIES*/ + 3 /* Y*/] - y) * t; break; - case 1/*STEPPED*/: - rotate = frames[i + 1/*ROTATE*/]; - x = frames[i + 2/*X*/]; - y = frames[i + 3/*Y*/]; + case 1 /* STEPPED*/: + rotate = frames[i + 1 /* ROTATE*/]; + x = frames[i + 2 /* X*/]; + y = frames[i + 3 /* Y*/]; break; default: - rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/); - x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/); - y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/); + rotate = this.getBezierValue(time, i, 1 /* ROTATE*/, curveType - 2 /* BEZIER*/); + x = this.getBezierValue(time, i, 2 /* X*/, curveType + 18 /* BEZIER_SIZE*/ - 2 /* BEZIER*/); + y = this.getBezierValue(time, i, 3 /* Y*/, curveType + 18 /* BEZIER_SIZE*/ * 2 - 2 /* BEZIER*/); } if (blend == MixBlend.setup) { - let data = constraint.data; + const data = constraint.data; + constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha; constraint.mixX = data.mixX + (x - data.mixX) * alpha; constraint.mixY = data.mixY + (y - data.mixY) * alpha; @@ -2159,63 +2351,68 @@ export class SequenceTimeline extends Timeline implements SlotTimeline { slotIndex: number; attachment: IHasTextureRegion; - constructor (frameCount: number, slotIndex: number, attachment: IHasTextureRegion) { - super(frameCount, [ - Property.sequence + "|" + slotIndex + "|" + attachment.sequence!.id - ]); + constructor(frameCount: number, slotIndex: number, attachment: IHasTextureRegion) { + super(frameCount, [`${Property.sequence}|${slotIndex}|${attachment.sequence.id}`]); this.slotIndex = slotIndex; this.attachment = attachment; } - getFrameEntries () { + getFrameEntries() { return SequenceTimeline.ENTRIES; } - getSlotIndex () { + getSlotIndex() { return this.slotIndex; } - getAttachment () { + getAttachment() { return this.attachment as unknown as Attachment; } /** Sets the time, mode, index, and frame time for the specified frame. * @param frame Between 0 and frameCount, inclusive. * @param time Seconds between frames. */ - setFrame (frame: number, time: number, mode: SequenceMode, index: number, delay: number) { - let frames = this.frames; + setFrame(frame: number, time: number, mode: SequenceMode, index: number, delay: number) { + const frames = this.frames; + frame *= SequenceTimeline.ENTRIES; frames[frame] = time; frames[frame + SequenceTimeline.MODE] = mode | (index << 4); frames[frame + SequenceTimeline.DELAY] = delay; } - apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { - let slot = skeleton.slots[this.slotIndex]; + apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + const slot = skeleton.slots[this.slotIndex]; + if (!slot.bone.active) return; - let slotAttachment = slot.attachment; - let attachment = this.attachment as unknown as Attachment; + const slotAttachment = slot.attachment; + const attachment = this.attachment as unknown as Attachment; + if (slotAttachment != attachment) { - if (!(slotAttachment instanceof VertexAttachment) - || (slotAttachment as VertexAttachment).timelineAttachment != attachment) return; + if (!(slotAttachment instanceof VertexAttachment) || (slotAttachment as VertexAttachment).timelineAttachment != attachment) return; } - let frames = this.frames; - if (time < frames[0]) { // Time is before first frame. + const frames = this.frames; + + if (time < frames[0]) { + // Time is before first frame. if (blend == MixBlend.setup || blend == MixBlend.first) slot.sequenceIndex = -1; + return; } - let i = Timeline.search(frames, time, SequenceTimeline.ENTRIES); - let before = frames[i]; - let modeAndIndex = frames[i + SequenceTimeline.MODE]; - let delay = frames[i + SequenceTimeline.DELAY]; + const i = Timeline.search(frames, time, SequenceTimeline.ENTRIES); + const before = frames[i]; + const modeAndIndex = frames[i + SequenceTimeline.MODE]; + const delay = frames[i + SequenceTimeline.DELAY]; if (!this.attachment.sequence) return; - let index = modeAndIndex >> 4, count = this.attachment.sequence!.regions.length; - let mode = SequenceModeValues[modeAndIndex & 0xf]; + let index = modeAndIndex >> 4; + const count = this.attachment.sequence.regions.length; + const mode = SequenceModeValues[modeAndIndex & 0xf]; + if (mode != SequenceMode.hold) { - index += (((time - before) / delay + 0.00001) | 0); + index += ((time - before) / delay + 0.00001) | 0; switch (mode) { case SequenceMode.once: index = Math.min(count - 1, index); @@ -2224,7 +2421,8 @@ export class SequenceTimeline extends Timeline implements SlotTimeline { index %= count; break; case SequenceMode.pingpong: { - let n = (count << 1) - 2; + const n = (count << 1) - 2; + index = n == 0 ? 0 : index % n; if (index >= count) index = n - index; break; @@ -2236,7 +2434,8 @@ export class SequenceTimeline extends Timeline implements SlotTimeline { index = count - 1 - (index % count); break; case SequenceMode.pingpongReverse: { - let n = (count << 1) - 2; + const n = (count << 1) - 2; + index = n == 0 ? 0 : (index + count - 1) % n; if (index >= count) index = n - index; } diff --git a/packages/runtime-4.1/src/core/AnimationState.ts b/packages/runtime-4.1/src/core/AnimationState.ts index 48cdcbdf..22e2e6bc 100644 --- a/packages/runtime-4.1/src/core/AnimationState.ts +++ b/packages/runtime-4.1/src/core/AnimationState.ts @@ -1,25 +1,9 @@ -import { - IAnimationState, - IAnimationStateListener, - ITrackEntry, - MathUtils, - MixBlend, - MixDirection, - Pool, - StringSet, - Utils -} from "@pixi-spine/base"; -import { - Animation, - AttachmentTimeline, - DrawOrderTimeline, - EventTimeline, - RotateTimeline, Timeline -} from './Animation'; -import {AnimationStateData} from "./AnimationStateData"; -import {Event} from './Event'; -import type {Skeleton} from "./Skeleton"; -import type {Slot} from "./Slot"; +import { IAnimationState, IAnimationStateListener, ITrackEntry, MathUtils, MixBlend, MixDirection, Pool, StringSet, Utils } from '@pixi-spine/base'; +import { Animation, AttachmentTimeline, DrawOrderTimeline, EventTimeline, RotateTimeline, Timeline } from './Animation'; +import type { AnimationStateData } from './AnimationStateData'; +import type { Event } from './Event'; +import type { Skeleton } from './Skeleton'; +import type { Slot } from './Slot'; /** Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies * multiple animations on top of each other (layering). @@ -28,8 +12,8 @@ import type {Slot} from "./Slot"; * @public * */ export class AnimationState implements IAnimationState { - static _emptyAnimation = new Animation("", [], 0); - private static emptyAnimation (): Animation { + static _emptyAnimation = new Animation('', [], 0); + private static emptyAnimation(): Animation { return AnimationState._emptyAnimation; } @@ -54,16 +38,18 @@ export class AnimationState implements IAnimationState { trackEntryPool = new Pool(() => new TrackEntry()); - constructor (data: AnimationStateData) { + constructor(data: AnimationStateData) { this.data = data; } /** Increments each track entry {@link TrackEntry#trackTime()}, setting queued animations as current if needed. */ - update (delta: number) { + update(delta: number) { delta *= this.timeScale; - let tracks = this.tracks; + const tracks = this.tracks; + for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (!current) continue; current.animationLast = current.nextAnimationLast; @@ -79,9 +65,11 @@ export class AnimationState implements IAnimationState { } let next = current.next; + if (next) { // When the next entry's delay is passed, change to the next entry, preserving leftover time. - let nextTime = current.trackLast - next.delay; + const nextTime = current.trackLast - next.delay; + if (nextTime >= 0) { next.delay = 0; next.trackTime += current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale; @@ -102,6 +90,7 @@ export class AnimationState implements IAnimationState { if (current.mixingFrom && this.updateMixingFrom(current, delta)) { // End mixing from entries once all have completed. let from: TrackEntry | null = current.mixingFrom; + current.mixingFrom = null; if (from) from.mixingTo = null; while (from) { @@ -117,11 +106,12 @@ export class AnimationState implements IAnimationState { } /** Returns true when all mixing from entries are complete. */ - updateMixingFrom (to: TrackEntry, delta: number): boolean { - let from = to.mixingFrom; + updateMixingFrom(to: TrackEntry, delta: number): boolean { + const from = to.mixingFrom; + if (!from) return true; - let finished = this.updateMixingFrom(from, delta); + const finished = this.updateMixingFrom(from, delta); from.animationLast = from.nextAnimationLast; from.trackLast = from.nextTrackLast; @@ -135,69 +125,76 @@ export class AnimationState implements IAnimationState { to.interruptAlpha = from.interruptAlpha; this.queue.end(from); } + return finished; } from.trackTime += delta * from.timeScale; to.mixTime += delta; + return false; } /** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the * animation state can be applied to multiple skeletons to pose them identically. * @returns True if any animations were applied. */ - apply (skeleton: Skeleton): boolean { - if (!skeleton) throw new Error("skeleton cannot be null."); + apply(skeleton: Skeleton): boolean { + if (!skeleton) throw new Error('skeleton cannot be null.'); if (this.animationsChanged) this._animationsChanged(); - let events = this.events; - let tracks = this.tracks; + const events = this.events; + const tracks = this.tracks; let applied = false; for (let i = 0, n = tracks.length; i < n; i++) { - let current = tracks[i]; + const current = tracks[i]; + if (!current || current.delay > 0) continue; applied = true; - let blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; + const blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend; // Apply mixing from entries first. let mix = current.alpha; - if (current.mixingFrom) - mix *= this.applyMixingFrom(current, skeleton, blend); - else if (current.trackTime >= current.trackEnd && !current.next) - mix = 0; + + if (current.mixingFrom) mix *= this.applyMixingFrom(current, skeleton, blend); + else if (current.trackTime >= current.trackEnd && !current.next) mix = 0; // Apply current entry. - let animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime; + const animationLast = current.animationLast; + const animationTime = current.getAnimationTime(); + let applyTime = animationTime; let applyEvents: Event[] | null = events; + if (current.reverse) { - applyTime = current.animation!.duration - applyTime; + applyTime = current.animation.duration - applyTime; applyEvents = null; } - let timelines = current.animation!.timelines; - let timelineCount = timelines.length; + const timelines = current.animation.timelines; + const timelineCount = timelines.length; + if ((i == 0 && mix == 1) || blend == MixBlend.add) { for (let ii = 0; ii < timelineCount; ii++) { // Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets // to sometimes stop rendering when using color correction, as their RGBA values become NaN. // (https://github.com/pixijs/pixi-spine/issues/302) Utils.webkit602BugfixHelper(mix, blend); - var timeline = timelines[ii]; - if (timeline instanceof AttachmentTimeline) - this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true); - else - timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn); + const timeline = timelines[ii]; + + if (timeline instanceof AttachmentTimeline) this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true); + else timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn); } } else { - let timelineMode = current.timelineMode; + const timelineMode = current.timelineMode; + + const shortestRotation = current.shortestRotation; + const firstFrame = !shortestRotation && current.timelinesRotation.length != timelineCount << 1; - let shortestRotation = current.shortestRotation; - let firstFrame = !shortestRotation && current.timelinesRotation.length != timelineCount << 1; if (firstFrame) current.timelinesRotation.length = timelineCount << 1; for (let ii = 0; ii < timelineCount; ii++) { - let timeline = timelines[ii]; - let timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup; + const timeline = timelines[ii]; + const timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup; + if (!shortestRotation && timeline instanceof RotateTimeline) { this.applyRotateTimeline(timeline, skeleton, applyTime, mix, timelineBlend, current.timelinesRotation, ii << 1, firstFrame); } else if (timeline instanceof AttachmentTimeline) { @@ -218,27 +215,34 @@ export class AnimationState implements IAnimationState { // Set slots attachments to the setup pose, if needed. This occurs if an animation that is mixing out sets attachments so // subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or // the time is before the first key). - var setupState = this.unkeyedState + SETUP; - var slots = skeleton.slots; - for (var i = 0, n = skeleton.slots.length; i < n; i++) { - var slot = slots[i]; + const setupState = this.unkeyedState + SETUP; + const slots = skeleton.slots; + + for (let i = 0, n = skeleton.slots.length; i < n; i++) { + const slot = slots[i]; + if (slot.attachmentState == setupState) { - var attachmentName = slot.data.attachmentName; + const attachmentName = slot.data.attachmentName; + slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName)); } } this.unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot. this.queue.drain(); + return applied; } - applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { - let from = to.mixingFrom!; + applyMixingFrom(to: TrackEntry, skeleton: Skeleton, blend: MixBlend) { + const from = to.mixingFrom; + if (from.mixingFrom) this.applyMixingFrom(from, skeleton, blend); let mix = 0; - if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes. + + if (to.mixDuration == 0) { + // Single frame mix to undo mixingFrom changes. mix = 1; if (blend == MixBlend.first) blend = MixBlend.setup; } else { @@ -247,34 +251,38 @@ export class AnimationState implements IAnimationState { if (blend != MixBlend.first) blend = from.mixBlend; } - let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; - let timelines = from.animation!.timelines; - let timelineCount = timelines.length; - let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix); - let animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime; + const attachments = mix < from.attachmentThreshold; + const drawOrder = mix < from.drawOrderThreshold; + const timelines = from.animation.timelines; + const timelineCount = timelines.length; + const alphaHold = from.alpha * to.interruptAlpha; + const alphaMix = alphaHold * (1 - mix); + const animationLast = from.animationLast; + const animationTime = from.getAnimationTime(); + let applyTime = animationTime; let events = null; - if (from.reverse) - applyTime = from.animation!.duration - applyTime; - else if (mix < from.eventThreshold) - events = this.events; + + if (from.reverse) applyTime = from.animation.duration - applyTime; + else if (mix < from.eventThreshold) events = this.events; if (blend == MixBlend.add) { - for (let i = 0; i < timelineCount; i++) - timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut); + for (let i = 0; i < timelineCount; i++) timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut); } else { - let timelineMode = from.timelineMode; - let timelineHoldMix = from.timelineHoldMix; + const timelineMode = from.timelineMode; + const timelineHoldMix = from.timelineHoldMix; + + const shortestRotation = from.shortestRotation; + const firstFrame = !shortestRotation && from.timelinesRotation.length != timelineCount << 1; - let shortestRotation = from.shortestRotation; - let firstFrame = !shortestRotation && from.timelinesRotation.length != timelineCount << 1; if (firstFrame) from.timelinesRotation.length = timelineCount << 1; from.totalAlpha = 0; for (let i = 0; i < timelineCount; i++) { - let timeline = timelines[i]; + const timeline = timelines[i]; let direction = MixDirection.mixOut; let timelineBlend: MixBlend; let alpha = 0; + switch (timelineMode[i]) { case SUBSEQUENT: if (!drawOrder && timeline instanceof DrawOrderTimeline) continue; @@ -295,7 +303,8 @@ export class AnimationState implements IAnimationState { break; default: timelineBlend = MixBlend.setup; - let holdMix = timelineHoldMix[i]; + const holdMix = timelineHoldMix[i]; + alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration); break; } @@ -303,13 +312,11 @@ export class AnimationState implements IAnimationState { if (!shortestRotation && timeline instanceof RotateTimeline) this.applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, from.timelinesRotation, i << 1, firstFrame); - else if (timeline instanceof AttachmentTimeline) - this.applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, attachments); + else if (timeline instanceof AttachmentTimeline) this.applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, attachments); else { // This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109 Utils.webkit602BugfixHelper(alpha, blend); - if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup) - direction = MixDirection.mixIn; + if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup) direction = MixDirection.mixIn; timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction); } } @@ -323,39 +330,50 @@ export class AnimationState implements IAnimationState { return mix; } - applyAttachmentTimeline (timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) { - var slot = skeleton.slots[timeline.slotIndex]; + applyAttachmentTimeline(timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) { + const slot = skeleton.slots[timeline.slotIndex]; + if (!slot.bone.active) return; - if (time < timeline.frames[0]) { // Time is before first frame. - if (blend == MixBlend.setup || blend == MixBlend.first) - this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments); - } else - this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments); + if (time < timeline.frames[0]) { + // Time is before first frame. + if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments); + } else this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments); // If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later. if (slot.attachmentState <= this.unkeyedState) slot.attachmentState = this.unkeyedState + SETUP; } - setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string | null, attachments: boolean) { + setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string | null, attachments: boolean) { slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName)); if (attachments) slot.attachmentState = this.unkeyedState + CURRENT; } - applyRotateTimeline (timeline: RotateTimeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend, - timelinesRotation: Array, i: number, firstFrame: boolean) { - + applyRotateTimeline( + timeline: RotateTimeline, + skeleton: Skeleton, + time: number, + alpha: number, + blend: MixBlend, + timelinesRotation: Array, + i: number, + firstFrame: boolean + ) { if (firstFrame) timelinesRotation[i] = 0; if (alpha == 1) { timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn); + return; } - let bone = skeleton.bones[timeline.boneIndex]; + const bone = skeleton.bones[timeline.boneIndex]; + if (!bone.active) return; - let frames = timeline.frames; - let r1 = 0, r2 = 0; + const frames = timeline.frames; + let r1 = 0; + let r2 = 0; + if (time < frames[0]) { switch (blend) { case MixBlend.setup: @@ -372,12 +390,16 @@ export class AnimationState implements IAnimationState { } // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. - let total = 0, diff = r2 - r1; + let total = 0; + let diff = r2 - r1; + diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360; if (diff == 0) { total = timelinesRotation[i]; } else { - let lastTotal = 0, lastDiff = 0; + let lastTotal = 0; + let lastDiff = 0; + if (firstFrame) { lastTotal = 0; lastDiff = diff; @@ -385,14 +407,16 @@ export class AnimationState implements IAnimationState { lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. lastDiff = timelinesRotation[i + 1]; // Difference between bones. } - let current = diff > 0, dir = lastTotal >= 0; + const current = diff > 0; + let dir = lastTotal >= 0; // Detect cross at 0 (not 180). + if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) { // A cross after a 360 rotation is a loop. if (Math.abs(lastTotal) > 180) lastTotal += 360 * MathUtils.signum(lastTotal); dir = current; } - total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. + total = diff + lastTotal - (lastTotal % 360); // Store loops as part of lastTotal. if (dir != current) total += 360 * MathUtils.signum(lastTotal); timelinesRotation[i] = total; } @@ -400,16 +424,20 @@ export class AnimationState implements IAnimationState { bone.rotation = r1 + total * alpha; } - queueEvents (entry: TrackEntry, animationTime: number) { - let animationStart = entry.animationStart, animationEnd = entry.animationEnd; - let duration = animationEnd - animationStart; - let trackLastWrapped = entry.trackLast % duration; + queueEvents(entry: TrackEntry, animationTime: number) { + const animationStart = entry.animationStart; + const animationEnd = entry.animationEnd; + const duration = animationEnd - animationStart; + const trackLastWrapped = entry.trackLast % duration; // Queue events before complete. - let events = this.events; - let i = 0, n = events.length; + const events = this.events; + let i = 0; + const n = events.length; + for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < trackLastWrapped) break; if (event.time > animationEnd) continue; // Discard events outside animation start/end. this.queue.event(entry, event); @@ -417,15 +445,15 @@ export class AnimationState implements IAnimationState { // Queue complete if completed a loop iteration or the animation. let complete = false; - if (entry.loop) - complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; - else - complete = animationTime >= animationEnd && entry.animationLast < animationEnd; + + if (entry.loop) complete = duration == 0 || trackLastWrapped > entry.trackTime % duration; + else complete = animationTime >= animationEnd && entry.animationLast < animationEnd; if (complete) this.queue.complete(entry); // Queue events after complete. for (; i < n; i++) { - let event = events[i]; + const event = events[i]; + if (event.time < animationStart) continue; // Discard events outside animation start/end. this.queue.event(entry, event); } @@ -435,11 +463,11 @@ export class AnimationState implements IAnimationState { * * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose, * rather than leaving them in their current pose. */ - clearTracks () { - let oldDrainDisabled = this.queue.drainDisabled; + clearTracks() { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; - for (let i = 0, n = this.tracks.length; i < n; i++) - this.clearTrack(i); + for (let i = 0, n = this.tracks.length; i < n; i++) this.clearTrack(i); this.tracks.length = 0; this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); @@ -449,9 +477,10 @@ export class AnimationState implements IAnimationState { * * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose, * rather than leaving them in their current pose. */ - clearTrack (trackIndex: number) { + clearTrack(trackIndex: number) { if (trackIndex >= this.tracks.length) return; - let current = this.tracks[trackIndex]; + const current = this.tracks[trackIndex]; + if (!current) return; this.queue.end(current); @@ -459,8 +488,10 @@ export class AnimationState implements IAnimationState { this.clearNext(current); let entry = current; + while (true) { - let from = entry.mixingFrom; + const from = entry.mixingFrom; + if (!from) break; this.queue.end(from); entry.mixingFrom = null; @@ -473,8 +504,9 @@ export class AnimationState implements IAnimationState { this.queue.drain(); } - setCurrent (index: number, current: TrackEntry, interrupt: boolean) { - let from = this.expandToIndex(index); + setCurrent(index: number, current: TrackEntry, interrupt: boolean) { + const from = this.expandToIndex(index); + this.tracks[index] = current; current.previous = null; @@ -485,8 +517,7 @@ export class AnimationState implements IAnimationState { current.mixTime = 0; // Store the interrupted mix percentage. - if (from.mixingFrom && from.mixDuration > 0) - current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); + if (from.mixingFrom && from.mixDuration > 0) current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in. } @@ -497,9 +528,11 @@ export class AnimationState implements IAnimationState { /** Sets an animation by name. * * See {@link #setAnimationWith()}. */ - setAnimation (trackIndex: number, animationName: string, loop: boolean = false) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (!animation) throw new Error("Animation not found: " + animationName); + setAnimation(trackIndex: number, animationName: string, loop = false) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (!animation) throw new Error(`Animation not found: ${animationName}`); + return this.setAnimationWith(trackIndex, animation, loop); } @@ -509,10 +542,11 @@ export class AnimationState implements IAnimationState { * duration. In either case {@link TrackEntry#trackEnd} determines when the track is cleared. * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - setAnimationWith (trackIndex: number, animation: Animation, loop: boolean = false) { - if (!animation) throw new Error("animation cannot be null."); + setAnimationWith(trackIndex: number, animation: Animation, loop = false) { + if (!animation) throw new Error('animation cannot be null.'); let interrupt = true; let current = this.expandToIndex(trackIndex); + if (current) { if (current.nextTrackLast == -1) { // Don't mix from an entry that was never applied. @@ -522,21 +556,24 @@ export class AnimationState implements IAnimationState { this.clearNext(current); current = current.mixingFrom; interrupt = false; - } else - this.clearNext(current); + } else this.clearNext(current); } - let entry = this.trackEntry(trackIndex, animation, loop, current); + const entry = this.trackEntry(trackIndex, animation, loop, current); + this.setCurrent(trackIndex, entry, interrupt); this.queue.drain(); + return entry; } /** Queues an animation by name. * * See {@link #addAnimationWith()}. */ - addAnimation (trackIndex: number, animationName: string, loop: boolean = false, delay: number = 0) { - let animation = this.data.skeletonData.findAnimation(animationName); - if (!animation) throw new Error("Animation not found: " + animationName); + addAnimation(trackIndex: number, animationName: string, loop = false, delay = 0) { + const animation = this.data.skeletonData.findAnimation(animationName); + + if (!animation) throw new Error(`Animation not found: ${animationName}`); + return this.addAnimationWith(trackIndex, animation, loop, delay); } @@ -548,16 +585,16 @@ export class AnimationState implements IAnimationState { * previous entry is looping, its next loop completion is used instead of its duration. * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - addAnimationWith (trackIndex: number, animation: Animation, loop: boolean = false, delay: number = 0) { - if (!animation) throw new Error("animation cannot be null."); + addAnimationWith(trackIndex: number, animation: Animation, loop = false, delay = 0) { + if (!animation) throw new Error('animation cannot be null.'); let last = this.expandToIndex(trackIndex); + if (last) { - while (last.next) - last = last.next; + while (last.next) last = last.next; } - let entry = this.trackEntry(trackIndex, animation, loop, last); + const entry = this.trackEntry(trackIndex, animation, loop, last); if (!last) { this.setCurrent(trackIndex, entry, true); @@ -569,6 +606,7 @@ export class AnimationState implements IAnimationState { } entry.delay = delay; + return entry; } @@ -586,10 +624,12 @@ export class AnimationState implements IAnimationState { * {@link TrackEntry#setMixDuration()}. Mixing from an empty animation causes the new animation to be applied more and * more over the mix duration. Properties keyed in the new animation transition from the value from lower tracks or from the * setup pose value if no lower tracks key the property to the value keyed in the new animation. */ - setEmptyAnimation (trackIndex: number, mixDuration: number = 0) { - let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation(), false); + setEmptyAnimation(trackIndex: number, mixDuration = 0) { + const entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation(), false); + entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } @@ -604,37 +644,43 @@ export class AnimationState implements IAnimationState { * loop completion is used instead of its duration. * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept * after the {@link AnimationStateListener#dispose()} event occurs. */ - addEmptyAnimation (trackIndex: number, mixDuration: number = 0, delay: number = 0) { - let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation(), false, delay); + addEmptyAnimation(trackIndex: number, mixDuration = 0, delay = 0) { + const entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation(), false, delay); + if (delay <= 0) entry.delay += entry.mixDuration - mixDuration; entry.mixDuration = mixDuration; entry.trackEnd = mixDuration; + return entry; } /** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix * duration. */ - setEmptyAnimations (mixDuration: number = 0) { - let oldDrainDisabled = this.queue.drainDisabled; + setEmptyAnimations(mixDuration = 0) { + const oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; for (let i = 0, n = this.tracks.length; i < n; i++) { - let current = this.tracks[i]; + const current = this.tracks[i]; + if (current) this.setEmptyAnimation(current.trackIndex, mixDuration); } this.queue.drainDisabled = oldDrainDisabled; this.queue.drain(); } - expandToIndex (index: number) { + expandToIndex(index: number) { if (index < this.tracks.length) return this.tracks[index]; Utils.ensureArrayCapacity(this.tracks, index + 1, null); this.tracks.length = index + 1; + return null; } /** @param last May be null. */ - trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry | null) { - let entry = this.trackEntryPool.obtain(); + trackEntry(trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry | null) { + const entry = this.trackEntryPool.obtain(); + entry.reset(); entry.trackIndex = trackIndex; entry.animation = animation; @@ -662,16 +708,18 @@ export class AnimationState implements IAnimationState { entry.alpha = 1; entry.mixTime = 0; - entry.mixDuration = !last ? 0 : this.data.getMix(last.animation!, animation); + entry.mixDuration = !last ? 0 : this.data.getMix(last.animation, animation); entry.interruptAlpha = 1; entry.totalAlpha = 0; entry.mixBlend = MixBlend.replace; + return entry; } /** Removes the {@link TrackEntry#getNext() next entry} and all entries after it for the specified entry. */ - clearNext (entry: TrackEntry) { + clearNext(entry: TrackEntry) { let next = entry.next; + while (next) { this.queue.dispose(next); next = next.next; @@ -679,16 +727,17 @@ export class AnimationState implements IAnimationState { entry.next = null; } - _animationsChanged () { + _animationsChanged() { this.animationsChanged = false; this.propertyIDs.clear(); - let tracks = this.tracks; + const tracks = this.tracks; + for (let i = 0, n = tracks.length; i < n; i++) { let entry = tracks[i]; + if (!entry) continue; - while (entry.mixingFrom) - entry = entry.mixingFrom; + while (entry.mixingFrom) entry = entry.mixingFrom; do { if (!entry.mixingTo || entry.mixBlend != MixBlend.add) this.computeHold(entry); entry = entry.mixingTo; @@ -696,114 +745,126 @@ export class AnimationState implements IAnimationState { } } - computeHold (entry: TrackEntry) { - let to = entry.mixingTo; - let timelines = entry.animation!.timelines; - let timelinesCount = entry.animation!.timelines.length; - let timelineMode = entry.timelineMode; + computeHold(entry: TrackEntry) { + const to = entry.mixingTo; + const timelines = entry.animation.timelines; + const timelinesCount = entry.animation.timelines.length; + const timelineMode = entry.timelineMode; + timelineMode.length = timelinesCount; - let timelineHoldMix = entry.timelineHoldMix; + const timelineHoldMix = entry.timelineHoldMix; + timelineHoldMix.length = 0; - let propertyIDs = this.propertyIDs; + const propertyIDs = this.propertyIDs; if (to && to.holdPrevious) { - for (let i = 0; i < timelinesCount; i++) - timelineMode[i] = propertyIDs.addAll(timelines[i].getPropertyIds()) ? HOLD_FIRST : HOLD_SUBSEQUENT; + for (let i = 0; i < timelinesCount; i++) timelineMode[i] = propertyIDs.addAll(timelines[i].getPropertyIds()) ? HOLD_FIRST : HOLD_SUBSEQUENT; + return; } - outer: - for (let i = 0; i < timelinesCount; i++) { - let timeline = timelines[i]; - let ids = timeline.getPropertyIds(); - if (!propertyIDs.addAll(ids)) - timelineMode[i] = SUBSEQUENT; - else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline - || timeline instanceof EventTimeline || !to.animation!.hasTimeline(ids)) { - timelineMode[i] = FIRST; - } else { - for (let next = to.mixingTo; next; next = next!.mixingTo) { - if (next.animation!.hasTimeline(ids)) continue; - if (entry.mixDuration > 0) { - timelineMode[i] = HOLD_MIX; - timelineHoldMix[i] = next; - continue outer; - } - break; + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < timelinesCount; i++) { + const timeline = timelines[i]; + const ids = timeline.getPropertyIds(); + + if (!propertyIDs.addAll(ids)) timelineMode[i] = SUBSEQUENT; + else if ( + !to || + timeline instanceof AttachmentTimeline || + timeline instanceof DrawOrderTimeline || + timeline instanceof EventTimeline || + !to.animation.hasTimeline(ids) + ) { + timelineMode[i] = FIRST; + } else { + for (let next = to.mixingTo; next; next = next.mixingTo) { + if (next.animation.hasTimeline(ids)) continue; + if (entry.mixDuration > 0) { + timelineMode[i] = HOLD_MIX; + timelineHoldMix[i] = next; + // eslint-disable-next-line no-labels + continue outer; } - timelineMode[i] = HOLD_FIRST; + break; } + timelineMode[i] = HOLD_FIRST; } + } } /** Returns the track entry for the animation currently playing on the track, or null if no animation is currently playing. */ - getCurrent (trackIndex: number) { + getCurrent(trackIndex: number) { if (trackIndex >= this.tracks.length) return null; + return this.tracks[trackIndex]; } /** Adds a listener to receive events for all track entries. */ - addListener (listener: AnimationStateListener) { - if (!listener) throw new Error("listener cannot be null."); + addListener(listener: AnimationStateListener) { + if (!listener) throw new Error('listener cannot be null.'); this.listeners.push(listener); } /** Removes the listener added with {@link #addListener()}. */ - removeListener (listener: AnimationStateListener) { - let index = this.listeners.indexOf(listener); + removeListener(listener: AnimationStateListener) { + const index = this.listeners.indexOf(listener); + if (index >= 0) this.listeners.splice(index, 1); } /** Removes all listeners added with {@link #addListener()}. */ - clearListeners () { + clearListeners() { this.listeners.length = 0; } /** Discards all listener notifications that have not yet been delivered. This can be useful to call from an * {@link AnimationStateListener} when it is known that further notifications that may have been already queued for delivery * are not wanted because new animations are being set. */ - clearListenerNotifications () { + clearListenerNotifications() { this.queue.clear(); } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; onEnd: (trackIndex: number) => any; - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; setAnimationByName(trackIndex: number, animationName: string, loop: boolean) { if (!AnimationState.deprecatedWarning1) { AnimationState.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on.'); } this.setAnimation(trackIndex, animationName, loop); } - private static deprecatedWarning2: boolean = false; + private static deprecatedWarning2 = false; addAnimationByName(trackIndex: number, animationName: string, loop: boolean, delay: number) { if (!AnimationState.deprecatedWarning2) { AnimationState.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on.'); } this.addAnimation(trackIndex, animationName, loop, delay); } - private static deprecatedWarning3: boolean = false; + private static deprecatedWarning3 = false; hasAnimation(animationName: string): boolean { - let animation = this.data.skeletonData.findAnimation(animationName); + const animation = this.data.skeletonData.findAnimation(animationName); + return animation !== null; } hasAnimationByName(animationName: string): boolean { if (!AnimationState.deprecatedWarning3) { AnimationState.deprecatedWarning3 = true; - console.warn("Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on."); + console.warn('Spine Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on.'); } + return this.hasAnimation(animationName); } } @@ -839,11 +900,11 @@ export class TrackEntry implements ITrackEntry { /** The index of the track where this track entry is either current or queued. * * See {@link AnimationState#getCurrent()}. */ - trackIndex: number = 0; + trackIndex = 0; /** If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its * duration. */ - loop: boolean = false; + loop = false; /** If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead * of being mixed out. @@ -856,45 +917,44 @@ export class TrackEntry implements ITrackEntry { * * Snapping will occur if `holdPrevious` is true and this animation does not key all the same properties as the * previous animation. */ - holdPrevious: boolean = false; + holdPrevious = false; - reverse: boolean = false; + reverse = false; - shortestRotation: boolean = false; + shortestRotation = false; /** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the * `eventThreshold`, event timelines are applied while this animation is being mixed out. Defaults to 0, so event * timelines are not applied while this animation is being mixed out. */ - eventThreshold: number = 0; + eventThreshold = 0; /** When the mix percentage ({@link #mixtime} / {@link #mixDuration}) is less than the * `attachmentThreshold`, attachment timelines are applied while this animation is being mixed out. Defaults to * 0, so attachment timelines are not applied while this animation is being mixed out. */ - attachmentThreshold: number = 0; + attachmentThreshold = 0; /** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the * `drawOrderThreshold`, draw order timelines are applied while this animation is being mixed out. Defaults to 0, * so draw order timelines are not applied while this animation is being mixed out. */ - drawOrderThreshold: number = 0; + drawOrderThreshold = 0; /** Seconds when this animation starts, both initially and after looping. Defaults to 0. * * When changing the `animationStart` time, it often makes sense to set {@link #animationLast} to the same * value to prevent timeline keys before the start time from triggering. */ - animationStart: number = 0; + animationStart = 0; /** Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will * loop back to {@link #animationStart} at this time. Defaults to the animation {@link Animation#duration}. */ - animationEnd: number = 0; - + animationEnd = 0; /** The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this * animation is applied, event timelines will fire all events between the `animationLast` time (exclusive) and * `animationTime` (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation * is applied. */ - animationLast: number = 0; + animationLast = 0; - nextAnimationLast: number = 0; + nextAnimationLast = 0; /** Seconds to postpone playing the animation. When this track entry is the current track entry, `delay` * postpones incrementing the {@link #trackTime}. When this track entry is queued, `delay` is the time from @@ -902,14 +962,15 @@ export class TrackEntry implements ITrackEntry { * track entry {@link TrackEntry#trackTime} >= this track entry's `delay`). * * {@link #timeScale} affects the delay. */ - delay: number = 0; + delay = 0; /** Current time in seconds this track entry has been the current track entry. The track time determines * {@link #animationTime}. The track time can be set to start the animation at a time other than 0, without affecting * looping. */ - trackTime: number = 0; + trackTime = 0; - trackLast: number = 0; nextTrackLast: number = 0; + trackLast = 0; + nextTrackLast = 0; /** The track time in seconds when this animation will be removed from the track. Defaults to the highest possible float * value, meaning the animation will be applied until a new animation is set or the track is cleared. If the track end time @@ -918,7 +979,7 @@ export class TrackEntry implements ITrackEntry { * * It may be desired to use {@link AnimationState#addEmptyAnimation()} rather than have the animation * abruptly cease being applied. */ - trackEnd: number = 0; + trackEnd = 0; /** Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or * faster. Defaults to 1. @@ -931,18 +992,18 @@ export class TrackEntry implements ITrackEntry { * the time scale is not 1, the delay may need to be adjusted. * * See AnimationState {@link AnimationState#timeScale} for affecting all animations. */ - timeScale: number = 0; + timeScale = 0; /** Values < 1 mix this animation with the skeleton's current pose (usually the pose resulting from lower tracks). Defaults * to 1, which overwrites the skeleton's current pose with this animation. * * Typically track 0 is used to completely pose the skeleton, then alpha is used on higher tracks. It doesn't make sense to * use alpha on track 0 if the skeleton pose is from the last frame render. */ - alpha: number = 0; + alpha = 0; /** Seconds from 0 to the {@link #getMixDuration()} when mixing from the previous animation to this animation. May be * slightly more than `mixDuration` when the mix is complete. */ - mixTime: number = 0; + mixTime = 0; /** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by AnimationStateData * {@link AnimationStateData#getMix()} based on the animation before this animation (if any). @@ -957,7 +1018,9 @@ export class TrackEntry implements ITrackEntry { * When using {@link AnimationState#addAnimation()} with a `delay` <= 0, note the * {@link #delay} is set using the mix duration from the {@link AnimationStateData}, not a mix duration set * afterward. */ - mixDuration: number = 0; interruptAlpha: number = 0; totalAlpha: number = 0; + mixDuration = 0; + interruptAlpha = 0; + totalAlpha = 0; /** Controls how properties keyed in the animation are mixed with lower tracks. Defaults to {@link MixBlend#replace}, which * replaces the values from the lower tracks with the animation values. {@link MixBlend#add} adds the animation values to @@ -970,7 +1033,7 @@ export class TrackEntry implements ITrackEntry { timelineHoldMix = new Array(); timelinesRotation = new Array(); - reset () { + reset() { this.next = null; this.previous = null; this.mixingFrom = null; @@ -985,16 +1048,19 @@ export class TrackEntry implements ITrackEntry { /** Uses {@link #trackTime} to compute the `animationTime`, which is between {@link #animationStart} * and {@link #animationEnd}. When the `trackTime` is 0, the `animationTime` is equal to the * `animationStart` time. */ - getAnimationTime () { + getAnimationTime() { if (this.loop) { - let duration = this.animationEnd - this.animationStart; + const duration = this.animationEnd - this.animationStart; + if (duration == 0) return this.animationStart; + return (this.trackTime % duration) + this.animationStart; } + return Math.min(this.trackTime + this.animationStart, this.animationEnd); } - setAnimationLast (animationLast: number) { + setAnimationLast(animationLast: number) { this.animationLast = animationLast; this.nextAnimationLast = animationLast; } @@ -1002,7 +1068,7 @@ export class TrackEntry implements ITrackEntry { /** Returns true if at least one loop has been completed. * * See {@link AnimationStateListener#complete()}. */ - isComplete () { + isComplete() { return this.trackTime >= this.animationEnd - this.animationStart; } @@ -1013,20 +1079,22 @@ export class TrackEntry implements ITrackEntry { * the short way or the long way around. The two rotations likely change over time, so which direction is the short or long * way also changes. If the short way was always chosen, bones would flip to the other side when that direction became the * long way. TrackEntry chooses the short way the first time it is applied and remembers that direction. */ - resetRotationDirections () { + resetRotationDirections() { this.timelinesRotation.length = 0; } - getTrackComplete () { - let duration = this.animationEnd - this.animationStart; + getTrackComplete() { + const duration = this.animationEnd - this.animationStart; + if (duration != 0) { if (this.loop) return duration * (1 + ((this.trackTime / duration) | 0)); // Completion of next loop. if (this.trackTime < duration) return duration; // Before duration. } + return this.trackTime; // Next update. } - //deprecated stuff + // deprecated stuff onComplete: (trackIndex: number, loopCount: number) => any; onEvent: (trackIndex: number, event: Event) => any; onStart: (trackIndex: number) => any; @@ -1038,15 +1106,16 @@ export class TrackEntry implements ITrackEntry { get time() { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } + return this.trackTime; } set time(value: number) { if (!TrackEntry.deprecatedWarning1) { TrackEntry.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on.'); } this.trackTime = value; } @@ -1054,15 +1123,16 @@ export class TrackEntry implements ITrackEntry { get endTime() { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } + return this.trackTime; } set endTime(value: number) { if (!TrackEntry.deprecatedWarning2) { TrackEntry.deprecatedWarning2 = true; - console.warn("Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); + console.warn('Spine Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on.'); } this.trackTime = value; } @@ -1080,79 +1150,84 @@ export class EventQueue { drainDisabled = false; animState: AnimationState; - constructor (animState: AnimationState) { + constructor(animState: AnimationState) { this.animState = animState; } - start (entry: TrackEntry) { + start(entry: TrackEntry) { this.objects.push(EventType.start); this.objects.push(entry); this.animState.animationsChanged = true; } - interrupt (entry: TrackEntry) { + interrupt(entry: TrackEntry) { this.objects.push(EventType.interrupt); this.objects.push(entry); } - end (entry: TrackEntry) { + end(entry: TrackEntry) { this.objects.push(EventType.end); this.objects.push(entry); this.animState.animationsChanged = true; } - dispose (entry: TrackEntry) { + dispose(entry: TrackEntry) { this.objects.push(EventType.dispose); this.objects.push(entry); } - complete (entry: TrackEntry) { + complete(entry: TrackEntry) { this.objects.push(EventType.complete); this.objects.push(entry); } - event (entry: TrackEntry, event: Event) { + event(entry: TrackEntry, event: Event) { this.objects.push(EventType.event); this.objects.push(entry); this.objects.push(event); } - drain () { + drain() { if (this.drainDisabled) return; this.drainDisabled = true; - let objects = this.objects; - let listeners = this.animState.listeners; + const objects = this.objects; + const listeners = this.animState.listeners; for (let i = 0; i < objects.length; i += 2) { - let type = objects[i] as EventType; - let entry = objects[i + 1] as TrackEntry; + const type = objects[i] as EventType; + const entry = objects[i + 1] as TrackEntry; + switch (type) { case EventType.start: if (entry.listener && entry.listener.start) entry.listener.start(entry); for (let ii = 0; ii < listeners.length; ii++) { - let listener = listeners[ii]; + const listener = listeners[ii]; + if (listener.start) listener.start(entry); } break; case EventType.interrupt: if (entry.listener && entry.listener.interrupt) entry.listener.interrupt(entry); for (let ii = 0; ii < listeners.length; ii++) { - let listener = listeners[ii]; + const listener = listeners[ii]; + if (listener.interrupt) listener.interrupt(entry); } break; case EventType.end: if (entry.listener && entry.listener.end) entry.listener.end(entry); for (let ii = 0; ii < listeners.length; ii++) { - let listener = listeners[ii]; + const listener = listeners[ii]; + if (listener.end) listener.end(entry); } // Fall through. case EventType.dispose: if (entry.listener && entry.listener.dispose) entry.listener.dispose(entry); for (let ii = 0; ii < listeners.length; ii++) { - let listener = listeners[ii]; + const listener = listeners[ii]; + if (listener.dispose) listener.dispose(entry); } this.animState.trackEntryPool.free(entry); @@ -1160,15 +1235,18 @@ export class EventQueue { case EventType.complete: if (entry.listener && entry.listener.complete) entry.listener.complete(entry); for (let ii = 0; ii < listeners.length; ii++) { - let listener = listeners[ii]; + const listener = listeners[ii]; + if (listener.complete) listener.complete(entry); } break; case EventType.event: - let event = objects[i++ + 2] as Event; + const event = objects[i++ + 2] as Event; + if (entry.listener && entry.listener.event) entry.listener.event(entry, event); for (let ii = 0; ii < listeners.length; ii++) { - let listener = listeners[ii]; + const listener = listeners[ii]; + if (listener.event) listener.event(entry, event); } break; @@ -1179,7 +1257,7 @@ export class EventQueue { this.drainDisabled = false; } - clear () { + clear() { this.objects.length = 0; } } @@ -1188,7 +1266,12 @@ export class EventQueue { * @public */ export enum EventType { - start, interrupt, end, dispose, complete, event + start, + interrupt, + end, + dispose, + complete, + event, } /** The interface to implement for receiving TrackEntry events. It is always safe to call AnimationState methods when receiving @@ -1224,23 +1307,17 @@ export interface AnimationStateListener extends IAnimationStateListener { * @public */ export abstract class AnimationStateAdapter implements AnimationStateListener { - start (entry: TrackEntry) { - } + start(entry: TrackEntry) {} - interrupt (entry: TrackEntry) { - } + interrupt(entry: TrackEntry) {} - end (entry: TrackEntry) { - } + end(entry: TrackEntry) {} - dispose (entry: TrackEntry) { - } + dispose(entry: TrackEntry) {} - complete (entry: TrackEntry) { - } + complete(entry: TrackEntry) {} - event (entry: TrackEntry, event: Event) { - } + event(entry: TrackEntry, event: Event) {} } /** 1. A previously applied timeline has set this property. diff --git a/packages/runtime-4.1/src/core/AnimationStateData.ts b/packages/runtime-4.1/src/core/AnimationStateData.ts index 2771e382..174d159f 100644 --- a/packages/runtime-4.1/src/core/AnimationStateData.ts +++ b/packages/runtime-4.1/src/core/AnimationStateData.ts @@ -1,6 +1,6 @@ -import {SkeletonData} from "./SkeletonData"; -import {IAnimationStateData, StringMap} from '@pixi-spine/base'; -import type {Animation} from './Animation'; +import type { SkeletonData } from './SkeletonData'; +import type { IAnimationStateData, StringMap } from '@pixi-spine/base'; +import type { Animation } from './Animation'; /** Stores mix (crossfade) durations to be applied when {@link AnimationState} animations are changed. * @public @@ -14,37 +14,41 @@ export class AnimationStateData implements IAnimationStateData 0.0001) { s = Math.abs(pa * pd - pb * pc) / s; pa /= sx; @@ -179,12 +191,13 @@ export class Bone implements Updatable, IBone { pc = 0; prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg; } - let rx = rotation + shearX - prx; - let ry = rotation + shearY - prx + 90; - let la = MathUtils.cosDeg(rx) * scaleX; - let lb = MathUtils.cosDeg(ry) * scaleY; - let lc = MathUtils.sinDeg(rx) * scaleX; - let ld = MathUtils.sinDeg(ry) * scaleY; + const rx = rotation + shearX - prx; + const ry = rotation + shearY - prx + 90; + const la = MathUtils.cosDeg(rx) * scaleX; + const lb = MathUtils.cosDeg(ry) * scaleY; + const lc = MathUtils.sinDeg(rx) * scaleX; + const ld = MathUtils.sinDeg(ry) * scaleY; + m.a = pa * la - pb * lc; m.c = pa * lb - pb * ld; m.b = pc * la + pd * lc; @@ -193,24 +206,25 @@ export class Bone implements Updatable, IBone { } case TransformMode.NoScale: case TransformMode.NoScaleOrReflection: { - let cos = MathUtils.cosDeg(rotation); - let sin = MathUtils.sinDeg(rotation); + const cos = MathUtils.cosDeg(rotation); + const sin = MathUtils.sinDeg(rotation); let za = (pa * cos + pb * sin) / sx; let zc = (pc * cos + pd * sin) / sy; let s = Math.sqrt(za * za + zc * zc); + if (s > 0.00001) s = 1 / s; za *= s; zc *= s; s = Math.sqrt(za * za + zc * zc); - if (this.data.transformMode == TransformMode.NoScale - && (pa * pd - pb * pc < 0) != (sx < 0 != sy < 0)) s = -s; - let r = Math.PI / 2 + Math.atan2(zc, za); - let zb = Math.cos(r) * s; - let zd = Math.sin(r) * s; - let la = MathUtils.cosDeg(shearX) * scaleX; - let lb = MathUtils.cosDeg(90 + shearY) * scaleY; - let lc = MathUtils.sinDeg(shearX) * scaleX; - let ld = MathUtils.sinDeg(90 + shearY) * scaleY; + if (this.data.transformMode == TransformMode.NoScale && pa * pd - pb * pc < 0 != (sx < 0 != sy < 0)) s = -s; + const r = Math.PI / 2 + Math.atan2(zc, za); + const zb = Math.cos(r) * s; + const zd = Math.sin(r) * s; + const la = MathUtils.cosDeg(shearX) * scaleX; + const lb = MathUtils.cosDeg(90 + shearY) * scaleY; + const lc = MathUtils.sinDeg(shearX) * scaleX; + const ld = MathUtils.sinDeg(90 + shearY) * scaleY; + m.a = za * la + zb * lc; m.c = za * lb + zb * ld; m.b = zc * la + zd * lc; @@ -225,8 +239,9 @@ export class Bone implements Updatable, IBone { } /** Sets this bone's local transform to the setup pose. */ - setToSetupPose () { - let data = this.data; + setToSetupPose() { + const data = this.data; + this.x = data.x; this.y = data.y; this.rotation = data.rotation; @@ -237,24 +252,26 @@ export class Bone implements Updatable, IBone { } /** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */ - getWorldRotationX () { + getWorldRotationX() { return Math.atan2(this.matrix.b, this.matrix.a) * MathUtils.radDeg; } /** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */ - getWorldRotationY () { + getWorldRotationY() { return Math.atan2(this.matrix.d, this.matrix.c) * MathUtils.radDeg; } /** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */ - getWorldScaleX () { - let m = this.matrix; + getWorldScaleX() { + const m = this.matrix; + return Math.sqrt(m.a * m.a + m.b * m.b); } /** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */ - getWorldScaleY () { - let m = this.matrix; + getWorldScaleY() { + const m = this.matrix; + return Math.sqrt(m.c * m.c + m.d * m.d); } @@ -266,9 +283,10 @@ export class Bone implements Updatable, IBone { * * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after * calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */ - updateAppliedTransform () { - let parent = this.parent; - let m = this.matrix; + updateAppliedTransform() { + const parent = this.parent; + const m = this.matrix; + if (!parent) { this.ax = m.tx - this.skeleton.x; this.ay = m.ty - this.skeleton.y; @@ -277,25 +295,30 @@ export class Bone implements Updatable, IBone { this.ascaleY = Math.sqrt(m.c * m.c + m.d * m.d); this.ashearX = 0; this.ashearY = Math.atan2(m.a * m.c + m.b * m.d, m.a * m.d - m.b * m.c) * MathUtils.radDeg; + return; } - let pm = parent.matrix; - let pid = 1 / (pm.a * pm.d - pm.b * pm.c); - let dx = m.tx - pm.tx, dy = m.ty - pm.ty; - this.ax = (dx * pm.d * pid - dy * pm.c * pid); - this.ay = (dy * pm.a * pid - dx * pm.b * pid); - let ia = pid * pm.d; - let id = pid * pm.a; - let ib = pid * pm.c; - let ic = pid * pm.b; - let ra = ia * m.a - ib * m.b; - let rb = ia * m.c - ib * m.d; - let rc = id * m.b - ic * m.a; - let rd = id * m.d - ic * m.c; + const pm = parent.matrix; + const pid = 1 / (pm.a * pm.d - pm.b * pm.c); + const dx = m.tx - pm.tx; + const dy = m.ty - pm.ty; + + this.ax = dx * pm.d * pid - dy * pm.c * pid; + this.ay = dy * pm.a * pid - dx * pm.b * pid; + const ia = pid * pm.d; + const id = pid * pm.a; + const ib = pid * pm.c; + const ic = pid * pm.b; + const ra = ia * m.a - ib * m.b; + const rb = ia * m.c - ib * m.d; + const rc = id * m.b - ic * m.a; + const rd = id * m.d - ic * m.c; + this.ashearX = 0; this.ascaleX = Math.sqrt(ra * ra + rc * rc); if (this.ascaleX > 0.0001) { - let det = ra * rd - rb * rc; + const det = ra * rd - rb * rc; + this.ascaleY = det / this.ascaleX; this.ashearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg; this.arotation = Math.atan2(rc, ra) * MathUtils.radDeg; @@ -309,36 +332,49 @@ export class Bone implements Updatable, IBone { /** Transforms a point from world coordinates to the bone's local coordinates. */ worldToLocal(world: Vector2) { - let m = this.matrix; - let a = m.a, b = m.c, c = m.b, d = m.d; - let invDet = 1 / (a * d - b * c); - let x = world.x - m.tx, y = world.y - m.ty; - world.x = (x * d * invDet - y * b * invDet); - world.y = (y * a * invDet - x * c * invDet); + const m = this.matrix; + const a = m.a; + const b = m.c; + const c = m.b; + const d = m.d; + const invDet = 1 / (a * d - b * c); + const x = world.x - m.tx; + const y = world.y - m.ty; + + world.x = x * d * invDet - y * b * invDet; + world.y = y * a * invDet - x * c * invDet; + return world; } /** Transforms a point from the bone's local coordinates to world coordinates. */ localToWorld(local: Vector2) { - let m = this.matrix; - let x = local.x, y = local.y; + const m = this.matrix; + const x = local.x; + const y = local.y; + local.x = x * m.a + y * m.c + m.tx; local.y = x * m.b + y * m.d + m.ty; + return local; } /** Transforms a world rotation to a local rotation. */ - worldToLocalRotation (worldRotation: number) { - let sin = MathUtils.sinDeg(worldRotation), cos = MathUtils.cosDeg(worldRotation); - let mat = this.matrix; + worldToLocalRotation(worldRotation: number) { + const sin = MathUtils.sinDeg(worldRotation); + const cos = MathUtils.cosDeg(worldRotation); + const mat = this.matrix; + return Math.atan2(mat.a * sin - mat.b * cos, mat.d * cos - mat.c * sin) * MathUtils.radDeg; } /** Transforms a local rotation to a world rotation. */ - localToWorldRotation (localRotation: number) { + localToWorldRotation(localRotation: number) { localRotation -= this.rotation - this.shearX; - let sin = MathUtils.sinDeg(localRotation), cos = MathUtils.cosDeg(localRotation); - let mat = this.matrix; + const sin = MathUtils.sinDeg(localRotation); + const cos = MathUtils.cosDeg(localRotation); + const mat = this.matrix; + return Math.atan2(cos * mat.b + sin * mat.d, cos * mat.a + sin * mat.c) * MathUtils.radDeg; } @@ -346,10 +382,15 @@ export class Bone implements Updatable, IBone { *

* After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and {@link #update()} will * need to be called on any child bones, recursively. */ - rotateWorld (degrees: number) { - let mat = this.matrix; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees); + rotateWorld(degrees: number) { + const mat = this.matrix; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + const cos = MathUtils.cosDeg(degrees); + const sin = MathUtils.sinDeg(degrees); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; diff --git a/packages/runtime-4.1/src/core/BoneData.ts b/packages/runtime-4.1/src/core/BoneData.ts index 23257d7e..48ae185a 100644 --- a/packages/runtime-4.1/src/core/BoneData.ts +++ b/packages/runtime-4.1/src/core/BoneData.ts @@ -1,11 +1,11 @@ -import {Color, TransformMode} from '@pixi-spine/base'; +import { Color, TransformMode } from '@pixi-spine/base'; /** Stores the setup pose for a {@link Bone}. * @public * */ export class BoneData { /** The index of the bone in {@link Skeleton#getBones()}. */ - index: number = 0; + index = 0; /** The name of the bone, which is unique across all bones in the skeleton. */ name: string; @@ -14,7 +14,7 @@ export class BoneData { parent: BoneData | null = null; /** The bone's length. */ - length: number = 0; + length = 0; /** The local x translation. */ x = 0; @@ -49,9 +49,9 @@ export class BoneData { * rendered at runtime. */ color = new Color(); - constructor (index: number, name: string, parent: BoneData | null) { - if (index < 0) throw new Error("index must be >= 0."); - if (!name) throw new Error("name cannot be null."); + constructor(index: number, name: string, parent: BoneData | null) { + if (index < 0) throw new Error('index must be >= 0.'); + if (!name) throw new Error('name cannot be null.'); this.index = index; this.name = name; this.parent = parent; diff --git a/packages/runtime-4.1/src/core/ConstraintData.ts b/packages/runtime-4.1/src/core/ConstraintData.ts index 40fe5bdd..172701cc 100644 --- a/packages/runtime-4.1/src/core/ConstraintData.ts +++ b/packages/runtime-4.1/src/core/ConstraintData.ts @@ -2,5 +2,5 @@ * @public * */ export abstract class ConstraintData { - constructor(public name: string, public order: number, public skinRequired: boolean) { } + constructor(public name: string, public order: number, public skinRequired: boolean) {} } diff --git a/packages/runtime-4.1/src/core/Event.ts b/packages/runtime-4.1/src/core/Event.ts index bf2305e3..5864322b 100644 --- a/packages/runtime-4.1/src/core/Event.ts +++ b/packages/runtime-4.1/src/core/Event.ts @@ -1,5 +1,5 @@ -import {EventData} from "./EventData"; -import {IEvent} from "@pixi-spine/base"; +import type { EventData } from './EventData'; +import type { IEvent } from '@pixi-spine/base'; /** Stores the current pose values for an {@link Event}. * @@ -10,17 +10,16 @@ import {IEvent} from "@pixi-spine/base"; * */ export class Event implements IEvent { data: EventData; - intValue: number = 0; - floatValue: number = 0; + intValue = 0; + floatValue = 0; stringValue: string | null = null; - time: number = 0; - volume: number = 0; - balance: number = 0; + time = 0; + volume = 0; + balance = 0; - constructor (time: number, data: EventData) { - if (!data) throw new Error("data cannot be null."); + constructor(time: number, data: EventData) { + if (!data) throw new Error('data cannot be null.'); this.time = time; this.data = data; } } - diff --git a/packages/runtime-4.1/src/core/EventData.ts b/packages/runtime-4.1/src/core/EventData.ts index e85c97b8..bf06e3a5 100644 --- a/packages/runtime-4.1/src/core/EventData.ts +++ b/packages/runtime-4.1/src/core/EventData.ts @@ -1,4 +1,4 @@ -import {IEventData} from "@pixi-spine/base"; +import type { IEventData } from '@pixi-spine/base'; /** Stores the setup pose values for an {@link Event}. * @@ -7,14 +7,14 @@ import {IEventData} from "@pixi-spine/base"; * */ export class EventData implements IEventData { name: string; - intValue: number = 0; - floatValue: number = 0; + intValue = 0; + floatValue = 0; stringValue: string | null = null; audioPath: string | null = null; - volume: number = 0; - balance: number = 0; + volume = 0; + balance = 0; - constructor (name: string) { + constructor(name: string) { this.name = name; } } diff --git a/packages/runtime-4.1/src/core/IkConstraint.ts b/packages/runtime-4.1/src/core/IkConstraint.ts index b7633c46..b622bd16 100644 --- a/packages/runtime-4.1/src/core/IkConstraint.ts +++ b/packages/runtime-4.1/src/core/IkConstraint.ts @@ -1,8 +1,8 @@ -import {Updatable} from "./Updatable"; -import {IkConstraintData} from "./IkConstraintData"; -import {Bone} from "./Bone"; -import {Skeleton} from "./Skeleton"; -import {MathUtils, settings, TransformMode} from "@pixi-spine/base"; +import type { Updatable } from './Updatable'; +import type { IkConstraintData } from './IkConstraintData'; +import type { Bone } from './Bone'; +import type { Skeleton } from './Skeleton'; +import { MathUtils, settings, TransformMode } from '@pixi-spine/base'; /** Stores the current pose for an IK constraint. An IK constraint adjusts the rotation of 1 or 2 constrained bones so the tip of * the last bone is as close to the target bone as possible. @@ -37,9 +37,9 @@ export class IkConstraint implements Updatable { softness = 0; active = false; - constructor (data: IkConstraintData, skeleton: Skeleton) { - if (!data) throw new Error("data cannot be null."); - if (!skeleton) throw new Error("skeleton cannot be null."); + constructor(data: IkConstraintData, skeleton: Skeleton) { + if (!data) throw new Error('data cannot be null.'); + if (!skeleton) throw new Error('skeleton cannot be null.'); this.data = data; this.mix = data.mix; this.softness = data.softness; @@ -49,23 +49,26 @@ export class IkConstraint implements Updatable { this.bones = new Array(); for (let i = 0; i < data.bones.length; i++) { - let bone = skeleton.findBone(data.bones[i].name); + const bone = skeleton.findBone(data.bones[i].name); + if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}`); this.bones.push(bone); } - let target = skeleton.findBone(data.target.name); + const target = skeleton.findBone(data.target.name); + if (!target) throw new Error(`Couldn't find bone ${data.target.name}`); this.target = target; } - isActive () { + isActive() { return this.active; } - update () { + update() { if (this.mix == 0) return; - let target = this.target; - let bones = this.bones; + const target = this.target; + const bones = this.bones; + switch (bones.length) { case 1: this.apply1(bones[0], target.worldX, target.worldY, this.compress, this.stretch, this.data.uniform, this.mix); @@ -77,45 +80,54 @@ export class IkConstraint implements Updatable { } /** Applies 1 bone IK. The target is specified in the world coordinate system. */ - apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { - let p = bone.parent.matrix; - if (!p) throw new Error("IK bone must have parent."); - let pa = p.a, pb = p.c, pc = p.b, pd = p.d; - let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0; + apply1(bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) { + const p = bone.parent.matrix; + + if (!p) throw new Error('IK bone must have parent.'); + const pa = p.a; + let pb = p.c; + const pc = p.b; + let pd = p.d; + let rotationIK = -bone.ashearX - bone.arotation; + let tx = 0; + let ty = 0; - let skelX = bone.skeleton.scaleX; - let skelY = settings.yDown? -bone.skeleton.scaleY : bone.skeleton.scaleY; + const skelX = bone.skeleton.scaleX; + const skelY = settings.yDown ? -bone.skeleton.scaleY : bone.skeleton.scaleY; - switch(bone.data.transformMode) { + switch (bone.data.transformMode) { case TransformMode.OnlyTranslation: tx = targetX - bone.worldX; ty = targetY - bone.worldY; - //TODO: possible bug in spine-ts runtime! + // TODO: possible bug in spine-ts runtime! if (settings.yDown) { ty = -ty; } break; case TransformMode.NoRotationOrReflection: - let s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); - let sa = pa / skelX; - let sc = pc / skelY; + const s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); + const sa = pa / skelX; + const sc = pc / skelY; + pb = -sc * s * skelX; pd = sa * s * skelY; rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg; // Fall through default: - let x = targetX - p.tx, y = targetY - p.ty; - let d = pa * pd - pb * pc; + const x = targetX - p.tx; + const y = targetY - p.ty; + const d = pa * pd - pb * pc; + tx = (x * pd - y * pb) / d - bone.ax; ty = (y * pa - x * pc) / d - bone.ay; } rotationIK += Math.atan2(ty, tx) * MathUtils.radDeg; if (bone.ascaleX < 0) rotationIK += 180; - if (rotationIK > 180) - rotationIK -= 360; - else if (rotationIK < -180) - rotationIK += 360; - let sx = bone.ascaleX, sy = bone.ascaleY; + if (rotationIK > 180) rotationIK -= 360; + else if (rotationIK < -180) rotationIK += 360; + let sx = bone.ascaleX; + let sy = bone.ascaleY; + if (compress || stretch) { switch (bone.data.transformMode) { case TransformMode.NoScale: @@ -123,23 +135,34 @@ export class IkConstraint implements Updatable { tx = targetX - bone.worldX; ty = targetY - bone.worldY; } - let b = bone.data.length * sx, dd = Math.sqrt(tx * tx + ty * ty); - if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) { - let s = (dd / b - 1) * alpha + 1; + const b = bone.data.length * sx; + const dd = Math.sqrt(tx * tx + ty * ty); + + if ((compress && dd < b) || (stretch && dd > b && b > 0.0001)) { + const s = (dd / b - 1) * alpha + 1; + sx *= s; if (uniform) sy *= s; } } - bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, - bone.ashearY); + bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY); } /** Applies 2 bone IK. The target is specified in the world coordinate system. * @param child A direct descendant of the parent bone. */ - apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, uniform: boolean, softness: number, alpha: number) { - let px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX; - let pmat = parent.matrix; - let os1 = 0, os2 = 0, s2 = 0; + apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, uniform: boolean, softness: number, alpha: number) { + const px = parent.ax; + const py = parent.ay; + let psx = parent.ascaleX; + let psy = parent.ascaleY; + let sx = psx; + let sy = psy; + let csx = child.ascaleX; + const pmat = parent.matrix; + let os1 = 0; + let os2 = 0; + let s2 = 0; + if (psx < 0) { psx = -psx; os1 = 180; @@ -155,10 +178,17 @@ export class IkConstraint implements Updatable { if (csx < 0) { csx = -csx; os2 = 180; - } else - os2 = 0; - let cx = child.ax, cy = 0, cwx = 0, cwy = 0, a = pmat.a, b = pmat.c, c = pmat.b, d = pmat.d; - let u = Math.abs(psx - psy) <= 0.0001; + } else os2 = 0; + const cx = child.ax; + let cy = 0; + let cwx = 0; + let cwy = 0; + let a = pmat.a; + let b = pmat.c; + let c = pmat.b; + let d = pmat.d; + const u = Math.abs(psx - psy) <= 0.0001; + if (!u || stretch) { cy = 0; cwx = a * cx + pmat.tx; @@ -168,117 +198,148 @@ export class IkConstraint implements Updatable { cwx = a * cx + b * cy + pmat.tx; cwy = c * cx + d * cy + pmat.ty; } - let pp = parent.parent.matrix; - if (!pp) throw new Error("IK parent must itself have a parent."); + const pp = parent.parent.matrix; + + if (!pp) throw new Error('IK parent must itself have a parent.'); a = pp.a; b = pp.c; c = pp.b; d = pp.d; - let id = 1 / (a * d - b * c), x = cwx - pp.tx, y = cwy - pp.ty; - let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; - let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2; + const id = 1 / (a * d - b * c); + let x = cwx - pp.tx; + let y = cwy - pp.ty; + const dx = (x * d - y * b) * id - px; + const dy = (y * a - x * c) * id - py; + const l1 = Math.sqrt(dx * dx + dy * dy); + let l2 = child.data.length * csx; + let a1; + let a2; + if (l1 < 0.0001) { this.apply1(parent, targetX, targetY, false, stretch, false, alpha); child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); + return; } x = targetX - pp.tx; y = targetY - pp.ty; - let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py; + let tx = (x * d - y * b) * id - px; + let ty = (y * a - x * c) * id - py; let dd = tx * tx + ty * ty; + if (softness != 0) { softness *= psx * (csx + 1) * 0.5; - let td = Math.sqrt(dd), sd = td - l1 - l2 * psx + softness; + const td = Math.sqrt(dd); + const sd = td - l1 - l2 * psx + softness; + if (sd > 0) { let p = Math.min(1, sd / (softness * 2)) - 1; + p = (sd - softness * (1 - p * p)) / td; tx -= p * tx; ty -= p * ty; dd = tx * tx + ty * ty; } } - outer: - if (u) { - l2 *= psx; - let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); - if (cos < -1) { - cos = -1; - a2 = Math.PI * bendDir; - } else if (cos > 1) { - cos = 1; - a2 = 0; - if (stretch) { - a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; - sx *= a; - if (uniform) sy *= a; - } - } else - a2 = Math.acos(cos) * bendDir; - a = l1 + l2 * cos; - b = l2 * Math.sin(a2); - a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); - } else { - a = psx * l2; - b = psy * l2; - let aa = a * a, bb = b * b, ta = Math.atan2(ty, tx); - c = bb * l1 * l1 + aa * dd - aa * bb; - let c1 = -2 * bb * l1, c2 = bb - aa; - d = c1 * c1 - 4 * c2 * c; - if (d >= 0) { - let q = Math.sqrt(d); - if (c1 < 0) q = -q; - q = -(c1 + q) * 0.5; - let r0 = q / c2, r1 = c / q; - let r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; - if (r * r <= dd) { - y = Math.sqrt(dd - r * r) * bendDir; - a1 = ta - Math.atan2(y, r); - a2 = Math.atan2(y / psy, (r - l1) / psx); - break outer; - } + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: if (u) { + l2 *= psx; + let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2); + + if (cos < -1) { + cos = -1; + a2 = Math.PI * bendDir; + } else if (cos > 1) { + cos = 1; + a2 = 0; + if (stretch) { + a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1; + sx *= a; + if (uniform) sy *= a; } - let minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0; - let maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; - c = -a * l1 / (aa - bb); - if (c >= -1 && c <= 1) { - c = Math.acos(c); - x = a * Math.cos(c) + l1; - y = b * Math.sin(c); - d = x * x + y * y; - if (d < minDist) { - minAngle = c; - minDist = d; - minX = x; - minY = y; - } - if (d > maxDist) { - maxAngle = c; - maxDist = d; - maxX = x; - maxY = y; - } + } else a2 = Math.acos(cos) * bendDir; + a = l1 + l2 * cos; + b = l2 * Math.sin(a2); + a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); + } else { + a = psx * l2; + b = psy * l2; + const aa = a * a; + const bb = b * b; + const ta = Math.atan2(ty, tx); + + c = bb * l1 * l1 + aa * dd - aa * bb; + const c1 = -2 * bb * l1; + const c2 = bb - aa; + + d = c1 * c1 - 4 * c2 * c; + if (d >= 0) { + let q = Math.sqrt(d); + + if (c1 < 0) q = -q; + q = -(c1 + q) * 0.5; + const r0 = q / c2; + const r1 = c / q; + const r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; + + if (r * r <= dd) { + y = Math.sqrt(dd - r * r) * bendDir; + a1 = ta - Math.atan2(y, r); + a2 = Math.atan2(y / psy, (r - l1) / psx); + // eslint-disable-next-line no-labels + break outer; } - if (dd <= (minDist + maxDist) * 0.5) { - a1 = ta - Math.atan2(minY * bendDir, minX); - a2 = minAngle * bendDir; - } else { - a1 = ta - Math.atan2(maxY * bendDir, maxX); - a2 = maxAngle * bendDir; + } + let minAngle = MathUtils.PI; + let minX = l1 - a; + let minDist = minX * minX; + let minY = 0; + let maxAngle = 0; + let maxX = l1 + a; + let maxDist = maxX * maxX; + let maxY = 0; + + c = (-a * l1) / (aa - bb); + if (c >= -1 && c <= 1) { + c = Math.acos(c); + x = a * Math.cos(c) + l1; + y = b * Math.sin(c); + d = x * x + y * y; + if (d < minDist) { + minAngle = c; + minDist = d; + minX = x; + minY = y; + } + if (d > maxDist) { + maxAngle = c; + maxDist = d; + maxX = x; + maxY = y; } } - let os = Math.atan2(cy, cx) * s2; + if (dd <= (minDist + maxDist) * 0.5) { + a1 = ta - Math.atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } else { + a1 = ta - Math.atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + const os = Math.atan2(cy, cx) * s2; let rotation = parent.arotation; + a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation; - if (a1 > 180) - a1 -= 360; - else if (a1 < -180) // + if (a1 > 180) a1 -= 360; + else if (a1 < -180) + // a1 += 360; parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, sy, 0, 0); rotation = child.arotation; a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation; - if (a2 > 180) - a2 -= 360; - else if (a2 < -180) // + if (a2 > 180) a2 -= 360; + else if (a2 < -180) + // a2 += 360; child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); } diff --git a/packages/runtime-4.1/src/core/IkConstraintData.ts b/packages/runtime-4.1/src/core/IkConstraintData.ts index 2255ecf5..5b3581ce 100644 --- a/packages/runtime-4.1/src/core/IkConstraintData.ts +++ b/packages/runtime-4.1/src/core/IkConstraintData.ts @@ -1,5 +1,5 @@ -import {ConstraintData} from "./ConstraintData"; -import {BoneData} from "./BoneData"; +import { ConstraintData } from './ConstraintData'; +import type { BoneData } from './BoneData'; /** Stores the setup pose for an {@link IkConstraint}. *

@@ -12,9 +12,11 @@ export class IkConstraintData extends ConstraintData { /** The bone that is the IK target. */ private _target: BoneData | null = null; - public set target (boneData: BoneData) { this._target = boneData; } - public get target () { - if (!this._target) throw new Error("BoneData not set.") + public set target(boneData: BoneData) { + this._target = boneData; + } + public get target() { + if (!this._target) throw new Error('BoneData not set.'); else return this._target; } @@ -38,7 +40,7 @@ export class IkConstraintData extends ConstraintData { /** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */ softness = 0; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } diff --git a/packages/runtime-4.1/src/core/PathConstraint.ts b/packages/runtime-4.1/src/core/PathConstraint.ts index c31b123c..c7e0e8a4 100644 --- a/packages/runtime-4.1/src/core/PathConstraint.ts +++ b/packages/runtime-4.1/src/core/PathConstraint.ts @@ -1,10 +1,10 @@ -import {PathAttachment} from "./attachments"; -import {Updatable} from "./Updatable"; -import {PathConstraintData, SpacingMode} from "./PathConstraintData"; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Skeleton} from "./Skeleton"; -import {MathUtils, PositionMode, RotateMode, Utils} from "@pixi-spine/base"; +import { PathAttachment } from './attachments'; +import type { Updatable } from './Updatable'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import type { Bone } from './Bone'; +import type { Slot } from './Slot'; +import type { Skeleton } from './Skeleton'; +import { MathUtils, PositionMode, RotateMode, Utils } from '@pixi-spine/base'; /** Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the * constrained bones so they follow a {@link PathAttachment}. * @@ -12,7 +12,9 @@ import {MathUtils, PositionMode, RotateMode, Utils} from "@pixi-spine/base"; * @public * */ export class PathConstraint implements Updatable { - static NONE = -1; static BEFORE = -2; static AFTER = -3; + static NONE = -1; + static BEFORE = -2; + static AFTER = -3; static epsilon = 0.00001; /** The path constraint's setup pose data. */ @@ -36,23 +38,28 @@ export class PathConstraint implements Updatable { mixY = 0; - spaces = new Array(); positions = new Array(); - world = new Array(); curves = new Array(); lengths = new Array(); + spaces = new Array(); + positions = new Array(); + world = new Array(); + curves = new Array(); + lengths = new Array(); segments = new Array(); active = false; - constructor (data: PathConstraintData, skeleton: Skeleton) { - if (!data) throw new Error("data cannot be null."); - if (!skeleton) throw new Error("skeleton cannot be null."); + constructor(data: PathConstraintData, skeleton: Skeleton) { + if (!data) throw new Error('data cannot be null.'); + if (!skeleton) throw new Error('skeleton cannot be null.'); this.data = data; this.bones = new Array(); for (let i = 0, n = data.bones.length; i < n; i++) { - let bone = skeleton.findBone(data.bones[i].name); + const bone = skeleton.findBone(data.bones[i].name); + if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}.`); this.bones.push(bone); } - let target = skeleton.findSlot(data.target.name); + const target = skeleton.findSlot(data.target.name); + if (!target) throw new Error(`Couldn't find target bone ${data.target.name}`); this.target = target; this.position = data.position; @@ -62,35 +69,44 @@ export class PathConstraint implements Updatable { this.mixY = data.mixY; } - isActive () { + isActive() { return this.active; } - update () { - let attachment = this.target.getAttachment(); + update() { + const attachment = this.target.getAttachment(); + if (!(attachment instanceof PathAttachment)) return; - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY; + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + if (mixRotate == 0 && mixX == 0 && mixY == 0) return; - let data = this.data; - let tangents = data.rotateMode == RotateMode.Tangent, scale = data.rotateMode == RotateMode.ChainScale; + const data = this.data; + const tangents = data.rotateMode == RotateMode.Tangent; + const scale = data.rotateMode == RotateMode.ChainScale; - let bones = this.bones; - let boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1; - let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : []; - let spacing = this.spacing; + const bones = this.bones; + const boneCount = bones.length; + const spacesCount = tangents ? boneCount : boneCount + 1; + const spaces = Utils.setArraySize(this.spaces, spacesCount); + const lengths: Array = scale ? (this.lengths = Utils.setArraySize(this.lengths, boneCount)) : []; + const spacing = this.spacing; switch (data.spacingMode) { case SpacingMode.Percent: if (scale) { for (let i = 0, n = spacesCount - 1; i < n; i++) { - let bone = bones[i]; - let setupLength = bone.data.length; - if (setupLength < PathConstraint.epsilon) - lengths[i] = 0; + const bone = bones[i]; + const setupLength = bone.data.length; + + if (setupLength < PathConstraint.epsilon) lengths[i] = 0; else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + lengths[i] = Math.sqrt(x * x + y * y); } } @@ -99,63 +115,80 @@ export class PathConstraint implements Updatable { break; case SpacingMode.Proportional: let sum = 0; - for (let i = 0, n = spacesCount - 1; i < n;) { - let bone = bones[i]; - let setupLength = bone.data.length; + + for (let i = 0, n = spacesCount - 1; i < n; ) { + const bone = bones[i]; + const setupLength = bone.data.length; + if (setupLength < PathConstraint.epsilon) { if (scale) lengths[i] = 0; spaces[++i] = spacing; } else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; - let length = Math.sqrt(x * x + y * y); + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + const length = Math.sqrt(x * x + y * y); + if (scale) lengths[i] = length; spaces[++i] = length; sum += length; } } if (sum > 0) { - sum = spacesCount / sum * spacing; - for (let i = 1; i < spacesCount; i++) - spaces[i] *= sum; + sum = (spacesCount / sum) * spacing; + for (let i = 1; i < spacesCount; i++) spaces[i] *= sum; } break; default: - let lengthSpacing = data.spacingMode == SpacingMode.Length; - for (let i = 0, n = spacesCount - 1; i < n;) { - let bone = bones[i]; - let setupLength = bone.data.length; + const lengthSpacing = data.spacingMode == SpacingMode.Length; + + for (let i = 0, n = spacesCount - 1; i < n; ) { + const bone = bones[i]; + const setupLength = bone.data.length; + if (setupLength < PathConstraint.epsilon) { if (scale) lengths[i] = 0; spaces[++i] = spacing; } else { - let x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b; - let length = Math.sqrt(x * x + y * y); + const x = setupLength * bone.matrix.a; + const y = setupLength * bone.matrix.b; + const length = Math.sqrt(x * x + y * y); + if (scale) lengths[i] = length; - spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + spaces[++i] = ((lengthSpacing ? setupLength + spacing : spacing) * length) / setupLength; } } } - let positions = this.computeWorldPositions(attachment, spacesCount, tangents); - let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; + const positions = this.computeWorldPositions(attachment, spacesCount, tangents); + let boneX = positions[0]; + let boneY = positions[1]; + let offsetRotation = data.offsetRotation; let tip = false; - if (offsetRotation == 0) - tip = data.rotateMode == RotateMode.Chain; + + if (offsetRotation == 0) tip = data.rotateMode == RotateMode.Chain; else { tip = false; - let p = this.target.bone.matrix; + const p = this.target.bone.matrix; + offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.degRad : -MathUtils.degRad; } for (let i = 0, p = 3; i < boneCount; i++, p += 3) { - let bone = bones[i]; - let mat = bone.matrix; + const bone = bones[i]; + const mat = bone.matrix; + mat.tx += (boneX - mat.tx) * mixX; mat.ty += (boneY - mat.ty) * mixY; - let x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; + const x = positions[p]; + const y = positions[p + 1]; + const dx = x - boneX; + const dy = y - boneY; + if (scale) { - let length = lengths[i]; + const length = lengths[i]; + if (length != 0) { - let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1; + const s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1; + mat.a *= s; mat.b *= s; } @@ -163,26 +196,31 @@ export class PathConstraint implements Updatable { boneX = x; boneY = y; if (mixRotate > 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d, r = 0, cos = 0, sin = 0; - if (tangents) - r = positions[p - 1]; - else if (spaces[i + 1] == 0) - r = positions[p + 2]; - else - r = Math.atan2(dy, dx); + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let r = 0; + let cos = 0; + let sin = 0; + + if (tangents) r = positions[p - 1]; + else if (spaces[i + 1] == 0) r = positions[p + 2]; + else r = Math.atan2(dy, dx); r -= Math.atan2(c, a); if (tip) { cos = Math.cos(r); sin = Math.sin(r); - let length = bone.data.length; + const length = bone.data.length; + boneX += (length * (cos * a - sin * c) - dx) * mixRotate; boneY += (length * (sin * a + cos * c) - dy) * mixRotate; } else { r += offsetRotation; } - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= mixRotate; cos = Math.cos(r); @@ -196,20 +234,27 @@ export class PathConstraint implements Updatable { } } - computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean) { - let target = this.target; + computeWorldPositions(path: PathAttachment, spacesCount: number, tangents: boolean) { + const target = this.target; let position = this.position; - let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array = this.world; - let closed = path.closed; - let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE; + const spaces = this.spaces; + const out = Utils.setArraySize(this.positions, spacesCount * 3 + 2); + let world: Array = this.world; + const closed = path.closed; + let verticesLength = path.worldVerticesLength; + let curveCount = verticesLength / 6; + let prevCurve = PathConstraint.NONE; if (!path.constantSpeed) { - let lengths = path.lengths; + const lengths = path.lengths; + curveCount -= closed ? 1 : 2; - let pathLength = lengths[curveCount]; + const pathLength = lengths[curveCount]; + if (this.data.positionMode == PositionMode.Percent) position *= pathLength; let multiplier; + switch (this.data.spacingMode) { case SpacingMode.Percent: multiplier = pathLength; @@ -222,7 +267,8 @@ export class PathConstraint implements Updatable { } world = Utils.setArraySize(this.world, 8); for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i] * multiplier; + const space = spaces[i] * multiplier; + position += space; let p = position; @@ -248,12 +294,13 @@ export class PathConstraint implements Updatable { // Determine curve containing position. for (; ; curve++) { - let length = lengths[curve]; + const length = lengths[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = lengths[curve - 1]; + const prev = lengths[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -263,12 +310,11 @@ export class PathConstraint implements Updatable { if (closed && curve == curveCount) { path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); path.computeWorldVertices(target, 0, 4, world, 4, 2); - } else - path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); + } else path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); } - this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, - tangents || (i > 0 && space == 0)); + this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0)); } + return out; } @@ -288,10 +334,25 @@ export class PathConstraint implements Updatable { } // Curve lengths. - let curves = Utils.setArraySize(this.curves, curveCount); + const curves = Utils.setArraySize(this.curves, curveCount); let pathLength = 0; - let x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; - let tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0; + let x1 = world[0]; + let y1 = world[1]; + let cx1 = 0; + let cy1 = 0; + let cx2 = 0; + let cy2 = 0; + let x2 = 0; + let y2 = 0; + let tmpx = 0; + let tmpy = 0; + let dddfx = 0; + let dddfy = 0; + let ddfx = 0; + let ddfy = 0; + let dfx = 0; + let dfy = 0; + for (let i = 0, w = 2; i < curveCount; i++, w += 6) { cx1 = world[w]; cy1 = world[w + 1]; @@ -327,6 +388,7 @@ export class PathConstraint implements Updatable { if (this.data.positionMode == PositionMode.Percent) position *= pathLength; let multiplier; + switch (this.data.spacingMode) { case SpacingMode.Percent: multiplier = pathLength; @@ -338,10 +400,12 @@ export class PathConstraint implements Updatable { multiplier = 1; } - let segments = this.segments; + const segments = this.segments; let curveLength = 0; + for (let i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { - let space = spaces[i] * multiplier; + const space = spaces[i] * multiplier; + position += space; let p = position; @@ -359,12 +423,13 @@ export class PathConstraint implements Updatable { // Determine curve containing position. for (; ; curve++) { - let length = curves[curve]; + const length = curves[curve]; + if (p > length) continue; - if (curve == 0) - p /= length; + if (curve == 0) p /= length; else { - let prev = curves[curve - 1]; + const prev = curves[curve - 1]; + p = (p - prev) / (length - prev); } break; @@ -374,6 +439,7 @@ export class PathConstraint implements Updatable { if (curve != prevCurve) { prevCurve = curve; let ii = curve * 6; + x1 = world[ii]; y1 = world[ii + 1]; cx1 = world[ii + 2]; @@ -414,53 +480,85 @@ export class PathConstraint implements Updatable { // Weight by segment length. p *= curveLength; for (; ; segment++) { - let length = segments[segment]; + const length = segments[segment]; + if (p > length) continue; - if (segment == 0) - p /= length; + if (segment == 0) p /= length; else { - let prev = segments[segment - 1]; + const prev = segments[segment - 1]; + p = segment + (p - prev) / (length - prev); } break; } this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0)); } + return out; } - addBeforePosition (p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx); + addBeforePosition(p: number, temp: Array, i: number, out: Array, o: number) { + const x1 = temp[i]; + const y1 = temp[i + 1]; + const dx = temp[i + 2] - x1; + const dy = temp[i + 3] - y1; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } - addAfterPosition (p: number, temp: Array, i: number, out: Array, o: number) { - let x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx); + addAfterPosition(p: number, temp: Array, i: number, out: Array, o: number) { + const x1 = temp[i + 2]; + const y1 = temp[i + 3]; + const dx = x1 - temp[i]; + const dy = y1 - temp[i + 1]; + const r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); out[o + 1] = y1 + p * Math.sin(r); out[o + 2] = r; } - addCurvePosition (p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, - out: Array, o: number, tangents: boolean) { + addCurvePosition( + p: number, + x1: number, + y1: number, + cx1: number, + cy1: number, + cx2: number, + cy2: number, + x2: number, + y2: number, + out: Array, + o: number, + tangents: boolean + ) { if (p == 0 || isNaN(p)) { out[o] = x1; out[o + 1] = y1; out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1); + return; } - let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; - let ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; - let x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + const tt = p * p; + const ttt = tt * p; + const u = 1 - p; + const uu = u * u; + const uuu = uu * u; + const ut = u * p; + const ut3 = ut * 3; + const uut3 = u * ut3; + const utt3 = ut3 * p; + const x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt; + const y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + out[o] = x; out[o + 1] = y; if (tangents) { - if (p < 0.001) - out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1); - else - out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + if (p < 0.001) out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1); + else out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); } } } diff --git a/packages/runtime-4.1/src/core/PathConstraintData.ts b/packages/runtime-4.1/src/core/PathConstraintData.ts index 31acb65b..9208289c 100644 --- a/packages/runtime-4.1/src/core/PathConstraintData.ts +++ b/packages/runtime-4.1/src/core/PathConstraintData.ts @@ -1,7 +1,7 @@ -import {ConstraintData} from "./ConstraintData"; -import type {SlotData} from "./SlotData"; -import type {BoneData} from "./BoneData"; -import { PositionMode, RotateMode } from "@pixi-spine/base"; +import { ConstraintData } from './ConstraintData'; +import type { SlotData } from './SlotData'; +import type { BoneData } from './BoneData'; +import { PositionMode, RotateMode } from '@pixi-spine/base'; /** Stores the setup pose for a {@link PathConstraint}. * @@ -9,15 +9,16 @@ import { PositionMode, RotateMode } from "@pixi-spine/base"; * @public * */ export class PathConstraintData extends ConstraintData { - /** The bones that will be modified by this path constraint. */ bones = new Array(); /** The slot whose path attachment will be used to constrained the bones. */ private _target: SlotData | null = null; - public set target (slotData: SlotData) { this._target = slotData; } - public get target () { - if (!this._target) throw new Error("SlotData not set.") + public set target(slotData: SlotData) { + this._target = slotData; + } + public get target() { + if (!this._target) throw new Error('SlotData not set.'); else return this._target; } @@ -31,19 +32,19 @@ export class PathConstraintData extends ConstraintData { rotateMode: RotateMode = RotateMode.Chain; /** An offset added to the constrained bone rotation. */ - offsetRotation: number = 0; + offsetRotation = 0; /** The position along the path. */ - position: number = 0; + position = 0; /** The spacing between bones. */ - spacing: number = 0; + spacing = 0; mixRotate = 0; mixX = 0; mixY = 0; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } @@ -54,5 +55,8 @@ export class PathConstraintData extends ConstraintData { * @public * */ export enum SpacingMode { - Length, Fixed, Percent, Proportional + Length, + Fixed, + Percent, + Proportional, } diff --git a/packages/runtime-4.1/src/core/Skeleton.ts b/packages/runtime-4.1/src/core/Skeleton.ts index b6d7abbf..5bd6674f 100644 --- a/packages/runtime-4.1/src/core/Skeleton.ts +++ b/packages/runtime-4.1/src/core/Skeleton.ts @@ -1,13 +1,13 @@ -import {Attachment, RegionAttachment, MeshAttachment, PathAttachment} from './attachments'; -import {Bone} from "./Bone"; -import {Slot} from "./Slot"; -import {Updatable} from "./Updatable"; -import {SkeletonData} from "./SkeletonData"; -import {IkConstraint} from "./IkConstraint"; -import {TransformConstraint} from "./TransformConstraint"; -import {PathConstraint} from "./PathConstraint"; -import {Skin} from "./Skin"; -import {Color, MathUtils, NumberArrayLike, settings, Utils, Vector2, ISkeleton} from "@pixi-spine/base"; +import { Attachment, RegionAttachment, MeshAttachment, PathAttachment } from './attachments'; +import { Bone } from './Bone'; +import { Slot } from './Slot'; +import type { Updatable } from './Updatable'; +import type { SkeletonData } from './SkeletonData'; +import { IkConstraint } from './IkConstraint'; +import { TransformConstraint } from './TransformConstraint'; +import { PathConstraint } from './PathConstraint'; +import type { Skin } from './Skin'; +import { Color, MathUtils, NumberArrayLike, settings, Utils, Vector2, ISkeleton } from '@pixi-spine/base'; /** Stores the current pose for a skeleton. * @@ -59,18 +59,19 @@ export class Skeleton implements ISkeleton { /** Sets the skeleton Y position, which is added to the root bone worldY position. */ y = 0; - constructor (data: SkeletonData) { - if (!data) throw new Error("data cannot be null."); + constructor(data: SkeletonData) { + if (!data) throw new Error('data cannot be null.'); this.data = data; this.bones = new Array(); for (let i = 0; i < data.bones.length; i++) { - let boneData = data.bones[i]; + const boneData = data.bones[i]; let bone: Bone; - if (!boneData.parent) - bone = new Bone(boneData, this, null); + + if (!boneData.parent) bone = new Bone(boneData, this, null); else { - let parent = this.bones[boneData.parent.index]; + const parent = this.bones[boneData.parent.index]; + bone = new Bone(boneData, this, parent); parent.children.push(bone); } @@ -80,28 +81,32 @@ export class Skeleton implements ISkeleton { this.slots = new Array(); this.drawOrder = new Array(); for (let i = 0; i < data.slots.length; i++) { - let slotData = data.slots[i]; - let bone = this.bones[slotData.boneData.index]; - let slot = new Slot(slotData, bone); + const slotData = data.slots[i]; + const bone = this.bones[slotData.boneData.index]; + const slot = new Slot(slotData, bone); + this.slots.push(slot); this.drawOrder.push(slot); } this.ikConstraints = new Array(); for (let i = 0; i < data.ikConstraints.length; i++) { - let ikConstraintData = data.ikConstraints[i]; + const ikConstraintData = data.ikConstraints[i]; + this.ikConstraints.push(new IkConstraint(ikConstraintData, this)); } this.transformConstraints = new Array(); for (let i = 0; i < data.transformConstraints.length; i++) { - let transformConstraintData = data.transformConstraints[i]; + const transformConstraintData = data.transformConstraints[i]; + this.transformConstraints.push(new TransformConstraint(transformConstraintData, this)); } this.pathConstraints = new Array(); for (let i = 0; i < data.pathConstraints.length; i++) { - let pathConstraintData = data.pathConstraints[i]; + const pathConstraintData = data.pathConstraints[i]; + this.pathConstraints.push(new PathConstraint(pathConstraintData, this)); } @@ -111,21 +116,26 @@ export class Skeleton implements ISkeleton { /** Caches information about bones and constraints. Must be called if the {@link #getSkin()} is modified or if bones, * constraints, or weighted path attachments are added or removed. */ - updateCache () { - let updateCache = this._updateCache; + updateCache() { + const updateCache = this._updateCache; + updateCache.length = 0; - let bones = this.bones; + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + bone.sorted = bone.data.skinRequired; bone.active = !bone.sorted; } if (this.skin) { - let skinBones = this.skin.bones; + const skinBones = this.skin.bones; + for (let i = 0, n = this.skin.bones.length; i < n; i++) { let bone: Bone | null = this.bones[skinBones[i].index]; + do { bone.sorted = false; bone.active = true; @@ -135,57 +145,67 @@ export class Skeleton implements ISkeleton { } // IK first, lowest hierarchy depth first. - let ikConstraints = this.ikConstraints; - let transformConstraints = this.transformConstraints; - let pathConstraints = this.pathConstraints; - let ikCount = ikConstraints.length, transformCount = transformConstraints.length, pathCount = pathConstraints.length; - let constraintCount = ikCount + transformCount + pathCount; - - outer: - for (let i = 0; i < constraintCount; i++) { - for (let ii = 0; ii < ikCount; ii++) { - let constraint = ikConstraints[ii]; - if (constraint.data.order == i) { - this.sortIkConstraint(constraint); - continue outer; - } + const ikConstraints = this.ikConstraints; + const transformConstraints = this.transformConstraints; + const pathConstraints = this.pathConstraints; + const ikCount = ikConstraints.length; + const transformCount = transformConstraints.length; + const pathCount = pathConstraints.length; + const constraintCount = ikCount + transformCount + pathCount; + + // eslint-disable-next-line no-restricted-syntax, no-labels + outer: for (let i = 0; i < constraintCount; i++) { + for (let ii = 0; ii < ikCount; ii++) { + const constraint = ikConstraints[ii]; + + if (constraint.data.order == i) { + this.sortIkConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < transformCount; ii++) { - let constraint = transformConstraints[ii]; - if (constraint.data.order == i) { - this.sortTransformConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < transformCount; ii++) { + const constraint = transformConstraints[ii]; + + if (constraint.data.order == i) { + this.sortTransformConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } - for (let ii = 0; ii < pathCount; ii++) { - let constraint = pathConstraints[ii]; - if (constraint.data.order == i) { - this.sortPathConstraint(constraint); - continue outer; - } + } + for (let ii = 0; ii < pathCount; ii++) { + const constraint = pathConstraints[ii]; + + if (constraint.data.order == i) { + this.sortPathConstraint(constraint); + // eslint-disable-next-line no-labels + continue outer; } } + } - for (let i = 0, n = bones.length; i < n; i++) - this.sortBone(bones[i]); + for (let i = 0, n = bones.length; i < n; i++) this.sortBone(bones[i]); } - sortIkConstraint (constraint: IkConstraint) { - constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!; + sortIkConstraint(constraint: IkConstraint) { + constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; - let target = constraint.target; + const target = constraint.target; + this.sortBone(target); - let constrained = constraint.bones; - let parent = constrained[0]; + const constrained = constraint.bones; + const parent = constrained[0]; + this.sortBone(parent); if (constrained.length == 1) { this._updateCache.push(constraint); this.sortReset(parent.children); } else { - let child = constrained[constrained.length - 1]; + const child = constrained[constrained.length - 1]; + this.sortBone(child); this._updateCache.push(constraint); @@ -195,47 +215,47 @@ export class Skeleton implements ISkeleton { } } - sortPathConstraint (constraint: PathConstraint) { - constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!; + sortPathConstraint(constraint: PathConstraint) { + constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; - let slot = constraint.target; - let slotIndex = slot.data.index; - let slotBone = slot.bone; + const slot = constraint.target; + const slotIndex = slot.data.index; + const slotBone = slot.bone; + if (this.skin) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone); - if (this.data.defaultSkin && this.data.defaultSkin != this.skin) - this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); - for (let i = 0, n = this.data.skins.length; i < n; i++) - this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + if (this.data.defaultSkin && this.data.defaultSkin != this.skin) this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); + for (let i = 0, n = this.data.skins.length; i < n; i++) this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + + const attachment = slot.getAttachment(); - let attachment = slot.getAttachment(); if (attachment instanceof PathAttachment) this.sortPathConstraintAttachmentWith(attachment, slotBone); - let constrained = constraint.bones; - let boneCount = constrained.length; - for (let i = 0; i < boneCount; i++) - this.sortBone(constrained[i]); + const constrained = constraint.bones; + const boneCount = constrained.length; + + for (let i = 0; i < boneCount; i++) this.sortBone(constrained[i]); this._updateCache.push(constraint); - for (let i = 0; i < boneCount; i++) - this.sortReset(constrained[i].children); - for (let i = 0; i < boneCount; i++) - constrained[i].sorted = true; + for (let i = 0; i < boneCount; i++) this.sortReset(constrained[i].children); + for (let i = 0; i < boneCount; i++) constrained[i].sorted = true; } - sortTransformConstraint (constraint: TransformConstraint) { - constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!; + sortTransformConstraint(constraint: TransformConstraint) { + constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true))); if (!constraint.active) return; this.sortBone(constraint.target); - let constrained = constraint.bones; - let boneCount = constrained.length; + const constrained = constraint.bones; + const boneCount = constrained.length; + if (constraint.data.local) { for (let i = 0; i < boneCount; i++) { - let child = constrained[i]; - this.sortBone(child.parent!); + const child = constrained[i]; + + this.sortBone(child.parent); this.sortBone(child); } } else { @@ -246,48 +266,50 @@ export class Skeleton implements ISkeleton { this._updateCache.push(constraint); - for (let i = 0; i < boneCount; i++) - this.sortReset(constrained[i].children); - for (let i = 0; i < boneCount; i++) - constrained[i].sorted = true; + for (let i = 0; i < boneCount; i++) this.sortReset(constrained[i].children); + for (let i = 0; i < boneCount; i++) constrained[i].sorted = true; } - sortPathConstraintAttachment (skin: Skin, slotIndex: number, slotBone: Bone) { - let attachments = skin.attachments[slotIndex]; + sortPathConstraintAttachment(skin: Skin, slotIndex: number, slotBone: Bone) { + const attachments = skin.attachments[slotIndex]; + if (!attachments) return; - for (let key in attachments) { + for (const key in attachments) { this.sortPathConstraintAttachmentWith(attachments[key], slotBone); } } - sortPathConstraintAttachmentWith (attachment: Attachment, slotBone: Bone) { + sortPathConstraintAttachmentWith(attachment: Attachment, slotBone: Bone) { if (!(attachment instanceof PathAttachment)) return; - let pathBones = (attachment).bones; - if (!pathBones) - this.sortBone(slotBone); + const pathBones = (attachment).bones; + + if (!pathBones) this.sortBone(slotBone); else { - let bones = this.bones; - for (let i = 0, n = pathBones.length; i < n;) { + const bones = this.bones; + + for (let i = 0, n = pathBones.length; i < n; ) { let nn = pathBones[i++]; + nn += i; - while (i < nn) - this.sortBone(bones[pathBones[i++]]); + while (i < nn) this.sortBone(bones[pathBones[i++]]); } } } - sortBone (bone: Bone) { + sortBone(bone: Bone) { if (!bone) return; if (bone.sorted) return; - let parent = bone.parent; + const parent = bone.parent; + if (parent) this.sortBone(parent); bone.sorted = true; this._updateCache.push(bone); } - sortReset (bones: Array) { + sortReset(bones: Array) { for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (!bone.active) continue; if (bone.sorted) this.sortReset(bone.children); bone.sorted = false; @@ -298,10 +320,12 @@ export class Skeleton implements ISkeleton { * * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine * Runtimes Guide. */ - updateWorldTransform () { - let bones = this.bones; + updateWorldTransform() { + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + bone.ax = bone.x; bone.ay = bone.y; bone.arotation = bone.rotation; @@ -311,54 +335,63 @@ export class Skeleton implements ISkeleton { bone.ashearY = bone.shearY; } - let updateCache = this._updateCache; - for (let i = 0, n = updateCache.length; i < n; i++) - updateCache[i].update(); + const updateCache = this._updateCache; + + for (let i = 0, n = updateCache.length; i < n; i++) updateCache[i].update(); } - updateWorldTransformWith (parent: Bone) { + updateWorldTransformWith(parent: Bone) { // Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection. - let rootBone = this.getRootBone(); - let pa = parent.matrix.a, pb = parent.matrix.c, pc = parent.matrix.b, pd = parent.matrix.d; + const rootBone = this.getRootBone(); + const pa = parent.matrix.a; + const pb = parent.matrix.c; + const pc = parent.matrix.b; + const pd = parent.matrix.d; + rootBone.matrix.tx = pa * this.x + pb * this.y + parent.worldX; rootBone.matrix.ty = pc * this.x + pd * this.y + parent.worldY; - let rotationY = rootBone.rotation + 90 + rootBone.shearY; - let la = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; - let lb = MathUtils.cosDeg(rotationY) * rootBone.scaleY; - let lc = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; - let ld = MathUtils.sinDeg(rotationY) * rootBone.scaleY; + const rotationY = rootBone.rotation + 90 + rootBone.shearY; + const la = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; + const lb = MathUtils.cosDeg(rotationY) * rootBone.scaleY; + const lc = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX; + const ld = MathUtils.sinDeg(rotationY) * rootBone.scaleY; const sx = this.scaleX; - const sy = settings.yDown? -this.scaleY : this.scaleY; + const sy = settings.yDown ? -this.scaleY : this.scaleY; + rootBone.matrix.a = (pa * la + pb * lc) * sx; rootBone.matrix.c = (pa * lb + pb * ld) * sx; rootBone.matrix.b = (pc * la + pd * lc) * sy; rootBone.matrix.d = (pc * lb + pd * ld) * sy; // Update everything except root bone. - let updateCache = this._updateCache; + const updateCache = this._updateCache; + for (let i = 0, n = updateCache.length; i < n; i++) { - let updatable = updateCache[i]; + const updatable = updateCache[i]; + if (updatable != rootBone) updatable.update(); } } /** Sets the bones, constraints, and slots to their setup pose values. */ - setToSetupPose () { + setToSetupPose() { this.setBonesToSetupPose(); this.setSlotsToSetupPose(); } /** Sets the bones and constraints to their setup pose values. */ - setBonesToSetupPose () { - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - bones[i].setToSetupPose(); + setBonesToSetupPose() { + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) bones[i].setToSetupPose(); + + const ikConstraints = this.ikConstraints; - let ikConstraints = this.ikConstraints; for (let i = 0, n = ikConstraints.length; i < n; i++) { - let constraint = ikConstraints[i]; + const constraint = ikConstraints[i]; + constraint.mix = constraint.data.mix; constraint.softness = constraint.data.softness; constraint.bendDirection = constraint.data.bendDirection; @@ -366,10 +399,12 @@ export class Skeleton implements ISkeleton { constraint.stretch = constraint.data.stretch; } - let transformConstraints = this.transformConstraints; + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; - let data = constraint.data; + const constraint = transformConstraints[i]; + const data = constraint.data; + constraint.mixRotate = data.mixRotate; constraint.mixX = data.mixX; constraint.mixY = data.mixY; @@ -378,10 +413,12 @@ export class Skeleton implements ISkeleton { constraint.mixShearY = data.mixShearY; } - let pathConstraints = this.pathConstraints; + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; - let data = constraint.data; + const constraint = pathConstraints[i]; + const data = constraint.data; + constraint.position = data.position; constraint.spacing = data.spacing; constraint.mixRotate = data.mixRotate; @@ -391,67 +428,77 @@ export class Skeleton implements ISkeleton { } /** Sets the slots and draw order to their setup pose values. */ - setSlotsToSetupPose () { - let slots = this.slots; + setSlotsToSetupPose() { + const slots = this.slots; + Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length); - for (let i = 0, n = slots.length; i < n; i++) - slots[i].setToSetupPose(); + for (let i = 0, n = slots.length; i < n; i++) slots[i].setToSetupPose(); } /** @returns May return null. */ - getRootBone () { + getRootBone() { if (this.bones.length == 0) return null; + return this.bones[0]; } /** @returns May be null. */ - findBone (boneName: string) { - if (!boneName) throw new Error("boneName cannot be null."); - let bones = this.bones; + findBone(boneName: string) { + if (!boneName) throw new Error('boneName cannot be null.'); + const bones = this.bones; + for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; + if (bone.data.name == boneName) return bone; } + return null; } /** @returns -1 if the bone was not found. */ - findBoneIndex (boneName: string) { - if (!boneName) throw new Error("boneName cannot be null."); - let bones = this.bones; - for (let i = 0, n = bones.length; i < n; i++) - if (bones[i].data.name == boneName) return i; + findBoneIndex(boneName: string) { + if (!boneName) throw new Error('boneName cannot be null.'); + const bones = this.bones; + + for (let i = 0, n = bones.length; i < n; i++) if (bones[i].data.name == boneName) return i; + return -1; } /** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it * repeatedly. * @returns May be null. */ - findSlot (slotName: string) { - if (!slotName) throw new Error("slotName cannot be null."); - let slots = this.slots; + findSlot(slotName: string) { + if (!slotName) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) return slot; } + return null; } /** @returns -1 if the bone was not found. */ - findSlotIndex (slotName: string) { - if (!slotName) throw new Error("slotName cannot be null."); - let slots = this.slots; - for (let i = 0, n = slots.length; i < n; i++) - if (slots[i].data.name == slotName) return i; + findSlotIndex(slotName: string) { + if (!slotName) throw new Error('slotName cannot be null.'); + const slots = this.slots; + + for (let i = 0, n = slots.length; i < n; i++) if (slots[i].data.name == slotName) return i; + return -1; } /** Sets a skin by name. * * See {@link #setSkin()}. */ - setSkinByName (skinName: string) { - let skin = this.data.findSkin(skinName); - if (!skin) throw new Error("Skin not found: " + skinName); + setSkinByName(skinName: string) { + const skin = this.data.findSkin(skinName); + + if (!skin) throw new Error(`Skin not found: ${skinName}`); this.setSkin(skin); } @@ -465,18 +512,20 @@ export class Skeleton implements ISkeleton { * {@link #setSlotsToSetupPose()}. Also, often {@link AnimationState#apply()} is called before the next time the * skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin. * @param newSkin May be null. */ - setSkin (newSkin: Skin) { + setSkin(newSkin: Skin) { if (newSkin == this.skin) return; if (newSkin) { - if (this.skin) - newSkin.attachAll(this, this.skin); + if (this.skin) newSkin.attachAll(this, this.skin); else { - let slots = this.slots; + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; - let name = slot.data.attachmentName; + const slot = slots[i]; + const name = slot.data.attachmentName; + if (name) { - let attachment = newSkin.getAttachment(i, name); + const attachment = newSkin.getAttachment(i, name); + if (attachment) slot.setAttachment(attachment); } } @@ -486,15 +535,16 @@ export class Skeleton implements ISkeleton { this.updateCache(); } - /** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment * name. * * See {@link #getAttachment()}. * @returns May be null. */ - getAttachmentByName (slotName: string, attachmentName: string): Attachment | null { - let slot = this.data.findSlot(slotName); + getAttachmentByName(slotName: string, attachmentName: string): Attachment | null { + const slot = this.data.findSlot(slotName); + if (!slot) throw new Error(`Can't find slot with name ${slotName}`); + return this.getAttachment(slot.index, attachmentName); } @@ -503,83 +553,99 @@ export class Skeleton implements ISkeleton { * * See [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide. * @returns May be null. */ - getAttachment (slotIndex: number, attachmentName: string): Attachment | null { - if (!attachmentName) throw new Error("attachmentName cannot be null."); + getAttachment(slotIndex: number, attachmentName: string): Attachment | null { + if (!attachmentName) throw new Error('attachmentName cannot be null.'); if (this.skin) { - let attachment = this.skin.getAttachment(slotIndex, attachmentName); + const attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment) return attachment; } if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; } /** A convenience method to set an attachment by finding the slot with {@link #findSlot()}, finding the attachment with * {@link #getAttachment()}, then setting the slot's {@link Slot#attachment}. * @param attachmentName May be null to clear the slot's attachment. */ - setAttachment (slotName: string, attachmentName: string) { - if (!slotName) throw new Error("slotName cannot be null."); - let slots = this.slots; + setAttachment(slotName: string, attachmentName: string) { + if (!slotName) throw new Error('slotName cannot be null.'); + const slots = this.slots; + for (let i = 0, n = slots.length; i < n; i++) { - let slot = slots[i]; + const slot = slots[i]; + if (slot.data.name == slotName) { let attachment: Attachment | null = null; + if (attachmentName) { attachment = this.getAttachment(i, attachmentName); - if (!attachment) throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName); + if (!attachment) throw new Error(`Attachment not found: ${attachmentName}, for slot: ${slotName}`); } slot.setAttachment(attachment); + return; } } - throw new Error("Slot not found: " + slotName); + throw new Error(`Slot not found: ${slotName}`); } - /** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method * than to call it repeatedly. * @return May be null. */ - findIkConstraint (constraintName: string) { - if (!constraintName) throw new Error("constraintName cannot be null."); - let ikConstraints = this.ikConstraints; + findIkConstraint(constraintName: string) { + if (!constraintName) throw new Error('constraintName cannot be null.'); + const ikConstraints = this.ikConstraints; + for (let i = 0, n = ikConstraints.length; i < n; i++) { - let ikConstraint = ikConstraints[i]; + const ikConstraint = ikConstraints[i]; + if (ikConstraint.data.name == constraintName) return ikConstraint; } + return null; } /** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of * this method than to call it repeatedly. * @return May be null. */ - findTransformConstraint (constraintName: string) { - if (!constraintName) throw new Error("constraintName cannot be null."); - let transformConstraints = this.transformConstraints; + findTransformConstraint(constraintName: string) { + if (!constraintName) throw new Error('constraintName cannot be null.'); + const transformConstraints = this.transformConstraints; + for (let i = 0, n = transformConstraints.length; i < n; i++) { - let constraint = transformConstraints[i]; + const constraint = transformConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } /** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method * than to call it repeatedly. * @return May be null. */ - findPathConstraint (constraintName: string) { - if (!constraintName) throw new Error("constraintName cannot be null."); - let pathConstraints = this.pathConstraints; + findPathConstraint(constraintName: string) { + if (!constraintName) throw new Error('constraintName cannot be null.'); + const pathConstraints = this.pathConstraints; + for (let i = 0, n = pathConstraints.length; i < n; i++) { - let constraint = pathConstraints[i]; + const constraint = pathConstraints[i]; + if (constraint.data.name == constraintName) return constraint; } + return null; } /** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose as `{ x: number, y: number, width: number, height: number }`. * Note that this method will create temporary objects which can add to garbage collection pressure. Use `getBounds()` if garbage collection is a concern. */ - getBoundsRect () { - let offset = new Vector2(); - let size = new Vector2(); + getBoundsRect() { + const offset = new Vector2(); + const size = new Vector2(); + this.getBounds(offset, size); + return { x: offset.x, y: offset.y, width: size.x, height: size.y }; } @@ -587,30 +653,39 @@ export class Skeleton implements ISkeleton { * @param offset An output value, the distance from the skeleton origin to the bottom left corner of the AABB. * @param size An output value, the width and height of the AABB. * @param temp Working memory to temporarily store attachments' computed world vertices. */ - getBounds (offset: Vector2, size: Vector2, temp: Array = new Array(2)) { - if (!offset) throw new Error("offset cannot be null."); - if (!size) throw new Error("size cannot be null."); - let drawOrder = this.drawOrder; - let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; + getBounds(offset: Vector2, size: Vector2, temp: Array = new Array(2)) { + if (!offset) throw new Error('offset cannot be null.'); + if (!size) throw new Error('size cannot be null.'); + const drawOrder = this.drawOrder; + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + let maxX = Number.NEGATIVE_INFINITY; + let maxY = Number.NEGATIVE_INFINITY; + for (let i = 0, n = drawOrder.length; i < n; i++) { - let slot = drawOrder[i]; + const slot = drawOrder[i]; + if (!slot.bone.active) continue; let verticesLength = 0; let vertices: NumberArrayLike | null = null; - let attachment = slot.getAttachment(); + const attachment = slot.getAttachment(); + if (attachment instanceof RegionAttachment) { verticesLength = 8; vertices = Utils.setArraySize(temp, verticesLength, 0); (attachment).computeWorldVertices(slot, vertices, 0, 2); } else if (attachment instanceof MeshAttachment) { - let mesh = (attachment); + const mesh = attachment; + verticesLength = mesh.worldVerticesLength; vertices = Utils.setArraySize(temp, verticesLength, 0); mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); } if (vertices) { for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) { - let x = vertices[ii], y = vertices[ii + 1]; + const x = vertices[ii]; + const y = vertices[ii + 1]; + minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); @@ -629,7 +704,7 @@ export class Skeleton implements ISkeleton { set flipX(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleX = value ? 1.0 : -1.0; } @@ -641,10 +716,10 @@ export class Skeleton implements ISkeleton { set flipY(value: boolean) { if (!Skeleton.deprecatedWarning1) { Skeleton.deprecatedWarning1 = true; - console.warn("Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY"); + console.warn('Spine Deprecation Warning: `Skeleton.flipX/flipY` was deprecated, please use scaleX/scaleY'); } this.scaleY = value ? 1.0 : -1.0; } - private static deprecatedWarning1: boolean = false; + private static deprecatedWarning1 = false; } diff --git a/packages/runtime-4.1/src/core/SkeletonBinary.ts b/packages/runtime-4.1/src/core/SkeletonBinary.ts index c07a05ec..e19dac86 100644 --- a/packages/runtime-4.1/src/core/SkeletonBinary.ts +++ b/packages/runtime-4.1/src/core/SkeletonBinary.ts @@ -1,30 +1,48 @@ -import type {Attachment, AttachmentLoader, MeshAttachment, VertexAttachment} from './attachments'; +import type { Attachment, AttachmentLoader, MeshAttachment, VertexAttachment } from './attachments'; import { - AlphaTimeline, Animation, - AttachmentTimeline, CurveTimeline, CurveTimeline1, CurveTimeline2, DeformTimeline, DrawOrderTimeline, EventTimeline, + AlphaTimeline, + Animation, + AttachmentTimeline, + CurveTimeline, + CurveTimeline1, + CurveTimeline2, + DeformTimeline, + DrawOrderTimeline, + EventTimeline, IkConstraintTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, - PathConstraintSpacingTimeline, RGB2Timeline, RGBA2Timeline, RGBATimeline, RGBTimeline, + PathConstraintSpacingTimeline, + RGB2Timeline, + RGBA2Timeline, + RGBATimeline, + RGBTimeline, RotateTimeline, - ScaleTimeline, ScaleXTimeline, ScaleYTimeline, SequenceTimeline, - ShearTimeline, ShearXTimeline, ShearYTimeline, + ScaleTimeline, + ScaleXTimeline, + ScaleYTimeline, + SequenceTimeline, + ShearTimeline, + ShearXTimeline, + ShearYTimeline, Timeline, TransformConstraintTimeline, - TranslateTimeline, TranslateXTimeline, TranslateYTimeline + TranslateTimeline, + TranslateXTimeline, + TranslateYTimeline, } from './Animation'; -import {Event} from './Event'; -import {SkeletonData} from './SkeletonData'; -import {SlotData} from './SlotData'; -import {BoneData} from './BoneData'; -import {IkConstraintData} from './IkConstraintData'; -import {TransformConstraintData} from './TransformConstraintData'; -import {PathConstraintData, SpacingMode} from './PathConstraintData'; -import {Skin} from './Skin'; -import {EventData} from './EventData'; -import {AttachmentType, BinaryInput, Color, IHasTextureRegion, PositionMode, Utils} from '@pixi-spine/base'; -import {BLEND_MODES} from '@pixi/constants'; -import {Sequence, SequenceModeValues} from "./attachments"; +import { Event } from './Event'; +import { SkeletonData } from './SkeletonData'; +import { SlotData } from './SlotData'; +import { BoneData } from './BoneData'; +import { IkConstraintData } from './IkConstraintData'; +import { TransformConstraintData } from './TransformConstraintData'; +import { PathConstraintData, SpacingMode } from './PathConstraintData'; +import { Skin } from './Skin'; +import { EventData } from './EventData'; +import { AttachmentType, BinaryInput, Color, IHasTextureRegion, PositionMode, Utils } from '@pixi-spine/base'; +import { BLEND_MODES } from '@pixi/constants'; +import { Sequence, SequenceModeValues } from './attachments'; /** Loads skeleton data in the Spine binary format. * @@ -35,7 +53,7 @@ import {Sequence, SequenceModeValues} from "./attachments"; * */ export class SkeletonBinary { ver40 = false; - static BlendModeValues = [ BLEND_MODES.NORMAL, BLEND_MODES.ADD, BLEND_MODES.MULTIPLY, BLEND_MODES.SCREEN]; + static BlendModeValues = [BLEND_MODES.NORMAL, BLEND_MODES.ADD, BLEND_MODES.MULTIPLY, BLEND_MODES.SCREEN]; /** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at * runtime than were used in Spine. * @@ -45,26 +63,29 @@ export class SkeletonBinary { attachmentLoader: AttachmentLoader; private linkedMeshes = new Array(); - constructor (attachmentLoader: AttachmentLoader) { + constructor(attachmentLoader: AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - readSkeletonData (binary: Uint8Array): SkeletonData { - let scale = this.scale; + readSkeletonData(binary: Uint8Array): SkeletonData { + const scale = this.scale; - let skeletonData = new SkeletonData(); - skeletonData.name = ""; // BOZO + const skeletonData = new SkeletonData(); - let input = new BinaryInput(binary); + skeletonData.name = ''; // BOZO + + const input = new BinaryInput(binary); + + const lowHash = input.readInt32(); + const highHash = input.readInt32(); - let lowHash = input.readInt32(); - let highHash = input.readInt32(); skeletonData.hash = highHash == 0 && lowHash == 0 ? null : highHash.toString(16) + lowHash.toString(16); skeletonData.version = input.readString(); const verShort = skeletonData.version.substr(0, 3); - if (verShort !== '4.0' && verShort !== '4.1') - { - let error = `Spine 4.1 loader cant load version ${skeletonData.version}. Please configure your pixi-spine bundle`; + + if (verShort !== '4.0' && verShort !== '4.1') { + const error = `Spine 4.1 loader cant load version ${skeletonData.version}. Please configure your pixi-spine bundle`; + console.error(error); } this.ver40 = verShort === '4.0'; @@ -73,7 +94,8 @@ export class SkeletonBinary { skeletonData.width = input.readFloat(); skeletonData.height = input.readFloat(); - let nonessential = input.readBoolean(); + const nonessential = input.readBoolean(); + if (nonessential) { skeletonData.fps = input.readFloat(); @@ -83,20 +105,24 @@ export class SkeletonBinary { let n = 0; // Strings. - n = input.readInt(true) + + n = input.readInt(true); for (let i = 0; i < n; i++) { - let str = input.readString(); - if (!str) throw new Error("String in string table must not be null."); + const str = input.readString(); + + if (!str) throw new Error('String in string table must not be null.'); input.strings.push(str); } // Bones. - n = input.readInt(true) + n = input.readInt(true); for (let i = 0; i < n; i++) { - let name = input.readString(); - if (!name) throw new Error("Bone name must not be null."); - let parent = i == 0 ? null : skeletonData.bones[input.readInt(true)]; - let data = new BoneData(i, name, parent); + const name = input.readString(); + + if (!name) throw new Error('Bone name must not be null.'); + const parent = i == 0 ? null : skeletonData.bones[input.readInt(true)]; + const data = new BoneData(i, name, parent); + data.rotation = input.readFloat(); data.x = input.readFloat() * scale; data.y = input.readFloat() * scale; @@ -114,14 +140,17 @@ export class SkeletonBinary { // Slots. n = input.readInt(true); for (let i = 0; i < n; i++) { - let slotName = input.readString(); - if (!slotName) throw new Error("Slot name must not be null."); - let boneData = skeletonData.bones[input.readInt(true)]; - let data = new SlotData(i, slotName, boneData); + const slotName = input.readString(); + + if (!slotName) throw new Error('Slot name must not be null.'); + const boneData = skeletonData.bones[input.readInt(true)]; + const data = new SlotData(i, slotName, boneData); + Color.rgba8888ToColor(data.color, input.readInt32()); - let darkColor = input.readInt32(); - if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor); + const darkColor = input.readInt32(); + + if (darkColor != -1) Color.rgb888ToColor((data.darkColor = new Color()), darkColor); data.attachmentName = input.readStringRef(); data.blendMode = input.readInt(true); @@ -131,14 +160,15 @@ export class SkeletonBinary { // IK constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let name = input.readString(); - if (!name) throw new Error("IK constraint data name must not be null."); - let data = new IkConstraintData(name); + const name = input.readString(); + + if (!name) throw new Error('IK constraint data name must not be null.'); + const data = new IkConstraintData(name); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.bones[input.readInt(true)]; data.mix = input.readFloat(); data.softness = input.readFloat() * scale; @@ -152,14 +182,15 @@ export class SkeletonBinary { // Transform constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let name = input.readString(); - if (!name) throw new Error("Transform constraint data name must not be null."); - let data = new TransformConstraintData(name); + const name = input.readString(); + + if (!name) throw new Error('Transform constraint data name must not be null.'); + const data = new TransformConstraintData(name); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.bones[input.readInt(true)]; data.local = input.readBoolean(); data.relative = input.readBoolean(); @@ -181,14 +212,15 @@ export class SkeletonBinary { // Path constraints. n = input.readInt(true); for (let i = 0, nn; i < n; i++) { - let name = input.readString(); - if (!name) throw new Error("Path constraint data name must not be null."); - let data = new PathConstraintData(name); + const name = input.readString(); + + if (!name) throw new Error('Path constraint data name must not be null.'); + const data = new PathConstraintData(name); + data.order = input.readInt(true); data.skinRequired = input.readBoolean(); nn = input.readInt(true); - for (let ii = 0; ii < nn; ii++) - data.bones.push(skeletonData.bones[input.readInt(true)]); + for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); data.target = skeletonData.slots[input.readInt(true)]; data.positionMode = input.readInt(true); data.spacingMode = input.readInt(true); @@ -205,7 +237,8 @@ export class SkeletonBinary { } // Default skin. - let defaultSkin = this.readSkin(input, skeletonData, true, nonessential); + const defaultSkin = this.readSkin(input, skeletonData, true, nonessential); + if (defaultSkin) { skeletonData.defaultSkin = defaultSkin; skeletonData.skins.push(defaultSkin); @@ -214,10 +247,12 @@ export class SkeletonBinary { // Skins. { let i = skeletonData.skins.length; - Utils.setArraySize(skeletonData.skins, n = i + input.readInt(true)); + + Utils.setArraySize(skeletonData.skins, (n = i + input.readInt(true))); for (; i < n; i++) { - let skin = this.readSkin(input, skeletonData, false, nonessential); - if (!skin) throw new Error("readSkin() should not have returned null."); + const skin = this.readSkin(input, skeletonData, false, nonessential); + + if (!skin) throw new Error('readSkin() should not have returned null.'); skeletonData.skins[i] = skin; } } @@ -225,13 +260,15 @@ export class SkeletonBinary { // Linked meshes. n = this.linkedMeshes.length; for (let i = 0; i < n; i++) { - let linkedMesh = this.linkedMeshes[i]; - let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); - if (!skin) throw new Error("Not skin found for linked mesh."); - if (!linkedMesh.parent) throw new Error("Linked mesh parent must not be null"); - let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + const linkedMesh = this.linkedMeshes[i]; + const skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + + if (!skin) throw new Error('Not skin found for linked mesh.'); + if (!linkedMesh.parent) throw new Error('Linked mesh parent must not be null'); + const parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + if (!parent) throw new Error(`Parent mesh not found: ${linkedMesh.parent}`); - linkedMesh.mesh.timelineAttachment = linkedMesh.inheritTimeline ? parent as VertexAttachment : linkedMesh.mesh; + linkedMesh.mesh.timelineAttachment = linkedMesh.inheritTimeline ? (parent as VertexAttachment) : linkedMesh.mesh; linkedMesh.mesh.setParentMesh(parent as MeshAttachment); // if (linkedMesh.mesh.region != null) linkedMesh.mesh.updateRegion(); } @@ -240,9 +277,11 @@ export class SkeletonBinary { // Events. n = input.readInt(true); for (let i = 0; i < n; i++) { - let eventName = input.readStringRef(); - if (!eventName) throw new Error - let data = new EventData(eventName); + const eventName = input.readStringRef(); + + if (!eventName) throw new Error(); + const data = new EventData(eventName); + data.intValue = input.readInt(false); data.floatValue = input.readFloat(); data.stringValue = input.readString(); @@ -257,72 +296,77 @@ export class SkeletonBinary { // Animations. n = input.readInt(true); for (let i = 0; i < n; i++) { - let animationName = input.readString(); - if (!animationName) throw new Error("Animatio name must not be null."); + const animationName = input.readString(); + + if (!animationName) throw new Error('Animatio name must not be null.'); skeletonData.animations.push(this.readAnimation(input, animationName, skeletonData)); } + return skeletonData; } - private readSkin (input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin | null { + private readSkin(input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin | null { let skin = null; let slotCount = 0; if (defaultSkin) { - slotCount = input.readInt(true) + slotCount = input.readInt(true); if (slotCount == 0) return null; - skin = new Skin("default"); + skin = new Skin('default'); } else { - let skinName = input.readStringRef(); - if (!skinName) throw new Error("Skin name must not be null."); + const skinName = input.readStringRef(); + + if (!skinName) throw new Error('Skin name must not be null.'); skin = new Skin(skinName); skin.bones.length = input.readInt(true); - for (let i = 0, n = skin.bones.length; i < n; i++) - skin.bones[i] = skeletonData.bones[input.readInt(true)]; + for (let i = 0, n = skin.bones.length; i < n; i++) skin.bones[i] = skeletonData.bones[input.readInt(true)]; - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]); - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]); - for (let i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]); + for (let i = 0, n = input.readInt(true); i < n; i++) skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]); slotCount = input.readInt(true); } for (let i = 0; i < slotCount; i++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let name = input.readStringRef(); - if (!name) throw new Error("Attachment name must not be null"); - let attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + const name = input.readStringRef(); + + if (!name) throw new Error('Attachment name must not be null'); + const attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + if (attachment) skin.setAttachment(slotIndex, name, attachment); } } + return skin; } - private readAttachment (input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment | null { - let scale = this.scale; + private readAttachment(input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment | null { + const scale = this.scale; let name = input.readStringRef(); + if (!name) name = attachmentName; switch (input.readByte()) { case AttachmentType.Region: { let path = input.readStringRef(); - let rotation = input.readFloat(); - let x = input.readFloat(); - let y = input.readFloat(); - let scaleX = input.readFloat(); - let scaleY = input.readFloat(); - let width = input.readFloat(); - let height = input.readFloat(); - let color = input.readInt32(); - let sequence = this.readSequence(input); + const rotation = input.readFloat(); + const x = input.readFloat(); + const y = input.readFloat(); + const scaleX = input.readFloat(); + const scaleY = input.readFloat(); + const width = input.readFloat(); + const height = input.readFloat(); + const color = input.readInt32(); + const sequence = this.readSequence(input); if (!path) path = name; - let region = this.attachmentLoader.newRegionAttachment(skin, name, path, sequence); + const region = this.attachmentLoader.newRegionAttachment(skin, name, path, sequence); + if (!region) return null; region.path = path; region.x = x * scale; @@ -335,32 +379,37 @@ export class SkeletonBinary { Color.rgba8888ToColor(region.color, color); region.sequence = sequence; if (sequence == null) region.updateRegion(); + return region; } case AttachmentType.BoundingBox: { - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let color = nonessential ? input.readInt32() : 0; + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const color = nonessential ? input.readInt32() : 0; + + const box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); - let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); if (!box) return null; box.worldVerticesLength = vertexCount << 1; - box.vertices = vertices.vertices!; + box.vertices = vertices.vertices; box.bones = vertices.bones; if (nonessential) Color.rgba8888ToColor(box.color, color); + return box; } case AttachmentType.Mesh: { let path = input.readStringRef(); - let color = input.readInt32(); - let vertexCount = input.readInt(true); - let uvs = this.readFloatArray(input, vertexCount << 1, 1); - let triangles = this.readShortArray(input); - let vertices = this.readVertices(input, vertexCount); - let hullLength = input.readInt(true); - let sequence = this.readSequence(input); + const color = input.readInt32(); + const vertexCount = input.readInt(true); + const uvs = this.readFloatArray(input, vertexCount << 1, 1); + const triangles = this.readShortArray(input); + const vertices = this.readVertices(input, vertexCount); + const hullLength = input.readInt(true); + const sequence = this.readSequence(input); let edges: number[] = []; - let width = 0, height = 0; + let width = 0; + let height = 0; + if (nonessential) { edges = this.readShortArray(input); width = input.readFloat(); @@ -368,12 +417,13 @@ export class SkeletonBinary { } if (!path) path = name; - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path, sequence); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path, sequence); + if (!mesh) return null; mesh.path = path; Color.rgba8888ToColor(mesh.color, color); mesh.bones = vertices.bones; - mesh.vertices = vertices.vertices!; + mesh.vertices = vertices.vertices; mesh.worldVerticesLength = vertexCount << 1; mesh.triangles = triangles; mesh.regionUVs = new Float32Array(uvs); @@ -385,23 +435,27 @@ export class SkeletonBinary { mesh.width = width * scale; mesh.height = height * scale; } + return mesh; } case AttachmentType.LinkedMesh: { let path = input.readStringRef(); - let color = input.readInt32(); - let skinName = input.readStringRef(); - let parent = input.readStringRef(); - let inheritTimelines = input.readBoolean(); - let sequence = this.readSequence(input); - let width = 0, height = 0; + const color = input.readInt32(); + const skinName = input.readStringRef(); + const parent = input.readStringRef(); + const inheritTimelines = input.readBoolean(); + const sequence = this.readSequence(input); + let width = 0; + let height = 0; + if (nonessential) { width = input.readFloat(); height = input.readFloat(); } if (!path) path = name; - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path, sequence); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path, sequence); + if (!mesh) return null; mesh.path = path; Color.rgba8888ToColor(mesh.color, color); @@ -411,88 +465,103 @@ export class SkeletonBinary { mesh.height = height * scale; } this.linkedMeshes.push(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritTimelines)); + return mesh; } case AttachmentType.Path: { - let closed = input.readBoolean(); - let constantSpeed = input.readBoolean(); - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let lengths = Utils.newArray(vertexCount / 3, 0); - for (let i = 0, n = lengths.length; i < n; i++) - lengths[i] = input.readFloat() * scale; - let color = nonessential ? input.readInt32() : 0; - - let path = this.attachmentLoader.newPathAttachment(skin, name); + const closed = input.readBoolean(); + const constantSpeed = input.readBoolean(); + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const lengths = Utils.newArray(vertexCount / 3, 0); + + for (let i = 0, n = lengths.length; i < n; i++) lengths[i] = input.readFloat() * scale; + const color = nonessential ? input.readInt32() : 0; + + const path = this.attachmentLoader.newPathAttachment(skin, name); + if (!path) return null; path.closed = closed; path.constantSpeed = constantSpeed; path.worldVerticesLength = vertexCount << 1; - path.vertices = vertices.vertices!; + path.vertices = vertices.vertices; path.bones = vertices.bones; path.lengths = lengths; if (nonessential) Color.rgba8888ToColor(path.color, color); + return path; } case AttachmentType.Point: { - let rotation = input.readFloat(); - let x = input.readFloat(); - let y = input.readFloat(); - let color = nonessential ? input.readInt32() : 0; + const rotation = input.readFloat(); + const x = input.readFloat(); + const y = input.readFloat(); + const color = nonessential ? input.readInt32() : 0; + + const point = this.attachmentLoader.newPointAttachment(skin, name); - let point = this.attachmentLoader.newPointAttachment(skin, name); if (!point) return null; point.x = x * scale; point.y = y * scale; point.rotation = rotation; if (nonessential) Color.rgba8888ToColor(point.color, color); + return point; } case AttachmentType.Clipping: { - let endSlotIndex = input.readInt(true); - let vertexCount = input.readInt(true); - let vertices = this.readVertices(input, vertexCount); - let color = nonessential ? input.readInt32() : 0; + const endSlotIndex = input.readInt(true); + const vertexCount = input.readInt(true); + const vertices = this.readVertices(input, vertexCount); + const color = nonessential ? input.readInt32() : 0; + + const clip = this.attachmentLoader.newClippingAttachment(skin, name); - let clip = this.attachmentLoader.newClippingAttachment(skin, name); if (!clip) return null; clip.endSlot = skeletonData.slots[endSlotIndex]; clip.worldVerticesLength = vertexCount << 1; - clip.vertices = vertices.vertices!; + clip.vertices = vertices.vertices; clip.bones = vertices.bones; if (nonessential) Color.rgba8888ToColor(clip.color, color); + return clip; } } + return null; } - private readSequence (input: BinaryInput) { + private readSequence(input: BinaryInput) { if (this.ver40 || !input.readBoolean()) return null; - let sequence = new Sequence(input.readInt(true)); + const sequence = new Sequence(input.readInt(true)); + sequence.start = input.readInt(true); sequence.digits = input.readInt(true); sequence.setupIndex = input.readInt(true); + return sequence; } private readDeformTimelineType(input: BinaryInput) { if (this.ver40) return ATTACHMENT_DEFORM; + return input.readByte(); } - private readVertices (input: BinaryInput, vertexCount: number): Vertices { - let scale = this.scale; - let verticesLength = vertexCount << 1; - let vertices = new Vertices(); + private readVertices(input: BinaryInput, vertexCount: number): Vertices { + const scale = this.scale; + const verticesLength = vertexCount << 1; + const vertices = new Vertices(); + if (!input.readBoolean()) { vertices.vertices = this.readFloatArray(input, verticesLength, scale); + return vertices; } - let weights = new Array(); - let bonesArray = new Array(); + const weights = new Array(); + const bonesArray = new Array(); + for (let i = 0; i < vertexCount; i++) { - let boneCount = input.readInt(true); + const boneCount = input.readInt(true); + bonesArray.push(boneCount); for (let ii = 0; ii < boneCount; ii++) { bonesArray.push(input.readInt(true)); @@ -503,51 +572,56 @@ export class SkeletonBinary { } vertices.vertices = Utils.toFloatArray(weights); vertices.bones = bonesArray; + return vertices; } - private readFloatArray (input: BinaryInput, n: number, scale: number): number[] { - let array = new Array(n); + private readFloatArray(input: BinaryInput, n: number, scale: number): number[] { + const array = new Array(n); + if (scale == 1) { - for (let i = 0; i < n; i++) - array[i] = input.readFloat(); + for (let i = 0; i < n; i++) array[i] = input.readFloat(); } else { - for (let i = 0; i < n; i++) - array[i] = input.readFloat() * scale; + for (let i = 0; i < n; i++) array[i] = input.readFloat() * scale; } + return array; } - private readShortArray (input: BinaryInput): number[] { - let n = input.readInt(true); - let array = new Array(n); - for (let i = 0; i < n; i++) - array[i] = input.readShort(); + private readShortArray(input: BinaryInput): number[] { + const n = input.readInt(true); + const array = new Array(n); + + for (let i = 0; i < n; i++) array[i] = input.readShort(); + return array; } - private readAnimation (input: BinaryInput, name: string, skeletonData: SkeletonData): Animation { + private readAnimation(input: BinaryInput, name: string, skeletonData: SkeletonData): Animation { input.readInt(true); // Number of timelines. - let timelines = new Array(); - let scale = this.scale; + const timelines = new Array(); + const scale = this.scale; // Slot timelines. + for (let i = 0, n = input.readInt(true); i < n; i++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let timelineType = input.readByte(); - let frameCount = input.readInt(true); - let frameLast = frameCount - 1; + const timelineType = input.readByte(); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; + switch (timelineType) { case SLOT_ATTACHMENT: { - let timeline = new AttachmentTimeline(frameCount, slotIndex); - for (let frame = 0; frame < frameCount; frame++) - timeline.setFrame(frame, input.readFloat(), input.readStringRef()); + const timeline = new AttachmentTimeline(frameCount, slotIndex); + + for (let frame = 0; frame < frameCount; frame++) timeline.setFrame(frame, input.readFloat(), input.readStringRef()); timelines.push(timeline); break; } case SLOT_RGBA: { - let bezierCount = input.readInt(true); - let timeline = new RGBATimeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGBATimeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -559,11 +633,11 @@ export class SkeletonBinary { timeline.setFrame(frame, time, r, g, b, a); if (frame == frameLast) break; - let time2 = input.readFloat(); - let r2 = input.readUnsignedByte() / 255.0; - let g2 = input.readUnsignedByte() / 255.0; - let b2 = input.readUnsignedByte() / 255.0; - let a2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const r2 = input.readUnsignedByte() / 255.0; + const g2 = input.readUnsignedByte() / 255.0; + const b2 = input.readUnsignedByte() / 255.0; + const a2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -585,8 +659,8 @@ export class SkeletonBinary { break; } case SLOT_RGB: { - let bezierCount = input.readInt(true); - let timeline = new RGBTimeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGBTimeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -597,10 +671,10 @@ export class SkeletonBinary { timeline.setFrame(frame, time, r, g, b); if (frame == frameLast) break; - let time2 = input.readFloat(); - let r2 = input.readUnsignedByte() / 255.0; - let g2 = input.readUnsignedByte() / 255.0; - let b2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const r2 = input.readUnsignedByte() / 255.0; + const g2 = input.readUnsignedByte() / 255.0; + const b2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -620,8 +694,8 @@ export class SkeletonBinary { break; } case SLOT_RGBA2: { - let bezierCount = input.readInt(true); - let timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -635,14 +709,14 @@ export class SkeletonBinary { for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2); if (frame == frameLast) break; - let time2 = input.readFloat(); - let nr = input.readUnsignedByte() / 255.0; - let ng = input.readUnsignedByte() / 255.0; - let nb = input.readUnsignedByte() / 255.0; - let na = input.readUnsignedByte() / 255.0; - let nr2 = input.readUnsignedByte() / 255.0; - let ng2 = input.readUnsignedByte() / 255.0; - let nb2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const nr = input.readUnsignedByte() / 255.0; + const ng = input.readUnsignedByte() / 255.0; + const nb = input.readUnsignedByte() / 255.0; + const na = input.readUnsignedByte() / 255.0; + const nr2 = input.readUnsignedByte() / 255.0; + const ng2 = input.readUnsignedByte() / 255.0; + const nb2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -670,8 +744,8 @@ export class SkeletonBinary { break; } case SLOT_RGB2: { - let bezierCount = input.readInt(true); - let timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex); + const bezierCount = input.readInt(true); + const timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex); let time = input.readFloat(); let r = input.readUnsignedByte() / 255.0; @@ -684,13 +758,13 @@ export class SkeletonBinary { for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, r, g, b, r2, g2, b2); if (frame == frameLast) break; - let time2 = input.readFloat(); - let nr = input.readUnsignedByte() / 255.0; - let ng = input.readUnsignedByte() / 255.0; - let nb = input.readUnsignedByte() / 255.0; - let nr2 = input.readUnsignedByte() / 255.0; - let ng2 = input.readUnsignedByte() / 255.0; - let nb2 = input.readUnsignedByte() / 255.0; + const time2 = input.readFloat(); + const nr = input.readUnsignedByte() / 255.0; + const ng = input.readUnsignedByte() / 255.0; + const nb = input.readUnsignedByte() / 255.0; + const nr2 = input.readUnsignedByte() / 255.0; + const ng2 = input.readUnsignedByte() / 255.0; + const nb2 = input.readUnsignedByte() / 255.0; switch (input.readByte()) { case CURVE_STEPPED: @@ -716,13 +790,16 @@ export class SkeletonBinary { break; } case SLOT_ALPHA: { - let timeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex); - let time = input.readFloat(), a = input.readUnsignedByte() / 255; + const timeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex); + let time = input.readFloat(); + let a = input.readUnsignedByte() / 255; + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, a); if (frame == frameLast) break; - let time2 = input.readFloat(); - let a2 = input.readUnsignedByte() / 255; + const time2 = input.readFloat(); + const a2 = input.readUnsignedByte() / 255; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -741,9 +818,13 @@ export class SkeletonBinary { // Bone timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let boneIndex = input.readInt(true); + const boneIndex = input.readInt(true); + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true); + const type = input.readByte(); + const frameCount = input.readInt(true); + const bezierCount = input.readInt(true); + switch (type) { case BONE_ROTATE: timelines.push(readTimeline1(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1)); @@ -780,13 +861,21 @@ export class SkeletonBinary { // IK constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1; - let timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index); - let time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale; + const index = input.readInt(true); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; + const timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index); + let time = input.readFloat(); + let mix = input.readFloat(); + let softness = input.readFloat() * scale; + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean()); if (frame == frameLast) break; - let time2 = input.readFloat(), mix2 = input.readFloat(), softness2 = input.readFloat() * scale; + const time2 = input.readFloat(); + const mix2 = input.readFloat(); + const softness2 = input.readFloat() * scale; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -804,15 +893,29 @@ export class SkeletonBinary { // Transform constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1; - let timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index); - let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat(), - mixScaleX = input.readFloat(), mixScaleY = input.readFloat(), mixShearY = input.readFloat(); + const index = input.readInt(true); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; + const timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index); + let time = input.readFloat(); + let mixRotate = input.readFloat(); + let mixX = input.readFloat(); + let mixY = input.readFloat(); + let mixScaleX = input.readFloat(); + let mixScaleY = input.readFloat(); + let mixShearY = input.readFloat(); + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY); if (frame == frameLast) break; - let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(), mixY2 = input.readFloat(), - mixScaleX2 = input.readFloat(), mixScaleY2 = input.readFloat(), mixShearY2 = input.readFloat(); + const time2 = input.readFloat(); + const mixRotate2 = input.readFloat(); + const mixX2 = input.readFloat(); + const mixY2 = input.readFloat(); + const mixScaleX2 = input.readFloat(); + const mixScaleY2 = input.readFloat(); + const mixShearY2 = input.readFloat(); + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -838,28 +941,44 @@ export class SkeletonBinary { // Path constraint timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let index = input.readInt(true); - let data = skeletonData.pathConstraints[index]; + const index = input.readInt(true); + const data = skeletonData.pathConstraints[index]; + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { switch (input.readByte()) { case PATH_POSITION: - timelines - .push(readTimeline1(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index), - data.positionMode == PositionMode.Fixed ? scale : 1)); + timelines.push( + readTimeline1( + input, + new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index), + data.positionMode == PositionMode.Fixed ? scale : 1 + ) + ); break; case PATH_SPACING: - timelines - .push(readTimeline1(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index), - data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1)); + timelines.push( + readTimeline1( + input, + new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index), + data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1 + ) + ); break; case PATH_MIX: - let timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index); - let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat(); + const timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index); + let time = input.readFloat(); + let mixRotate = input.readFloat(); + let mixX = input.readFloat(); + let mixY = input.readFloat(); + for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY); if (frame == frameLast) break; - let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(), - mixY2 = input.readFloat(); + const time2 = input.readFloat(); + const mixRotate2 = input.readFloat(); + const mixX2 = input.readFloat(); + const mixY2 = input.readFloat(); + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -881,54 +1000,58 @@ export class SkeletonBinary { // Deform timelines. for (let i = 0, n = input.readInt(true); i < n; i++) { - let skin = skeletonData.skins[input.readInt(true)]; + const skin = skeletonData.skins[input.readInt(true)]; + for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); + for (let iii = 0, nnn = input.readInt(true); iii < nnn; iii++) { - let attachmentName = input.readStringRef(); - if (!attachmentName) throw new Error("attachmentName must not be null."); - let attachment = skin.getAttachment(slotIndex, attachmentName); - let timelineType = this.readDeformTimelineType(input); - let frameCount = input.readInt(true); - let frameLast = frameCount - 1; + const attachmentName = input.readStringRef(); + + if (!attachmentName) throw new Error('attachmentName must not be null.'); + const attachment = skin.getAttachment(slotIndex, attachmentName); + const timelineType = this.readDeformTimelineType(input); + const frameCount = input.readInt(true); + const frameLast = frameCount - 1; switch (timelineType) { case ATTACHMENT_DEFORM: { - let vertexAttachment = attachment as VertexAttachment; - let weighted = vertexAttachment.bones; - let vertices = vertexAttachment.vertices; - let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + const vertexAttachment = attachment as VertexAttachment; + const weighted = vertexAttachment.bones; + const vertices = vertexAttachment.vertices; + const deformLength = weighted ? (vertices.length / 3) * 2 : vertices.length; - - let bezierCount = input.readInt(true); - let timeline = new DeformTimeline(frameCount, bezierCount, slotIndex, vertexAttachment); + const bezierCount = input.readInt(true); + const timeline = new DeformTimeline(frameCount, bezierCount, slotIndex, vertexAttachment); let time = input.readFloat(); + for (let frame = 0, bezier = 0; ; frame++) { let deform; let end = input.readInt(true); - if (end == 0) - deform = weighted ? Utils.newFloatArray(deformLength) : vertices; + + if (end == 0) deform = weighted ? Utils.newFloatArray(deformLength) : vertices; else { deform = Utils.newFloatArray(deformLength); - let start = input.readInt(true); + const start = input.readInt(true); + end += start; + /* eslint-disable max-depth*/ if (scale == 1) { - for (let v = start; v < end; v++) - deform[v] = input.readFloat(); + for (let v = start; v < end; v++) deform[v] = input.readFloat(); } else { - for (let v = start; v < end; v++) - deform[v] = input.readFloat() * scale; + for (let v = start; v < end; v++) deform[v] = input.readFloat() * scale; } if (!weighted) { - for (let v = 0, vn = deform.length; v < vn; v++) - deform[v] += vertices[v]; + for (let v = 0, vn = deform.length; v < vn; v++) deform[v] += vertices[v]; } + /* eslint-enable max-depth*/ } timeline.setFrame(frame, time, deform); if (frame == frameLast) break; - let time2 = input.readFloat(); + const time2 = input.readFloat(); + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -942,12 +1065,13 @@ export class SkeletonBinary { break; } case ATTACHMENT_SEQUENCE: { - let timeline = new SequenceTimeline(frameCount, slotIndex, attachment as unknown as IHasTextureRegion); + const timeline = new SequenceTimeline(frameCount, slotIndex, attachment as unknown as IHasTextureRegion); + for (let frame = 0; frame < frameCount; frame++) { - let time = input.readFloat(); - let modeAndIndex = input.readInt32(); - timeline.setFrame(frame, time, SequenceModeValues[modeAndIndex & 0xf], modeAndIndex >> 4, - input.readFloat()); + const time = input.readFloat(); + const modeAndIndex = input.readInt32(); + + timeline.setFrame(frame, time, SequenceModeValues[modeAndIndex & 0xf], modeAndIndex >> 4, input.readFloat()); } timelines.push(timeline); break; @@ -958,45 +1082,50 @@ export class SkeletonBinary { } // Draw order timeline. - let drawOrderCount = input.readInt(true); + const drawOrderCount = input.readInt(true); + if (drawOrderCount > 0) { - let timeline = new DrawOrderTimeline(drawOrderCount); - let slotCount = skeletonData.slots.length; + const timeline = new DrawOrderTimeline(drawOrderCount); + const slotCount = skeletonData.slots.length; + for (let i = 0; i < drawOrderCount; i++) { - let time = input.readFloat(); - let offsetCount = input.readInt(true); - let drawOrder = Utils.newArray(slotCount, 0); - for (let ii = slotCount - 1; ii >= 0; ii--) - drawOrder[ii] = -1; - let unchanged = Utils.newArray(slotCount - offsetCount, 0); - let originalIndex = 0, unchangedIndex = 0; + const time = input.readFloat(); + const offsetCount = input.readInt(true); + const drawOrder = Utils.newArray(slotCount, 0); + + for (let ii = slotCount - 1; ii >= 0; ii--) drawOrder[ii] = -1; + const unchanged = Utils.newArray(slotCount - offsetCount, 0); + let originalIndex = 0; + let unchangedIndex = 0; + for (let ii = 0; ii < offsetCount; ii++) { - let slotIndex = input.readInt(true); + const slotIndex = input.readInt(true); // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; + + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + input.readInt(true)] = originalIndex++; } // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. - for (let ii = slotCount - 1; ii >= 0; ii--) - if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + for (let ii = slotCount - 1; ii >= 0; ii--) if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; timeline.setFrame(i, time, drawOrder); } timelines.push(timeline); } // Event timeline. - let eventCount = input.readInt(true); + const eventCount = input.readInt(true); + if (eventCount > 0) { - let timeline = new EventTimeline(eventCount); + const timeline = new EventTimeline(eventCount); + for (let i = 0; i < eventCount; i++) { - let time = input.readFloat(); - let eventData = skeletonData.events[input.readInt(true)]; - let event = new Event(time, eventData); + const time = input.readFloat(); + const eventData = skeletonData.events[input.readInt(true)]; + const event = new Event(time, eventData); + event.intValue = input.readInt(false); event.floatValue = input.readFloat(); event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue; @@ -1010,19 +1139,21 @@ export class SkeletonBinary { } let duration = 0; - for (let i = 0, n = timelines.length; i < n; i++) - duration = Math.max(duration, timelines[i].getDuration()); + + for (let i = 0, n = timelines.length; i < n; i++) duration = Math.max(duration, timelines[i].getDuration()); + return new Animation(name, timelines, duration); } } class LinkedMesh { - parent: string | null; skin: string | null; + parent: string | null; + skin: string | null; slotIndex: number; mesh: MeshAttachment; inheritTimeline: boolean; - constructor (mesh: MeshAttachment, skin: string | null, slotIndex: number, parent: string | null, inheritDeform: boolean) { + constructor(mesh: MeshAttachment, skin: string | null, slotIndex: number, parent: string | null, inheritDeform: boolean) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; @@ -1032,15 +1163,19 @@ class LinkedMesh { } class Vertices { - constructor (public bones: Array | null = null, public vertices: Array | Float32Array | null = null) { } + constructor(public bones: Array | null = null, public vertices: Array | Float32Array | null = null) {} } -function readTimeline1 (input: BinaryInput, timeline: CurveTimeline1, scale: number): CurveTimeline1 { - let time = input.readFloat(), value = input.readFloat() * scale; +function readTimeline1(input: BinaryInput, timeline: CurveTimeline1, scale: number): CurveTimeline1 { + let time = input.readFloat(); + let value = input.readFloat() * scale; + for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) { timeline.setFrame(frame, time, value); if (frame == frameLast) break; - let time2 = input.readFloat(), value2 = input.readFloat() * scale; + const time2 = input.readFloat(); + const value2 = input.readFloat() * scale; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -1051,15 +1186,22 @@ function readTimeline1 (input: BinaryInput, timeline: CurveTimeline1, scale: num time = time2; value = value2; } + return timeline; } -function readTimeline2 (input: BinaryInput, timeline: CurveTimeline2, scale: number): CurveTimeline2 { - let time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale; +function readTimeline2(input: BinaryInput, timeline: CurveTimeline2, scale: number): CurveTimeline2 { + let time = input.readFloat(); + let value1 = input.readFloat() * scale; + let value2 = input.readFloat() * scale; + for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) { timeline.setFrame(frame, time, value1, value2); if (frame == frameLast) break; - let time2 = input.readFloat(), nvalue1 = input.readFloat() * scale, nvalue2 = input.readFloat() * scale; + const time2 = input.readFloat(); + const nvalue1 = input.readFloat() * scale; + const nvalue2 = input.readFloat() * scale; + switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -1072,11 +1214,22 @@ function readTimeline2 (input: BinaryInput, timeline: CurveTimeline2, scale: num value1 = nvalue1; value2 = nvalue2; } + return timeline; } -function setBezier (input: BinaryInput, timeline: CurveTimeline, bezier: number, frame: number, value: number, - time1: number, time2: number, value1: number, value2: number, scale: number) { +function setBezier( + input: BinaryInput, + timeline: CurveTimeline, + bezier: number, + frame: number, + value: number, + time1: number, + time2: number, + value1: number, + value2: number, + scale: number +) { timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), input.readFloat() * scale, time2, value2); } diff --git a/packages/runtime-4.1/src/core/SkeletonBounds.ts b/packages/runtime-4.1/src/core/SkeletonBounds.ts index 5eaea674..126d81aa 100644 --- a/packages/runtime-4.1/src/core/SkeletonBounds.ts +++ b/packages/runtime-4.1/src/core/SkeletonBounds.ts @@ -1,8 +1,8 @@ -import {BoundingBoxAttachment} from "./attachments"; -import {SkeletonBoundsBase} from "@pixi-spine/base"; +import type { BoundingBoxAttachment } from './attachments'; +import { SkeletonBoundsBase } from '@pixi-spine/base'; /** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are * provided along with convenience methods for doing hit detection. * @public * */ -export class SkeletonBounds extends SkeletonBoundsBase{}; \ No newline at end of file +export class SkeletonBounds extends SkeletonBoundsBase {} diff --git a/packages/runtime-4.1/src/core/SkeletonData.ts b/packages/runtime-4.1/src/core/SkeletonData.ts index a0832567..e18967ee 100644 --- a/packages/runtime-4.1/src/core/SkeletonData.ts +++ b/packages/runtime-4.1/src/core/SkeletonData.ts @@ -1,12 +1,12 @@ -import type {ISkeletonData} from "@pixi-spine/base"; -import type {Animation} from "./Animation"; -import {BoneData} from "./BoneData"; -import {SlotData} from "./SlotData"; -import {Skin} from "./Skin"; -import {EventData} from "./EventData"; -import {IkConstraintData} from "./IkConstraintData"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {PathConstraintData} from "./PathConstraintData"; +import type { ISkeletonData } from '@pixi-spine/base'; +import type { Animation } from './Animation'; +import type { BoneData } from './BoneData'; +import type { SlotData } from './SlotData'; +import type { Skin } from './Skin'; +import type { EventData } from './EventData'; +import type { IkConstraintData } from './IkConstraintData'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { PathConstraintData } from './PathConstraintData'; /** Stores the setup pose and all of the stateless data for a skeleton. * @@ -15,7 +15,6 @@ import {PathConstraintData} from "./PathConstraintData"; * @public * */ export class SkeletonData implements ISkeletonData { - /** The skeleton's name, which by default is the name of the skeleton data file, if possible. May be null. */ name: string | null = null; @@ -48,16 +47,16 @@ export class SkeletonData implements ISkeletonData(); /** The X coordinate of the skeleton's axis aligned bounding box in the setup pose. */ - x: number = 0; + x = 0; /** The Y coordinate of the skeleton's axis aligned bounding box in the setup pose. */ - y: number = 0; + y = 0; /** The width of the skeleton's axis aligned bounding box in the setup pose. */ - width: number = 0; + width = 0; /** The height of the skeleton's axis aligned bounding box in the setup pose. */ - height: number = 0; + height = 0; /** The Spine version used to export the skeleton data, or null. */ version: string | null = null; @@ -78,130 +77,157 @@ export class SkeletonData implements ISkeletonData(); - constructor (attachmentLoader: AttachmentLoader) { + constructor(attachmentLoader: AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - readSkeletonData (json: string | any): SkeletonData { - let scale = this.scale; - let skeletonData = new SkeletonData(); - let root = typeof (json) === "string" ? JSON.parse(json) : json; + readSkeletonData(json: string | any): SkeletonData { + const scale = this.scale; + const skeletonData = new SkeletonData(); + const root = typeof json === 'string' ? JSON.parse(json) : json; // Skeleton - let skeletonMap = root.skeleton; + const skeletonMap = root.skeleton; + if (skeletonMap) { skeletonData.hash = skeletonMap.hash; skeletonData.version = skeletonMap.spine; const verShort = skeletonData.version.substr(0, 3); - if (verShort !== '4.0' && verShort !== '4.1') - { - let error = `Spine 4.1 loader cant load version ${skeletonMap.spine}. Please configure your pixi-spine bundle`; + + if (verShort !== '4.0' && verShort !== '4.1') { + const error = `Spine 4.1 loader cant load version ${skeletonMap.spine}. Please configure your pixi-spine bundle`; + console.error(error); } skeletonData.x = skeletonMap.x; @@ -74,27 +94,30 @@ export class SkeletonJson { // Bones if (root.bones) { for (let i = 0; i < root.bones.length; i++) { - let boneMap = root.bones[i]; + const boneMap = root.bones[i]; let parent: BoneData = null; - let parentName: string = getValue(boneMap, "parent", null); + const parentName: string = getValue(boneMap, 'parent', null); + if (parentName != null) { parent = skeletonData.findBone(parentName); - if (parent == null) throw new Error("Parent bone not found: " + parentName); + if (parent == null) throw new Error(`Parent bone not found: ${parentName}`); } - let data = new BoneData(skeletonData.bones.length, boneMap.name, parent); - data.length = getValue(boneMap, "length", 0) * scale; - data.x = getValue(boneMap, "x", 0) * scale; - data.y = getValue(boneMap, "y", 0) * scale; - data.rotation = getValue(boneMap, "rotation", 0); - data.scaleX = getValue(boneMap, "scaleX", 1); - data.scaleY = getValue(boneMap, "scaleY", 1); - data.shearX = getValue(boneMap, "shearX", 0); - data.shearY = getValue(boneMap, "shearY", 0); - data.transformMode = Utils.enumValue(TransformMode, getValue(boneMap, "transform", "Normal")); - data.skinRequired = getValue(boneMap, "skin", false); - - let color = getValue(boneMap, "color", null); + const data = new BoneData(skeletonData.bones.length, boneMap.name, parent); + + data.length = getValue(boneMap, 'length', 0) * scale; + data.x = getValue(boneMap, 'x', 0) * scale; + data.y = getValue(boneMap, 'y', 0) * scale; + data.rotation = getValue(boneMap, 'rotation', 0); + data.scaleX = getValue(boneMap, 'scaleX', 1); + data.scaleY = getValue(boneMap, 'scaleY', 1); + data.shearX = getValue(boneMap, 'shearX', 0); + data.shearY = getValue(boneMap, 'shearY', 0); + data.transformMode = Utils.enumValue(TransformMode, getValue(boneMap, 'transform', 'Normal')); + data.skinRequired = getValue(boneMap, 'skin', false); + + const color = getValue(boneMap, 'color', null); + if (color) data.color.setFromString(color); skeletonData.bones.push(data); @@ -104,19 +127,22 @@ export class SkeletonJson { // Slots. if (root.slots) { for (let i = 0; i < root.slots.length; i++) { - let slotMap = root.slots[i]; - let boneData = skeletonData.findBone(slotMap.bone); + const slotMap = root.slots[i]; + const boneData = skeletonData.findBone(slotMap.bone); + if (!boneData) throw new Error(`Couldn't find bone ${slotMap.bone} for slot ${slotMap.name}`); - let data = new SlotData(skeletonData.slots.length, slotMap.name, boneData); + const data = new SlotData(skeletonData.slots.length, slotMap.name, boneData); + + const color: string = getValue(slotMap, 'color', null); - let color: string = getValue(slotMap, "color", null); if (color) data.color.setFromString(color); - let dark: string = getValue(slotMap, "dark", null); + const dark: string = getValue(slotMap, 'dark', null); + if (dark) data.darkColor = Color.fromString(dark); - data.attachmentName = getValue(slotMap, "attachment", null); - data.blendMode = SkeletonJson.blendModeFromString(getValue(slotMap, "blend", "normal")); + data.attachmentName = getValue(slotMap, 'attachment', null); + data.blendMode = SkeletonJson.blendModeFromString(getValue(slotMap, 'blend', 'normal')); skeletonData.slots.push(data); } } @@ -124,26 +150,28 @@ export class SkeletonJson { // IK constraints if (root.ik) { for (let i = 0; i < root.ik.length; i++) { - let constraintMap = root.ik[i]; - let data = new IkConstraintData(constraintMap.name); - data.order = getValue(constraintMap, "order", 0); - data.skinRequired = getValue(constraintMap, "skin", false); + const constraintMap = root.ik[i]; + const data = new IkConstraintData(constraintMap.name); + + data.order = getValue(constraintMap, 'order', 0); + data.skinRequired = getValue(constraintMap, 'skin', false); for (let ii = 0; ii < constraintMap.bones.length; ii++) { - let boneName = constraintMap.bones[ii]; - let bone = skeletonData.findBone(boneName); - if (bone == null) throw new Error("IK bone not found: " + boneName); + const boneName = constraintMap.bones[ii]; + const bone = skeletonData.findBone(boneName); + + if (bone == null) throw new Error(`IK bone not found: ${boneName}`); data.bones.push(bone); } data.target = skeletonData.findBone(constraintMap.target); - data.mix = getValue(constraintMap, "mix", 1); - data.softness = getValue(constraintMap, "softness", 0) * scale; - data.bendDirection = getValue(constraintMap, "bendPositive", true) ? 1 : -1; - data.compress = getValue(constraintMap, "compress", false); - data.stretch = getValue(constraintMap, "stretch", false); - data.uniform = getValue(constraintMap, "uniform", false); + data.mix = getValue(constraintMap, 'mix', 1); + data.softness = getValue(constraintMap, 'softness', 0) * scale; + data.bendDirection = getValue(constraintMap, 'bendPositive', true) ? 1 : -1; + data.compress = getValue(constraintMap, 'compress', false); + data.stretch = getValue(constraintMap, 'stretch', false); + data.uniform = getValue(constraintMap, 'uniform', false); skeletonData.ikConstraints.push(data); } @@ -152,38 +180,41 @@ export class SkeletonJson { // Transform constraints. if (root.transform) { for (let i = 0; i < root.transform.length; i++) { - let constraintMap = root.transform[i]; - let data = new TransformConstraintData(constraintMap.name); - data.order = getValue(constraintMap, "order", 0); - data.skinRequired = getValue(constraintMap, "skin", false); + const constraintMap = root.transform[i]; + const data = new TransformConstraintData(constraintMap.name); + + data.order = getValue(constraintMap, 'order', 0); + data.skinRequired = getValue(constraintMap, 'skin', false); for (let ii = 0; ii < constraintMap.bones.length; ii++) { - let boneName = constraintMap.bones[ii]; - let bone = skeletonData.findBone(boneName); + const boneName = constraintMap.bones[ii]; + const bone = skeletonData.findBone(boneName); + if (!bone) throw new Error(`Couldn't find bone ${boneName} for transform constraint ${constraintMap.name}.`); data.bones.push(bone); } - let targetName: string = constraintMap.target; - let target = skeletonData.findBone(targetName); + const targetName: string = constraintMap.target; + const target = skeletonData.findBone(targetName); + if (!target) throw new Error(`Couldn't find target bone ${targetName} for transform constraint ${constraintMap.name}.`); data.target = target; - data.local = getValue(constraintMap, "local", false); - data.relative = getValue(constraintMap, "relative", false); - data.offsetRotation = getValue(constraintMap, "rotation", 0); - data.offsetX = getValue(constraintMap, "x", 0) * scale; - data.offsetY = getValue(constraintMap, "y", 0) * scale; - data.offsetScaleX = getValue(constraintMap, "scaleX", 0); - data.offsetScaleY = getValue(constraintMap, "scaleY", 0); - data.offsetShearY = getValue(constraintMap, "shearY", 0); - - data.mixRotate = getValue(constraintMap, "mixRotate", 1); - data.mixX = getValue(constraintMap, "mixX", 1); - data.mixY = getValue(constraintMap, "mixY", data.mixX); - data.mixScaleX = getValue(constraintMap, "mixScaleX", 1); - data.mixScaleY = getValue(constraintMap, "mixScaleY", data.mixScaleX); - data.mixShearY = getValue(constraintMap, "mixShearY", 1); + data.local = getValue(constraintMap, 'local', false); + data.relative = getValue(constraintMap, 'relative', false); + data.offsetRotation = getValue(constraintMap, 'rotation', 0); + data.offsetX = getValue(constraintMap, 'x', 0) * scale; + data.offsetY = getValue(constraintMap, 'y', 0) * scale; + data.offsetScaleX = getValue(constraintMap, 'scaleX', 0); + data.offsetScaleY = getValue(constraintMap, 'scaleY', 0); + data.offsetShearY = getValue(constraintMap, 'shearY', 0); + + data.mixRotate = getValue(constraintMap, 'mixRotate', 1); + data.mixX = getValue(constraintMap, 'mixX', 1); + data.mixY = getValue(constraintMap, 'mixY', data.mixX); + data.mixScaleX = getValue(constraintMap, 'mixScaleX', 1); + data.mixScaleY = getValue(constraintMap, 'mixScaleY', data.mixScaleX); + data.mixShearY = getValue(constraintMap, 'mixShearY', 1); skeletonData.transformConstraints.push(data); } @@ -192,34 +223,37 @@ export class SkeletonJson { // Path constraints. if (root.path) { for (let i = 0; i < root.path.length; i++) { - let constraintMap = root.path[i]; - let data = new PathConstraintData(constraintMap.name); - data.order = getValue(constraintMap, "order", 0); - data.skinRequired = getValue(constraintMap, "skin", false); + const constraintMap = root.path[i]; + const data = new PathConstraintData(constraintMap.name); + + data.order = getValue(constraintMap, 'order', 0); + data.skinRequired = getValue(constraintMap, 'skin', false); for (let ii = 0; ii < constraintMap.bones.length; ii++) { - let boneName = constraintMap.bones[ii]; - let bone = skeletonData.findBone(boneName); + const boneName = constraintMap.bones[ii]; + const bone = skeletonData.findBone(boneName); + if (!bone) throw new Error(`Couldn't find bone ${boneName} for path constraint ${constraintMap.name}.`); data.bones.push(bone); } - let targetName: string = constraintMap.target; - let target = skeletonData.findSlot(targetName); + const targetName: string = constraintMap.target; + const target = skeletonData.findSlot(targetName); + if (!target) throw new Error(`Couldn't find target slot ${targetName} for path constraint ${constraintMap.name}.`); data.target = target; - data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, "positionMode", "Percent")); - data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, "spacingMode", "Length")); - data.rotateMode = Utils.enumValue(RotateMode, getValue(constraintMap, "rotateMode", "Tangent")); - data.offsetRotation = getValue(constraintMap, "rotation", 0); - data.position = getValue(constraintMap, "position", 0); + data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, 'positionMode', 'Percent')); + data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, 'spacingMode', 'Length')); + data.rotateMode = Utils.enumValue(RotateMode, getValue(constraintMap, 'rotateMode', 'Tangent')); + data.offsetRotation = getValue(constraintMap, 'rotation', 0); + data.position = getValue(constraintMap, 'position', 0); if (data.positionMode == PositionMode.Fixed) data.position *= scale; - data.spacing = getValue(constraintMap, "spacing", 0); + data.spacing = getValue(constraintMap, 'spacing', 0); if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale; - data.mixRotate = getValue(constraintMap, "mixRotate", 1); - data.mixX = getValue(constraintMap, "mixX", 1); - data.mixY = getValue(constraintMap, "mixY", data.mixX); + data.mixRotate = getValue(constraintMap, 'mixRotate', 1); + data.mixX = getValue(constraintMap, 'mixX', 1); + data.mixY = getValue(constraintMap, 'mixY', data.mixX); skeletonData.pathConstraints.push(data); } @@ -228,13 +262,14 @@ export class SkeletonJson { // Skins. if (root.skins) { for (let i = 0; i < root.skins.length; i++) { - let skinMap = root.skins[i] - let skin = new Skin(skinMap.name); + const skinMap = root.skins[i]; + const skin = new Skin(skinMap.name); if (skinMap.bones) { for (let ii = 0; ii < skinMap.bones.length; ii++) { - let boneName = skinMap.bones[ii]; - let bone = skeletonData.findBone(boneName); + const boneName = skinMap.bones[ii]; + const bone = skeletonData.findBone(boneName); + if (!bone) throw new Error(`Couldn't find bone ${boneName} for skin ${skinMap.name}.`); skin.bones.push(bone); } @@ -242,8 +277,9 @@ export class SkeletonJson { if (skinMap.ik) { for (let ii = 0; ii < skinMap.ik.length; ii++) { - let constraintName = skinMap.ik[ii]; - let constraint = skeletonData.findIkConstraint(constraintName); + const constraintName = skinMap.ik[ii]; + const constraint = skeletonData.findIkConstraint(constraintName); + if (!constraint) throw new Error(`Couldn't find IK constraint ${constraintName} for skin ${skinMap.name}.`); skin.constraints.push(constraint); } @@ -251,8 +287,9 @@ export class SkeletonJson { if (skinMap.transform) { for (let ii = 0; ii < skinMap.transform.length; ii++) { - let constraintName = skinMap.transform[ii]; - let constraint = skeletonData.findTransformConstraint(constraintName); + const constraintName = skinMap.transform[ii]; + const constraint = skeletonData.findTransformConstraint(constraintName); + if (!constraint) throw new Error(`Couldn't find transform constraint ${constraintName} for skin ${skinMap.name}.`); skin.constraints.push(constraint); } @@ -260,33 +297,39 @@ export class SkeletonJson { if (skinMap.path) { for (let ii = 0; ii < skinMap.path.length; ii++) { - let constraintName = skinMap.path[ii]; - let constraint = skeletonData.findPathConstraint(constraintName); + const constraintName = skinMap.path[ii]; + const constraint = skeletonData.findPathConstraint(constraintName); + if (!constraint) throw new Error(`Couldn't find path constraint ${constraintName} for skin ${skinMap.name}.`); skin.constraints.push(constraint); } } - for (let slotName in skinMap.attachments) { - let slot = skeletonData.findSlot(slotName); + for (const slotName in skinMap.attachments) { + const slot = skeletonData.findSlot(slotName); + if (!slot) throw new Error(`Couldn't find slot ${slotName} for skin ${skinMap.name}.`); - let slotMap = skinMap.attachments[slotName]; - for (let entryName in slotMap) { - let attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData); + const slotMap = skinMap.attachments[slotName]; + + for (const entryName in slotMap) { + const attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData); + if (attachment) skin.setAttachment(slot.index, entryName, attachment); } } skeletonData.skins.push(skin); - if (skin.name == "default") skeletonData.defaultSkin = skin; + if (skin.name == 'default') skeletonData.defaultSkin = skin; } } // Linked meshes. for (let i = 0, n = this.linkedMeshes.length; i < n; i++) { - let linkedMesh = this.linkedMeshes[i]; - let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + const linkedMesh = this.linkedMeshes[i]; + const skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + if (!skin) throw new Error(`Skin not found: ${linkedMesh.skin}`); - let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + const parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + if (!parent) throw new Error(`Parent mesh not found: ${linkedMesh.parent}`); linkedMesh.mesh.timelineAttachment = linkedMesh.inheritTimeline ? parent : linkedMesh.mesh; linkedMesh.mesh.setParentMesh(parent); @@ -296,16 +339,17 @@ export class SkeletonJson { // Events. if (root.events) { - for (let eventName in root.events) { - let eventMap = root.events[eventName]; - let data = new EventData(eventName); - data.intValue = getValue(eventMap, "int", 0); - data.floatValue = getValue(eventMap, "float", 0); - data.stringValue = getValue(eventMap, "string", ""); - data.audioPath = getValue(eventMap, "audio", null); + for (const eventName in root.events) { + const eventMap = root.events[eventName]; + const data = new EventData(eventName); + + data.intValue = getValue(eventMap, 'int', 0); + data.floatValue = getValue(eventMap, 'float', 0); + data.stringValue = getValue(eventMap, 'string', ''); + data.audioPath = getValue(eventMap, 'audio', null); if (data.audioPath) { - data.volume = getValue(eventMap, "volume", 1); - data.balance = getValue(eventMap, "balance", 0); + data.volume = getValue(eventMap, 'volume', 1); + data.balance = getValue(eventMap, 'balance', 0); } skeletonData.events.push(data); } @@ -313,8 +357,9 @@ export class SkeletonJson { // Animations. if (root.animations) { - for (let animationName in root.animations) { - let animationMap = root.animations[animationName]; + for (const animationName in root.animations) { + const animationMap = root.animations[animationName]; + this.readAnimation(animationMap, animationName, skeletonData); } } @@ -322,148 +367,181 @@ export class SkeletonJson { return skeletonData; } - readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment | null { - let scale = this.scale; - name = getValue(map, "name", name); + readAttachment(map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment | null { + const scale = this.scale; + + name = getValue(map, 'name', name); + + switch (getValue(map, 'type', 'region')) { + case 'region': { + const path = getValue(map, 'path', name); + const sequence = this.readSequence(getValue(map, 'sequence', null)); + const region = this.attachmentLoader.newRegionAttachment(skin, name, path, sequence); - switch (getValue(map, "type", "region")) { - case "region": { - let path = getValue(map, "path", name); - let sequence = this.readSequence(getValue(map, "sequence", null)); - let region = this.attachmentLoader.newRegionAttachment(skin, name, path, sequence); if (!region) return null; region.path = path; - region.x = getValue(map, "x", 0) * scale; - region.y = getValue(map, "y", 0) * scale; - region.scaleX = getValue(map, "scaleX", 1); - region.scaleY = getValue(map, "scaleY", 1); - region.rotation = getValue(map, "rotation", 0); + region.x = getValue(map, 'x', 0) * scale; + region.y = getValue(map, 'y', 0) * scale; + region.scaleX = getValue(map, 'scaleX', 1); + region.scaleY = getValue(map, 'scaleY', 1); + region.rotation = getValue(map, 'rotation', 0); region.width = map.width * scale; region.height = map.height * scale; region.sequence = sequence; - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) region.color.setFromString(color); // if (region.region != null) region.updateRegion(); return region; } - case "boundingbox": { - let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + case 'boundingbox': { + const box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + if (!box) return null; this.readVertices(map, box, map.vertexCount << 1); - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) box.color.setFromString(color); + return box; } - case "mesh": - case "linkedmesh": { - let path = getValue(map, "path", name); - let sequence = this.readSequence(getValue(map, "sequence", null)); - let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path, sequence); + case 'mesh': + case 'linkedmesh': { + const path = getValue(map, 'path', name); + const sequence = this.readSequence(getValue(map, 'sequence', null)); + const mesh = this.attachmentLoader.newMeshAttachment(skin, name, path, sequence); + if (!mesh) return null; mesh.path = path; - let color = getValue(map, "color", null); + const color = getValue(map, 'color', null); + if (color) mesh.color.setFromString(color); - mesh.width = getValue(map, "width", 0) * scale; - mesh.height = getValue(map, "height", 0) * scale; + mesh.width = getValue(map, 'width', 0) * scale; + mesh.height = getValue(map, 'height', 0) * scale; mesh.sequence = sequence; - let parent: string = getValue(map, "parent", null); + const parent: string = getValue(map, 'parent', null); + if (parent) { - this.linkedMeshes.push(new LinkedMesh(mesh, getValue(map, "skin", null), slotIndex, parent, getValue(map, "timelines", true))); + this.linkedMeshes.push(new LinkedMesh(mesh, getValue(map, 'skin', null), slotIndex, parent, getValue(map, 'timelines', true))); + return mesh; } - let uvs: Array = map.uvs; + const uvs: Array = map.uvs; + this.readVertices(map, mesh, uvs.length); mesh.triangles = map.triangles; mesh.regionUVs = new Float32Array(uvs); // if (mesh.region != null) mesh.updateRegion(); - mesh.edges = getValue(map, "edges", null); - mesh.hullLength = getValue(map, "hull", 0) * 2; + mesh.edges = getValue(map, 'edges', null); + mesh.hullLength = getValue(map, 'hull', 0) * 2; + return mesh; } - case "path": { - let path = this.attachmentLoader.newPathAttachment(skin, name); + case 'path': { + const path = this.attachmentLoader.newPathAttachment(skin, name); + if (!path) return null; - path.closed = getValue(map, "closed", false); - path.constantSpeed = getValue(map, "constantSpeed", true); + path.closed = getValue(map, 'closed', false); + path.constantSpeed = getValue(map, 'constantSpeed', true); + + const vertexCount = map.vertexCount; - let vertexCount = map.vertexCount; this.readVertices(map, path, vertexCount << 1); - let lengths: Array = Utils.newArray(vertexCount / 3, 0); - for (let i = 0; i < map.lengths.length; i++) - lengths[i] = map.lengths[i] * scale; + const lengths: Array = Utils.newArray(vertexCount / 3, 0); + + for (let i = 0; i < map.lengths.length; i++) lengths[i] = map.lengths[i] * scale; path.lengths = lengths; - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) path.color.setFromString(color); + return path; } - case "point": { - let point = this.attachmentLoader.newPointAttachment(skin, name); + case 'point': { + const point = this.attachmentLoader.newPointAttachment(skin, name); + if (!point) return null; - point.x = getValue(map, "x", 0) * scale; - point.y = getValue(map, "y", 0) * scale; - point.rotation = getValue(map, "rotation", 0); + point.x = getValue(map, 'x', 0) * scale; + point.y = getValue(map, 'y', 0) * scale; + point.rotation = getValue(map, 'rotation', 0); + + const color = getValue(map, 'color', null); - let color = getValue(map, "color", null); if (color) point.color.setFromString(color); + return point; } - case "clipping": { - let clip = this.attachmentLoader.newClippingAttachment(skin, name); + case 'clipping': { + const clip = this.attachmentLoader.newClippingAttachment(skin, name); + if (!clip) return null; - let end = getValue(map, "end", null); + const end = getValue(map, 'end', null); + if (end != null) { - let slot = skeletonData.findSlot(end); - if (slot == null) throw new Error("Clipping end slot not found: " + end); + const slot = skeletonData.findSlot(end); + + if (slot == null) throw new Error(`Clipping end slot not found: ${end}`); clip.endSlot = slot; } - let vertexCount = map.vertexCount; + const vertexCount = map.vertexCount; + this.readVertices(map, clip, vertexCount << 1); - let color: string = getValue(map, "color", null); + const color: string = getValue(map, 'color', null); + if (color) clip.color.setFromString(color); + return clip; } } + return null; } - readSequence (map: any) { + readSequence(map: any) { if (map == null) return null; - let sequence = new Sequence(getValue(map, "count", 0)); - sequence.start = getValue(map, "start", 1); - sequence.digits = getValue(map, "digits", 0); - sequence.setupIndex = getValue(map, "setup", 0); + const sequence = new Sequence(getValue(map, 'count', 0)); + + sequence.start = getValue(map, 'start', 1); + sequence.digits = getValue(map, 'digits', 0); + sequence.setupIndex = getValue(map, 'setup', 0); + return sequence; } - readVertices (map: any, attachment: VertexAttachment, verticesLength: number) { - let scale = this.scale; + readVertices(map: any, attachment: VertexAttachment, verticesLength: number) { + const scale = this.scale; + attachment.worldVerticesLength = verticesLength; - let vertices: Array = map.vertices; + const vertices: Array = map.vertices; + if (verticesLength == vertices.length) { - let scaledVertices = Utils.toFloatArray(vertices); + const scaledVertices = Utils.toFloatArray(vertices); + if (scale != 1) { - for (let i = 0, n = vertices.length; i < n; i++) - scaledVertices[i] *= scale; + for (let i = 0, n = vertices.length; i < n; i++) scaledVertices[i] *= scale; } attachment.vertices = scaledVertices; + return; } - let weights = new Array(); - let bones = new Array(); - for (let i = 0, n = vertices.length; i < n;) { - let boneCount = vertices[i++]; + const weights = new Array(); + const bones = new Array(); + + for (let i = 0, n = vertices.length; i < n; ) { + const boneCount = vertices[i++]; + bones.push(boneCount); for (let nn = i + boneCount * 4; i < nn; i += 4) { bones.push(vertices[i]); @@ -476,45 +554,52 @@ export class SkeletonJson { attachment.vertices = Utils.toFloatArray(weights); } - readAnimation (map: any, name: string, skeletonData: SkeletonData) { - let scale = this.scale; - let timelines = new Array(); + readAnimation(map: any, name: string, skeletonData: SkeletonData) { + const scale = this.scale; + const timelines = new Array(); // Slot timelines. if (map.slots) { - for (let slotName in map.slots) { - let slotMap = map.slots[slotName]; - let slot = skeletonData.findSlot(slotName); - if (!slot) throw new Error("Slot not found: " + slotName); - let slotIndex = slot.index; - for (let timelineName in slotMap) { - let timelineMap = slotMap[timelineName]; + for (const slotName in map.slots) { + const slotMap = map.slots[slotName]; + const slot = skeletonData.findSlot(slotName); + + if (!slot) throw new Error(`Slot not found: ${slotName}`); + const slotIndex = slot.index; + + for (const timelineName in slotMap) { + const timelineMap = slotMap[timelineName]; + if (!timelineMap) continue; - let frames = timelineMap.length; - if (timelineName == "attachment") { - let timeline = new AttachmentTimeline(frames, slotIndex); + const frames = timelineMap.length; + + if (timelineName == 'attachment') { + const timeline = new AttachmentTimeline(frames, slotIndex); + for (let frame = 0; frame < frames; frame++) { - let keyMap = timelineMap[frame]; - timeline.setFrame(frame, getValue(keyMap, "time", 0), getValue(keyMap, "name", null)); + const keyMap = timelineMap[frame]; + + timeline.setFrame(frame, getValue(keyMap, 'time', 0), getValue(keyMap, 'name', null)); } timelines.push(timeline); - - } else if (timelineName == "rgba") { - let timeline = new RGBATimeline(frames, frames << 2, slotIndex); + } else if (timelineName == 'rgba') { + const timeline = new RGBATimeline(frames, frames << 2, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.color); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b, color.a); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.color); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.color); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -527,23 +612,24 @@ export class SkeletonJson { } timelines.push(timeline); - - } else if (timelineName == "rgb") { - let timeline = new RGBTimeline(frames, frames * 3, slotIndex); + } else if (timelineName == 'rgb') { + const timeline = new RGBTimeline(frames, frames * 3, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.color); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.color); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.color); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -555,28 +641,29 @@ export class SkeletonJson { } timelines.push(timeline); - - } else if (timelineName == "alpha") { + } else if (timelineName == 'alpha') { timelines.push(readTimeline1(timelineMap, new AlphaTimeline(frames, frames, slotIndex), 0, 1)); - } else if (timelineName == "rgba2") { - let timeline = new RGBA2Timeline(frames, frames * 7, slotIndex); + } else if (timelineName == 'rgba2') { + const timeline = new RGBA2Timeline(frames, frames * 7, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.light); let color2 = Color.fromString(keyMap.dark); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.light); - let newColor2 = Color.fromString(nextMap.dark); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.light); + const newColor2 = Color.fromString(nextMap.dark); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -593,26 +680,27 @@ export class SkeletonJson { } timelines.push(timeline); - - } else if (timelineName == "rgb2") { - let timeline = new RGB2Timeline(frames, frames * 6, slotIndex); + } else if (timelineName == 'rgb2') { + const timeline = new RGB2Timeline(frames, frames * 6, slotIndex); let keyMap = timelineMap[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let color = Color.fromString(keyMap.light); let color2 = Color.fromString(keyMap.dark); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, color.r, color.g, color.b, color2.r, color2.g, color2.b); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let newColor = Color.fromString(nextMap.light); - let newColor2 = Color.fromString(nextMap.dark); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const newColor = Color.fromString(nextMap.light); + const newColor2 = Color.fromString(nextMap.dark); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1); @@ -635,44 +723,56 @@ export class SkeletonJson { // Bone timelines. if (map.bones) { - for (let boneName in map.bones) { - let boneMap = map.bones[boneName]; - let bone = skeletonData.findBone(boneName); - if (!bone) throw new Error("Bone not found: " + boneName); - let boneIndex = bone.index; - for (let timelineName in boneMap) { - let timelineMap = boneMap[timelineName]; - let frames = timelineMap.length; + for (const boneName in map.bones) { + const boneMap = map.bones[boneName]; + const bone = skeletonData.findBone(boneName); + + if (!bone) throw new Error(`Bone not found: ${boneName}`); + const boneIndex = bone.index; + + for (const timelineName in boneMap) { + const timelineMap = boneMap[timelineName]; + const frames = timelineMap.length; + if (frames == 0) continue; - if (timelineName === "rotate") { + if (timelineName === 'rotate') { timelines.push(readTimeline1(timelineMap, new RotateTimeline(frames, frames, boneIndex), 0, 1)); - } else if (timelineName === "translate") { - let timeline = new TranslateTimeline(frames, frames << 1, boneIndex); - timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, scale)); - } else if (timelineName === "translatex") { - let timeline = new TranslateXTimeline(frames, frames, boneIndex); + } else if (timelineName === 'translate') { + const timeline = new TranslateTimeline(frames, frames << 1, boneIndex); + + timelines.push(readTimeline2(timelineMap, timeline, 'x', 'y', 0, scale)); + } else if (timelineName === 'translatex') { + const timeline = new TranslateXTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, scale)); - } else if (timelineName === "translatey") { - let timeline = new TranslateYTimeline(frames, frames, boneIndex); + } else if (timelineName === 'translatey') { + const timeline = new TranslateYTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, scale)); - } else if (timelineName === "scale") { - let timeline = new ScaleTimeline(frames, frames << 1, boneIndex); - timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 1, 1)); - } else if (timelineName === "scalex") { - let timeline = new ScaleXTimeline(frames, frames, boneIndex); + } else if (timelineName === 'scale') { + const timeline = new ScaleTimeline(frames, frames << 1, boneIndex); + + timelines.push(readTimeline2(timelineMap, timeline, 'x', 'y', 1, 1)); + } else if (timelineName === 'scalex') { + const timeline = new ScaleXTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 1, 1)); - } else if (timelineName === "scaley") { - let timeline = new ScaleYTimeline(frames, frames, boneIndex); + } else if (timelineName === 'scaley') { + const timeline = new ScaleYTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 1, 1)); - } else if (timelineName === "shear") { - let timeline = new ShearTimeline(frames, frames << 1, boneIndex); - timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, 1)); - } else if (timelineName === "shearx") { - let timeline = new ShearXTimeline(frames, frames, boneIndex); + } else if (timelineName === 'shear') { + const timeline = new ShearTimeline(frames, frames << 1, boneIndex); + + timelines.push(readTimeline2(timelineMap, timeline, 'x', 'y', 0, 1)); + } else if (timelineName === 'shearx') { + const timeline = new ShearXTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, 1)); - } else if (timelineName === "sheary") { - let timeline = new ShearYTimeline(frames, frames, boneIndex); + } else if (timelineName === 'sheary') { + const timeline = new ShearYTimeline(frames, frames, boneIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, 1)); } } @@ -681,32 +781,44 @@ export class SkeletonJson { // IK constraint timelines. if (map.ik) { - for (let constraintName in map.ik) { - let constraintMap = map.ik[constraintName]; + for (const constraintName in map.ik) { + const constraintMap = map.ik[constraintName]; let keyMap = constraintMap[0]; + if (!keyMap) continue; - let constraint = skeletonData.findIkConstraint(constraintName); - if (!constraint) throw new Error("IK Constraint not found: " + constraintName); - let constraintIndex = skeletonData.ikConstraints.indexOf(constraint); - let timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex); + const constraint = skeletonData.findIkConstraint(constraintName); - let time = getValue(keyMap, "time", 0); - let mix = getValue(keyMap, "mix", 1); - let softness = getValue(keyMap, "softness", 0) * scale; + if (!constraint) throw new Error(`IK Constraint not found: ${constraintName}`); + const constraintIndex = skeletonData.ikConstraints.indexOf(constraint); + const timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex); + + let time = getValue(keyMap, 'time', 0); + let mix = getValue(keyMap, 'mix', 1); + let softness = getValue(keyMap, 'softness', 0) * scale; for (let frame = 0, bezier = 0; ; frame++) { - timeline.setFrame(frame, time, mix, softness, getValue(keyMap, "bendPositive", true) ? 1 : -1, getValue(keyMap, "compress", false), getValue(keyMap, "stretch", false)); - let nextMap = constraintMap[frame + 1]; + timeline.setFrame( + frame, + time, + mix, + softness, + getValue(keyMap, 'bendPositive', true) ? 1 : -1, + getValue(keyMap, 'compress', false), + getValue(keyMap, 'stretch', false) + ); + const nextMap = constraintMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let mix2 = getValue(nextMap, "mix", 1); - let softness2 = getValue(nextMap, "softness", 0) * scale; - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const mix2 = getValue(nextMap, 'mix', 1); + const softness2 = getValue(nextMap, 'softness', 0) * scale; + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, scale); @@ -723,40 +835,44 @@ export class SkeletonJson { // Transform constraint timelines. if (map.transform) { - for (let constraintName in map.transform) { - let timelineMap = map.transform[constraintName]; + for (const constraintName in map.transform) { + const timelineMap = map.transform[constraintName]; let keyMap = timelineMap[0]; + if (!keyMap) continue; - let constraint = skeletonData.findTransformConstraint(constraintName); - if (!constraint) throw new Error("Transform constraint not found: " + constraintName); - let constraintIndex = skeletonData.transformConstraints.indexOf(constraint); - let timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length * 6, constraintIndex); + const constraint = skeletonData.findTransformConstraint(constraintName); - let time = getValue(keyMap, "time", 0); - let mixRotate = getValue(keyMap, "mixRotate", 1); - let mixX = getValue(keyMap, "mixX", 1); - let mixY = getValue(keyMap, "mixY", mixX); - let mixScaleX = getValue(keyMap, "mixScaleX", 1); - let mixScaleY = getValue(keyMap, "mixScaleY", mixScaleX); - let mixShearY = getValue(keyMap, "mixShearY", 1); + if (!constraint) throw new Error(`Transform constraint not found: ${constraintName}`); + const constraintIndex = skeletonData.transformConstraints.indexOf(constraint); + const timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length * 6, constraintIndex); + + let time = getValue(keyMap, 'time', 0); + let mixRotate = getValue(keyMap, 'mixRotate', 1); + let mixX = getValue(keyMap, 'mixX', 1); + let mixY = getValue(keyMap, 'mixY', mixX); + let mixScaleX = getValue(keyMap, 'mixScaleX', 1); + let mixScaleY = getValue(keyMap, 'mixScaleY', mixScaleX); + const mixShearY = getValue(keyMap, 'mixShearY', 1); for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let mixRotate2 = getValue(nextMap, "mixRotate", 1); - let mixX2 = getValue(nextMap, "mixX", 1); - let mixY2 = getValue(nextMap, "mixY", mixX2); - let mixScaleX2 = getValue(nextMap, "mixScaleX", 1); - let mixScaleY2 = getValue(nextMap, "mixScaleY", mixScaleX2); - let mixShearY2 = getValue(nextMap, "mixShearY", 1); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const mixRotate2 = getValue(nextMap, 'mixRotate', 1); + const mixX2 = getValue(nextMap, 'mixX', 1); + const mixY2 = getValue(nextMap, 'mixY', mixX2); + const mixScaleX2 = getValue(nextMap, 'mixScaleX', 1); + const mixScaleY2 = getValue(nextMap, 'mixScaleY', mixScaleX2); + const mixShearY2 = getValue(nextMap, 'mixShearY', 1); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1); @@ -781,41 +897,52 @@ export class SkeletonJson { // Path constraint timelines. if (map.path) { - for (let constraintName in map.path) { - let constraintMap = map.path[constraintName]; - let constraint = skeletonData.findPathConstraint(constraintName); - if (!constraint) throw new Error("Path constraint not found: " + constraintName); - let constraintIndex = skeletonData.pathConstraints.indexOf(constraint); - for (let timelineName in constraintMap) { - let timelineMap = constraintMap[timelineName]; + for (const constraintName in map.path) { + const constraintMap = map.path[constraintName]; + const constraint = skeletonData.findPathConstraint(constraintName); + + if (!constraint) throw new Error(`Path constraint not found: ${constraintName}`); + const constraintIndex = skeletonData.pathConstraints.indexOf(constraint); + + for (const timelineName in constraintMap) { + const timelineMap = constraintMap[timelineName]; let keyMap = timelineMap[0]; + if (!keyMap) continue; - let frames = timelineMap.length; - if (timelineName === "position") { - let timeline = new PathConstraintPositionTimeline(frames, frames, constraintIndex); + const frames = timelineMap.length; + + if (timelineName === 'position') { + const timeline = new PathConstraintPositionTimeline(frames, frames, constraintIndex); + timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.positionMode == PositionMode.Fixed ? scale : 1)); - } else if (timelineName === "spacing") { - let timeline = new PathConstraintSpacingTimeline(frames, frames, constraintIndex); - timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.spacingMode == SpacingMode.Length || constraint.spacingMode == SpacingMode.Fixed ? scale : 1)); - } else if (timelineName === "mix") { - let timeline = new PathConstraintMixTimeline(frames, frames * 3, constraintIndex); - let time = getValue(keyMap, "time", 0); - let mixRotate = getValue(keyMap, "mixRotate", 1); - let mixX = getValue(keyMap, "mixX", 1); - let mixY = getValue(keyMap, "mixY", mixX); + } else if (timelineName === 'spacing') { + const timeline = new PathConstraintSpacingTimeline(frames, frames, constraintIndex); + + timelines.push( + readTimeline1(timelineMap, timeline, 0, constraint.spacingMode == SpacingMode.Length || constraint.spacingMode == SpacingMode.Fixed ? scale : 1) + ); + } else if (timelineName === 'mix') { + const timeline = new PathConstraintMixTimeline(frames, frames * 3, constraintIndex); + let time = getValue(keyMap, 'time', 0); + let mixRotate = getValue(keyMap, 'mixRotate', 1); + let mixX = getValue(keyMap, 'mixX', 1); + let mixY = getValue(keyMap, 'mixY', mixX); + for (let frame = 0, bezier = 0; ; frame++) { timeline.setFrame(frame, time, mixRotate, mixX, mixY); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let mixRotate2 = getValue(nextMap, "mixRotate", 1); - let mixX2 = getValue(nextMap, "mixX", 1); - let mixY2 = getValue(nextMap, "mixY", mixX2); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const mixRotate2 = getValue(nextMap, 'mixRotate', 1); + const mixX2 = getValue(nextMap, 'mixX', 1); + const mixY2 = getValue(nextMap, 'mixY', mixX2); + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1); @@ -835,17 +962,19 @@ export class SkeletonJson { // ver40 compatibility if (map.deform) { - map.attachments = { }; - for (let deformName in map.deform) { - let deformMap = map.deform[deformName]; - let outMap = map.attachments[deformName] = {} - for (let slotName in deformMap) { - let slotMap = deformMap[slotName]; - let outMap2 = outMap[slotName] = {}; - for (let innerMapName in slotMap) { + map.attachments = {}; + for (const deformName in map.deform) { + const deformMap = map.deform[deformName]; + const outMap = (map.attachments[deformName] = {}); + + for (const slotName in deformMap) { + const slotMap = deformMap[slotName]; + const outMap2 = (outMap[slotName] = {}); + + for (const innerMapName in slotMap) { outMap2[innerMapName] = { - deform: slotMap[innerMapName] - } + deform: slotMap[innerMapName], + }; } } } @@ -853,77 +982,85 @@ export class SkeletonJson { // Attachment timelines. if (map.attachments) { - for (let attachmentsName in map.attachments) { - let attachmentsMap = map.attachments[attachmentsName]; - let skin = skeletonData.findSkin(attachmentsName); + for (const attachmentsName in map.attachments) { + const attachmentsMap = map.attachments[attachmentsName]; + const skin = skeletonData.findSkin(attachmentsName); + if (skin == null) { - if (settings.FAIL_ON_NON_EXISTING_SKIN) { - throw new Error("Skin not found: " + attachmentsName); - } else { - continue; - } + if (settings.FAIL_ON_NON_EXISTING_SKIN) { + throw new Error(`Skin not found: ${attachmentsName}`); + } else { + continue; + } } - for (let slotMapName in attachmentsMap) { - let slotMap = attachmentsMap[slotMapName]; - let slot = skeletonData.findSlot(slotMapName); - if (!slot) throw new Error("Slot not found: " + slotMapName); - let slotIndex = slot.index; - for (let attachmentMapName in slotMap) { - let attachmentMap = slotMap[attachmentMapName]; - let attachment = skin.getAttachment(slotIndex, attachmentMapName); - - for (let timelineMapName in attachmentMap) { - let timelineMap = attachmentMap[timelineMapName]; + for (const slotMapName in attachmentsMap) { + const slotMap = attachmentsMap[slotMapName]; + const slot = skeletonData.findSlot(slotMapName); + + if (!slot) throw new Error(`Slot not found: ${slotMapName}`); + const slotIndex = slot.index; + + for (const attachmentMapName in slotMap) { + const attachmentMap = slotMap[attachmentMapName]; + const attachment = skin.getAttachment(slotIndex, attachmentMapName); + + for (const timelineMapName in attachmentMap) { + const timelineMap = attachmentMap[timelineMapName]; let keyMap = timelineMap[0]; + if (!keyMap) continue; - if (timelineMapName == "deform") { - let weighted = attachment.bones; - let vertices = attachment.vertices; - let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + if (timelineMapName == 'deform') { + const weighted = attachment.bones; + const vertices = attachment.vertices; + const deformLength = weighted ? (vertices.length / 3) * 2 : vertices.length; + + const timeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment); + let time = getValue(keyMap, 'time', 0); - let timeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment); - let time = getValue(keyMap, "time", 0); for (let frame = 0, bezier = 0; ; frame++) { let deform: NumberArrayLike; - let verticesValue: Array = getValue(keyMap, "vertices", null); - if (!verticesValue) - deform = weighted ? Utils.newFloatArray(deformLength) : vertices; + const verticesValue: Array = getValue(keyMap, 'vertices', null); + + if (!verticesValue) deform = weighted ? Utils.newFloatArray(deformLength) : vertices; else { deform = Utils.newFloatArray(deformLength); - let start = getValue(keyMap, "offset", 0); + const start = getValue(keyMap, 'offset', 0); + Utils.arrayCopy(verticesValue, 0, deform, start, verticesValue.length); if (scale != 1) { - for (let i = start, n = i + verticesValue.length; i < n; i++) - deform[i] *= scale; + for (let i = start, n = i + verticesValue.length; i < n; i++) deform[i] *= scale; } if (!weighted) { - for (let i = 0; i < deformLength; i++) - deform[i] += vertices[i]; + for (let i = 0; i < deformLength; i++) deform[i] += vertices[i]; } } timeline.setFrame(frame, time, deform); - let nextMap = timelineMap[frame + 1]; + const nextMap = timelineMap[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); break; } - let time2 = getValue(nextMap, "time", 0); - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const curve = keyMap.curve; + if (curve) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1); time = time2; keyMap = nextMap; } timelines.push(timeline); - } else if (timelineMapName == "sequence") { - let timeline = new SequenceTimeline(timelineMap.length, slotIndex, attachment as unknown as IHasTextureRegion); + } else if (timelineMapName == 'sequence') { + const timeline = new SequenceTimeline(timelineMap.length, slotIndex, attachment as unknown as IHasTextureRegion); let lastDelay = 0; + for (let frame = 0; frame < timelineMap.length; frame++) { - let delay = getValue(keyMap, "delay", lastDelay); - let time = getValue(keyMap, "time", 0); - let mode = SequenceMode[getValue(keyMap, "mode", "hold")] as unknown as number; - let index = getValue(keyMap, "index", 0); + const delay = getValue(keyMap, 'delay', lastDelay); + const time = getValue(keyMap, 'time', 0); + const mode = SequenceMode[getValue(keyMap, 'mode', 'hold')] as unknown as number; + const index = getValue(keyMap, 'index', 0); + timeline.setFrame(frame, time, mode, index, delay); lastDelay = delay; keyMap = timelineMap[frame + 1]; @@ -938,55 +1075,61 @@ export class SkeletonJson { // Draw order timelines. if (map.drawOrder) { - let timeline = new DrawOrderTimeline(map.drawOrder.length); - let slotCount = skeletonData.slots.length; + const timeline = new DrawOrderTimeline(map.drawOrder.length); + const slotCount = skeletonData.slots.length; let frame = 0; + for (let i = 0; i < map.drawOrder.length; i++, frame++) { - let drawOrderMap = map.drawOrder[i]; + const drawOrderMap = map.drawOrder[i]; let drawOrder: Array | null = null; - let offsets = getValue(drawOrderMap, "offsets", null); + const offsets = getValue(drawOrderMap, 'offsets', null); + if (offsets) { drawOrder = Utils.newArray(slotCount, -1); - let unchanged = Utils.newArray(slotCount - offsets.length, 0); - let originalIndex = 0, unchangedIndex = 0; + const unchanged = Utils.newArray(slotCount - offsets.length, 0); + let originalIndex = 0; + let unchangedIndex = 0; + for (let ii = 0; ii < offsets.length; ii++) { - let offsetMap = offsets[ii]; - let slot = skeletonData.findSlot(offsetMap.slot); - if (!slot) throw new Error("Slot not found: " + slot); - let slotIndex = slot.index; + const offsetMap = offsets[ii]; + const slot = skeletonData.findSlot(offsetMap.slot); + + if (!slot) throw new Error(`Slot not found: ${slot}`); + const slotIndex = slot.index; // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; + + while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++; // Set changed items. drawOrder[originalIndex + offsetMap.offset] = originalIndex++; } // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; + while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++; // Fill in unchanged items. - for (let ii = slotCount - 1; ii >= 0; ii--) - if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + for (let ii = slotCount - 1; ii >= 0; ii--) if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; } - timeline.setFrame(frame, getValue(drawOrderMap, "time", 0), drawOrder); + timeline.setFrame(frame, getValue(drawOrderMap, 'time', 0), drawOrder); } timelines.push(timeline); } // Event timelines. if (map.events) { - let timeline = new EventTimeline(map.events.length); + const timeline = new EventTimeline(map.events.length); let frame = 0; + for (let i = 0; i < map.events.length; i++, frame++) { - let eventMap = map.events[i]; - let eventData = skeletonData.findEvent(eventMap.name); - if (!eventData) throw new Error("Event not found: " + eventMap.name); - let event = new Event(Utils.toSinglePrecision(getValue(eventMap, "time", 0)), eventData); - event.intValue = getValue(eventMap, "int", eventData.intValue); - event.floatValue = getValue(eventMap, "float", eventData.floatValue); - event.stringValue = getValue(eventMap, "string", eventData.stringValue); + const eventMap = map.events[i]; + const eventData = skeletonData.findEvent(eventMap.name); + + if (!eventData) throw new Error(`Event not found: ${eventMap.name}`); + const event = new Event(Utils.toSinglePrecision(getValue(eventMap, 'time', 0)), eventData); + + event.intValue = getValue(eventMap, 'int', eventData.intValue); + event.floatValue = getValue(eventMap, 'float', eventData.floatValue); + event.stringValue = getValue(eventMap, 'string', eventData.stringValue); if (event.data.audioPath) { - event.volume = getValue(eventMap, "volume", 1); - event.balance = getValue(eventMap, "balance", 0); + event.volume = getValue(eventMap, 'volume', 1); + event.balance = getValue(eventMap, 'balance', 0); } timeline.setFrame(frame, event); } @@ -994,31 +1137,32 @@ export class SkeletonJson { } let duration = 0; - for (let i = 0, n = timelines.length; i < n; i++) - duration = Math.max(duration, timelines[i].getDuration()); + + for (let i = 0, n = timelines.length; i < n; i++) duration = Math.max(duration, timelines[i].getDuration()); if (isNaN(duration)) { - throw new Error("Error while parsing animation, duration is NaN"); + throw new Error('Error while parsing animation, duration is NaN'); } skeletonData.animations.push(new Animation(name, timelines, duration)); } - static blendModeFromString (str: string) { + static blendModeFromString(str: string) { str = str.toLowerCase(); - if (str == "normal") return BLEND_MODES.NORMAL; - if (str == "additive") return BLEND_MODES.ADD; - if (str == "multiply") return BLEND_MODES.MULTIPLY; - if (str == "screen") return BLEND_MODES.SCREEN; + if (str == 'normal') return BLEND_MODES.NORMAL; + if (str == 'additive') return BLEND_MODES.ADD; + if (str == 'multiply') return BLEND_MODES.MULTIPLY; + if (str == 'screen') return BLEND_MODES.SCREEN; throw new Error(`Unknown blend mode: ${str}`); } } class LinkedMesh { - parent: string; skin: string; + parent: string; + skin: string; slotIndex: number; mesh: MeshAttachment; inheritTimeline: boolean; - constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { + constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; @@ -1027,20 +1171,24 @@ class LinkedMesh { } } -function readTimeline1 (keys: any[], timeline: CurveTimeline1, defaultValue: number, scale: number) { +function readTimeline1(keys: any[], timeline: CurveTimeline1, defaultValue: number, scale: number) { let keyMap = keys[0]; - let time = getValue(keyMap, "time", 0); - let value = getValue(keyMap, "value", defaultValue) * scale; + let time = getValue(keyMap, 'time', 0); + let value = getValue(keyMap, 'value', defaultValue) * scale; let bezier = 0; + for (let frame = 0; ; frame++) { timeline.setFrame(frame, time, value); - let nextMap = keys[frame + 1]; + const nextMap = keys[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); + return timeline; } - let time2 = getValue(nextMap, "time", 0); - let value2 = getValue(nextMap, "value", defaultValue) * scale; + const time2 = getValue(nextMap, 'time', 0); + const value2 = getValue(nextMap, 'value', defaultValue) * scale; + if (keyMap.curve) bezier = readCurve(keyMap.curve, timeline, bezier, frame, 0, time, time2, value, value2, scale); time = time2; value = value2; @@ -1048,23 +1196,27 @@ function readTimeline1 (keys: any[], timeline: CurveTimeline1, defaultValue: num } } -function readTimeline2 (keys: any[], timeline: CurveTimeline2, name1: string, name2: string, defaultValue: number, scale: number) { +function readTimeline2(keys: any[], timeline: CurveTimeline2, name1: string, name2: string, defaultValue: number, scale: number) { let keyMap = keys[0]; - let time = getValue(keyMap, "time", 0); + let time = getValue(keyMap, 'time', 0); let value1 = getValue(keyMap, name1, defaultValue) * scale; let value2 = getValue(keyMap, name2, defaultValue) * scale; let bezier = 0; + for (let frame = 0; ; frame++) { timeline.setFrame(frame, time, value1, value2); - let nextMap = keys[frame + 1]; + const nextMap = keys[frame + 1]; + if (!nextMap) { timeline.shrink(bezier); + return timeline; } - let time2 = getValue(nextMap, "time", 0); - let nvalue1 = getValue(nextMap, name1, defaultValue) * scale; - let nvalue2 = getValue(nextMap, name2, defaultValue) * scale; - let curve = keyMap.curve; + const time2 = getValue(nextMap, 'time', 0); + const nvalue1 = getValue(nextMap, name1, defaultValue) * scale; + const nvalue2 = getValue(nextMap, name2, defaultValue) * scale; + const curve = keyMap.curve; + if (curve) { bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale); bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale); @@ -1076,21 +1228,23 @@ function readTimeline2 (keys: any[], timeline: CurveTimeline2, name1: string, na } } -function readCurve (curve: any, timeline: CurveTimeline, bezier: number, frame: number, value: number, time1: number, time2: number, - value1: number, value2: number, scale: number) { - if (curve == "stepped") { +function readCurve(curve: any, timeline: CurveTimeline, bezier: number, frame: number, value: number, time1: number, time2: number, value1: number, value2: number, scale: number) { + if (curve == 'stepped') { timeline.setStepped(frame); + return bezier; } - let i = value << 2; - let cx1 = curve[i]; - let cy1 = curve[i + 1] * scale; - let cx2 = curve[i + 2]; - let cy2 = curve[i + 3] * scale; + const i = value << 2; + const cx1 = curve[i]; + const cy1 = curve[i + 1] * scale; + const cx2 = curve[i + 2]; + const cy2 = curve[i + 3] * scale; + timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2); + return bezier + 1; } -function getValue (map: any, property: string, defaultValue: any) { +function getValue(map: any, property: string, defaultValue: any) { return map[property] !== undefined ? map[property] : defaultValue; } diff --git a/packages/runtime-4.1/src/core/Skin.ts b/packages/runtime-4.1/src/core/Skin.ts index e21dc388..5b8ac479 100644 --- a/packages/runtime-4.1/src/core/Skin.ts +++ b/packages/runtime-4.1/src/core/Skin.ts @@ -1,15 +1,15 @@ -import {Attachment, MeshAttachment} from './attachments'; -import {BoneData} from "./BoneData"; -import {ConstraintData} from "./ConstraintData"; -import {Skeleton} from "./Skeleton"; +import { Attachment, MeshAttachment } from './attachments'; +import type { BoneData } from './BoneData'; +import type { ConstraintData } from './ConstraintData'; +import type { Skeleton } from './Skeleton'; -import type {StringMap, ISkin} from '@pixi-spine/base'; +import type { StringMap, ISkin } from '@pixi-spine/base'; /** Stores an entry in the skin consisting of the slot index, name, and attachment * @public * **/ export class SkinEntry { - constructor(public slotIndex: number, public name: string, public attachment: Attachment) { } + constructor(public slotIndex: number, public name: string, public attachment: Attachment) {} } /** Stores attachments by slot index and attachment name. @@ -26,25 +26,27 @@ export class Skin implements ISkin { bones = Array(); constraints = new Array(); - constructor (name: string) { - if (!name) throw new Error("name cannot be null."); + constructor(name: string) { + if (!name) throw new Error('name cannot be null.'); this.name = name; } /** Adds an attachment to the skin for the specified slot index and name. */ - setAttachment (slotIndex: number, name: string, attachment: Attachment) { - if (!attachment) throw new Error("attachment cannot be null."); - let attachments = this.attachments; + setAttachment(slotIndex: number, name: string, attachment: Attachment) { + if (!attachment) throw new Error('attachment cannot be null.'); + const attachments = this.attachments; + if (slotIndex >= attachments.length) attachments.length = slotIndex + 1; if (!attachments[slotIndex]) attachments[slotIndex] = {}; attachments[slotIndex][name] = attachment; } /** Adds all attachments, bones, and constraints from the specified skin to this skin. */ - addSkin (skin: Skin) { + addSkin(skin: Skin) { for (let i = 0; i < skin.bones.length; i++) { - let bone = skin.bones[i]; + const bone = skin.bones[i]; let contained = false; + for (let ii = 0; ii < this.bones.length; ii++) { if (this.bones[ii] == bone) { contained = true; @@ -55,8 +57,9 @@ export class Skin implements ISkin { } for (let i = 0; i < skin.constraints.length; i++) { - let constraint = skin.constraints[i]; + const constraint = skin.constraints[i]; let contained = false; + for (let ii = 0; ii < this.constraints.length; ii++) { if (this.constraints[ii] == constraint) { contained = true; @@ -66,19 +69,22 @@ export class Skin implements ISkin { if (!contained) this.constraints.push(constraint); } - let attachments = skin.getAttachments(); + const attachments = skin.getAttachments(); + for (let i = 0; i < attachments.length; i++) { - var attachment = attachments[i]; + const attachment = attachments[i]; + this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment); } } /** Adds all bones and constraints and copies of all attachments from the specified skin to this skin. Mesh attachments are not * copied, instead a new linked mesh is created. The attachment copies can be modified without affecting the originals. */ - copySkin (skin: Skin) { + copySkin(skin: Skin) { for (let i = 0; i < skin.bones.length; i++) { - let bone = skin.bones[i]; + const bone = skin.bones[i]; let contained = false; + for (let ii = 0; ii < this.bones.length; ii++) { if (this.bones[ii] == bone) { contained = true; @@ -89,8 +95,9 @@ export class Skin implements ISkin { } for (let i = 0; i < skin.constraints.length; i++) { - let constraint = skin.constraints[i]; + const constraint = skin.constraints[i]; let contained = false; + for (let ii = 0; ii < this.constraints.length; ii++) { if (this.constraints[ii] == constraint) { contained = true; @@ -100,9 +107,11 @@ export class Skin implements ISkin { if (!contained) this.constraints.push(constraint); } - let attachments = skin.getAttachments(); + const attachments = skin.getAttachments(); + for (let i = 0; i < attachments.length; i++) { - var attachment = attachments[i]; + const attachment = attachments[i]; + if (!attachment.attachment) continue; if (attachment.attachment instanceof MeshAttachment) { attachment.attachment = attachment.attachment.newLinkedMesh(); @@ -115,62 +124,75 @@ export class Skin implements ISkin { } /** Returns the attachment for the specified slot index and name, or null. */ - getAttachment (slotIndex: number, name: string): Attachment | null { - let dictionary = this.attachments[slotIndex]; + getAttachment(slotIndex: number, name: string): Attachment | null { + const dictionary = this.attachments[slotIndex]; + return dictionary ? dictionary[name] : null; } /** Removes the attachment in the skin for the specified slot index and name, if any. */ - removeAttachment (slotIndex: number, name: string) { - let dictionary = this.attachments[slotIndex]; + removeAttachment(slotIndex: number, name: string) { + const dictionary = this.attachments[slotIndex]; + if (dictionary) delete dictionary[name]; } /** Returns all attachments in this skin. */ - getAttachments (): Array { - let entries = new Array(); - for (var i = 0; i < this.attachments.length; i++) { - let slotAttachments = this.attachments[i]; + getAttachments(): Array { + const entries = new Array(); + + for (let i = 0; i < this.attachments.length; i++) { + const slotAttachments = this.attachments[i]; + if (slotAttachments) { - for (let name in slotAttachments) { - let attachment = slotAttachments[name]; + for (const name in slotAttachments) { + const attachment = slotAttachments[name]; + if (attachment) entries.push(new SkinEntry(i, name, attachment)); } } } + return entries; } /** Returns all attachments in this skin for the specified slot index. */ - getAttachmentsForSlot (slotIndex: number, attachments: Array) { - let slotAttachments = this.attachments[slotIndex]; + getAttachmentsForSlot(slotIndex: number, attachments: Array) { + const slotAttachments = this.attachments[slotIndex]; + if (slotAttachments) { - for (let name in slotAttachments) { - let attachment = slotAttachments[name]; + for (const name in slotAttachments) { + const attachment = slotAttachments[name]; + if (attachment) attachments.push(new SkinEntry(slotIndex, name, attachment)); } } } /** Clears all attachments, bones, and constraints. */ - clear () { + clear() { this.attachments.length = 0; this.bones.length = 0; this.constraints.length = 0; } /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ - attachAll (skeleton: Skeleton, oldSkin: Skin) { + attachAll(skeleton: Skeleton, oldSkin: Skin) { let slotIndex = 0; + for (let i = 0; i < skeleton.slots.length; i++) { - let slot = skeleton.slots[i]; - let slotAttachment = slot.getAttachment(); + const slot = skeleton.slots[i]; + const slotAttachment = slot.getAttachment(); + if (slotAttachment && slotIndex < oldSkin.attachments.length) { - let dictionary = oldSkin.attachments[slotIndex]; - for (let key in dictionary) { - let skinAttachment: Attachment = dictionary[key]; + const dictionary = oldSkin.attachments[slotIndex]; + + for (const key in dictionary) { + const skinAttachment: Attachment = dictionary[key]; + if (slotAttachment == skinAttachment) { - let attachment = this.getAttachment(slotIndex, key); + const attachment = this.getAttachment(slotIndex, key); + if (attachment) slot.setAttachment(attachment); break; } diff --git a/packages/runtime-4.1/src/core/Slot.ts b/packages/runtime-4.1/src/core/Slot.ts index 1a443f96..64bcf2b8 100644 --- a/packages/runtime-4.1/src/core/Slot.ts +++ b/packages/runtime-4.1/src/core/Slot.ts @@ -1,9 +1,9 @@ -import {Color, ISlot} from '@pixi-spine/base'; +import { Color, ISlot } from '@pixi-spine/base'; -import {Attachment, VertexAttachment} from './attachments/Attachment'; -import type {Bone} from './Bone'; -import type {SlotData} from './SlotData'; -import type {Skeleton} from './Skeleton'; +import { Attachment, VertexAttachment } from './attachments/Attachment'; +import type { Bone } from './Bone'; +import type { SlotData } from './SlotData'; +import type { Skeleton } from './Skeleton'; /** Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store * state for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared @@ -11,7 +11,7 @@ import type {Skeleton} from './Skeleton'; * @public * */ export class Slot implements ISlot { - //this is canon + // this is canon blendMode: number; /** The slot's setup pose data. */ data: SlotData; @@ -29,11 +29,11 @@ export class Slot implements ISlot { attachment: Attachment | null = null; - attachmentState: number = 0; + attachmentState = 0; /** The index of the texture region to display when the slot's attachment has a {@link Sequence}. -1 represents the * {@link Sequence#getSetupIndex()}. */ - sequenceIndex: number = -1; + sequenceIndex = -1; /** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a * weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions. @@ -41,9 +41,9 @@ export class Slot implements ISlot { * See {@link VertexAttachment#computeWorldVertices()} and {@link DeformTimeline}. */ deform = new Array(); - constructor (data: SlotData, bone: Bone) { - if (!data) throw new Error("data cannot be null."); - if (!bone) throw new Error("bone cannot be null."); + constructor(data: SlotData, bone: Bone) { + if (!data) throw new Error('data cannot be null.'); + if (!bone) throw new Error('bone cannot be null.'); this.data = data; this.bone = bone; this.color = new Color(); @@ -54,22 +54,25 @@ export class Slot implements ISlot { } /** The skeleton this slot belongs to. */ - getSkeleton (): Skeleton { + getSkeleton(): Skeleton { return this.bone.skeleton; } /** The current attachment for the slot, or null if the slot has no attachment. */ - getAttachment (): Attachment | null { + getAttachment(): Attachment | null { return this.attachment; } /** Sets the slot's attachment and, if the attachment changed, resets {@link #sequenceIndex} and clears the {@link #deform}. * The deform is not cleared if the old attachment has the same {@link VertexAttachment#getTimelineAttachment()} as the * specified attachment. */ - setAttachment (attachment: Attachment | null) { + setAttachment(attachment: Attachment | null) { if (this.attachment == attachment) return; - if (!(attachment instanceof VertexAttachment) || !(this.attachment instanceof VertexAttachment) - || (attachment).timelineAttachment != (this.attachment).timelineAttachment) { + if ( + !(attachment instanceof VertexAttachment) || + !(this.attachment instanceof VertexAttachment) || + (attachment).timelineAttachment != (this.attachment).timelineAttachment + ) { this.deform.length = 0; } this.attachment = attachment; @@ -77,11 +80,10 @@ export class Slot implements ISlot { } /** Sets this slot to the setup pose. */ - setToSetupPose () { + setToSetupPose() { this.color.setFromColor(this.data.color); - if (this.darkColor) this.darkColor.setFromColor(this.data.darkColor!); - if (!this.data.attachmentName) - this.attachment = null; + if (this.darkColor) this.darkColor.setFromColor(this.data.darkColor); + if (!this.data.attachmentName) this.attachment = null; else { this.attachment = null; this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName)); diff --git a/packages/runtime-4.1/src/core/SlotData.ts b/packages/runtime-4.1/src/core/SlotData.ts index 694781e1..786e7e5f 100644 --- a/packages/runtime-4.1/src/core/SlotData.ts +++ b/packages/runtime-4.1/src/core/SlotData.ts @@ -1,15 +1,15 @@ -import {Color} from '@pixi-spine/base'; +import { Color } from '@pixi-spine/base'; -import type {ISlotData} from '@pixi-spine/base'; -import {BLEND_MODES} from '@pixi/constants'; -import {BoneData} from "./BoneData"; +import type { ISlotData } from '@pixi-spine/base'; +import { BLEND_MODES } from '@pixi/constants'; +import type { BoneData } from './BoneData'; /** Stores the setup pose for a {@link Slot}. * @public * */ export class SlotData implements ISlotData { /** The index of the slot in {@link Skeleton#getSlots()}. */ - index: number = 0; + index = 0; /** The name of the slot, which is unique across all slots in the skeleton. */ name: string; @@ -31,10 +31,10 @@ export class SlotData implements ISlotData { /** The blend mode for drawing the slot's attachment. */ blendMode: BLEND_MODES = BLEND_MODES.NORMAL; - constructor (index: number, name: string, boneData: BoneData) { - if (index < 0) throw new Error("index must be >= 0."); - if (!name) throw new Error("name cannot be null."); - if (!boneData) throw new Error("boneData cannot be null."); + constructor(index: number, name: string, boneData: BoneData) { + if (index < 0) throw new Error('index must be >= 0.'); + if (!name) throw new Error('name cannot be null.'); + if (!boneData) throw new Error('boneData cannot be null.'); this.index = index; this.name = name; this.boneData = boneData; diff --git a/packages/runtime-4.1/src/core/TransformConstraint.ts b/packages/runtime-4.1/src/core/TransformConstraint.ts index 8835fb8a..cd0f9211 100644 --- a/packages/runtime-4.1/src/core/TransformConstraint.ts +++ b/packages/runtime-4.1/src/core/TransformConstraint.ts @@ -1,8 +1,8 @@ -import {Updatable} from "./Updatable"; -import {TransformConstraintData} from "./TransformConstraintData"; -import {Bone} from "./Bone"; -import {MathUtils, Vector2} from "@pixi-spine/base"; -import {Skeleton} from "./Skeleton"; +import type { Updatable } from './Updatable'; +import type { TransformConstraintData } from './TransformConstraintData'; +import type { Bone } from './Bone'; +import { MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Skeleton } from './Skeleton'; /** Stores the current pose for a transform constraint. A transform constraint adjusts the world transform of the constrained * bones to match that of the target bone. @@ -11,7 +11,6 @@ import {Skeleton} from "./Skeleton"; * @public * */ export class TransformConstraint implements Updatable { - /** The transform constraint's setup pose data. */ data: TransformConstraintData; @@ -21,14 +20,19 @@ export class TransformConstraint implements Updatable { /** The target bone whose world transform will be copied to the constrained bones. */ target: Bone; - mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0; + mixRotate = 0; + mixX = 0; + mixY = 0; + mixScaleX = 0; + mixScaleY = 0; + mixShearY = 0; temp = new Vector2(); active = false; - constructor (data: TransformConstraintData, skeleton: Skeleton) { - if (!data) throw new Error("data cannot be null."); - if (!skeleton) throw new Error("skeleton cannot be null."); + constructor(data: TransformConstraintData, skeleton: Skeleton) { + if (!data) throw new Error('data cannot be null.'); + if (!skeleton) throw new Error('skeleton cannot be null.'); this.data = data; this.mixRotate = data.mixRotate; this.mixX = data.mixX; @@ -38,61 +42,71 @@ export class TransformConstraint implements Updatable { this.mixShearY = data.mixShearY; this.bones = new Array(); for (let i = 0; i < data.bones.length; i++) { - let bone = skeleton.findBone(data.bones[i].name); + const bone = skeleton.findBone(data.bones[i].name); + if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}.`); this.bones.push(bone); } - let target = skeleton.findBone(data.target.name); + const target = skeleton.findBone(data.target.name); + if (!target) throw new Error(`Couldn't find target bone ${data.target.name}.`); this.target = target; } - isActive () { + isActive() { return this.active; } - update () { + update() { if (this.mixRotate == 0 && this.mixX == 0 && this.mixY == 0 && this.mixScaleX == 0 && this.mixScaleX == 0 && this.mixShearY == 0) return; if (this.data.local) { - if (this.data.relative) - this.applyRelativeLocal(); - else - this.applyAbsoluteLocal(); - } else { - if (this.data.relative) - this.applyRelativeWorld(); - else - this.applyAbsoluteWorld(); - } + if (this.data.relative) this.applyRelativeLocal(); + else this.applyAbsoluteLocal(); + } else if (this.data.relative) this.applyRelativeWorld(); + else this.applyAbsoluteWorld(); } - applyAbsoluteWorld () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - let translate = mixX != 0 || mixY != 0; + applyAbsoluteWorld() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; + const translate = mixX != 0 || mixY != 0; - let target = this.target; + const target = this.target; const targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect; - let offsetShearY = this.data.offsetShearY * degRadReflect; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; + + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; const mat = bone.matrix; if (mixRotate != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= mixRotate; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -100,7 +114,8 @@ export class TransformConstraint implements Updatable { } if (translate) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += (temp.x - mat.tx) * mixX; mat.ty += (temp.y - mat.ty) * mixY; @@ -108,62 +123,80 @@ export class TransformConstraint implements Updatable { if (mixScaleX != 0) { let s = Math.sqrt(mat.a * mat.a + mat.b * mat.b); + if (s != 0) s = (s + (Math.sqrt(ta * ta + tc * tc) - s + this.data.offsetScaleX) * mixScaleX) / s; mat.a *= s; mat.b *= s; } if (mixScaleY != 0) { let s = Math.sqrt(mat.c * mat.c + mat.d * mat.d); + if (s != 0) s = (s + (Math.sqrt(tb * tb + td * td) - s + this.data.offsetScaleY) * mixScaleY) / s; mat.c *= s; mat.d *= s; - } if (mixShearY > 0) { - let b = mat.c, d = mat.d; - let by = Math.atan2(d, b); + const b = mat.c; + const d = mat.d; + const by = Math.atan2(d, b); let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(mat.b, mat.a)); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r = by + (r + offsetShearY) * mixShearY; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; - } bone.updateAppliedTransform(); } } - applyRelativeWorld () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - let translate = mixX != 0 || mixY != 0; + applyRelativeWorld() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; + const translate = mixX != 0 || mixY != 0; + + const target = this.target; + const targetMat = target.matrix; + const ta = targetMat.a; + const tb = targetMat.c; + const tc = targetMat.b; + const td = targetMat.d; + const degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; + const offsetRotation = this.data.offsetRotation * degRadReflect; + const offsetShearY = this.data.offsetShearY * degRadReflect; - let target = this.target; - let targetMat = target.matrix; - let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.d; - let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad; - let offsetRotation = this.data.offsetRotation * degRadReflect, offsetShearY = this.data.offsetShearY * degRadReflect; + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; const mat = bone.matrix; if (mixRotate != 0) { - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; let r = Math.atan2(tc, ta) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; r *= mixRotate; - let cos = Math.cos(r), sin = Math.sin(r); + const cos = Math.cos(r); + const sin = Math.sin(r); + mat.a = cos * a - sin * c; mat.c = cos * b - sin * d; mat.b = sin * a + cos * c; @@ -171,32 +204,39 @@ export class TransformConstraint implements Updatable { } if (translate) { - let temp = this.temp; + const temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); mat.tx += temp.x * mixX; mat.ty += temp.y * mixY; } if (mixScaleX != 0) { - let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * mixScaleX + 1; + const s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * mixScaleX + 1; + mat.a *= s; mat.b *= s; } if (mixScaleY != 0) { - let s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * mixScaleY + 1; + const s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * mixScaleY + 1; + mat.c *= s; mat.d *= s; } if (mixShearY > 0) { let r = Math.atan2(td, tb) - Math.atan2(tc, ta); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // + + if (r > MathUtils.PI) r -= MathUtils.PI2; + else if (r < -MathUtils.PI) + // r += MathUtils.PI2; - let b = mat.c, d = mat.d; + const b = mat.c; + const d = mat.d; + r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY; - let s = Math.sqrt(b * b + d * d); + const s = Math.sqrt(b * b + d * d); + mat.c = Math.cos(r) * s; mat.d = Math.sin(r) * s; } @@ -205,36 +245,47 @@ export class TransformConstraint implements Updatable { } } - applyAbsoluteLocal () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; + applyAbsoluteLocal() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; - let target = this.target; + const target = this.target; + + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; + const bone = bones[i]; let rotation = bone.arotation; + if (mixRotate != 0) { let r = target.arotation - rotation + this.data.offsetRotation; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; rotation += r * mixRotate; } - let x = bone.ax, y = bone.ay; + let x = bone.ax; + let y = bone.ay; + x += (target.ax - x + this.data.offsetX) * mixX; y += (target.ay - y + this.data.offsetY) * mixY; - let scaleX = bone.ascaleX, scaleY = bone.ascaleY; - if (mixScaleX != 0 && scaleX != 0) - scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * mixScaleX) / scaleX; - if (mixScaleY != 0 && scaleY != 0) - scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * mixScaleY) / scaleY; + let scaleX = bone.ascaleX; + let scaleY = bone.ascaleY; + + if (mixScaleX != 0 && scaleX != 0) scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * mixScaleX) / scaleX; + if (mixScaleY != 0 && scaleY != 0) scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * mixScaleY) / scaleY; let shearY = bone.ashearY; + if (mixShearY != 0) { let r = target.ashearY - shearY + this.data.offsetShearY; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; shearY += r * mixShearY; } @@ -243,22 +294,27 @@ export class TransformConstraint implements Updatable { } } - applyRelativeLocal () { - let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; + applyRelativeLocal() { + const mixRotate = this.mixRotate; + const mixX = this.mixX; + const mixY = this.mixY; + const mixScaleX = this.mixScaleX; + const mixScaleY = this.mixScaleY; + const mixShearY = this.mixShearY; + + const target = this.target; - let target = this.target; + const bones = this.bones; - let bones = this.bones; for (let i = 0, n = bones.length; i < n; i++) { - let bone = bones[i]; - - let rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate; - let x = bone.ax + (target.ax + this.data.offsetX) * mixX; - let y = bone.ay + (target.ay + this.data.offsetY) * mixY; - let scaleX = bone.ascaleX * (((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX) + 1); - let scaleY = bone.ascaleY * (((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY) + 1); - let shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY; + const bone = bones[i]; + + const rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate; + const x = bone.ax + (target.ax + this.data.offsetX) * mixX; + const y = bone.ay + (target.ay + this.data.offsetY) * mixY; + const scaleX = bone.ascaleX * ((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX + 1); + const scaleY = bone.ascaleY * ((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY + 1); + const shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY; bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); } diff --git a/packages/runtime-4.1/src/core/TransformConstraintData.ts b/packages/runtime-4.1/src/core/TransformConstraintData.ts index ae73af62..aef77e78 100644 --- a/packages/runtime-4.1/src/core/TransformConstraintData.ts +++ b/packages/runtime-4.1/src/core/TransformConstraintData.ts @@ -1,5 +1,5 @@ -import {BoneData} from './BoneData'; -import {ConstraintData} from './ConstraintData'; +import type { BoneData } from './BoneData'; +import { ConstraintData } from './ConstraintData'; /** Stores the setup pose for a {@link TransformConstraint}. * @@ -7,15 +7,16 @@ import {ConstraintData} from './ConstraintData'; * @public * */ export class TransformConstraintData extends ConstraintData { - /** The bones that will be modified by this transform constraint. */ bones = new Array(); /** The target bone whose world transform will be copied to the constrained bones. */ private _target: BoneData | null = null; - public set target (boneData: BoneData) { this._target = boneData; } - public get target () { - if (!this._target) throw new Error("BoneData not set.") + public set target(boneData: BoneData) { + this._target = boneData; + } + public get target() { + if (!this._target) throw new Error('BoneData not set.'); else return this._target; } @@ -47,7 +48,7 @@ export class TransformConstraintData extends ConstraintData { relative = false; local = false; - constructor (name: string) { + constructor(name: string) { super(name, 0, false); } } diff --git a/packages/runtime-4.1/src/core/Updatable.ts b/packages/runtime-4.1/src/core/Updatable.ts index 7f2cb492..147656ea 100644 --- a/packages/runtime-4.1/src/core/Updatable.ts +++ b/packages/runtime-4.1/src/core/Updatable.ts @@ -2,11 +2,11 @@ * @public * */ export interface Updatable { - update (): void; + update(): void; /** Returns false when this item has not been updated because a skin is required and the {@link Skeleton#skin active skin} * does not contain this item. * @see Skin#getBones() * @see Skin#getConstraints() */ - isActive (): boolean; + isActive(): boolean; } diff --git a/packages/runtime-4.1/src/core/attachments/Attachment.ts b/packages/runtime-4.1/src/core/attachments/Attachment.ts index 3894b4e7..23c5278c 100644 --- a/packages/runtime-4.1/src/core/attachments/Attachment.ts +++ b/packages/runtime-4.1/src/core/attachments/Attachment.ts @@ -1,7 +1,7 @@ -import {AttachmentType, Utils} from '@pixi-spine/base'; -import type {IAttachment, NumberArrayLike} from '@pixi-spine/base'; +import { AttachmentType, Utils } from '@pixi-spine/base'; +import type { IAttachment, NumberArrayLike } from '@pixi-spine/base'; -import type {Slot} from '../Slot'; +import type { Slot } from '../Slot'; /** * The base class for all attachments. @@ -11,12 +11,12 @@ export abstract class Attachment implements IAttachment { name: string; type: AttachmentType; - constructor (name: string) { - if (!name) throw new Error("name cannot be null."); + constructor(name: string) { + if (!name) throw new Error('name cannot be null.'); this.name = name; } - abstract copy (): Attachment; + abstract copy(): Attachment; } /** @@ -48,7 +48,7 @@ export abstract class VertexAttachment extends Attachment { * May be null if no attachment-specific timelines should be applied. */ timelineAttachment: Attachment = this; - constructor (name: string) { + constructor(name: string) { super(name); } @@ -66,40 +66,57 @@ export abstract class VertexAttachment extends Attachment { * `stride` / 2. * @param offset The `worldVertices` index to begin writing values. * @param stride The number of `worldVertices` entries between the value pairs written. */ - computeWorldVertices (slot: Slot, start: number, count: number, worldVertices: NumberArrayLike, offset: number, stride: number) { + computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: NumberArrayLike, offset: number, stride: number) { count = offset + (count >> 1) * stride; - let skeleton = slot.bone.skeleton; - let deformArray = slot.deform; + const skeleton = slot.bone.skeleton; + const deformArray = slot.deform; let vertices = this.vertices; - let bones = this.bones; + const bones = this.bones; + if (!bones) { if (deformArray.length > 0) vertices = deformArray; - let mat = slot.bone.matrix; - let x = mat.tx; - let y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; + const mat = slot.bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + for (let v = start, w = offset; w < count; v += 2, w += stride) { - let vx = vertices[v], vy = vertices[v + 1]; + const vx = vertices[v]; + const vy = vertices[v + 1]; + worldVertices[w] = vx * a + vy * b + x; worldVertices[w + 1] = vx * c + vy * d + y; } + return; } - let v = 0, skip = 0; + let v = 0; + let skip = 0; + for (let i = 0; i < start; i += 2) { - let n = bones[v]; + const n = bones[v]; + v += n + 1; skip += n; } - let skeletonBones = skeleton.bones; + const skeletonBones = skeleton.bones; + if (deformArray.length == 0) { for (let w = offset, b = skip * 3; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b]; + const vy = vertices[b + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } @@ -107,14 +124,20 @@ export abstract class VertexAttachment extends Attachment { worldVertices[w + 1] = wy; } } else { - let deform = deformArray; + const deform = deformArray; + for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { - let wx = 0, wy = 0; + let wx = 0; + let wy = 0; let n = bones[v++]; + n += v; for (; v < n; v++, b += 3, f += 2) { - let mat = skeletonBones[bones[v]].matrix; - let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; + const mat = skeletonBones[bones[v]].matrix; + const vx = vertices[b] + deform[f]; + const vy = vertices[b + 1] + deform[f + 1]; + const weight = vertices[b + 2]; + wx += (vx * mat.a + vy * mat.c + mat.tx) * weight; wy += (vx * mat.b + vy * mat.d + mat.ty) * weight; } @@ -125,12 +148,11 @@ export abstract class VertexAttachment extends Attachment { } /** Does not copy id (generated) or name (set on construction). **/ - copyTo (attachment: VertexAttachment) { + copyTo(attachment: VertexAttachment) { if (this.bones) { attachment.bones = new Array(this.bones.length); Utils.arrayCopy(this.bones, 0, attachment.bones, 0, this.bones.length); - } else - attachment.bones = null; + } else attachment.bones = null; if (this.vertices) { attachment.vertices = Utils.newFloatArray(this.vertices.length); diff --git a/packages/runtime-4.1/src/core/attachments/AttachmentLoader.ts b/packages/runtime-4.1/src/core/attachments/AttachmentLoader.ts index 8f445513..efe72523 100644 --- a/packages/runtime-4.1/src/core/attachments/AttachmentLoader.ts +++ b/packages/runtime-4.1/src/core/attachments/AttachmentLoader.ts @@ -1,32 +1,31 @@ -import {Skin} from '../Skin'; -import type {RegionAttachment} from './RegionAttachment'; -import type {MeshAttachment} from './MeshAttachment'; -import type {BoundingBoxAttachment} from './BoundingBoxAttachment'; -import type {PathAttachment} from './PathAttachment'; -import type {PointAttachment} from './PointAttachment'; -import type {ClippingAttachment} from './ClippingAttachment'; -import type {Sequence} from './Sequence'; +import type { Skin } from '../Skin'; +import type { RegionAttachment } from './RegionAttachment'; +import type { MeshAttachment } from './MeshAttachment'; +import type { BoundingBoxAttachment } from './BoundingBoxAttachment'; +import type { PathAttachment } from './PathAttachment'; +import type { PointAttachment } from './PointAttachment'; +import type { ClippingAttachment } from './ClippingAttachment'; +import type { Sequence } from './Sequence'; /** * @public */ export interface AttachmentLoader { /** @return May be null to not load an attachment. */ - newRegionAttachment (skin: Skin, name: string, path: string, sequence: Sequence | null): RegionAttachment; + newRegionAttachment(skin: Skin, name: string, path: string, sequence: Sequence | null): RegionAttachment; /** @return May be null to not load an attachment. */ - newMeshAttachment (skin: Skin, name: string, path: string, sequence: Sequence | null): MeshAttachment; + newMeshAttachment(skin: Skin, name: string, path: string, sequence: Sequence | null): MeshAttachment; /** @return May be null to not load an attachment. */ - newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment; + newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment; /** @return May be null to not load an attachment */ - newPathAttachment (skin: Skin, name: string): PathAttachment; + newPathAttachment(skin: Skin, name: string): PathAttachment; /** @return May be null to not load an attachment */ - newPointAttachment (skin: Skin, name: string): PointAttachment; + newPointAttachment(skin: Skin, name: string): PointAttachment; /** @return May be null to not load an attachment */ - newClippingAttachment (skin: Skin, name: string): ClippingAttachment; + newClippingAttachment(skin: Skin, name: string): ClippingAttachment; } - diff --git a/packages/runtime-4.1/src/core/attachments/BoundingBoxAttachment.ts b/packages/runtime-4.1/src/core/attachments/BoundingBoxAttachment.ts index 24500b94..de6247a9 100644 --- a/packages/runtime-4.1/src/core/attachments/BoundingBoxAttachment.ts +++ b/packages/runtime-4.1/src/core/attachments/BoundingBoxAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color} from '@pixi-spine/base'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color } from '@pixi-spine/base'; /** * @public @@ -8,14 +8,16 @@ export class BoundingBoxAttachment extends VertexAttachment { type = AttachmentType.BoundingBox; color = new Color(1, 1, 1, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new BoundingBoxAttachment(this.name); + copy(): Attachment { + const copy = new BoundingBoxAttachment(this.name); + this.copyTo(copy); copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.1/src/core/attachments/ClippingAttachment.ts b/packages/runtime-4.1/src/core/attachments/ClippingAttachment.ts index ae664da4..50da75e7 100644 --- a/packages/runtime-4.1/src/core/attachments/ClippingAttachment.ts +++ b/packages/runtime-4.1/src/core/attachments/ClippingAttachment.ts @@ -1,6 +1,6 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IClippingAttachment} from '@pixi-spine/base'; -import type {SlotData} from '../SlotData'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IClippingAttachment } from '@pixi-spine/base'; +import type { SlotData } from '../SlotData'; /** * @public @@ -16,16 +16,17 @@ export class ClippingAttachment extends VertexAttachment implements IClippingAtt * are not usually rendered at runtime. */ color = new Color(0.2275, 0.2275, 0.8078, 1); // ce3a3aff - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new ClippingAttachment(this.name); + copy(): Attachment { + const copy = new ClippingAttachment(this.name); + this.copyTo(copy); copy.endSlot = this.endSlot; copy.color.setFromColor(this.color); + return copy; } } - diff --git a/packages/runtime-4.1/src/core/attachments/MeshAttachment.ts b/packages/runtime-4.1/src/core/attachments/MeshAttachment.ts index 60693d43..2852aeb0 100644 --- a/packages/runtime-4.1/src/core/attachments/MeshAttachment.ts +++ b/packages/runtime-4.1/src/core/attachments/MeshAttachment.ts @@ -1,8 +1,7 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, IMeshAttachment, IHasTextureRegion, - NumberArrayLike, TextureRegion, Utils} from '@pixi-spine/base'; -import {Sequence} from './Sequence'; -import type {Slot} from '../Slot'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, IMeshAttachment, IHasTextureRegion, NumberArrayLike, TextureRegion, Utils } from '@pixi-spine/base'; +import type { Sequence } from './Sequence'; +import type { Slot } from '../Slot'; /** * @public @@ -25,13 +24,13 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment, color = new Color(1, 1, 1, 1); /** The width of the mesh's image. Available only when nonessential data was exported. */ - width: number = 0; + width = 0; /** The height of the mesh's image. Available only when nonessential data was exported. */ - height: number = 0; + height = 0; /** The number of entries at the beginning of {@link #vertices} that make up the mesh hull. */ - hullLength: number = 0; + hullLength = 0; /** Vertex index pairs describing edges for controling triangulation. Mesh triangles will never cross edges. Only available if * nonessential data was exported. Triangulation is not performed at runtime. */ @@ -43,7 +42,7 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment, tempColor = new Color(0, 0, 0, 0); - constructor (name: string, path: string) { + constructor(name: string, path: string) { super(name); this.path = path; } @@ -51,12 +50,12 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment, /** The parent mesh if this is a linked mesh, else null. A linked mesh shares the {@link #bones}, {@link #vertices}, * {@link #regionUVs}, {@link #triangles}, {@link #hullLength}, {@link #edges}, {@link #width}, and {@link #height} with the * parent mesh, but may have a different {@link #name} or {@link #path} (and therefore a different texture). */ - getParentMesh () { + getParentMesh() { return this.parentMesh; } /** @param parentMesh May be null. */ - setParentMesh (parentMesh: MeshAttachment) { + setParentMesh(parentMesh: MeshAttachment) { this.parentMesh = parentMesh; if (parentMesh) { this.bones = parentMesh.bones; @@ -65,14 +64,15 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment, this.regionUVs = parentMesh.regionUVs; this.triangles = parentMesh.triangles; this.hullLength = parentMesh.hullLength; - this.worldVerticesLength = parentMesh.worldVerticesLength + this.worldVerticesLength = parentMesh.worldVerticesLength; } } - copy (): Attachment { + copy(): Attachment { if (this.parentMesh) return this.newLinkedMesh(); - let copy = new MeshAttachment(this.name, this.path); + const copy = new MeshAttachment(this.name, this.path); + copy.region = this.region; copy.color.setFromColor(this.color); @@ -96,19 +96,21 @@ export class MeshAttachment extends VertexAttachment implements IMeshAttachment, return copy; } - computeWorldVertices (slot: Slot, start: number, count: number, worldVertices: NumberArrayLike, offset: number, stride: number) { + computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: NumberArrayLike, offset: number, stride: number) { if (this.sequence != null) this.sequence.apply(slot, this); super.computeWorldVertices(slot, start, count, worldVertices, offset, stride); } /** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. **/ - newLinkedMesh (): MeshAttachment { - let copy = new MeshAttachment(this.name, this.path); + newLinkedMesh(): MeshAttachment { + const copy = new MeshAttachment(this.name, this.path); + copy.region = this.region; copy.color.setFromColor(this.color); copy.timelineAttachment = this.timelineAttachment; copy.setParentMesh(this.parentMesh ? this.parentMesh : this); // if (copy.region != null) copy.updateRegion(); + return copy; } } diff --git a/packages/runtime-4.1/src/core/attachments/PathAttachment.ts b/packages/runtime-4.1/src/core/attachments/PathAttachment.ts index 82a6c336..3e1442f5 100644 --- a/packages/runtime-4.1/src/core/attachments/PathAttachment.ts +++ b/packages/runtime-4.1/src/core/attachments/PathAttachment.ts @@ -1,5 +1,5 @@ -import {Attachment, VertexAttachment} from "./Attachment"; -import {AttachmentType, Color, Utils} from "@pixi-spine/base"; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, Utils } from '@pixi-spine/base'; /** * @public @@ -21,18 +21,20 @@ export class PathAttachment extends VertexAttachment { * rendered at runtime. */ color = new Color(1, 1, 1, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - copy (): Attachment { - let copy = new PathAttachment(this.name); + copy(): Attachment { + const copy = new PathAttachment(this.name); + this.copyTo(copy); copy.lengths = new Array(this.lengths.length); Utils.arrayCopy(this.lengths, 0, copy.lengths, 0, this.lengths.length); copy.closed = closed; copy.constantSpeed = this.constantSpeed; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.1/src/core/attachments/PointAttachment.ts b/packages/runtime-4.1/src/core/attachments/PointAttachment.ts index 5512c0e3..972f48bd 100644 --- a/packages/runtime-4.1/src/core/attachments/PointAttachment.ts +++ b/packages/runtime-4.1/src/core/attachments/PointAttachment.ts @@ -1,6 +1,6 @@ -import {Attachment, VertexAttachment} from './Attachment'; -import {AttachmentType, Color, MathUtils, Vector2} from "@pixi-spine/base"; -import type {Bone} from '../Bone'; +import { Attachment, VertexAttachment } from './Attachment'; +import { AttachmentType, Color, MathUtils, Vector2 } from '@pixi-spine/base'; +import type { Bone } from '../Bone'; /** * @public @@ -8,39 +8,45 @@ import type {Bone} from '../Bone'; export class PointAttachment extends VertexAttachment { type = AttachmentType.Point; - x: number = 0; - y: number = 0; - rotation: number = 0; + x = 0; + y = 0; + rotation = 0; /** The color of the point attachment as it was in Spine. Available only when nonessential data was exported. Point attachments * are not usually rendered at runtime. */ color = new Color(0.38, 0.94, 0, 1); - constructor (name: string) { + constructor(name: string) { super(name); } - computeWorldPosition (bone: Bone, point: Vector2) { + computeWorldPosition(bone: Bone, point: Vector2) { const mat = bone.matrix; + point.x = this.x * mat.a + this.y * mat.c + bone.worldX; point.y = this.x * mat.b + this.y * mat.d + bone.worldY; + return point; } - computeWorldRotation (bone: Bone) { + computeWorldRotation(bone: Bone) { const mat = bone.matrix; - let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation); - let x = cos * mat.a + sin * mat.c; - let y = cos * mat.b + sin * mat.d; + const cos = MathUtils.cosDeg(this.rotation); + const sin = MathUtils.sinDeg(this.rotation); + const x = cos * mat.a + sin * mat.c; + const y = cos * mat.b + sin * mat.d; + return Math.atan2(y, x) * MathUtils.radDeg; } - copy (): Attachment { - let copy = new PointAttachment(this.name); + copy(): Attachment { + const copy = new PointAttachment(this.name); + copy.x = this.x; copy.y = this.y; copy.rotation = this.rotation; copy.color.setFromColor(this.color); + return copy; } } diff --git a/packages/runtime-4.1/src/core/attachments/RegionAttachment.ts b/packages/runtime-4.1/src/core/attachments/RegionAttachment.ts index 8fc16b44..2b73352f 100644 --- a/packages/runtime-4.1/src/core/attachments/RegionAttachment.ts +++ b/packages/runtime-4.1/src/core/attachments/RegionAttachment.ts @@ -1,8 +1,7 @@ -import {Attachment} from './Attachment'; -import {AttachmentType, NumberArrayLike, Color, TextureRegion, Utils, - IHasTextureRegion, IRegionAttachment} from "@pixi-spine/base"; -import {Sequence} from './Sequence'; -import type {Slot} from '../Slot'; +import { Attachment } from './Attachment'; +import { AttachmentType, NumberArrayLike, Color, TextureRegion, Utils, IHasTextureRegion, IRegionAttachment } from '@pixi-spine/base'; +import type { Sequence } from './Sequence'; +import type { Slot } from '../Slot'; /** * @public @@ -50,34 +49,36 @@ export class RegionAttachment extends Attachment implements IRegionAttachment, I tempColor = new Color(1, 1, 1, 1); - constructor (name: string, path: string) { + constructor(name: string, path: string) { super(name); this.path = path; } /** Calculates the {@link #offset} using the region settings. Must be called after changing region settings. */ - updateRegion (): void { - if (!this.region) throw new Error("Region not set."); - let region = this.region; - let regionScaleX = this.width / this.region.originalWidth * this.scaleX; - let regionScaleY = this.height / this.region.originalHeight * this.scaleY; - let localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX; - let localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY; - let localX2 = localX + this.region.width * regionScaleX; - let localY2 = localY + this.region.height * regionScaleY; - let radians = this.rotation * Math.PI / 180; - let cos = Math.cos(radians); - let sin = Math.sin(radians); - let x = this.x, y = this.y; - let localXCos = localX * cos + x; - let localXSin = localX * sin; - let localYCos = localY * cos + y; - let localYSin = localY * sin; - let localX2Cos = localX2 * cos + x; - let localX2Sin = localX2 * sin; - let localY2Cos = localY2 * cos + y; - let localY2Sin = localY2 * sin; - let offset = this.offset; + updateRegion(): void { + if (!this.region) throw new Error('Region not set.'); + const region = this.region; + const regionScaleX = (this.width / this.region.originalWidth) * this.scaleX; + const regionScaleY = (this.height / this.region.originalHeight) * this.scaleY; + const localX = (-this.width / 2) * this.scaleX + this.region.offsetX * regionScaleX; + const localY = (-this.height / 2) * this.scaleY + this.region.offsetY * regionScaleY; + const localX2 = localX + this.region.width * regionScaleX; + const localY2 = localY + this.region.height * regionScaleY; + const radians = (this.rotation * Math.PI) / 180; + const cos = Math.cos(radians); + const sin = Math.sin(radians); + const x = this.x; + const y = this.y; + const localXCos = localX * cos + x; + const localXSin = localX * sin; + const localYCos = localY * cos + y; + const localYSin = localY * sin; + const localX2Cos = localX2 * cos + x; + const localX2Sin = localX2 * sin; + const localY2Cos = localY2 * cos + y; + const localY2Sin = localY2 * sin; + const offset = this.offset; + offset[0] = localXCos - localYSin; offset[1] = localYCos + localXSin; offset[2] = localXCos - localY2Sin; @@ -87,7 +88,8 @@ export class RegionAttachment extends Attachment implements IRegionAttachment, I offset[6] = localX2Cos - localYSin; offset[7] = localYCos + localX2Sin; - let uvs = this.uvs; + const uvs = this.uvs; + if (region.degrees == 90) { uvs[2] = region.u; uvs[3] = region.v2; @@ -117,16 +119,20 @@ export class RegionAttachment extends Attachment implements IRegionAttachment, I * @param worldVertices The output world vertices. Must have a length >= offset + 8. * @param offset The worldVertices index to begin writing values. * @param stride The number of worldVertices entries between the value pairs written. */ - computeWorldVertices (slot: Slot, worldVertices: NumberArrayLike, offset: number, stride: number) { - if (this.sequence != null) - this.sequence.apply(slot, this); - - let bone = slot.bone; - let vertexOffset = this.offset; - let mat = bone.matrix; - let x = mat.tx, y = mat.ty; - let a = mat.a, b = mat.c, c = mat.b, d = mat.d; - let offsetX = 0, offsetY = 0; + computeWorldVertices(slot: Slot, worldVertices: NumberArrayLike, offset: number, stride: number) { + if (this.sequence != null) this.sequence.apply(slot, this); + + const bone = slot.bone; + const vertexOffset = this.offset; + const mat = bone.matrix; + const x = mat.tx; + const y = mat.ty; + const a = mat.a; + const b = mat.c; + const c = mat.b; + const d = mat.d; + let offsetX = 0; + let offsetY = 0; offsetX = vertexOffset[0]; offsetY = vertexOffset[1]; @@ -152,8 +158,9 @@ export class RegionAttachment extends Attachment implements IRegionAttachment, I worldVertices[offset + 1] = offsetX * c + offsetY * d + y; } - copy (): Attachment { - let copy = new RegionAttachment(this.name, this.path); + copy(): Attachment { + const copy = new RegionAttachment(this.name, this.path); + copy.region = this.region; copy.rendererObject = this.rendererObject; copy.x = this.x; @@ -167,6 +174,7 @@ export class RegionAttachment extends Attachment implements IRegionAttachment, I Utils.arrayCopy(this.offset, 0, copy.offset, 0, 8); copy.color.setFromColor(this.color); copy.sequence = this.sequence != null ? this.sequence.copy() : null; + return copy; } diff --git a/packages/runtime-4.1/src/core/attachments/Sequence.ts b/packages/runtime-4.1/src/core/attachments/Sequence.ts index b2acbeab..80ab04ec 100644 --- a/packages/runtime-4.1/src/core/attachments/Sequence.ts +++ b/packages/runtime-4.1/src/core/attachments/Sequence.ts @@ -1,79 +1,84 @@ import { Utils, TextureRegion, IHasTextureRegion, ISequence } from '@pixi-spine/base'; -import { Slot } from "../Slot"; +import type { Slot } from '../Slot'; /** * @public */ export class Sequence implements ISequence { - private static _nextID = 0; + private static _nextID = 0; - id = Sequence.nextID(); - regions: TextureRegion[]; - start = 0; - digits = 0; - /** The index of the region to show for the setup pose. */ - setupIndex = 0; + id = Sequence.nextID(); + regions: TextureRegion[]; + start = 0; + digits = 0; + /** The index of the region to show for the setup pose. */ + setupIndex = 0; - constructor (count: number) { - this.regions = new Array(count); - } + constructor(count: number) { + this.regions = new Array(count); + } - copy (): Sequence { - let copy = new Sequence(this.regions.length); - Utils.arrayCopy(this.regions, 0, copy.regions, 0, this.regions.length); - copy.start = this.start; - copy.digits = this.digits; - copy.setupIndex = this.setupIndex; - return copy; - } + copy(): Sequence { + const copy = new Sequence(this.regions.length); - apply (slot: Slot, attachment: IHasTextureRegion) { - let index = slot.sequenceIndex; - if (index == -1) index = this.setupIndex; - if (index >= this.regions.length) index = this.regions.length - 1; - let region = this.regions[index]; - if (attachment.region != region) { - attachment.region = region; - // attachment.updateRegion(); - } - } + Utils.arrayCopy(this.regions, 0, copy.regions, 0, this.regions.length); + copy.start = this.start; + copy.digits = this.digits; + copy.setupIndex = this.setupIndex; - getPath (basePath: string, index: number): string { - let result = basePath; - let frame = (this.start + index).toString(); - for (let i = this.digits - frame.length; i > 0; i--) - result += "0"; - result += frame; - return result; - } + return copy; + } - private static nextID (): number { - return Sequence._nextID++; - } + apply(slot: Slot, attachment: IHasTextureRegion) { + let index = slot.sequenceIndex; + + if (index == -1) index = this.setupIndex; + if (index >= this.regions.length) index = this.regions.length - 1; + const region = this.regions[index]; + + if (attachment.region != region) { + attachment.region = region; + // attachment.updateRegion(); + } + } + + getPath(basePath: string, index: number): string { + let result = basePath; + const frame = (this.start + index).toString(); + + for (let i = this.digits - frame.length; i > 0; i--) result += '0'; + result += frame; + + return result; + } + + private static nextID(): number { + return Sequence._nextID++; + } } /** * @public */ export enum SequenceMode { - hold = 0, - once = 1, - loop = 2, - pingpong = 3, - onceReverse = 4, - loopReverse = 5, - pingpongReverse = 6 + hold = 0, + once = 1, + loop = 2, + pingpong = 3, + onceReverse = 4, + loopReverse = 5, + pingpongReverse = 6, } /** * @public */ export const SequenceModeValues = [ - SequenceMode.hold, - SequenceMode.once, - SequenceMode.loop, - SequenceMode.pingpong, - SequenceMode.onceReverse, - SequenceMode.loopReverse, - SequenceMode.pingpongReverse + SequenceMode.hold, + SequenceMode.once, + SequenceMode.loop, + SequenceMode.pingpong, + SequenceMode.onceReverse, + SequenceMode.loopReverse, + SequenceMode.pingpongReverse, ]; diff --git a/rush.json b/rush.json deleted file mode 100644 index f842c1ab..00000000 --- a/rush.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", - "rushVersion": "5.43.0", - "pnpmVersion": "4.14.4", - "pnpmOptions": { - "pnpmStore": "global", - "strictPeerDependencies": false, - "resolutionStrategy": "fast" - }, - "nodeSupportedVersionRange": ">=10.0.0 <18.0.0", - "suppressNodeLtsWarning": true, - "repository": { - "url": "https://github.com/pixijs/pixi-spine", - "defaultBranch": "master", - "defaultRemote": "origin" - }, - "eventHooks": { - "preRushInstall": [], - "postRushInstall": [], - "preRushBuild": [], - "postRushBuild": [] - }, - "projects": [ - { - "packageName": "@pixi-spine/base", - "projectFolder": "packages/base", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/loader-base", - "projectFolder": "packages/loader-base", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/runtime-3.8", - "projectFolder": "packages/runtime-3.8", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/runtime-3.7", - "projectFolder": "packages/runtime-3.7", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/runtime-4.0", - "projectFolder": "packages/runtime-4.0", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/runtime-4.1", - "projectFolder": "packages/runtime-4.1", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/loader-3.8", - "projectFolder": "packages/loader-3.8", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/loader-4.0", - "projectFolder": "packages/loader-4.0", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/loader-4.1", - "projectFolder": "packages/loader-4.1", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/loader-uni", - "projectFolder": "packages/loader-uni", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "pixi-spine", - "projectFolder": "bundles/pixi-spine", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/all-3.8", - "projectFolder": "bundles/all-3.8", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/all-4.0", - "projectFolder": "bundles/all-4.0", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/all-4.1", - "projectFolder": "bundles/all-4.1", - "reviewCategory": "production", - "versionPolicyName": "PixiSpine" - }, - { - "packageName": "@pixi-spine/eslint-config", - "projectFolder": "tools/eslint-config", - "reviewCategory": "production", - "shouldPublish": false - }, - { - "packageName": "@pixi-spine/rollup-config", - "projectFolder": "tools/rollup-config", - "reviewCategory": "production", - "shouldPublish": false - } - ] -} diff --git a/packages/base/scripts/injectGlobalMixins.js b/scripts/injectGlobalMixins.js similarity index 100% rename from packages/base/scripts/injectGlobalMixins.js rename to scripts/injectGlobalMixins.js diff --git a/tools/eslint-config/index.js b/tools/eslint-config/index.js deleted file mode 100644 index bd0c2c55..00000000 --- a/tools/eslint-config/index.js +++ /dev/null @@ -1,31 +0,0 @@ -require('@rushstack/eslint-patch/modern-module-resolution'); - -module.exports = { - "env": { - "browser": true, - "es6": true - }, - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "rules": { - "@typescript-eslint/triple-slash-reference": [ - "off" - ], - "spaced-comment": [ - "off" - ], - "no-duplicate-imports": [ - "off" - ] - }, - "extends": [ - "@pixi/eslint-config" - ] -}; \ No newline at end of file diff --git a/tools/eslint-config/package.json b/tools/eslint-config/package.json deleted file mode 100644 index 1117fe32..00000000 --- a/tools/eslint-config/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "@pixi-spine/eslint-config", - "version": "1.0.0", - "description": "ESLint config for pixi-spine", - "main": "index.js", - "scripts": { - "build": "echo @pixi-spine/eslint-config does not build!", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/pixijs/pixi-spine.git" - }, - "keywords": [ - "pixi-spine", - "eslint-config" - ], - "author": "Ivan Igorevich Popelyshev ", - "bugs": { - "url": "https://github.com/pixijs/pixi-spine/issues" - }, - "homepage": "https://github.com/pixijs/pixi-spine/#readme", - "dependencies": { - "@rushstack/eslint-patch": "~1.0.3", - "@pixi/eslint-config": "~1.0.1", - "@typescript-eslint/parser": "~3.9.1", - "@typescript-eslint/eslint-plugin": "~3.9.1" - } -} diff --git a/tools/rollup-config/index.mjs b/tools/rollup-config/index.mjs new file mode 100644 index 00000000..fadacc37 --- /dev/null +++ b/tools/rollup-config/index.mjs @@ -0,0 +1,184 @@ +// from https://github.com/pixijs/extension-scripts, adapted to this monorepo + +import path from 'node:path'; +import rename from '@pixi/rollup-plugin-rename-node-modules'; +import esbuild from 'rollup-plugin-esbuild'; +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import { string } from "rollup-plugin-string"; +import replace from '@rollup/plugin-replace'; + +// function that eats arguments and spits rollup configs +export default (extensionConfig, pkg) => { + + const compiled = (new Date()).toUTCString().replace(/GMT/g, 'UTC'); + const banner = [ + `/*!`, + ` * ${pkg.name} - v${pkg.version}`, + ` * Compiled ${compiled}`, + ` *`, + ` * ${pkg.name} is licensed under the MIT License.`, + ` * http://www.opensource.org/licenses/mit-license`, + ` * `, + ` * Copyright ${(new Date()).getFullYear()}, ${pkg.author}, All Rights Reserved`, + ` */`, + ].join('\n'); + + // External dependencies, not bundled + const externalBrowser = [] + .concat(Object.keys(pkg.peerDependencies || {})); + + const externalNpm = [] + .concat(Object.keys(pkg.peerDependencies || {})) + .concat(Object.keys(pkg.dependencies || {})); + + const builtInPackages = [ + 'accessibility', + 'app', + 'assets', + 'basis', + 'canvas-display', + 'canvas-extract', + 'canvas-graphics', + 'canvas-mesh', + 'canvas-particle-container', + 'canvas-renderer', + 'canvas-prepare', + 'canvas-sprite', + 'canvas-sprite-tiling', + 'canvas-text', + 'compressed-textures', + 'core', + 'display', + 'events', + 'extensions', + 'extract', + 'graphics-extras', + 'graphics', + 'math-extras', + 'math', + 'mesh-extras', + 'mesh', + 'mixin-cache-as-bitmap', + 'mixin-get-child-by-name', + 'mixin-get-global-position', + 'particle-container', + 'prepare', + 'runner', + 'settings', + 'sprite-animated', + 'sprite-tiling', + 'sprite', + 'spritesheet', + 'text-bitmap', + 'text', + 'ticker', + 'unsafe-eval', + ].reduce((acc, name) => ({ ...acc, [`@pixi/${name}`]: 'PIXI' }), {}); + + // Plugins for module and browser output + const plugins = [ + commonjs(), + resolve(), + string({ + include: [ + '**/*.frag', + '**/*.vert', + ], + }), + replace({ + __VERSION__: pkg.version, + }), + ]; + + // These are the PixiJS built-in default globals + // for the browser bundle when referencing other core packages + const globals = { + '@pixi/utils': 'PIXI.utils', + '@pixi/filter-alpha': 'PIXI.filters', + '@pixi/filter-blur': 'PIXI.filters', + '@pixi/filter-color-matrix': 'PIXI.filters', + '@pixi/filter-displacement': 'PIXI.filters', + '@pixi/filter-fxaa': 'PIXI.filters', + '@pixi/filter-noise': 'PIXI.filters', + ...builtInPackages, + ...extensionConfig.globals, + }; + + const source = pkg.source ?? 'src/index.ts'; + const basePath = path.dirname(path.join(process.cwd(), source)); + const bundle = path.join(process.cwd(), extensionConfig.bundle); + const bundleModule = path.join(process.cwd(), extensionConfig.bundleModule); + const mainDir = path.dirname(path.join(process.cwd(), pkg.main)); + const moduleDir = path.dirname(path.join(process.cwd(), pkg.module)); + let namespace = extensionConfig.namespace; + let footer; + + // If we're adding to the main PIXI namespace, we need to + // make sure we don't override the PIXI global, so we'll do this + // to insert the output of the extension into the PIXI global + if (namespace === 'PIXI') { + namespace = pkg.name.replace(/[^a-z-]/ig, '_').replace(/-/g, ''); + footer = `Object.assign(PIXI, ${namespace});`; + } + + return [ + { + plugins: [ + ...plugins, + rename(), + esbuild({ target: 'ES2020' }) + ], + external: externalNpm, + input: source, + output: [ + { + dir: mainDir, + entryFileNames: '[name].js', + format: 'cjs', + preserveModules: true, + preserveModulesRoot: basePath, + sourcemap: true, + exports: 'named', + }, + { + dir: moduleDir, + entryFileNames: '[name].mjs', + format: 'esm', + preserveModules: true, + preserveModulesRoot: basePath, + sourcemap: true, + exports: 'named', + } + ], + }, + { + plugins: [...plugins, esbuild({ + target: 'ES2017', + minify: true, + })], + external: externalBrowser, + input: source, + treeshake: false, + output: [ + { + banner, + file: bundle, + format: 'iife', + name: namespace, + footer, + sourcemap: true, + globals, + exports: 'named', + }, + { + banner, + file: bundleModule, + format: 'esm', + sourcemap: true, + exports: 'named', + } + ], + } + ]; +} \ No newline at end of file diff --git a/tools/rollup-config/main.js b/tools/rollup-config/main.js index 05f570e6..85e0baa8 100644 --- a/tools/rollup-config/main.js +++ b/tools/rollup-config/main.js @@ -6,6 +6,7 @@ const {globals} = require('@pixi-build-tools/globals'); const resolve = require('rollup-plugin-node-resolve'); const string = require('rollup-plugin-string').string; + const sourcemaps = require('rollup-plugin-sourcemaps'); const commonjs = require('@rollup/plugin-commonjs'); const replace = require('rollup-plugin-replace'); diff --git a/tools/rollup-config/package.json b/tools/rollup-config/package.json index ba238c61..f6bc86ca 100644 --- a/tools/rollup-config/package.json +++ b/tools/rollup-config/package.json @@ -2,32 +2,23 @@ "name": "@pixi-spine/rollup-config", "version": "1.0.0", "description": "ESLint config for pixi-spine", - "main": "index.js", + "main": "index.mjs", + "type": "module", + "private": true, "scripts": { - "build": "echo @pixi-spine/rollup-config does not build!", - "test": "echo \"Error: no test specified\" && exit 1" + "build": "echo @pixi-spine/rollup-config does not build!" }, "repository": { "type": "git", "url": "git+https://github.com/pixijs/pixi-spine.git" }, "keywords": [ - "pixi-spine", - "eslint-config" + "pixi-spine" ], "author": "Ivan Igorevich Popelyshev ", "bugs": { "url": "https://github.com/pixijs/pixi-spine/issues" }, "homepage": "https://github.com/pixijs/pixi-spine/#readme", - "dependencies": { - "@pixi-build-tools/globals": "^1.0.6", - "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-string": "^3.0.0", - "rollup-plugin-sourcemaps": "^0.6.3", - "@rollup/plugin-commonjs": "^19.0.1", - "rollup-plugin-replace": "^2.2.0", - "rollup-plugin-terser": "^7.0.2", - "@rollup/plugin-typescript": "^8.2.3" - } -} + "dependencies": {} +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index b303f00f..d9ad1461 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es6", "allowJs": true, "removeComments": false, "strict": true, @@ -16,7 +16,11 @@ "declaration": false, "declarationMap": false, "allowSyntheticDefaultImports": true, - "lib": ["es6", "dom"], + "importsNotUsedAsValues": "error", + "lib": [ + "es6", + "dom" + ], }, "exclude": [ "node_modules", @@ -30,4 +34,4 @@ "*.vert", "*.frag" ] -} +} \ No newline at end of file